ๅŒ…่ฏฆ็ป†ไฟกๆฏ

grunfeld

ilokesto22MIT0.0.11

  • ๐Ÿš€ ๊ฐ„๋‹จํ•œ API: ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ์—†์ด ๋ช‡ ์ค„์˜ ์ฝ”๋“œ๋กœ ๋Œ€ํ™”์ƒ์ž๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค - ๐ŸŽฏ ๋™๊ธฐ/๋น„๋™๊ธฐ ์ง€์›: ์ผ๋ฐ˜์ ์ธ ์•Œ๋ฆผ๋ถ€ํ„ฐ ์‚ฌ์šฉ์ž ์‘๋‹ต์ด ํ•„์š”ํ•œ ํ™•์ธ ๋Œ€ํ™”์ƒ์ž๊นŒ์ง€ ๋ชจ๋“  ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค - ๐Ÿ“ฑ ์œ ์—ฐํ•œ ์œ„์น˜ ์„ค์ •: ์ค‘์•™ ๋ชจ๋‹ฌ, ํ•˜๋‹จ ์‹œํŠธ ๋“ฑ ๋‹ค์–‘ํ•œ UI ํŒจํ„ด์— ๋งž์ถฐ ์œ„์น˜๋ฅผ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค - ๐Ÿ”„ ์Šค๋งˆํŠธํ•œ ์Šคํƒ ๊ด€๋ฆฌ: ์—ฌ๋Ÿฌ ๋Œ€ํ™”์ƒ์ž๊ฐ€ ์—ด๋ฆด ๋•Œ ๋…ผ๋ฆฌ์ ์ธ LIFO(Last In First Out) ์ˆœ์„œ๋กœ

่‡ช่ฟฐๆ–‡ไปถ

Grunfeld

ํŠน์ง•

  • ๐Ÿš€ ๊ฐ„๋‹จํ•œ API: ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ์—†์ด ๋ช‡ ์ค„์˜ ์ฝ”๋“œ๋กœ ๋Œ€ํ™”์ƒ์ž๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
  • ๐ŸŽฏ ๋™๊ธฐ/๋น„๋™๊ธฐ ์ง€์›: ์ผ๋ฐ˜์ ์ธ ์•Œ๋ฆผ๋ถ€ํ„ฐ ์‚ฌ์šฉ์ž ์‘๋‹ต์ด ํ•„์š”ํ•œ ํ™•์ธ ๋Œ€ํ™”์ƒ์ž๊นŒ์ง€ ๋ชจ๋“  ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค
  • ๐Ÿ“ฑ ์œ ์—ฐํ•œ ์œ„์น˜ ์„ค์ •: ์ค‘์•™ ๋ชจ๋‹ฌ, ํ•˜๋‹จ ์‹œํŠธ ๋“ฑ ๋‹ค์–‘ํ•œ UI ํŒจํ„ด์— ๋งž์ถฐ ์œ„์น˜๋ฅผ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
  • ๐Ÿ”„ ์Šค๋งˆํŠธํ•œ ์Šคํƒ ๊ด€๋ฆฌ: ์—ฌ๋Ÿฌ ๋Œ€ํ™”์ƒ์ž๊ฐ€ ์—ด๋ฆด ๋•Œ ๋…ผ๋ฆฌ์ ์ธ LIFO(Last In First Out) ์ˆœ์„œ๋กœ ๊ด€๋ฆฌ๋ฉ๋‹ˆ๋‹ค
  • ๐ŸŽจ ์ปค์Šคํ…€ ์Šคํƒ€์ผ๋ง: ๋ฐฑ๋“œ๋กญ ์Šคํƒ€์ผ๋ถ€ํ„ฐ ๊ฐœ๋ณ„ ๋Œ€ํ™”์ƒ์ž ์Šคํƒ€์ผ๊นŒ์ง€ ์ž์œ ๋กญ๊ฒŒ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค
  • ๐Ÿ‘† ์ง๊ด€์ ์ธ UX: ๋ฐฐ๊ฒฝ ํด๋ฆญ์œผ๋กœ ๋‹ซ๊ธฐ, ์ž๋™ ํฌ์ปค์Šค ๊ด€๋ฆฌ ๋“ฑ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ณ ๋ คํ•œ ๊ธฐ๋Šฅ๋“ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค๋Š” React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ๊ฐ„๋‹จํ•˜๊ณ  ๊ฐ€๋ฒผ์šด ๋Œ€ํ™”์ƒ์ž(dialog) ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

์„ค์น˜

npm install grunfeld
# ๋˜๋Š”
yarn add grunfeld

ํŠน์ง•

  • ๐Ÿš€ ๊ฐ„๋‹จํ•œ API๋กœ ๋Œ€ํ™”์ƒ์ž ๊ด€๋ฆฌ
  • ๏ฟฝ ๋™๊ธฐ/๋น„๋™๊ธฐ ๋Œ€ํ™”์ƒ์ž ์ง€์›
  • ๐Ÿ“ฑ ์œ„์น˜ ์„ค์ • ๊ฐ€๋Šฅ ('center' ๋˜๋Š” 'bottom')
  • ๐Ÿ”„ ๋‹ค์ค‘ ๋Œ€ํ™”์ƒ์ž ์Šคํƒ ์ง€์›
  • ๐ŸŽจ ์ปค์Šคํ…€ ์Šคํƒ€์ผ๋ง ์ง€์›
  • ๐Ÿ‘† Light dismiss (๋ฐฐ๊ฒฝ ํด๋ฆญ์œผ๋กœ ๋‹ซ๊ธฐ) ์˜ต์…˜

์‚ฌ์šฉ๋ฒ•

๊ธฐ๋ณธ ์„ค์ •

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ตœ์ƒ์œ„ ๋ ˆ๋ฒจ์— GrunfeldProvider๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”. ์ด ์ปดํฌ๋„ŒํŠธ๋Š” ๋ชจ๋“  ๋Œ€ํ™”์ƒ์ž๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ์ปจํ…์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:

import { GrunfeldProvider } from "grunfeld";

function App() {
  return (
    <GrunfeldProvider
      options={{
        defaultPosition: "center",
        defaultDismiss: true,
        backdropStyle: {
          /* ์ปค์Šคํ…€ ๋ฐฑ๋“œ๋กญ ์Šคํƒ€์ผ */
        },
      }}
    >
      {/* ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์šฉ */}
    </GrunfeldProvider>
  );
}

๊ธฐ๋ณธ ๋Œ€ํ™”์ƒ์ž ํ‘œ์‹œ

๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ํ˜•ํƒœ์˜ ๋Œ€ํ™”์ƒ์ž์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž์—๊ฒŒ ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ฃผ๊ฑฐ๋‚˜ ๊ฐ„๋‹จํ•œ ์ƒํ˜ธ์ž‘์šฉ์ด ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค:

import { grunfeld } from "grunfeld";

function YourComponent() {
  const showDialog = () => {
    grunfeld.add({
      element: <div>์•ˆ๋…•ํ•˜์„ธ์š”!</div>,
      position: "center",
      lightDismiss: true,
    });
  };

  return <button onClick={showDialog}>๋Œ€ํ™”์ƒ์ž ์—ด๊ธฐ</button>;
}

๋น„๋™๊ธฐ ๋Œ€ํ™”์ƒ์ž (์‚ฌ์šฉ์ž ์‘๋‹ต ๋Œ€๊ธฐ)

์‚ฌ์šฉ์ž์˜ ์„ ํƒ์ด๋‚˜ ์ž…๋ ฅ์„ ๊ธฐ๋‹ค๋ ค์•ผ ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. Promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ async/await ํŒจํ„ด์œผ๋กœ ์‚ฌ์šฉ์ž์˜ ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

import { grunfeld } from "grunfeld";

function YourComponent() {
  const showConfirmDialog = async () => {
    const result = await grunfeld.addAsync((removeWith) => ({
      element: (
        <div>
          <p>์ •๋ง ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?</p>
          <button onClick={() => removeWith(true)}>ํ™•์ธ</button>
          <button onClick={() => removeWith(false)}>์ทจ์†Œ</button>
        </div>
      ),
      position: "center",
    }));

    if (result) {
      console.log("์‚ฌ์šฉ์ž๊ฐ€ ํ™•์ธ์„ ํด๋ฆญํ–ˆ์Šต๋‹ˆ๋‹ค");
    } else {
      console.log("์‚ฌ์šฉ์ž๊ฐ€ ์ทจ์†Œ๋ฅผ ํด๋ฆญํ–ˆ์Šต๋‹ˆ๋‹ค");
    }
  };

  return <button onClick={showConfirmDialog}>ํ™•์ธ ๋Œ€ํ™”์ƒ์ž</button>;
}

๊ฐ„๋‹จํ•œ ๋Œ€ํ™”์ƒ์ž (ReactNode๋งŒ ์ „๋‹ฌ)

๋ณต์žกํ•œ ์„ค์ • ์—†์ด JSX ์š”์†Œ๋‚˜ ๋ฌธ์ž์—ด์„ ์ง์ ‘ ์ „๋‹ฌํ•˜์—ฌ ๋น ๋ฅด๊ฒŒ ๋Œ€ํ™”์ƒ์ž๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

import { grunfeld } from "grunfeld";

function YourComponent() {
  const showSimpleDialog = () => {
    // ReactNode๋ฅผ ์ง์ ‘ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
    grunfeld.add(<div>๊ฐ„๋‹จํ•œ ๋ฉ”์‹œ์ง€</div>);
  };

  return <button onClick={showSimpleDialog}>๊ฐ„๋‹จํ•œ ๋Œ€ํ™”์ƒ์ž</button>;
}

๋Œ€ํ™”์ƒ์ž ์ œ๊ฑฐ

Grunfeld๋Š” ๋Œ€ํ™”์ƒ์ž๋“ค์„ ์Šคํƒ(Stack) ๊ตฌ์กฐ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋Œ€ํ™”์ƒ์ž๋“ค ๊ฐ„์˜ ๋งฅ๋ฝ์  ๊ด€๊ณ„๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, A ๋Œ€ํ™”์ƒ์ž์—์„œ B ๋Œ€ํ™”์ƒ์ž๋ฅผ ์—ด์—ˆ๋‹ค๋ฉด, B๊ฐ€ A์˜ ๊ฒฐ๊ณผ๋กœ ์ƒ์„ฑ๋œ ๊ฒƒ์ด๋ฏ€๋กœ B๊ฐ€ ๋จผ์ € ๋‹ซํ˜€์•ผ ๋…ผ๋ฆฌ์ ์œผ๋กœ ์˜ฌ๋ฐ”๋ฆ…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์›์น™์— ๋”ฐ๋ผ remove()๋Š” ํ•ญ์ƒ ๊ฐ€์žฅ ์ตœ๊ทผ์— ์—ด๋ฆฐ ๋Œ€ํ™”์ƒ์ž๋ถ€ํ„ฐ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

import { grunfeld } from "grunfeld";

// ๊ฐ€์žฅ ์ตœ๊ทผ ๋Œ€ํ™”์ƒ์ž ์ œ๊ฑฐ
grunfeld.remove();

// ๋ชจ๋“  ๋Œ€ํ™”์ƒ์ž ์ œ๊ฑฐ
grunfeld.clear();

API ์ฐธ์กฐ

GrunfeldProvider

์†์„ฑ ํƒ€์ž… ๊ธฐ๋ณธ๊ฐ’ ์„ค๋ช…
children ReactNode ํ•„์ˆ˜ ์ž์‹ ์ปดํฌ๋„ŒํŠธ
options GrunfeldProviderOptions - ๊ธฐ๋ณธ ์„ค์ • ์˜ต์…˜

GrunfeldProviderOptions

์†์„ฑ ํƒ€์ž… ๊ธฐ๋ณธ๊ฐ’ ์„ค๋ช…
defaultPosition 'center' \ 'bottom' 'center' ๋Œ€ํ™”์ƒ์ž์˜ ๊ธฐ๋ณธ ์œ„์น˜
defaultDismiss boolean true ๊ธฐ๋ณธ light dismiss ์„ค์ •
backdropStyle CSSProperties - ๋ฐฑ๋“œ๋กญ ์ปค์Šคํ…€ ์Šคํƒ€์ผ

grunfeld ๊ฐ์ฒด

grunfeld.add(dialog)

์ƒˆ๋กœ์šด ๋Œ€ํ™”์ƒ์ž๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

๋งค๊ฐœ๋ณ€์ˆ˜:

  • dialog: GrunfeldProps - ๋Œ€ํ™”์ƒ์ž ์„ค์ •

GrunfeldProps:

type GrunfeldProps =
  | {
      element: React.ReactNode;
      position?: "center" | "bottom";
      lightDismiss?: boolean;
      dismissCallback?: () => unknown;
    }
  | React.ReactNode;

grunfeld.addAsync<T>(dialog)

๋น„๋™๊ธฐ ๋Œ€ํ™”์ƒ์ž๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‚ฌ์šฉ์ž ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค.

๋งค๊ฐœ๋ณ€์ˆ˜:

  • dialog: (removeWith: (data: T) => T) => GrunfeldProps - ๋Œ€ํ™”์ƒ์ž ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜

๋ฐ˜ํ™˜๊ฐ’:

  • Promise<T> - ์‚ฌ์šฉ์ž๊ฐ€ removeWith๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์ „๋‹ฌํ•œ ๋ฐ์ดํ„ฐ

grunfeld.remove()

๊ฐ€์žฅ ์ตœ๊ทผ์— ์ถ”๊ฐ€๋œ ๋Œ€ํ™”์ƒ์ž๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ฉ”์„œ๋“œ๋Š” LIFO(Last In First Out) ์›์น™์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๋Œ€ํ™”์ƒ์ž๊ฐ€ ์—ด๋ ค์žˆ์„ ๋•Œ, ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ์—ด๋ฆฐ ๋Œ€ํ™”์ƒ์ž๋ถ€ํ„ฐ ์ˆœ์„œ๋Œ€๋กœ ๋‹ซํž™๋‹ˆ๋‹ค. ์ด๋Š” ๋Œ€ํ™”์ƒ์ž๋“ค ๊ฐ„์˜ ๋งฅ๋ฝ์  ๊ด€๊ณ„๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์„ค๊ณ„์ž…๋‹ˆ๋‹ค.

// ์˜ˆ์‹œ: A โ†’ B โ†’ C ์ˆœ์„œ๋กœ ์—ด๋ฆฐ ๊ฒฝ์šฐ
grunfeld.remove(); // C๊ฐ€ ๋‹ซํž˜
grunfeld.remove(); // B๊ฐ€ ๋‹ซํž˜
grunfeld.remove(); // A๊ฐ€ ๋‹ซํž˜

grunfeld.clear()

๋ชจ๋“  ๋Œ€ํ™”์ƒ์ž๋ฅผ ํ•œ ๋ฒˆ์— ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

๊ธด๊ธ‰ํ•œ ์ƒํ™ฉ์ด๋‚˜ ํŽ˜์ด์ง€ ์ „ํ™˜ ์‹œ ๋ชจ๋“  ๋Œ€ํ™”์ƒ์ž๋ฅผ ์ •๋ฆฌํ•ด์•ผ ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ๋Œ€ํ™”์ƒ์ž์˜ dismissCallback์ด ์žˆ๋‹ค๋ฉด ๋ชจ๋‘ ์‹คํ–‰๋œ ํ›„ ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

๊ณ ๊ธ‰ ์‚ฌ์šฉ๋ฒ•

์ปค์Šคํ…€ dismiss ์ฝœ๋ฐฑ

๋Œ€ํ™”์ƒ์ž๊ฐ€ ๋‹ซํž ๋•Œ ํŠน์ • ๋กœ์ง์„ ์‹คํ–‰ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ dismissCallback์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ •๋ฆฌ ์ž‘์—…, ์ƒํƒœ ์—…๋ฐ์ดํŠธ, ๋ถ„์„ ์ด๋ฒคํŠธ ์ „์†ก ๋“ฑ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค:

grunfeld.add({
  element: <MyDialog />,
  dismissCallback: () => {
    console.log("๋Œ€ํ™”์ƒ์ž๊ฐ€ ๋‹ซํ˜”์Šต๋‹ˆ๋‹ค");
    // ์ •๋ฆฌ ์ž‘์—… ์ˆ˜ํ–‰
  },
});

์œ„์น˜๋ณ„ ๋Œ€ํ™”์ƒ์ž

๋‹ค์–‘ํ•œ UI ํŒจํ„ด์— ๋งž์ถฐ ๋Œ€ํ™”์ƒ์ž์˜ ์œ„์น˜๋ฅผ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ์œ„์น˜๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:

// ์ค‘์•™์— ํ‘œ์‹œ
grunfeld.add({
  element: <CenterDialog />,
  position: "center",
});

// ํ•˜๋‹จ์— ํ‘œ์‹œ (๋ฐ”ํ…€ ์‹œํŠธ ์Šคํƒ€์ผ)
grunfeld.add({
  element: <BottomSheet />,
  position: "bottom",
});