From f1674e6873609091ac44ffefa0327f713e482a54 Mon Sep 17 00:00:00 2001 From: kimminna Date: Sun, 5 Jul 2026 18:15:10 +0900 Subject: [PATCH 01/10] =?UTF-8?q?feat(ui):=20RepeatSelector=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 반복 일정(매일/매주/매달) 선택 드롭다운 컴포넌트를 추가했습니다 - 선택된 빈도에 따라 세부 설정(주간 요일 체크박스 / 월간 반복일 입력) 섹션이 교체되도록 구현했습니다 - 월간 세부 설정 값이 피그마 디자인처럼 우측에 고정되도록 정렬했습니다 - Dropdown 트리거와 패널 사이 여백을 제거해 피그마 디자인과 일치시켰습니다 --- .../src/components/index.ts | 1 + .../components/layout/dropdown/Dropdown.tsx | 2 +- .../RepeatSelector.stories.tsx | 153 +++++++++++++ .../repeat/repeat-selector/RepeatSelector.tsx | 206 ++++++++++++++++++ 4 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.stories.tsx create mode 100644 packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx diff --git a/packages/timo-design-system/src/components/index.ts b/packages/timo-design-system/src/components/index.ts index 4e1baf22..16350b51 100644 --- a/packages/timo-design-system/src/components/index.ts +++ b/packages/timo-design-system/src/components/index.ts @@ -9,3 +9,4 @@ export { CreateButton } from "@components/button/create-button/CreateButton"; export { AddTaskButton } from "@components/button/add-task-button/AddTaskButton"; export { TodayBadge } from "@components/badge/today-badge/TodayBadge"; export { PlayButton } from "@components/button/play-button/PlayButton"; +export { RepeatSelector } from "@components/repeat/repeat-selector/RepeatSelector"; 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 c254fb76..8522cc88 100644 --- a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx +++ b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx @@ -105,7 +105,7 @@ const DropdownPanel = ({ children, className }: DropdownPanelProps) => { return (
diff --git a/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.stories.tsx b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.stories.tsx new file mode 100644 index 00000000..f6501ead --- /dev/null +++ b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.stories.tsx @@ -0,0 +1,153 @@ +import { RepeatSelector } from "@components/repeat/repeat-selector/RepeatSelector"; +import { useState } from "react"; + +import type { Meta, StoryObj } from "@storybook/react"; + +const meta = { + title: "Components/Repeat/RepeatSelector", + component: RepeatSelector, + parameters: { + layout: "centered", + backgrounds: { + default: "light-gray", + values: [ + { name: "light-gray", value: "#F5F5F5" }, + { name: "dark", value: "#333333" }, + { name: "white", value: "#FFFFFF" }, + ], + }, + }, + argTypes: { + frequency: { + control: "select", + options: ["daily", "weekly", "monthly"], + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const TRIGGER = ( + + 반복 설정 + +); + +const KOREAN_OPTIONS = [ + { frequency: "daily", label: "매일" }, + { frequency: "weekly", label: "매주" }, + { frequency: "monthly", label: "매달" }, +] as const; + +const WEEKDAYS_KO = [ + { id: "mon", label: "월요일마다" }, + { id: "tue", label: "화요일마다" }, + { id: "wed", label: "수요일마다" }, + { id: "thu", label: "목요일마다" }, + { id: "fri", label: "금요일마다" }, + { id: "sat", label: "토요일마다" }, + { id: "sun", label: "일요일마다" }, +]; + +export const Daily: Story = { + args: { + trigger: TRIGGER, + detailHeading: "세부 설정", + options: [...KOREAN_OPTIONS], + frequency: "daily", + weekly: { + weekdays: WEEKDAYS_KO, + selectedWeekdayIds: [], + }, + monthly: { + repeatDayLabel: "일 마다", + repeatDay: "3", + }, + }, +}; + +const WeeklyDemo = () => { + const [selectedWeekdayIds, setSelectedWeekdayIds] = useState([]); + + return ( + + setSelectedWeekdayIds((prev) => + prev.includes(id) + ? prev.filter((item) => item !== id) + : [...prev, id], + ), + }} + monthly={{ + repeatDayLabel: "일 마다", + repeatDay: "3", + }} + /> + ); +}; + +export const Weekly: Story = { + args: { + trigger: TRIGGER, + detailHeading: "세부 설정", + options: [...KOREAN_OPTIONS], + frequency: "weekly", + weekly: { + weekdays: WEEKDAYS_KO, + selectedWeekdayIds: [], + }, + monthly: { + repeatDayLabel: "일 마다", + repeatDay: "3", + }, + }, + render: () => , +}; + +const MonthlyDemo = () => { + const [repeatDay, setRepeatDay] = useState("3"); + + return ( + + ); +}; + +export const Monthly: Story = { + args: { + trigger: TRIGGER, + detailHeading: "세부 설정", + options: [...KOREAN_OPTIONS], + frequency: "monthly", + weekly: { + weekdays: WEEKDAYS_KO, + selectedWeekdayIds: [], + }, + monthly: { + repeatDayLabel: "일 마다", + repeatDay: "3", + }, + }, + render: () => , +}; diff --git a/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx new file mode 100644 index 00000000..8977a5e5 --- /dev/null +++ b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx @@ -0,0 +1,206 @@ +"use client"; + +import { Checkbox } from "@components/checkbox/Checkbox"; +import { Dropdown } from "@components/layout/dropdown/Dropdown"; +import { ChevronDownIcon } from "@icons"; +import { cn } from "@lib"; +import { useState } from "react"; + +import type { ReactNode } from "react"; + +export type RepeatFrequency = "daily" | "weekly" | "monthly"; + +export interface RepeatOption { + frequency: RepeatFrequency; + label: string; +} + +export interface WeekdayOption { + id: string; + label: string; +} + +export interface RepeatWeeklyDetail { + weekdays: WeekdayOption[]; + selectedWeekdayIds: string[]; + onWeekdayToggle?: (id: string) => void; +} + +export interface RepeatMonthlyDetail { + repeatDayLabel: string; + repeatDay: string; + onRepeatDayChange?: (value: string) => void; +} + +const DETAIL_ALIGN: Record = { + daily: "items-start", + weekly: "items-start", + monthly: "items-end", +}; + +export interface RepeatSelectorProps { + trigger: ReactNode; + detailHeading: string; + options: RepeatOption[]; + frequency: RepeatFrequency; + onFrequencyChange?: (frequency: RepeatFrequency) => void; + weekly: RepeatWeeklyDetail; + monthly: RepeatMonthlyDetail; +} + +interface RepeatFrequencyListProps { + options: RepeatOption[]; + selectedFrequency: RepeatFrequency; + onSelect: (frequency: RepeatFrequency) => void; +} + +const RepeatFrequencyList = ({ + options, + selectedFrequency, + onSelect, +}: RepeatFrequencyListProps) => { + return ( +
+ {options.map(({ frequency: value, label }, index) => ( +
+ {index > 0 && ( +
+ )} + onSelect(value)} + aria-pressed={value === selectedFrequency} + > + + {label} + + +
+ ))} +
+ ); +}; + +const RepeatWeeklyDetailSection = ({ + weekdays, + selectedWeekdayIds, + onWeekdayToggle, +}: RepeatWeeklyDetail) => { + return ( +
+ {weekdays.map(({ id, label }) => ( +
+ + {label} + + onWeekdayToggle?.(id)} + /> +
+ ))} +
+ ); +}; + +type RepeatMonthlyDetailSectionProps = RepeatMonthlyDetail & { + ariaLabel: string; +}; + +const RepeatMonthlyDetailSection = ({ + repeatDayLabel, + repeatDay, + onRepeatDayChange, + ariaLabel, +}: RepeatMonthlyDetailSectionProps) => { + return ( +
+
+ onRepeatDayChange?.(event.target.value)} + aria-label={ariaLabel} + className="typo-headline-m-14 text-timo-black w-full bg-transparent text-right outline-none" + /> +
+ + {repeatDayLabel} + +
+ ); +}; + +export const RepeatSelector = ({ + trigger, + detailHeading, + options, + frequency, + onFrequencyChange, + weekly, + monthly, +}: RepeatSelectorProps) => { + const [isPicking, setIsPicking] = useState(true); + + const selectedLabel = options.find( + (option) => option.frequency === frequency, + )?.label; + + const handleSelectFrequency = (value: RepeatFrequency) => { + onFrequencyChange?.(value); + setIsPicking(false); + }; + + return ( + + {trigger} + + + + + {isPicking ? ( + + ) : ( +
+ + {detailHeading} + + + {frequency === "weekly" && ( + + )} + {frequency === "monthly" && ( + + )} +
+ )} +
+
+ ); +}; From 0b63b02ac0789e5e8f9c8b73127f4be1c4ecd82f Mon Sep 17 00:00:00 2001 From: kimminna Date: Sun, 5 Jul 2026 18:23:07 +0900 Subject: [PATCH 02/10] =?UTF-8?q?feat(ui):=20TimeSelector=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 예상 시간을 직접 입력하거나 목록에서 선택할 수 있는 드롭다운 컴포넌트를 추가했습니다 - AI 추천 시간 선택 버튼과 활성화 상태 스타일을 구현했습니다 --- .../src/components/index.ts | 1 + .../time-selector/TimeSelector.stories.tsx | 54 +++++++++ .../time/time-selector/TimeSelector.tsx | 104 ++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 packages/timo-design-system/src/components/time/time-selector/TimeSelector.stories.tsx create mode 100644 packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx diff --git a/packages/timo-design-system/src/components/index.ts b/packages/timo-design-system/src/components/index.ts index 16350b51..1367d79c 100644 --- a/packages/timo-design-system/src/components/index.ts +++ b/packages/timo-design-system/src/components/index.ts @@ -9,4 +9,5 @@ export { CreateButton } from "@components/button/create-button/CreateButton"; export { AddTaskButton } from "@components/button/add-task-button/AddTaskButton"; export { TodayBadge } from "@components/badge/today-badge/TodayBadge"; export { PlayButton } from "@components/button/play-button/PlayButton"; +export { TimeSelector } from "@components/time/time-selector/TimeSelector"; export { RepeatSelector } from "@components/repeat/repeat-selector/RepeatSelector"; diff --git a/packages/timo-design-system/src/components/time/time-selector/TimeSelector.stories.tsx b/packages/timo-design-system/src/components/time/time-selector/TimeSelector.stories.tsx new file mode 100644 index 00000000..6eeee573 --- /dev/null +++ b/packages/timo-design-system/src/components/time/time-selector/TimeSelector.stories.tsx @@ -0,0 +1,54 @@ +import { TimeSelector } from "@components/time/time-selector/TimeSelector"; + +import type { Meta, StoryObj } from "@storybook/react"; + +const meta = { + title: "Components/Time/TimeSelector", + component: TimeSelector, + 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: ["ai", 15, 30, 45, 60, 120], + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const TRIGGER = ( + + 예상 시간 + +); + +const TIMES = [ + { minute: 15, value: "15", unit: "min" }, + { minute: 30, value: "30", unit: "min" }, + { minute: 45, value: "45", unit: "min" }, + { minute: 60, value: "1", unit: "h" }, + { minute: 120, value: "2", unit: "h" }, +]; + +export const Default: Story = { + args: { trigger: TRIGGER, times: TIMES }, +}; + +export const Selected: Story = { + args: { trigger: TRIGGER, times: TIMES, selected: 60 }, +}; + +export const AiSelected: Story = { + args: { trigger: TRIGGER, times: TIMES, selected: "ai" }, +}; diff --git a/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx b/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx new file mode 100644 index 00000000..c3d08f37 --- /dev/null +++ b/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx @@ -0,0 +1,104 @@ +import { Dropdown } from "@components/layout/dropdown/Dropdown"; +import { AiDefaultIcon, AiWhiteIcon } from "@icons"; +import { cn } from "@lib"; + +import type { ReactNode } from "react"; + +export type TimeSelection = number | "ai"; + +export interface TimeOption { + minute: number; + value: string; + unit: string; +} + +export interface TimeSelectorProps { + trigger: ReactNode; + time?: string; + onTimeChange?: (time: string) => void; + times: TimeOption[]; + selected?: TimeSelection; + onSelect?: (value: TimeSelection) => void; +} + +export const TimeSelector = ({ + trigger, + time = "00:00", + onTimeChange, + times, + selected, + onSelect, +}: TimeSelectorProps) => { + const isAiSelected = selected === "ai"; + + return ( + + {trigger} + + +
+ + + onTimeChange?.(event.target.value)} + aria-label="예상 시간 직접 입력" + className={cn( + "typo-headline-r-14 min-w-0 flex-1 bg-transparent text-right outline-none", + isAiSelected ? "text-white" : "text-timo-black", + )} + /> +
+ +
+ {times.map(({ minute, value, unit }, index) => { + const isSelected = minute === selected; + + return ( + onSelect?.(minute)} + aria-pressed={isSelected} + className={cn( + "justify-between px-[3.5px] py-[2.5px]", + isSelected && "bg-timo-blue-300", + )} + > + + {value} + + + {unit} + + + ); + })} +
+
+
+ ); +}; From c0f0161dadbc982eac3a9c1276be4b5f49ca21c5 Mon Sep 17 00:00:00 2001 From: kimminna Date: Sun, 5 Jul 2026 20:45:09 +0900 Subject: [PATCH 03/10] =?UTF-8?q?feat(ui):=20Dropdown.Item=EC=97=90=20clos?= =?UTF-8?q?eOnSelect=20=EC=98=B5=EC=85=98=20=EC=B6=94=EA=B0=80=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 아이템 선택 시 닫히는 기본 동작은 유지하되, closeOnSelect={false}로 개별 소비 컴포넌트가 이를 끌 수 있도록 옵션을 추가했습니다 --- .../src/components/layout/dropdown/Dropdown.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 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 e7534d68..3ff2347e 100644 --- a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx +++ b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx @@ -129,9 +129,16 @@ const DropdownPanel = ({ ); }; -export type DropdownItemProps = ButtonHTMLAttributes; +export interface DropdownItemProps extends ButtonHTMLAttributes { + closeOnSelect?: boolean; +} -const DropdownItem = ({ className, onClick, ...rest }: DropdownItemProps) => { +const DropdownItem = ({ + className, + onClick, + closeOnSelect = true, + ...rest +}: DropdownItemProps) => { const { close } = useDropdownContext(); return ( @@ -140,7 +147,7 @@ const DropdownItem = ({ className, onClick, ...rest }: DropdownItemProps) => { {...rest} onClick={(e) => { onClick?.(e); - close(); + if (closeOnSelect) close(); }} className={cn( "rounded-4 flex w-full items-center transition-colors duration-200 ease-in-out", From 8ad8a116446b42d0451e0afd0c773ffa4bb1cf4d Mon Sep 17 00:00:00 2001 From: kimminna Date: Sun, 5 Jul 2026 20:46:10 +0900 Subject: [PATCH 04/10] =?UTF-8?q?fix(ui):=20RepeatSelector=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=EC=83=81=ED=83=9C=20=EB=8F=99=EA=B8=B0=ED=99=94=20?= =?UTF-8?q?=EB=B0=8F=20=EB=B0=98=EB=B3=B5=20=EC=9D=BC=EC=A0=95=20=EB=9D=BC?= =?UTF-8?q?=EB=B2=A8=20=EC=B6=94=EA=B0=80=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 빈도 선택 시 드롭다운이 닫히지 않고 계속 열려 있도록 closeOnSelect={false}를 적용했습니다 - 선택된 빈도를 내부 state로 관리해, 부모의 onFrequencyChange 연결 여부와 무관하게 클릭 즉시 라벨과 상세 섹션이 동기화되도록 고쳤습니다 - 피그마 디자인에 맞춰 트리거 버튼 위에 "반복 일정" 라벨을 추가했습니다 --- .../repeat/repeat-selector/RepeatSelector.tsx | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx index 8977a5e5..a9bf4305 100644 --- a/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx +++ b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx @@ -68,6 +68,7 @@ const RepeatFrequencyList = ({ )} onSelect(value)} + closeOnSelect={false} aria-pressed={value === selectedFrequency} > @@ -140,12 +141,15 @@ export const RepeatSelector = ({ monthly, }: RepeatSelectorProps) => { const [isPicking, setIsPicking] = useState(true); + const [selectedFrequency, setSelectedFrequency] = + useState(frequency); const selectedLabel = options.find( - (option) => option.frequency === frequency, + (option) => option.frequency === selectedFrequency, )?.label; const handleSelectFrequency = (value: RepeatFrequency) => { + setSelectedFrequency(value); onFrequencyChange?.(value); setIsPicking(false); }; @@ -155,44 +159,50 @@ export const RepeatSelector = ({ {trigger} - + + +
{isPicking ? ( ) : (
{detailHeading} - {frequency === "weekly" && ( + {selectedFrequency === "weekly" && ( )} - {frequency === "monthly" && ( + {selectedFrequency === "monthly" && ( Date: Sun, 5 Jul 2026 20:46:39 +0900 Subject: [PATCH 05/10] =?UTF-8?q?fix(ui):=20TimeSelector=20=EC=9D=B8?= =?UTF-8?q?=ED=92=8B=20=ED=8F=AC=EC=BB=A4=EC=8A=A4=20=ED=91=9C=EC=8B=9C=20?= =?UTF-8?q?=EB=B3=B5=EA=B5=AC=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - outline-none으로 사라졌던 키보드 포커스 표시를 focus-visible:ring-2로 복구했습니다 --- .../src/components/time/time-selector/TimeSelector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx b/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx index c3d08f37..da63dd09 100644 --- a/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx +++ b/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx @@ -58,7 +58,7 @@ export const TimeSelector = ({ onChange={(event) => onTimeChange?.(event.target.value)} aria-label="예상 시간 직접 입력" className={cn( - "typo-headline-r-14 min-w-0 flex-1 bg-transparent text-right outline-none", + "typo-headline-r-14 focus-visible:ring-timo-blue-300 min-w-0 flex-1 rounded-xs bg-transparent text-right outline-none focus-visible:ring-2", isAiSelected ? "text-white" : "text-timo-black", )} /> From b2e5021b3c4e6fd9819755c4297692c0fb258897 Mon Sep 17 00:00:00 2001 From: kimminna Date: Mon, 6 Jul 2026 15:25:50 +0900 Subject: [PATCH 06/10] =?UTF-8?q?refactor(ui):=20RepeatSelector=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81=20(#8?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 옵션 항목을 감싸던 div className="contents"를 Fragment로 교체해 불필요한 DOM 노드를 없앴습니다 - 스토리의 중복된 기본 props를 meta 레벨 args로 올리고, 각 스토리는 frequency만 오버라이드하도록 정리했습니다 --- .../RepeatSelector.stories.tsx | 109 +++++++----------- .../repeat/repeat-selector/RepeatSelector.tsx | 6 +- 2 files changed, 44 insertions(+), 71 deletions(-) diff --git a/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.stories.tsx b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.stories.tsx index f6501ead..79c6ec16 100644 --- a/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.stories.tsx +++ b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.stories.tsx @@ -1,33 +1,11 @@ -import { RepeatSelector } from "@components/repeat/repeat-selector/RepeatSelector"; +import { + RepeatSelector, + type RepeatSelectorProps, +} from "@components/repeat/repeat-selector/RepeatSelector"; import { useState } from "react"; import type { Meta, StoryObj } from "@storybook/react"; -const meta = { - title: "Components/Repeat/RepeatSelector", - component: RepeatSelector, - parameters: { - layout: "centered", - backgrounds: { - default: "light-gray", - values: [ - { name: "light-gray", value: "#F5F5F5" }, - { name: "dark", value: "#333333" }, - { name: "white", value: "#FFFFFF" }, - ], - }, - }, - argTypes: { - frequency: { - control: "select", - options: ["daily", "weekly", "monthly"], - }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - const TRIGGER = ( 반복 설정 @@ -50,12 +28,30 @@ const WEEKDAYS_KO = [ { id: "sun", label: "일요일마다" }, ]; -export const Daily: Story = { +const meta = { + title: "Components/Repeat/RepeatSelector", + component: RepeatSelector, + parameters: { + layout: "centered", + backgrounds: { + default: "light-gray", + values: [ + { name: "light-gray", value: "#F5F5F5" }, + { name: "dark", value: "#333333" }, + { name: "white", value: "#FFFFFF" }, + ], + }, + }, + argTypes: { + frequency: { + control: "select", + options: ["daily", "weekly", "monthly"], + }, + }, args: { trigger: TRIGGER, detailHeading: "세부 설정", options: [...KOREAN_OPTIONS], - frequency: "daily", weekly: { weekdays: WEEKDAYS_KO, selectedWeekdayIds: [], @@ -65,18 +61,26 @@ export const Daily: Story = { repeatDay: "3", }, }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Daily: Story = { + args: { + frequency: "daily", + }, }; -const WeeklyDemo = () => { +const WeeklyDemo = (args: RepeatSelectorProps) => { const [selectedWeekdayIds, setSelectedWeekdayIds] = useState([]); return ( @@ -86,46 +90,26 @@ const WeeklyDemo = () => { : [...prev, id], ), }} - monthly={{ - repeatDayLabel: "일 마다", - repeatDay: "3", - }} /> ); }; export const Weekly: Story = { args: { - trigger: TRIGGER, - detailHeading: "세부 설정", - options: [...KOREAN_OPTIONS], frequency: "weekly", - weekly: { - weekdays: WEEKDAYS_KO, - selectedWeekdayIds: [], - }, - monthly: { - repeatDayLabel: "일 마다", - repeatDay: "3", - }, }, - render: () => , + render: (args) => , }; -const MonthlyDemo = () => { +const MonthlyDemo = (args: RepeatSelectorProps) => { const [repeatDay, setRepeatDay] = useState("3"); return ( { export const Monthly: Story = { args: { - trigger: TRIGGER, - detailHeading: "세부 설정", - options: [...KOREAN_OPTIONS], frequency: "monthly", - weekly: { - weekdays: WEEKDAYS_KO, - selectedWeekdayIds: [], - }, - monthly: { - repeatDayLabel: "일 마다", - repeatDay: "3", - }, }, - render: () => , + render: (args) => , }; diff --git a/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx index a9bf4305..2a553b04 100644 --- a/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx +++ b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx @@ -4,7 +4,7 @@ import { Checkbox } from "@components/checkbox/Checkbox"; import { Dropdown } from "@components/layout/dropdown/Dropdown"; import { ChevronDownIcon } from "@icons"; import { cn } from "@lib"; -import { useState } from "react"; +import { Fragment, useState } from "react"; import type { ReactNode } from "react"; @@ -62,7 +62,7 @@ const RepeatFrequencyList = ({ return (
{options.map(({ frequency: value, label }, index) => ( -
+ {index > 0 && (
)} @@ -75,7 +75,7 @@ const RepeatFrequencyList = ({ {label} -
+
))}
); From ebc64c99ccae6cc3328d3258e63fdd59d6e29a53 Mon Sep 17 00:00:00 2001 From: kimminna Date: Tue, 7 Jul 2026 00:26:52 +0900 Subject: [PATCH 07/10] =?UTF-8?q?refactor(ui):=20RepeatSelector,=20TimeSel?= =?UTF-8?q?ector=20import=20=EA=B2=BD=EB=A1=9C=EB=A5=BC=20=EC=83=81?= =?UTF-8?q?=EB=8C=80=20=EA=B2=BD=EB=A1=9C=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - alias import(@components, @icons, @lib)를 상대 경로 import로 변경했습니다 --- .../components/repeat/repeat-selector/RepeatSelector.tsx | 9 +++++---- .../src/components/time/time-selector/TimeSelector.tsx | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx index 2a553b04..f13ead3f 100644 --- a/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx +++ b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx @@ -1,11 +1,12 @@ "use client"; -import { Checkbox } from "@components/checkbox/Checkbox"; -import { Dropdown } from "@components/layout/dropdown/Dropdown"; -import { ChevronDownIcon } from "@icons"; -import { cn } from "@lib"; import { Fragment, useState } from "react"; +import { ChevronDownIcon } from "../../../icons"; +import { cn } from "../../../lib"; +import { Checkbox } from "../../checkbox/Checkbox"; +import { Dropdown } from "../../layout/dropdown/Dropdown"; + import type { ReactNode } from "react"; export type RepeatFrequency = "daily" | "weekly" | "monthly"; diff --git a/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx b/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx index da63dd09..147ca8fd 100644 --- a/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx +++ b/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx @@ -1,6 +1,6 @@ -import { Dropdown } from "@components/layout/dropdown/Dropdown"; -import { AiDefaultIcon, AiWhiteIcon } from "@icons"; -import { cn } from "@lib"; +import { AiDefaultIcon, AiWhiteIcon } from "../../../icons"; +import { cn } from "../../../lib"; +import { Dropdown } from "../../layout/dropdown/Dropdown"; import type { ReactNode } from "react"; From ddfc7b71001e5eb81c52f806534f46dcb9ba6e8d Mon Sep 17 00:00:00 2001 From: kimminna Date: Tue, 7 Jul 2026 00:31:18 +0900 Subject: [PATCH 08/10] =?UTF-8?q?refactor(ui):=20RepeatSelector,=20TimeSel?= =?UTF-8?q?ector=20=EC=8A=A4=ED=86=A0=EB=A6=AC=20import=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=EB=A5=BC=20=EC=83=81=EB=8C=80=20=EA=B2=BD=EB=A1=9C?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - alias import(@components)를 상대 경로 import로 변경했습니다 --- .../repeat/repeat-selector/RepeatSelector.stories.tsx | 6 ++---- .../components/time/time-selector/TimeSelector.stories.tsx | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.stories.tsx b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.stories.tsx index 79c6ec16..536dd72e 100644 --- a/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.stories.tsx +++ b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.stories.tsx @@ -1,9 +1,7 @@ -import { - RepeatSelector, - type RepeatSelectorProps, -} from "@components/repeat/repeat-selector/RepeatSelector"; import { useState } from "react"; +import { RepeatSelector, RepeatSelectorProps } from "./RepeatSelector"; + import type { Meta, StoryObj } from "@storybook/react"; const TRIGGER = ( diff --git a/packages/timo-design-system/src/components/time/time-selector/TimeSelector.stories.tsx b/packages/timo-design-system/src/components/time/time-selector/TimeSelector.stories.tsx index 6eeee573..e29b8414 100644 --- a/packages/timo-design-system/src/components/time/time-selector/TimeSelector.stories.tsx +++ b/packages/timo-design-system/src/components/time/time-selector/TimeSelector.stories.tsx @@ -1,4 +1,4 @@ -import { TimeSelector } from "@components/time/time-selector/TimeSelector"; +import { TimeSelector } from "./TimeSelector"; import type { Meta, StoryObj } from "@storybook/react"; From 06310972cede21ae811bf1a0055ed3491d802b17 Mon Sep 17 00:00:00 2001 From: kimminna Date: Tue, 7 Jul 2026 02:29:44 +0900 Subject: [PATCH 09/10] =?UTF-8?q?fix(ui):=20RepeatSelector=20=EC=9B=94?= =?UTF-8?q?=EA=B0=84=20=EB=B0=98=EB=B3=B5=20=EC=9E=85=EB=A0=A5=EC=9D=98=20?= =?UTF-8?q?=ED=8F=AC=EC=BB=A4=EC=8A=A4=20=ED=91=9C=EC=8B=9C=EC=99=80=20?= =?UTF-8?q?=EB=9D=BC=EB=B2=A8=20=EC=97=B0=EB=8F=99=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - outline-none으로 제거되어 있던 입력창 포커스 스타일을 focus-visible:ring-2로 대체했습니다 - input에 useId로 생성한 고유 id를 부여하고 sr-only label과 htmlFor로 연결했습니다 --- .../repeat/repeat-selector/RepeatSelector.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx index f13ead3f..07eb816e 100644 --- a/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx +++ b/packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx @@ -1,6 +1,6 @@ "use client"; -import { Fragment, useState } from "react"; +import { Fragment, useId, useState } from "react"; import { ChevronDownIcon } from "../../../icons"; import { cn } from "../../../lib"; @@ -114,15 +114,20 @@ const RepeatMonthlyDetailSection = ({ onRepeatDayChange, ariaLabel, }: RepeatMonthlyDetailSectionProps) => { + const inputId = useId(); + return (
+
onRepeatDayChange?.(event.target.value)} - aria-label={ariaLabel} - className="typo-headline-m-14 text-timo-black w-full bg-transparent text-right outline-none" + className="typo-headline-m-14 text-timo-black focus-visible:ring-timo-blue-300 w-full bg-transparent text-right outline-none focus-visible:ring-2" />
From e3448937e0f6b05be2ddf7bfbe29597d1bdb432d Mon Sep 17 00:00:00 2001 From: kimminna Date: Tue, 7 Jul 2026 02:30:41 +0900 Subject: [PATCH 10/10] =?UTF-8?q?fix(ui):=20=EB=B0=B0=EC=97=B4=20map=20?= =?UTF-8?q?=EC=9D=B8=EB=8D=B1=EC=8A=A4=20=EC=86=8D=EC=84=B1=20minute?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/time/time-selector/TimeSelector.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx b/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx index 147ca8fd..232b50b8 100644 --- a/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx +++ b/packages/timo-design-system/src/components/time/time-selector/TimeSelector.tsx @@ -65,12 +65,12 @@ export const TimeSelector = ({
- {times.map(({ minute, value, unit }, index) => { + {times.map(({ minute, value, unit }) => { const isSelected = minute === selected; return ( onSelect?.(minute)} aria-pressed={isSelected} className={cn(