From 4310446b9e38f09b115938302bdbedb17748cb05 Mon Sep 17 00:00:00 2001 From: kimminna Date: Thu, 2 Jul 2026 15:09:39 +0900 Subject: [PATCH 01/12] =?UTF-8?q?file(ui):=20Color=C2=B7Typography=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A5=BC=20tokens=20?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Color, Typography 컴포넌트를 components/tokens 폴더 하위로 이동했습니다 - index.ts의 Color, Typography import 경로를 갱신했습니다 --- packages/timo-design-system/src/components/index.ts | 4 ++-- .../src/components/{ => tokens}/color/Color.stories.tsx | 0 .../src/components/{ => tokens}/color/Color.tsx | 0 .../components/{ => tokens}/typography/Typography.stories.tsx | 0 .../src/components/{ => tokens}/typography/Typography.tsx | 0 5 files changed, 2 insertions(+), 2 deletions(-) rename packages/timo-design-system/src/components/{ => tokens}/color/Color.stories.tsx (100%) rename packages/timo-design-system/src/components/{ => tokens}/color/Color.tsx (100%) rename packages/timo-design-system/src/components/{ => tokens}/typography/Typography.stories.tsx (100%) rename packages/timo-design-system/src/components/{ => tokens}/typography/Typography.tsx (100%) diff --git a/packages/timo-design-system/src/components/index.ts b/packages/timo-design-system/src/components/index.ts index 7917669b..68f73dfb 100644 --- a/packages/timo-design-system/src/components/index.ts +++ b/packages/timo-design-system/src/components/index.ts @@ -1,6 +1,6 @@ export { Checkbox } from "@components/checkbox/Checkbox"; -export { Color } from "@components/color/Color"; -export { Typography } from "@components/typography/Typography"; +export { Color } from "@components/tokens/color/Color"; +export { Typography } from "@components/tokens/typography/Typography"; export { Tag } from "@components/tag/Tag"; export { PriorityIcon } from "@components/priority-icon/PriorityIcon"; export { CreateButton } from "@components/button/create-button/CreateButton"; diff --git a/packages/timo-design-system/src/components/color/Color.stories.tsx b/packages/timo-design-system/src/components/tokens/color/Color.stories.tsx similarity index 100% rename from packages/timo-design-system/src/components/color/Color.stories.tsx rename to packages/timo-design-system/src/components/tokens/color/Color.stories.tsx diff --git a/packages/timo-design-system/src/components/color/Color.tsx b/packages/timo-design-system/src/components/tokens/color/Color.tsx similarity index 100% rename from packages/timo-design-system/src/components/color/Color.tsx rename to packages/timo-design-system/src/components/tokens/color/Color.tsx diff --git a/packages/timo-design-system/src/components/typography/Typography.stories.tsx b/packages/timo-design-system/src/components/tokens/typography/Typography.stories.tsx similarity index 100% rename from packages/timo-design-system/src/components/typography/Typography.stories.tsx rename to packages/timo-design-system/src/components/tokens/typography/Typography.stories.tsx diff --git a/packages/timo-design-system/src/components/typography/Typography.tsx b/packages/timo-design-system/src/components/tokens/typography/Typography.tsx similarity index 100% rename from packages/timo-design-system/src/components/typography/Typography.tsx rename to packages/timo-design-system/src/components/tokens/typography/Typography.tsx From ca05434fd46f382d083866e3975266036723d97e Mon Sep 17 00:00:00 2001 From: kimminna Date: Thu, 2 Jul 2026 15:11:12 +0900 Subject: [PATCH 02/12] =?UTF-8?q?feat(ui):=20PrioritySelector=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Figma 디자인(node-id 894-32397)을 기반으로 우선순위 선택 컴포넌트를 추가했습니다 - PriorityIcon을 priority 폴더 하위로 이동해 관련 컴포넌트를 함께 묶었습니다 - 선택 상태 전환 시 배경·텍스트·아이콘 색상에 트랜지션을 적용했습니다 - Storybook 스토리와 배경 파라미터를 추가했습니다 --- .../src/components/index.ts | 3 +- .../priority-icon/PriorityIcon.stories.tsx | 2 +- .../priority-icon/PriorityIcon.tsx | 5 +- .../PrioritySelector.stories.tsx | 48 ++++++++++++++++ .../priority-selector/PrioritySelector.tsx | 55 +++++++++++++++++++ 5 files changed, 110 insertions(+), 3 deletions(-) rename packages/timo-design-system/src/components/{ => priority}/priority-icon/PriorityIcon.stories.tsx (97%) rename packages/timo-design-system/src/components/{ => priority}/priority-icon/PriorityIcon.tsx (79%) create mode 100644 packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.stories.tsx create mode 100644 packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx diff --git a/packages/timo-design-system/src/components/index.ts b/packages/timo-design-system/src/components/index.ts index 68f73dfb..af8d2ea4 100644 --- a/packages/timo-design-system/src/components/index.ts +++ b/packages/timo-design-system/src/components/index.ts @@ -2,6 +2,7 @@ export { Checkbox } from "@components/checkbox/Checkbox"; export { Color } from "@components/tokens/color/Color"; export { Typography } from "@components/tokens/typography/Typography"; export { Tag } from "@components/tag/Tag"; -export { PriorityIcon } from "@components/priority-icon/PriorityIcon"; +export { PriorityIcon } from "@components/priority/priority-icon/PriorityIcon"; +export { PrioritySelector } from "@components/priority/priority-selector/PrioritySelector"; export { CreateButton } from "@components/button/create-button/CreateButton"; export { TodayBadge } from "@components/badge/today-badge/TodayBadge"; diff --git a/packages/timo-design-system/src/components/priority-icon/PriorityIcon.stories.tsx b/packages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.stories.tsx similarity index 97% rename from packages/timo-design-system/src/components/priority-icon/PriorityIcon.stories.tsx rename to packages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.stories.tsx index 8b5b7e5e..eff68835 100644 --- a/packages/timo-design-system/src/components/priority-icon/PriorityIcon.stories.tsx +++ b/packages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.stories.tsx @@ -3,7 +3,7 @@ import { PriorityIcon, type Priority } from "./PriorityIcon"; import type { Meta, StoryObj } from "@storybook/react"; const meta = { - title: "Components/PriorityIcon", + title: "Components/Priority/PriorityIcon", component: PriorityIcon, parameters: { layout: "centered", diff --git a/packages/timo-design-system/src/components/priority-icon/PriorityIcon.tsx b/packages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.tsx similarity index 79% rename from packages/timo-design-system/src/components/priority-icon/PriorityIcon.tsx rename to packages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.tsx index dbed858c..abc9c91d 100644 --- a/packages/timo-design-system/src/components/priority-icon/PriorityIcon.tsx +++ b/packages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.tsx @@ -26,7 +26,10 @@ export interface PriorityIconProps { export const PriorityIcon = ({ priority }: PriorityIconProps) => { return (
); }; diff --git a/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.stories.tsx b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.stories.tsx new file mode 100644 index 00000000..810f1176 --- /dev/null +++ b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.stories.tsx @@ -0,0 +1,48 @@ +import { PrioritySelector } from "./PrioritySelector"; + +import type { Meta, StoryObj } from "@storybook/react"; + +const meta = { + title: "Components/Priority/PrioritySelector", + component: PrioritySelector, + parameters: { + layout: "centered", + backgrounds: { + default: "light-gray", + values: [ + { name: "light-gray", value: "#F5F5F5" }, + { name: "dark", value: "#333333" }, + { name: "white", value: "#FFFFFF" }, + ], + }, + }, + argTypes: { + selected: { + control: "select", + options: ["매우중요", "중요", "보통", "낮음"], + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; + +export const 매우중요: Story = { + args: { selected: "매우중요" }, +}; + +export const 중요: Story = { + args: { selected: "중요" }, +}; + +export const 보통: Story = { + args: { selected: "보통" }, +}; + +export const 낮음: Story = { + args: { selected: "낮음" }, +}; diff --git a/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx new file mode 100644 index 00000000..e2a74bf6 --- /dev/null +++ b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx @@ -0,0 +1,55 @@ +import { cn } from "@lib"; + +import { PriorityIcon } from "../priority-icon/PriorityIcon"; + +export type PriorityLevel = "매우중요" | "중요" | "보통" | "낮음"; + +const PRIORITY_LEVELS: PriorityLevel[] = ["매우중요", "중요", "보통", "낮음"]; + +const PRIORITY_BG_COLOR: Record = { + 매우중요: "bg-timo-red", + 중요: "bg-timo-orange", + 보통: "bg-timo-gray", + 낮음: "bg-timo-gray-900", +}; + +export interface PrioritySelectorProps { + selected?: PriorityLevel; + onSelect?: (priority: PriorityLevel) => void; +} + +export const PrioritySelector = ({ + selected, + onSelect, +}: PrioritySelectorProps) => { + return ( +
+ {PRIORITY_LEVELS.map((priority) => { + const isSelected = priority === selected; + + return ( + + ); + })} +
+ ); +}; From 36cc97785a9d84fbaab55f072bf512ebf875f4b9 Mon Sep 17 00:00:00 2001 From: kimminna Date: Thu, 2 Jul 2026 15:55:28 +0900 Subject: [PATCH 03/12] =?UTF-8?q?refactor(ui):=20Dropdown=20=EA=B3=B5?= =?UTF-8?q?=ED=86=B5=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EC=B6=9C=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 드롭다운 형태 셀렉터들이 공유하는 패널·행 스타일을 Dropdown/Dropdown.Item으로 분리했습니다 - PrioritySelector가 Dropdown을 사용하도록 리팩토링했습니다 --- .../layout/dropdown/Dropdown.stories.tsx | 38 ++++++++++++++++ .../components/layout/dropdown/Dropdown.tsx | 44 +++++++++++++++++++ .../priority-selector/PrioritySelector.tsx | 15 +++---- 3 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 packages/timo-design-system/src/components/layout/dropdown/Dropdown.stories.tsx create mode 100644 packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx diff --git a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.stories.tsx b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.stories.tsx new file mode 100644 index 00000000..ad38d1f4 --- /dev/null +++ b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.stories.tsx @@ -0,0 +1,38 @@ +import { Dropdown } from "./Dropdown"; + +import type { Meta, StoryObj } from "@storybook/react"; + +const meta = { + title: "Components/layout/Dropdown", + parameters: { + layout: "centered", + backgrounds: { + default: "light-gray", + values: [ + { name: "light-gray", value: "#F5F5F5" }, + { name: "dark", value: "#333333" }, + { name: "white", value: "#FFFFFF" }, + ], + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const SAMPLE_ITEMS = ["옵션 1", "옵션 2", "옵션 3"]; + +export const Default: Story = { + name: "Basic Usage", + render: () => ( + + {SAMPLE_ITEMS.map((item) => ( + + + {item} + + + ))} + + ), +}; diff --git a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx new file mode 100644 index 00000000..bf0be624 --- /dev/null +++ b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx @@ -0,0 +1,44 @@ +import { cn } from "@lib"; + +import type { ReactNode } from "react"; + +export interface DropdownProps { + children: ReactNode; + className?: string; +} + +const DropdownRoot = ({ children, className }: DropdownProps) => { + return ( +
+ {children} +
+ ); +}; + +export interface DropdownItemProps { + children: ReactNode; + className?: string; + onClick?: () => void; +} + +const DropdownItem = ({ children, className, onClick }: DropdownItemProps) => { + return ( + + ); +}; + +export const Dropdown = Object.assign(DropdownRoot, { Item: DropdownItem }); diff --git a/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx index e2a74bf6..da07b62c 100644 --- a/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx +++ b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx @@ -1,7 +1,7 @@ +import { Dropdown } from "@components/layout/dropdown/Dropdown"; +import { PriorityIcon } from "@components/priority/priority-icon/PriorityIcon"; import { cn } from "@lib"; -import { PriorityIcon } from "../priority-icon/PriorityIcon"; - export type PriorityLevel = "매우중요" | "중요" | "보통" | "낮음"; const PRIORITY_LEVELS: PriorityLevel[] = ["매우중요", "중요", "보통", "낮음"]; @@ -23,17 +23,16 @@ export const PrioritySelector = ({ onSelect, }: PrioritySelectorProps) => { return ( -
+ {PRIORITY_LEVELS.map((priority) => { const isSelected = priority === selected; return ( - + ); })} -
+ ); }; From f433393c4b49dc61d216974747a94dfb1ab2142d Mon Sep 17 00:00:00 2001 From: kimminna Date: Thu, 2 Jul 2026 15:56:02 +0900 Subject: [PATCH 04/12] =?UTF-8?q?feat(ui):=20TagSelector=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20?= =?UTF-8?q?Tag=EB=A5=BC=20TagIcon=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=A6=84?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Tag 컴포넌트를 TagIcon으로 이름을 변경해 tag-icon 폴더로 이동했습니다 - Dropdown을 사용하는 TagSelector 컴포넌트를 새로 추가했습니다 - index.ts의 export를 TagIcon, TagSelector로 갱신했습니다 --- .../src/components/index.ts | 3 +- .../TagIcon.stories.tsx} | 8 +-- .../tag/{Tag.tsx => tag-icon/TagIcon.tsx} | 4 +- .../tag/tag-selector/TagSelector.stories.tsx | 48 +++++++++++++++++ .../tag/tag-selector/TagSelector.tsx | 51 +++++++++++++++++++ 5 files changed, 107 insertions(+), 7 deletions(-) rename packages/timo-design-system/src/components/tag/{Tag.stories.tsx => tag-icon/TagIcon.stories.tsx} (79%) rename packages/timo-design-system/src/components/tag/{Tag.tsx => tag-icon/TagIcon.tsx} (82%) create mode 100644 packages/timo-design-system/src/components/tag/tag-selector/TagSelector.stories.tsx create mode 100644 packages/timo-design-system/src/components/tag/tag-selector/TagSelector.tsx diff --git a/packages/timo-design-system/src/components/index.ts b/packages/timo-design-system/src/components/index.ts index af8d2ea4..6a98357b 100644 --- a/packages/timo-design-system/src/components/index.ts +++ b/packages/timo-design-system/src/components/index.ts @@ -1,7 +1,8 @@ export { Checkbox } from "@components/checkbox/Checkbox"; export { Color } from "@components/tokens/color/Color"; export { Typography } from "@components/tokens/typography/Typography"; -export { Tag } from "@components/tag/Tag"; +export { TagIcon } from "@components/tag/tag-icon/TagIcon"; +export { TagSelector } from "@components/tag/tag-selector/TagSelector"; export { PriorityIcon } from "@components/priority/priority-icon/PriorityIcon"; export { PrioritySelector } from "@components/priority/priority-selector/PrioritySelector"; export { CreateButton } from "@components/button/create-button/CreateButton"; diff --git a/packages/timo-design-system/src/components/tag/Tag.stories.tsx b/packages/timo-design-system/src/components/tag/tag-icon/TagIcon.stories.tsx similarity index 79% rename from packages/timo-design-system/src/components/tag/Tag.stories.tsx rename to packages/timo-design-system/src/components/tag/tag-icon/TagIcon.stories.tsx index 6e61d914..1fc91c0c 100644 --- a/packages/timo-design-system/src/components/tag/Tag.stories.tsx +++ b/packages/timo-design-system/src/components/tag/tag-icon/TagIcon.stories.tsx @@ -1,10 +1,10 @@ -import { Tag } from "./Tag"; +import { TagIcon } from "@components/tag/tag-icon/TagIcon"; import type { Meta, StoryObj } from "@storybook/react"; const meta = { - title: "Components/Tag", - component: Tag, + title: "Components/Tag/TagIcon", + component: TagIcon, parameters: { layout: "centered", }, @@ -19,7 +19,7 @@ const meta = { description: "태그 스타일 베리언트", }, }, -} satisfies Meta; +} satisfies Meta; export default meta; type Story = StoryObj; diff --git a/packages/timo-design-system/src/components/tag/Tag.tsx b/packages/timo-design-system/src/components/tag/tag-icon/TagIcon.tsx similarity index 82% rename from packages/timo-design-system/src/components/tag/Tag.tsx rename to packages/timo-design-system/src/components/tag/tag-icon/TagIcon.tsx index 96e6efe3..0190b01f 100644 --- a/packages/timo-design-system/src/components/tag/Tag.tsx +++ b/packages/timo-design-system/src/components/tag/tag-icon/TagIcon.tsx @@ -1,11 +1,11 @@ export type TagVariant = "default" | "blue"; -export interface TagProps { +export interface TagIconProps { text: string; variant?: TagVariant; } -export const Tag = ({ text, variant = "default" }: TagProps) => { +export const TagIcon = ({ text, variant = "default" }: TagIconProps) => { const isBlue = variant === "blue"; return (
; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; + +export const 일상: Story = { + args: { selected: "일상" }, +}; + +export const 운동: Story = { + args: { selected: "운동" }, +}; + +export const 업무: Story = { + args: { selected: "업무" }, +}; + +export const 기타: Story = { + args: { selected: "기타" }, +}; diff --git a/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.tsx b/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.tsx new file mode 100644 index 00000000..dd16051d --- /dev/null +++ b/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.tsx @@ -0,0 +1,51 @@ +import { Dropdown } from "@components/layout/dropdown/Dropdown"; +import { PlusIcon } from "@icons"; +import { cn } from "@lib"; + +export type Tag = "일상" | "운동" | "업무" | "기타"; + +const TAGS: Tag[] = ["일상", "운동", "업무", "기타"]; + +export interface TagSelectorProps { + selected?: Tag; + onSelect?: (tag: Tag) => void; + onAddClick?: () => void; +} + +export const TagSelector = ({ + selected, + onSelect, + onAddClick, +}: TagSelectorProps) => { + return ( + + {TAGS.map((tag) => { + const isSelected = tag === selected; + + return ( + onSelect?.(tag)} + className={cn("px-1.5 py-1", isSelected && "bg-timo-gray-500")} + > + + {tag} + + + ); + })} + + + + 추가 + + + + + ); +}; From e93c916b05968a35d413ecd83e238348876a2404 Mon Sep 17 00:00:00 2001 From: kimminna Date: Thu, 2 Jul 2026 16:12:30 +0900 Subject: [PATCH 05/12] =?UTF-8?q?feat(ui):=20Dropdown=EC=97=90=20Trigger?= =?UTF-8?q?=C2=B7Panel=20=EB=B0=8F=20=EC=97=B4=EB=A6=BC/=EB=8B=AB=ED=9E=98?= =?UTF-8?q?=20=EB=8F=99=EC=9E=91=20=EC=B6=94=EA=B0=80=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dropdown이 내부 open 상태와 외부 클릭 감지를 갖도록 확장했습니다 - Trigger 클릭 시 토글, 바깥 클릭 시 자동으로 닫히도록 구현했습니다 - PrioritySelector·TagSelector가 trigger prop을 받아 스스로 트리거를 조합하도록 변경했습니다 --- .../layout/dropdown/Dropdown.stories.tsx | 22 +++-- .../components/layout/dropdown/Dropdown.tsx | 92 ++++++++++++++++++- .../PrioritySelector.stories.tsx | 16 +++- .../priority-selector/PrioritySelector.tsx | 54 ++++++----- .../tag/tag-selector/TagSelector.stories.tsx | 16 +++- .../tag/tag-selector/TagSelector.tsx | 60 ++++++------ 6 files changed, 190 insertions(+), 70 deletions(-) diff --git a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.stories.tsx b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.stories.tsx index ad38d1f4..4618c68d 100644 --- a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.stories.tsx +++ b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.stories.tsx @@ -25,14 +25,20 @@ const SAMPLE_ITEMS = ["옵션 1", "옵션 2", "옵션 3"]; export const Default: Story = { name: "Basic Usage", render: () => ( - - {SAMPLE_ITEMS.map((item) => ( - - - {item} - - - ))} + + + 트리거 + + + + {SAMPLE_ITEMS.map((item) => ( + + + {item} + + + ))} + ), }; diff --git a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx index bf0be624..55278633 100644 --- a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx +++ b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx @@ -1,6 +1,34 @@ +"use client"; + import { cn } from "@lib"; +import { + createContext, + useContext, + useEffect, + useRef, + useState, + type ReactNode, +} from "react"; + +interface DropdownContextValue { + isOpen: boolean; + toggle: () => void; + close: () => void; +} + +const DropdownContext = createContext(null); + +const useDropdownContext = (): DropdownContextValue => { + const context = useContext(DropdownContext); -import type { ReactNode } from "react"; + if (!context) { + throw new Error( + "Dropdown.Trigger, Dropdown.Panel은 Dropdown 내부에서만 사용할 수 있습니다.", + ); + } + + return context; +}; export interface DropdownProps { children: ReactNode; @@ -8,10 +36,64 @@ export interface DropdownProps { } const DropdownRoot = ({ children, className }: DropdownProps) => { + const [isOpen, setIsOpen] = useState(false); + const rootRef = useRef(null); + + useEffect(() => { + if (!isOpen) return; + + const handleOutsideClick = (event: MouseEvent) => { + if (!rootRef.current?.contains(event.target as Node)) { + setIsOpen(false); + } + }; + + document.addEventListener("mousedown", handleOutsideClick); + + return () => document.removeEventListener("mousedown", handleOutsideClick); + }, [isOpen]); + + const toggle = () => setIsOpen((prev) => !prev); + const close = () => setIsOpen(false); + + return ( + +
+ {children} +
+
+ ); +}; + +export interface DropdownTriggerProps { + children: ReactNode; + className?: string; +} + +const DropdownTrigger = ({ children, className }: DropdownTriggerProps) => { + const { toggle } = useDropdownContext(); + + return ( + + ); +}; + +export interface DropdownPanelProps { + children: ReactNode; + className?: string; +} + +const DropdownPanel = ({ children, className }: DropdownPanelProps) => { + const { isOpen } = useDropdownContext(); + + if (!isOpen) return null; + return (
@@ -41,4 +123,8 @@ const DropdownItem = ({ children, className, onClick }: DropdownItemProps) => { ); }; -export const Dropdown = Object.assign(DropdownRoot, { Item: DropdownItem }); +export const Dropdown = Object.assign(DropdownRoot, { + Trigger: DropdownTrigger, + Panel: DropdownPanel, + Item: DropdownItem, +}); diff --git a/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.stories.tsx b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.stories.tsx index 810f1176..0c6564bf 100644 --- a/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.stories.tsx +++ b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.stories.tsx @@ -27,22 +27,28 @@ const meta = { export default meta; type Story = StoryObj; +const TRIGGER = ( + + 우선순위 + +); + export const Default: Story = { - args: {}, + args: { trigger: TRIGGER }, }; export const 매우중요: Story = { - args: { selected: "매우중요" }, + args: { trigger: TRIGGER, selected: "매우중요" }, }; export const 중요: Story = { - args: { selected: "중요" }, + args: { trigger: TRIGGER, selected: "중요" }, }; export const 보통: Story = { - args: { selected: "보통" }, + args: { trigger: TRIGGER, selected: "보통" }, }; export const 낮음: Story = { - args: { selected: "낮음" }, + args: { trigger: TRIGGER, selected: "낮음" }, }; diff --git a/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx index da07b62c..c1a88c09 100644 --- a/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx +++ b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx @@ -2,6 +2,8 @@ import { Dropdown } from "@components/layout/dropdown/Dropdown"; import { PriorityIcon } from "@components/priority/priority-icon/PriorityIcon"; import { cn } from "@lib"; +import type { ReactNode } from "react"; + export type PriorityLevel = "매우중요" | "중요" | "보통" | "낮음"; const PRIORITY_LEVELS: PriorityLevel[] = ["매우중요", "중요", "보통", "낮음"]; @@ -14,41 +16,47 @@ const PRIORITY_BG_COLOR: Record = { }; export interface PrioritySelectorProps { + trigger: ReactNode; selected?: PriorityLevel; onSelect?: (priority: PriorityLevel) => void; } export const PrioritySelector = ({ + trigger, selected, onSelect, }: PrioritySelectorProps) => { return ( - - {PRIORITY_LEVELS.map((priority) => { - const isSelected = priority === selected; - - return ( - onSelect?.(priority)} - className={cn( - "gap-2.25 py-0.5 pr-1 pl-2.75", - isSelected && PRIORITY_BG_COLOR[priority], - )} - > - - - + {trigger} + + + {PRIORITY_LEVELS.map((priority) => { + const isSelected = priority === selected; + + return ( + onSelect?.(priority)} className={cn( - "typo-headline-r-14 whitespace-nowrap transition-colors duration-200 ease-in-out", - isSelected ? "text-white" : "text-timo-black", + "gap-2.25 py-0.5 pr-1 pl-2.75", + isSelected && PRIORITY_BG_COLOR[priority], )} > - {priority} - - - ); - })} + + + + {priority} + + + ); + })} + ); }; diff --git a/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.stories.tsx b/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.stories.tsx index be952228..2852a30a 100644 --- a/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.stories.tsx +++ b/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.stories.tsx @@ -27,22 +27,28 @@ const meta = { export default meta; type Story = StoryObj; +const TRIGGER = ( + + 태그 + +); + export const Default: Story = { - args: {}, + args: { trigger: TRIGGER }, }; export const 일상: Story = { - args: { selected: "일상" }, + args: { trigger: TRIGGER, selected: "일상" }, }; export const 운동: Story = { - args: { selected: "운동" }, + args: { trigger: TRIGGER, selected: "운동" }, }; export const 업무: Story = { - args: { selected: "업무" }, + args: { trigger: TRIGGER, selected: "업무" }, }; export const 기타: Story = { - args: { selected: "기타" }, + args: { trigger: TRIGGER, selected: "기타" }, }; diff --git a/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.tsx b/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.tsx index dd16051d..9e155c95 100644 --- a/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.tsx +++ b/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.tsx @@ -2,50 +2,58 @@ import { Dropdown } from "@components/layout/dropdown/Dropdown"; import { PlusIcon } from "@icons"; import { cn } from "@lib"; +import type { ReactNode } from "react"; + export type Tag = "일상" | "운동" | "업무" | "기타"; const TAGS: Tag[] = ["일상", "운동", "업무", "기타"]; export interface TagSelectorProps { + trigger: ReactNode; selected?: Tag; onSelect?: (tag: Tag) => void; onAddClick?: () => void; } export const TagSelector = ({ + trigger, selected, onSelect, onAddClick, }: TagSelectorProps) => { return ( - {TAGS.map((tag) => { - const isSelected = tag === selected; - - return ( - onSelect?.(tag)} - className={cn("px-1.5 py-1", isSelected && "bg-timo-gray-500")} - > - {trigger} + + + {TAGS.map((tag) => { + const isSelected = tag === selected; + + return ( + onSelect?.(tag)} + className={cn("px-1.5 py-1", isSelected && "bg-timo-gray-500")} > - {tag} - - - ); - })} - - - - 추가 - - - + + {tag} + + + ); + })} + + + + 추가 + + + + ); }; From b9d9d6c93156a618460ce280c8524a722d7f06bf Mon Sep 17 00:00:00 2001 From: kimminna Date: Thu, 2 Jul 2026 16:24:00 +0900 Subject: [PATCH 06/12] =?UTF-8?q?fix(ui):=20TagSelector=20=EB=B2=94?= =?UTF-8?q?=EC=9A=A9=ED=99=94=20=EB=B0=8F=20Dropdown=20=EC=A0=91=EA=B7=BC?= =?UTF-8?q?=EC=84=B1=20=EA=B0=9C=EC=84=A0=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TagSelector에 하드코딩된 태그 목록을 제거하고 tags prop으로 외부에서 주입받도록 변경했습니다 - DropdownItemProps를 ButtonHTMLAttributes로 확장해 aria-pressed, disabled 등 네이티브 버튼 속성을 전달할 수 있도록 했습니다 - PrioritySelector, TagSelector의 선택된 항목에 aria-pressed를 추가해 스크린리더가 선택 상태를 인지할 수 있도록 했습니다 --- .../src/components/layout/dropdown/Dropdown.tsx | 17 +++++------------ .../priority-selector/PrioritySelector.tsx | 1 + .../tag/tag-selector/TagSelector.stories.tsx | 12 +++++++----- .../components/tag/tag-selector/TagSelector.tsx | 13 ++++++------- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx index 55278633..1d79d4c2 100644 --- a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx +++ b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx @@ -1,5 +1,3 @@ -"use client"; - import { cn } from "@lib"; import { createContext, @@ -7,6 +5,7 @@ import { useEffect, useRef, useState, + type ButtonHTMLAttributes, type ReactNode, } from "react"; @@ -102,24 +101,18 @@ const DropdownPanel = ({ children, className }: DropdownPanelProps) => { ); }; -export interface DropdownItemProps { - children: ReactNode; - className?: string; - onClick?: () => void; -} +export type DropdownItemProps = ButtonHTMLAttributes; -const DropdownItem = ({ children, className, onClick }: DropdownItemProps) => { +const DropdownItem = ({ className, ...rest }: DropdownItemProps) => { return ( + /> ); }; diff --git a/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx index c1a88c09..f4354e97 100644 --- a/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx +++ b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx @@ -38,6 +38,7 @@ export const PrioritySelector = ({ onSelect?.(priority)} + aria-pressed={isSelected} className={cn( "gap-2.25 py-0.5 pr-1 pl-2.75", isSelected && PRIORITY_BG_COLOR[priority], diff --git a/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.stories.tsx b/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.stories.tsx index 2852a30a..676ee259 100644 --- a/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.stories.tsx +++ b/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.stories.tsx @@ -33,22 +33,24 @@ const TRIGGER = ( ); +const TAGS = ["일상", "운동", "업무", "기타"]; + export const Default: Story = { - args: { trigger: TRIGGER }, + args: { trigger: TRIGGER, tags: TAGS }, }; export const 일상: Story = { - args: { trigger: TRIGGER, selected: "일상" }, + args: { trigger: TRIGGER, tags: TAGS, selected: "일상" }, }; export const 운동: Story = { - args: { trigger: TRIGGER, selected: "운동" }, + args: { trigger: TRIGGER, tags: TAGS, selected: "운동" }, }; export const 업무: Story = { - args: { trigger: TRIGGER, selected: "업무" }, + args: { trigger: TRIGGER, tags: TAGS, selected: "업무" }, }; export const 기타: Story = { - args: { trigger: TRIGGER, selected: "기타" }, + args: { trigger: TRIGGER, tags: TAGS, selected: "기타" }, }; diff --git a/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.tsx b/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.tsx index 9e155c95..3f4a606e 100644 --- a/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.tsx +++ b/packages/timo-design-system/src/components/tag/tag-selector/TagSelector.tsx @@ -4,19 +4,17 @@ import { cn } from "@lib"; import type { ReactNode } from "react"; -export type Tag = "일상" | "운동" | "업무" | "기타"; - -const TAGS: Tag[] = ["일상", "운동", "업무", "기타"]; - export interface TagSelectorProps { trigger: ReactNode; - selected?: Tag; - onSelect?: (tag: Tag) => void; + tags: string[]; + selected?: string; + onSelect?: (tag: string) => void; onAddClick?: () => void; } export const TagSelector = ({ trigger, + tags, selected, onSelect, onAddClick, @@ -26,13 +24,14 @@ export const TagSelector = ({ {trigger} - {TAGS.map((tag) => { + {tags.map((tag) => { const isSelected = tag === selected; return ( onSelect?.(tag)} + aria-pressed={isSelected} className={cn("px-1.5 py-1", isSelected && "bg-timo-gray-500")} > Date: Thu, 2 Jul 2026 16:27:47 +0900 Subject: [PATCH 07/12] =?UTF-8?q?fix(ui):=20Dropdown=EC=97=90=20Escape=20?= =?UTF-8?q?=ED=82=A4=20=EB=8B=AB=ED=9E=98=20=EB=8F=99=EC=9E=91=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 바깥 클릭뿐 아니라 Escape 키 입력 시에도 Dropdown이 닫히도록 keydown 리스너를 추가했습니다 - 키보드 사용자도 패널을 닫을 수 있도록 접근성을 개선했습니다 --- .../src/components/layout/dropdown/Dropdown.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx index 1d79d4c2..b8e2db1c 100644 --- a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx +++ b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx @@ -47,9 +47,19 @@ const DropdownRoot = ({ children, className }: DropdownProps) => { } }; + const handleEscapeKeydown = (event: KeyboardEvent) => { + if (event.key === "Escape") { + setIsOpen(false); + } + }; + document.addEventListener("mousedown", handleOutsideClick); + document.addEventListener("keydown", handleEscapeKeydown); - return () => document.removeEventListener("mousedown", handleOutsideClick); + return () => { + document.removeEventListener("mousedown", handleOutsideClick); + document.removeEventListener("keydown", handleEscapeKeydown); + }; }, [isOpen]); const toggle = () => setIsOpen((prev) => !prev); From 68c7e4f381cd3eeebd6868127cb73e4cb631c8d7 Mon Sep 17 00:00:00 2001 From: kimminna Date: Sat, 4 Jul 2026 20:02:43 +0900 Subject: [PATCH 08/12] =?UTF-8?q?fix(root):=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=9C=EB=84=88=EB=A0=88=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EB=A6=AC=20=ED=85=9C=ED=94=8C=EB=A6=BF=20imp?= =?UTF-8?q?ort=20=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 스토리 템플릿의 컴포넌트 import를 상대 경로에서 tsconfig 알리아스 기반 절대 경로(@components/...)로 변경했습니다 --- turbo/generators/templates/component/component.stories.tsx.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/turbo/generators/templates/component/component.stories.tsx.hbs b/turbo/generators/templates/component/component.stories.tsx.hbs index 32aa5b0f..d66b219a 100644 --- a/turbo/generators/templates/component/component.stories.tsx.hbs +++ b/turbo/generators/templates/component/component.stories.tsx.hbs @@ -1,4 +1,4 @@ -import { {{pascalCase name}} } from "./{{pascalCase name}}"; +import { {{pascalCase name}} } from "@components/{{kebabCase name}}/{{pascalCase name}}"; import type { Meta, StoryObj } from "@storybook/react"; From 9ec2f46e95dd6558adb5a991a9076c94bfb1ce54 Mon Sep 17 00:00:00 2001 From: kimminna Date: Sat, 4 Jul 2026 20:10:51 +0900 Subject: [PATCH 09/12] =?UTF-8?q?chore(ui):=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=9E=84=ED=8F=AC=ED=8A=B8=EB=AC=B8=20=EC=A0=88?= =?UTF-8?q?=EB=8C=80=20=EA=B2=BD=EB=A1=9C=EB=A1=9C=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/layout/dropdown/Dropdown.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.stories.tsx b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.stories.tsx index 4618c68d..d8a9e687 100644 --- a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.stories.tsx +++ b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.stories.tsx @@ -1,4 +1,4 @@ -import { Dropdown } from "./Dropdown"; +import { Dropdown } from "@components/layout/dropdown/Dropdown"; import type { Meta, StoryObj } from "@storybook/react"; From 7a9622a0e1e792f259fe2a4831c7a9264e9bca7a Mon Sep 17 00:00:00 2001 From: kimminna Date: Sun, 5 Jul 2026 01:19:38 +0900 Subject: [PATCH 10/12] =?UTF-8?q?refactor(ui):=20PriorityLevel=20=EA=B0=92?= =?UTF-8?q?=EA=B3=BC=20=ED=91=9C=EC=8B=9C=20=EB=9D=BC=EB=B2=A8=EC=9D=84=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PriorityLevel/Priority 타입 값을 영문 키(urgent/high/medium/low)로 변경했습니다 - 표시용 한글 라벨은 PRIORITY_LABEL로 분리해 값과 라벨의 결합도를 낮췄습니다 --- .../priority-icon/PriorityIcon.stories.tsx | 20 +++++++++--------- .../priority/priority-icon/PriorityIcon.tsx | 16 +++++++------- .../PrioritySelector.stories.tsx | 10 ++++----- .../priority-selector/PrioritySelector.tsx | 21 ++++++++++++------- 4 files changed, 37 insertions(+), 30 deletions(-) diff --git a/packages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.stories.tsx b/packages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.stories.tsx index eff68835..73b72b62 100644 --- a/packages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.stories.tsx +++ b/packages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.stories.tsx @@ -11,7 +11,7 @@ const meta = { argTypes: { priority: { control: "select", - options: ["매우중요", "중요", "보통", "낮음", "Disable", "white", "blue"], + options: ["urgent", "high", "medium", "low", "Disable", "white", "blue"], }, }, } satisfies Meta; @@ -20,19 +20,19 @@ export default meta; type Story = StoryObj; export const 매우중요: Story = { - args: { priority: "매우중요" }, + args: { priority: "urgent" }, }; export const 중요: Story = { - args: { priority: "중요" }, + args: { priority: "high" }, }; export const 보통: Story = { - args: { priority: "보통" }, + args: { priority: "medium" }, }; export const 낮음: Story = { - args: { priority: "낮음" }, + args: { priority: "low" }, }; export const Disable: Story = { @@ -57,17 +57,17 @@ export const blue: Story = { }; const ALL_PRIORITIES: Priority[] = [ - "매우중요", - "중요", - "보통", - "낮음", + "urgent", + "high", + "medium", + "low", "Disable", "white", "blue", ]; export const AllPriorities: Story = { - args: { priority: "매우중요" }, + args: { priority: "urgent" }, parameters: { backgrounds: { default: "light-gray", diff --git a/packages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.tsx b/packages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.tsx index abc9c91d..328b3797 100644 --- a/packages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.tsx +++ b/packages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.tsx @@ -1,19 +1,19 @@ import { cn } from "@lib"; export type Priority = - | "매우중요" - | "중요" - | "보통" - | "낮음" + | "urgent" + | "high" + | "medium" + | "low" | "Disable" | "white" | "blue"; const PRIORITY_COLOR: Record = { - 매우중요: "bg-timo-red", - 중요: "bg-timo-orange", - 보통: "bg-timo-gray-600", - 낮음: "bg-timo-black", + urgent: "bg-timo-red", + high: "bg-timo-orange", + medium: "bg-timo-gray-600", + low: "bg-timo-black", Disable: "bg-timo-gray-500", white: "bg-white", blue: "bg-timo-blue-300", diff --git a/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.stories.tsx b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.stories.tsx index 0c6564bf..ac1b5f03 100644 --- a/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.stories.tsx +++ b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.stories.tsx @@ -19,7 +19,7 @@ const meta = { argTypes: { selected: { control: "select", - options: ["매우중요", "중요", "보통", "낮음"], + options: ["urgent", "high", "medium", "low"], }, }, } satisfies Meta; @@ -38,17 +38,17 @@ export const Default: Story = { }; export const 매우중요: Story = { - args: { trigger: TRIGGER, selected: "매우중요" }, + args: { trigger: TRIGGER, selected: "urgent" }, }; export const 중요: Story = { - args: { trigger: TRIGGER, selected: "중요" }, + args: { trigger: TRIGGER, selected: "high" }, }; export const 보통: Story = { - args: { trigger: TRIGGER, selected: "보통" }, + args: { trigger: TRIGGER, selected: "medium" }, }; export const 낮음: Story = { - args: { trigger: TRIGGER, selected: "낮음" }, + args: { trigger: TRIGGER, selected: "low" }, }; diff --git a/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx index f4354e97..a761cd40 100644 --- a/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx +++ b/packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx @@ -4,15 +4,22 @@ import { cn } from "@lib"; import type { ReactNode } from "react"; -export type PriorityLevel = "매우중요" | "중요" | "보통" | "낮음"; +export type PriorityLevel = "urgent" | "high" | "medium" | "low"; -const PRIORITY_LEVELS: PriorityLevel[] = ["매우중요", "중요", "보통", "낮음"]; +const PRIORITY_LEVELS: PriorityLevel[] = ["urgent", "high", "medium", "low"]; + +const PRIORITY_LABEL: Record = { + urgent: "매우중요", + high: "중요", + medium: "보통", + low: "낮음", +}; const PRIORITY_BG_COLOR: Record = { - 매우중요: "bg-timo-red", - 중요: "bg-timo-orange", - 보통: "bg-timo-gray", - 낮음: "bg-timo-gray-900", + urgent: "bg-timo-red", + high: "bg-timo-orange", + medium: "bg-timo-gray", + low: "bg-timo-gray-900", }; export interface PrioritySelectorProps { @@ -52,7 +59,7 @@ export const PrioritySelector = ({ isSelected ? "text-white" : "text-timo-black", )} > - {priority} + {PRIORITY_LABEL[priority]} ); From a625897e4491496b04711f8f893320db347b9d51 Mon Sep 17 00:00:00 2001 From: kimminna Date: Sun, 5 Jul 2026 01:23:57 +0900 Subject: [PATCH 11/12] =?UTF-8?q?fix(ui):=20Dropdown=EC=97=90=20use=20clie?= =?UTF-8?q?nt=20=EC=A7=80=EC=8B=9C=EC=96=B4=20=EC=B6=94=EA=B0=80=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - useState/useEffect/useRef/useContext를 사용하는 Dropdown이 Next.js App Router에서 Server Component로 오인되지 않도록 use client 경계를 명시했습니다 --- .../src/components/layout/dropdown/Dropdown.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx index b8e2db1c..e1445d75 100644 --- a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx +++ b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx @@ -1,3 +1,5 @@ +"use client"; + import { cn } from "@lib"; import { createContext, From 8950c6ead9a986f99cfd03b8065d2201a109c0c4 Mon Sep 17 00:00:00 2001 From: kimminna Date: Sun, 5 Jul 2026 01:30:12 +0900 Subject: [PATCH 12/12] =?UTF-8?q?fix(ui):=20Dropdown.Trigger=EC=97=90=20ar?= =?UTF-8?q?ia-expanded=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20aria-haspopup=20?= =?UTF-8?q?=EC=86=8C=EB=B9=84=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EC=9C=84=EC=9E=84=20(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dropdown.Trigger가 isOpen 상태를 aria-expanded로 자동 노출하도록 했습니다 - Dropdown.Trigger가 버튼 속성을 그대로 전달받도록 해, aria-haspopup 값을 각 소비 컴포넌트가 자신의 콘텐츠 구조에 맞게 지정할 수 있게 했습니다 - TagSelector, PrioritySelector에 aria-haspopup="menu"를 추가했습니다 --- .../components/layout/dropdown/Dropdown.tsx | 19 ++++++++++--------- .../priority-selector/PrioritySelector.tsx | 2 +- .../tag/tag-selector/TagSelector.tsx | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx index e1445d75..c254fb76 100644 --- a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx +++ b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx @@ -76,18 +76,19 @@ const DropdownRoot = ({ children, className }: DropdownProps) => { ); }; -export interface DropdownTriggerProps { - children: ReactNode; - className?: string; -} +export type DropdownTriggerProps = ButtonHTMLAttributes; -const DropdownTrigger = ({ children, className }: DropdownTriggerProps) => { - const { toggle } = useDropdownContext(); +const DropdownTrigger = ({ className, ...rest }: DropdownTriggerProps) => { + const { isOpen, toggle } = useDropdownContext(); return ( - +