lil.dev
Published on

๐ŸŽ‰ ์ปฌ๋Ÿฌ๋ฆฌ์ŠคํŠธ ํ”„๋กœ์ ํŠธ #7 drawer ๋งŒ๋“ค๊ณ  pallete ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ

๊ธ€์“ด์ด

    ๐Ÿ“Œ ๋ชฉ์ฐจ

    Welcome

    โœจ ์ปฌ๋Ÿฌ๋ฆฌ์ŠคํŠธ ์‚ฌ์ดํŠธ(์•„์ง ์—†์Œ

    ๐Ÿ’๐Ÿป

    1. drawer ๋งŒ๋“ค๊ธฐ
    2. pallete ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ

    1. drawer ์‚ฌ์šฉ๋ฒ• ์ตํžˆ๊ธฐ - ์Šฌ๋กฏ,์น ๋“œ๋Ÿฐ ์“ฐ๋Š” ๋ฒ•

    1-1. daisy ui๋กœ drawer์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด Drawer.tsx๋ฅผ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

    drawer component

    import * as React from 'react'
    import './Nav.css'
    
    //Drawer์„ ์—ฌ๋Š” ์˜คํ”ˆ ๋ฒ„ํŠผ์„ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.
    //์ €๋Š” ์ด๋ฏธ์ง€๋ฅผ ์“ธ ๊ฑฐ๋ผ label์‚ฌ์ด์— img๋ฅผ ๋„ฃ์–ด์ฃผ์—ˆ์–ด์š”.
    export function DrawerOpenButton() {
      return (
        <label role="button" htmlFor="my-drawer" className="drawer-button">
          <img src="src/img/icons8-menu.svg"></img>
        </label>
      )
    }
    
    //์—ฌ๊ธฐ Drawer()์•ˆ์— ๋“ค์–ด์žˆ๋Š” {children}์ด
    //Drawer์„ ์—ด์—ˆ์„ ๋•Œ ๋“ค์–ด๊ฐˆ ๋‚ด์šฉ๋“ค์ž…๋‹ˆ๋‹ค.
    function Drawer({ children }) {
      return (
        <div className="drawer">
          <input id="my-drawer" type="checkbox" className="drawer-toggle" />
          {/* drawer-content์•ˆ์— children์„ ๋„ฃ์œผ๋ฉด ๋œ๋‹ค. */}
          <div className="drawer-content">{children}</div>
          <div className="drawer-side">
            <label htmlFor="my-drawer" className="drawer-overlay"></label>
    
            <ul className="menu p-4 overflow-y-auto w-80 bg-base-100 text-base-content mb-0">
              <li>
                <a>Sidebar Item 1</a>
              </li>
              <li>
                <a>Sidebar Item 2</a>
              </li>
            </ul>
          </div>
        </div>
      )
    }
    
    export default Drawer
    

    1-2. ์ด๋ ‡๊ฒŒ 'Drawer.tsx'๋ฅผ ๋งŒ๋“ค์—ˆ์œผ๋ฉด, ์ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋„ฃ์„ ํŽ˜์ด์ง€๋ฅผ ๊ณจ๋ผ์•ผํ•˜์ฃ .

    ์ €์˜ ๊ฒฝ์šฐ๋Š” ์œ„์˜ ๋„ค๋น„๊ฒŒ์ด์…˜๊นŒ์ง€๋„ ๋ฎ๊ธฐ๋ฅผ ํฌ๋งํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— 'Index.tsx'์— importํ–ˆ์Šต๋‹ˆ๋‹ค.

    import Drawer from './Drawer'
    

    ์ด๋ ‡๊ฒŒ import๋ฅผ ํ•ด์ฃผ์—ˆ์œผ๋ฉด, Drawer๊ฐ€ ์—ด๋ฆด ๋•Œ ๊ฐ€๋ ค์งˆ ์š”์†Œ๋“ค์„ ๋ชจ๋‘ ๊ฐ์‹ธ๋„๋ก <Drawer><Drawer />๋ฅผ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

    ReactDOM.render(
      <React.StrictMode>
        <BrowserRouter>
          <Drawer>
            <Nav />
            <Routes>
              <Route path="/" element={<TestForm />} />
              <Route path="/login/*" element={<Login />} />
              {/* ์ƒ๋‹จ์— ์œ„์น˜ํ•˜๋Š” ๋ผ์šฐํŠธ๋“ค์˜ ๊ทœ์น™์„ ๋ชจ๋‘ ํ™•์ธ, ์ผ์น˜ํ•˜๋Š” ๋ผ์šฐํŠธ๊ฐ€ ์—†๋Š”๊ฒฝ์šฐ ์ฒ˜๋ฆฌ */}
              <Route path="*" element={<Notfound />}></Route>
            </Routes>
          </Drawer>
        </BrowserRouter>
      </React.StrictMode>,
      document.getElementById('root')
    )
    

    ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Drawer ๋งŒ๋“ค๊ธฐ๋Š” ๋!

    2. ์ด๋ฒˆ์—” pallete๋ฅผ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

    2-1. ๋จผ์ € 'Pallete.tsx'๋ผ๋Š” ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  ๋ช…๋ น์–ด rfce๋กœ ๊ธฐ๋ณธ ํ‹€์„ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

    2-2.pallete์•ˆ์˜ Tooltip.provider๋ฅผ pallete table ์œ„์— ๊ฐ์‹ธ๊ณ  ๊ทธ ์•ˆ์— ์žˆ๋Š” ๋ชจ๋“  ์š”์†Œ๋ฅผ ์˜ฎ๊ฒจ์ค๋‹ˆ๋‹ค..

    ์ด ๋•Œ ๋ฏธ๋ฆฌ ์ง€์ •ํ•ด๋†“์€ WHITEHEX์™€ colors ๋ฐฐ์—ด ๋“ฑ Pallete์— ํ•„์š”ํ•œ ์š”์†Œ๋“ค๋„ ํ•จ๊ป˜ ์˜ฎ๊ฒจ์ค๋‹ˆ๋‹ค.

    import React from 'react'
    import * as Tabs from '@radix-ui/react-tabs'
    import * as Tooltip from '@radix-ui/react-tooltip'
    import allPallete from './pallete.json'
    
    // ["base", "deep"]
    const palleteTypes = Object.keys(allPallete)
    const BOX_COUNT = 10
    const WHITE_HEX = '#ffffff'
    
    function Pallete() {
      const [colors, setColors] = React.useState(Array(BOX_COUNT).fill(WHITE_HEX)) // ๋ฌธ์ž์—ด
    
      //6๋ฒˆ - addSelected ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด
      function addSelected(hex: string) {
        setColors((old) => {
          // findIndex๋Š” ๋ฐฐ์—ด์˜ ๊ฐ’ ์ค‘์—์„œ ์กฐ๊ฑด์— ๋งž๋Š” ๊ฐ’์ด ์žˆ๋Š” ์ฒซ ๋ฒˆ์งธ index๋ฅผ ์ฐพ๋Š”๋‹ค!
          // hex ์ค‘์—์„œ hex์˜ ์ƒ‰์ด white์ธ ์ฒซ๋ฒˆ์งธ index๋ฅผ ์ฐพ์•„๋‚ธ๋‹ค.
          const firstWhiteIndex = old.findIndex((hex) => hex === WHITE_HEX)
          if (firstWhiteIndex === -1) {
            // ์ธ๋ฑ์Šค๋ฅผ ๋ชป ์ฐพ์œผ๋ฉด! -1์„ ๋ฐ˜ํ™˜
            return old // ์›๋ž˜ colors๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜... ๋” ์ฑ„์šธ ํ•˜์–€์นธ์ด ์—†์œผ๋ฏ€๋กœ!
          }
    
          // ์ด๋ฏธ ๋ฐ”๋€ ์ƒ‰์€ old๋Œ€๋กœ ๋†”๋‘๊ณ 
          const copy = [...old]
          // ์ฐพ์•„๋‚ธ ์ฒซ ๋ฒˆ์งธ ์ธ๋ฑ์Šค์— hex๊ฐ’์„ ๋„ฃ๋Š”๋‹ค.
          copy[firstWhiteIndex] = hex
          return copy // newState !!!
        })
      }
    
      //7๋ฒˆ - deleteSelected ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด
      function deleteSelected(targetIndex: number) {
        setColors((old) => {
          const copy = [...old]
          // targetIndex์˜ ๊ฐ’์„ ํฐ์ƒ‰ ํ—ฅ์Šค๋ฅผ ๋„ฃ๋Š”๋‹ค.
          copy[targetIndex] = WHITE_HEX
          return copy // newState !!!
        })
      }
    
      return (
        <Tooltip.Provider delayDuration={800} skipDelayDuration={500}>
          <input hidden type="text" name="colors" value={JSON.stringify(colors)} />
          <table className="selectedContainer flex flex-row">
            <tbody>
              {/* 2๋ฒˆ - map์„ ์จ์„œ ๋ฐฐ์—ด์˜ ๊ฐ’์„ ํ™”๋ฉด์— ๋ฟŒ๋ ค์ค€๋‹ค. */}
              {/* https://beta.reactjs.org/learn/rendering-lists */}
              <tr>
                {/* 7.2๋ฒˆ - targetIndex๋Š” ์—ฌ๊ธฐ์„œ index๋ฅผ ๋ฐ›์•„์˜จ ๊ฐ’์ด๋‹ค. */}
                {colors.map((color, index) => (
                  // 3๋ฒˆ - ์Šคํƒ€์ผ์— map์œผ๋กœ ๋ฐ›์•„์˜จ color ๋ฌธ์ž์—ด ๊ฐ’์„ backgroundColor๋กœ ์ง€์ •ํ•ด์ค€๋‹ค.
                  <td
                    key={`${color}-${index}`}
                    // ์ด๊ฒŒ ์„ ํƒํ•œ ์นธ์˜ index
                    onClick={() => deleteSelected(index)}
                    style={{
                      backgroundColor: color,
                    }}
                  >
                    {color}
                  </td>
                ))}
              </tr>
            </tbody>
          </table>
          <div id="pallete-box">
            <Tabs.Root defaultValue={Object.keys(allPallete)[0]} orientation="horizontal">
              <Tabs.List aria-label="select pallete type">
                {/* ํŒ”๋ ˆํŠธ์˜ ํƒ€์ž…๋งˆ๋‹ค Tabs.Trigger๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค*/}
                {palleteTypes.map((value) => (
                  <Tabs.Trigger key={value} value={value} className="bg-yellow-200">
                    {value}
                  </Tabs.Trigger>
                ))}
              </Tabs.List>
              {/* ["base", "deep"]*/}
              {/* ํŒ”๋ ˆํŠธ์˜ ํƒ€์ž…๋งˆ๋‹ค Tabs.Content๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค*/}
              {palleteTypes.map((value) => (
                <Tabs.Content key={value} value={value}>
                  <div id="pallete" className="flex flex-row flex-wrap">
                    {/* 4๋ฒˆ - pallete๋„ ๊ฐ’์ด ์—ฌ๋Ÿฌ ๊ฐœ์ด๋ฏ€๋กœ map์„ ์‚ฌ์šฉํ•œ๋‹ค. */}
                    {allPallete[value].map(({ hex, colorName }, index) => (
                      <Tooltip.Root>
                        <Tooltip.Trigger asChild>
                          <button
                            key={index}
                            onClick={() => addSelected(hex)}
                            className="roundButton"
                            style={{ backgroundColor: hex }}
                          >
                            {colorName}
                          </button>
                        </Tooltip.Trigger>
                        <Tooltip.Content className="tooltip-content">
                          {hex}
                          <Tooltip.Arrow className="tooltip-arrow" width="20" height="10" />
                        </Tooltip.Content>
                      </Tooltip.Root>
                    ))}
                  </div>
                </Tabs.Content>
              ))}
            </Tabs.Root>
          </div>
        </Tooltip.Provider>
      )
    }
    
    export default Pallete
    

    ์ด๋ ‡๊ฒŒ Pallete๋ฅผ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ„๋ฆฌํ•˜๋ฉด TestForm.tsx์˜ ์ฝ”๋“œ๊ฐ€ 130์ค„์ด๋‚˜ ์ค„์–ด๋“ค์–ด ํ›จ์”ฌ ๋ณด๊ธฐ ํŽธํ•ด์ง‘๋‹ˆ๋‹ค.