- Published on
๐ ์ปฌ๋ฌ๋ฆฌ์คํธ ํ๋ก์ ํธ #12 Pallete table - resizable columns
- ๊ธ์ด์ด
๐ ๋ชฉ์ฐจ
๐๐ป
- ์ ํ์นธ ํ ์ด๋ธ width ์กฐ์ ํ๊ฒ ํด๋ณด๊ธฐ -'resizable columns' 1-1. ๋ค์ ๋ง๋๋ resizable columns
์ ํ์นธ ํ ์ด๋ธ width ์กฐ์ ํ๊ฒ ํด๋ณด๊ธฐ
์๋๋ฆฌ์ค
Scenario: ์ ํ์นธ์ ๊ฐ๋ก ๊ธธ์ด๋ฅผ ๋ง์๋๋ก ๋๋ฆฌ๊ฑฐ๋ ์ค์ผ ์ ์๋ค
Given ์ ํ์นธ์ ๋ ๋ํ๊ณ
When ์ํ๋ ์นธ์ ์ ํํด์ ์ก์๋น๊ธฐ๋ฉด
Then ์ ํ์นธ์ ์ค์ด๊ฑฐ๋ ๋์ผ ์ ์๋ค
1-1. ๋ค์ ๋ง๋๋ resizable columns
์ปฌ๋ฌ๋ฆฌ์คํธ์ 2๊ต์ ์ํ์
์์ข
์ด๋ฅผ ๋ค์ํ ๋น์จ๋ก ์๋ผ
๋ฌธ์ ์ ๋ง๋ ์กฐํ๋ก์ด ๋ฐฐ์์ ํํํ๋ ์ํ์
๋๋ค.
๋ณดํต ์๋์ ๊ฐ์ ๋ชจ์ต์ผ๋ก ์์ฑ๋๋๋ฐ์.
์ด๋ ๊ฒ ์ฌ๋ฌ ์์ ์ข
์ด๋ฅผ ๋ค์ํ ๋๋น๋ก ์๋ผ ๋ถ์ด๋ ๊ณผ์ ์
์น์์๋ ํ ์ ์๋๋ก
์ฌ์ด์ฆ๋ฅผ ์กฐ์ ํ ์ ์๋ ํ
์ด๋ธ ์ปฌ๋ผ์ ๋ง๋ค๋ ค๊ณ ํฉ๋๋ค..!
๊ทธ๋ผ ๋จผ์ ํ
์ด๋ธ์ ๋ง๋ค๊ธฐ ์ ์กฐ๊ฑด๋ค์ ํ์ธํด์ผ ํ๋๋ฐ์,
์ผ๋จ ์ ์ฒด ๋ฐ์ค์ ํฌ๊ธฐ๋ ๋ฐ์์ ์ฃผ์
ํ ์ ์์ด์ผ ํฉ๋๋ค.
์๋ฅผ ๋ค์ด ์ ์ฒด ํฌ๊ธฐ๋ฅผ 500ํฝ์
์ง๋ฆฌ ๋ฐ์ค๋ก ๋ง๋ค์ด ๋ณผ๊ฒ์.
๋ฐ์ค๋ฅผ 1/4, 1/4, 1/2์ ๋น์จ์ ๊ฐ์ง ์ธ ๊ฐ์ ์นธ์ผ๋ก ๋๋์ด๋ด ๋๋ค.
์ฌ๊ธฐ์ ์ด๋ก ๋ฐ์ค์ ์ค๋ฅธ์ชฝ ๋ชจ์๋ฆฌ๋ฅผ ํด๋ฆญํ๋ฉด?
๊ฐ๋งํ ์์๋ ๋ฐ์ค์ ์ํ๋ฅผ โ๋ณ๊ฒฝ ์ค ๋ชจ๋โ๋ก ๋ฐ๊พธ๊ณ
ํด๋ฆญ์ ํ ์๊ฐ์ ๋ชจ์๋ฆฌ x์ขํ๋ฅผ โ์์ ์ขํโ๋ก ๊ธฐ๋กํด์ผํฉ๋๋ค!
๊ทธ๋์ผ ํด๋ฆญ์ ํ์ง ์๋ ์๊ฐ์๋ ๋ฐ์ค ๋๋น ๋ณ๊ฒฝ์ด ๋์ง ์๊ณ ,
๋ ๋ฐ์ค ๋ชจ์๋ฆฌ์ ์์น๊ฐ ๋ฐ๋์์ ๋ ๋ฐ๋ ์ขํ์ โ์์ ์ขํโ๋ฅผ ๋น๊ตํด์
๋ชจ์๋ฆฌ๊ฐ ์์ง์ธ ๊ฑฐ๋ฆฌ๋ฅผ ๊ตฌํด ๋ฐ์ค ๋๋น๋ฅผ ๋ณ๊ฒฝํ ์ ์๊ธฐ ๋๋ฌธ์ด์ฃ !
์ด๋ก ๋ฐ์ค์ ์ค๋ฅธ์ชฝ ๋ชจ์๋ฆฌ๋ฅผ ํด๋ฆญํ ๋ค์ ๋๋๊ทธ๋ก ๋ง์ฐ์ค๋ฅผ ์์ง์ด๋ฉด?
๋ฐ๋ ์ด๋ก ์์์ ์ค๋ฅธ์ชฝ ๋ชจ์๋ฆฌ x ์ขํ ๊ฐ์ ์์์ผํ๊ณ ,
์ฒ์ โ์์ ์ขํโ์ โ๋ฐ๋ ์ขํโ ์ฌ์ด์ ์ฆ๊ฐ๊ฐ = โ๋ ์ขํ ๊ฐ ๊ฑฐ๋ฆฌโ๋ ์์์ผ ํฉ๋๋ค.
-> ์ด๋ก ์์๊ฐ ์ปค์ง๋ฉด ๋ ์ขํ ์ฌ์ด์ ๊ฑฐ๋ฆฌ๋ ์์(+)
์ด๊ณ
-> ์ด๋ก ์์๊ฐ ์ค์ด๋ค๋ฉด ๋ ์ขํ ์ฌ์ด์ ๊ฑฐ๋ฆฌ๋ ์์(-)
๊ฒ ์ฃ ?
์ด ๋ 2๊ฐ์ง ๋ ์ ๊ฒฝ์จ์ผ ํ๋ ์ฌํญ์ด ์์ต๋๋ค.
์ฒซ๋ฒ์งธ๋, ๋ง์ฐ์ค๋ฅผ ํด๋ฆญํ ํ ์์ง์ด๋ ์ํฉ(๋ณ๊ฒฝ ์ค ๋ชจ๋)
์์
๋ฆฌ์กํธ์ useState
๋ฅผ ์ฌ์ฉํ๋ฉด ~๋ฐฐ์ด์ ๋ณ๊ฒฝ ์ค ๋ชจ๋ ์ํ๋ฅผ ๋ฃ์ด์ฃผ์ด์ผ ํฉ๋๋ค.
๊ทธ๋ณด๋ค ๋จผ์ , ํด๋ฆญ ํ ๋๋๊ทธ๋ฅผ ํ ํ ๋ง์ฐ์ค๋ฅผ ๋์ผ๋ฉด(mouseUp)
๋์ด์ ๋ณ๊ฒฝ์ด ๋์ง ์๋๋ก
์ํ๊ฐ์ false
๋ก ๋ฐ๊ฟ์ฃผ์ด์ผ ํ๊ตฌ์. ๊ทธ ๋ค์ ๋ณ๊ฒฝ ์ค ๋ชจ๋๋ฅผ ๋ํ๋ด๋ ์ํ์ธ โisChangingโ์
์์กด์ฑ ๋ฐฐ์ด์ ๋ฃ์ด์ฃผ๋ฉด ๋๋๊ทธ๊ฐ ๋๋ฌ์ ๋๋ ๋ ์ด์ ์ด๋ก ์์์ ๋๋น๊ฐ ๋ณ๊ฒฝ๋์ง ์์ต๋๋ค.
๋๋ฒ์งธ๋, ๋ง์ฐ์ค๋ฅผ ์์ง์ผ ์ ์๋ ์ต๋ ๊ฑฐ๋ฆฌ์ ์ต์ ๊ฑฐ๋ฆฌ๋ฅผ ๋ฐ์ ธ์ฃผ์ด์ผ ํ๋ ๊ฒ์ธ๋ฐ์.
๋ง์ฝ ์ด ๋ ๊ฑฐ๋ฆฌ๋ฅผ ์ ํด์ฃผ์ง ์๋๋ค๋ฉด, ์ด๋ก ์์์ ํฌ๊ธฐ๊ฐ ํ๋ ์์๋ฅผ ์นจ๋ฒํด ์ผ์ผ๋ฒ๋ฆฌ๊ฑฐ๋
๋
ธ๋ ์์๊ฐ ์ผ์ผ์ง ์ ์์ต๋๋ค.
์ฐ๋ฆฌ๋ ์ด๋ฏธ ๋ง๋ค์ด๋์ ์์๋ ์ผ์ผ์ง๊ธฐ๋ฅผ ๋ฐ๋ผ์ง ์๊ธฐ ๋๋ฌธ์,
ํ ์์๊ฐ ๊ฐ์ง ์ ์๋ ์ต๋ ๊ฑฐ๋ฆฌ์ ์ต์ ๊ฑฐ๋ฆฌ๋ฅผ ์ ํด์ค ๊ฑฐ์์.
์ผ๋จ ํ ์์๊ฐ ๊ฐ์ง ์ ์๋ ์ต์ ๋๋น๋ฅผ 8px๋ก ์ ํ๊ฒ ์ต๋๋ค.
๊ทธ๋ผ ์์ ์ด๋ก ์์๊ฐ ์ค๋ฅธ์ชฝ์ผ๋ก ์ญ์ฑ ๋์ด๋ฌ์ ๋
์ด๋ก ์์์ ์ต๋ ๋๋น๋ ๋ช์ด ๋ ๊น์?
๊ธฐ์กด์ ์ด๋ก ์์๊ฐ ๊ฐ์ง ๋๋น์ ํ๋ ์์๊ฐ ๊ฐ์ง ๋๋น์ ์ดํฉ์์
ํ๋ ์์๊ฐ ๊ฐ์ ธ์ผํ๋ ์ต์ ๋๋น์ธ 8px
์ ๋บ ๊ฐ์ด ๋๊ฒ ์ฃ .
๋ฐ๋๋ก ์ด๋ก ์์์ ์ต์ ๋๋น๋ ๊ทธ๋ผ ๋ช์ด ๋ ๊น์?
์ฐ๋ฆฌ๊ฐ ์ ํด๋ ์์์ ์ต์ ๋๋น 8px
์ด ๋๊ฒ ์ฃ .
์ด๊ฑธ ์ฝ๋๋ก ํํํ์๋ฉด ์ด๋ ๊ฒ ๋ฉ๋๋ค.
if (isChanging) {
const diff = e.clientX - startX
const left = oldSegments[changingIndex]
const right = oldSegments[changingIndex + 1]
const maxRatio = left + right - 8
const diffAsRatio = diff / width
const newLeft = Math.max(Math.min(left + diffAsRatio, maxRatio), 8)
const newRight = Math.max(Math.min(right - diffAsRatio, maxRatio), 8)
const newSegments = [...oldSegments]
newSegments[changingIndex] = newLeft
newSegments[changingIndex + 1] = newRight
setSegments(newSegments)
console.log(newSegments)
}
์ฝ๋
import React, { useEffect, useState } from 'react'
const width = 512
// ๊ตฌ์กฐ ๋ถํด ํ ๋น
// https://beta.reactjs.org/learn/passing-props-to-a-component
function ResizableBoxes({
colors,
deleteSelected,
}: {
colors: string[]
deleteSelected: (targetIndex: number) => void
}) {
const BOX_COUNT = colors.length
const [segments, setSegments] = useState<number[]>(Array(BOX_COUNT).fill(1 / BOX_COUNT))
const [oldSegments, setOldSegments] = useState<number[]>(Array(BOX_COUNT).fill(1 / BOX_COUNT))
const [startX, setStartX] = useState(0)
const [changingIndex, setChangingIndex] = useState(0)
const [isChanging, setIsChanging] = useState(false)
useEffect(() => {
//๋ง์ฐ์ค๊ฐ ์์ง์ผ ๋ ๋ง๋ค ๋ฐ์ค ํฌ๊ธฐ๋ฅผ ์กฐ์ ํด์ฃผ๋ ํธ๋ค๋ฌ
function moveHandler(e: MouseEvent) {
if (isChanging) {
// ์ฒ์ ์์ํ ๋ ์ผ์ชฝ ๋ฐ์ค๋, ์ค๋ฅธ์ชฝ ๋ฐ์ค ํฌ๊ธฐ๋ฅผ ๊ฐ์ ธ์จ๋ค
const left = oldSegments[changingIndex]
const right = oldSegments[changingIndex + 1]
// ์ผ์ชฝ ์ค๋ฅธ์ชฝ ๋ฐ์ค ๋๋น์ ํฉ์์...
// ์ต๋๋? ํฉ - 1/16
// ์ต์ 1/16
const maxRatio = left + right - 1 / 16
// ํ์ฌ ๋ง์ฐ์ค ์์น์, ๊พน ๋๋ฅด๊ธฐ ์์ํ ์์น์์ ์ฐจ์ด
const diff = e.clientX - startX
// ๋ง์ฐ์ค๊ฐ ์์ง์ธ ๋ณ์๊ฐ... ์ ์ฒด ํฌ๊ธฐ์์ ๋น์จ๋ก ์ผ๋ง๋ฅผ ์ฐจ์งํ๋์ง?
const diffAsRatio = diff / width
// const ์ต๋๊ฐ = 1 / 2;
// Math.min(1, ์ต๋๊ฐ); // => 1/2 ์ต๋๊ฐ๊น์ง ๊ฐ๋ฅ!
// Math.min(1 / 3, 1 / 2); // => 1/3
// const ์ต์๊ฐ = 1 / 16;
// Math.max(1 / 2, ์ต์๊ฐ); // => 1/2
// Math.max(0, ์ต์๊ฐ); // => 1/16
const newLeft = Math.max(Math.min(left + diffAsRatio, maxRatio), 1 / 16)
const newRight = Math.max(Math.min(right - diffAsRatio, maxRatio), 1 / 16)
const newSegments = [...oldSegments]
newSegments[changingIndex] = newLeft
newSegments[changingIndex + 1] = newRight
setSegments(newSegments)
}
}
// 1. ๋ง์ฐ์ค๋ฅผ ๋ผ์์ ๋... ๋ ์ด์ ๋ณ๊ฒฝ๋์ง ์๊ฒ ํด์ฃผ๋ ์น๊ตฌ
function upHandler() {
// 2.
setIsChanging(false)
}
// ์ด ๋ ํธ๋ค๋ฌ๋ฅผ ์๋์ฐ(ํ๋ฉด)์ ๋ฌ์์ค
window.addEventListener('mousemove', moveHandler)
window.addEventListener('mouseup', upHandler)
// cleanup => 3.
return () => {
// ํธ๋ค๋ฌ๋ฅผ ์๋์ฐ์์ ๋ผ์ด์ค...
window.removeEventListener('mousemove', moveHandler)
window.removeEventListener('mouseup', upHandler)
}
// ์์กด์ฑ ๋ฐฐ์ด์ isChanging์ด false๋ก ๋ณํ๋ฉด?
}, [isChanging, oldSegments, startX])
return (
<table className="selectedContainer flex flex-row">
<tr className="flex flex-row w-1/2 h-48">
{segments.map((segment, i) => (
<td
className="flex flex-row p-0"
key={i}
style={{
width: `${segment * 100}%`,
backgroundColor: colors[i],
}}
>
<div className="h-full w-11/12" onClick={() => deleteSelected(i)}>
{colors[i]}
</div>
{i < segments.length - 1 && (
<div
className="bg-blue-600 w-0.5 hover:w-2 transition-all p-0 m-0 h-full cursor-pointer"
//์์ ์ขํ๋ฅผ ๊ธฐ๋กํ๊ธฐ ์ํด ๋ชจ์๋ฆฌ๋ฅผ ๊พน ๋๋ฅด๋ฉด
onMouseDown={(e) => {
console.log('x', e.clientX, 'y', e.clientY)
setStartX(e.clientX) // ์์ x์ขํ
setChangingIndex(i) // ๋ฐ๊พธ๋ ค๋ ๊ทธ ์ธ๋ฑ์ค
setOldSegments(segments) // ์์ํ ๋ ๋ฐ์ค ํฌ๊ธฐ
setIsChanging(true) // ๋ณ๊ฒฝ ์ค์์ ์๋ ค์ฃผ๋ flag
}}
></div>
)}
</td>
))}
</tr>
</table>
)
// https://tailwindcss.com/docs/cursor
}
export default ResizableBoxes