diff --git a/packages/tailwind-config/theme.css b/packages/tailwind-config/theme.css index 5c2c7bab..bdbbc4f0 100644 --- a/packages/tailwind-config/theme.css +++ b/packages/tailwind-config/theme.css @@ -1,4 +1,8 @@ @import "./tokens/colors.css"; @import "./tokens/typography.css"; @import "./tokens/radius.css"; -@import "./scrollbar.css"; \ No newline at end of file +@import "./scrollbar.css"; + +@theme { + --shadow-timo: 0px 0px 3px 0px rgba(0, 0, 0, 0.12); +} \ No newline at end of file diff --git a/packages/timo-design-system/src/components/dropdown-view/DropdownView.stories.tsx b/packages/timo-design-system/src/components/dropdown-view/DropdownView.stories.tsx new file mode 100644 index 00000000..d93b0d79 --- /dev/null +++ b/packages/timo-design-system/src/components/dropdown-view/DropdownView.stories.tsx @@ -0,0 +1,25 @@ +import { DropdownView } from "@components/dropdown-view/DropdownView"; +import { useState } from "react"; + +import type { Meta, StoryObj } from "@storybook/react"; + +const meta = { + title: "Components/DropdownView", + component: DropdownView, + parameters: { layout: "centered" }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const DefaultStory = () => { + const [value, setValue] = useState(""); + return ( + + ); +}; + +export const Default: Story = { + args: { items: [], value: "", onChange: () => {} }, + render: () => , +}; diff --git a/packages/timo-design-system/src/components/dropdown-view/DropdownView.tsx b/packages/timo-design-system/src/components/dropdown-view/DropdownView.tsx new file mode 100644 index 00000000..f48958e4 --- /dev/null +++ b/packages/timo-design-system/src/components/dropdown-view/DropdownView.tsx @@ -0,0 +1,65 @@ +import { Dropdown } from "@components/layout/dropdown/Dropdown"; +import { ChevronDownIcon, ChevronUpIcon } from "@icons"; +import { cn } from "@lib"; +import { Fragment } from "react"; + +export interface DropdownViewProps { + items: string[]; + value: string; + onChange: (value: string) => void; + placeholder?: string; + className?: string; +} + +export const DropdownView = ({ + items, + value, + onChange, + placeholder = "기본", + className, +}: DropdownViewProps) => { + return ( + + + + {value || placeholder} + +
+ + +
+
+ + + {items.map((item, i) => { + const isSelected = item === value; + + return ( + + onChange(item)} + className="typo-body-m-12 text-timo-gray-900 px-1.5 py-1" + > + {item} + + {i < items.length - 1 && ( +
+ )} + + ); + })} + + + ); +}; diff --git a/packages/timo-design-system/src/components/index.ts b/packages/timo-design-system/src/components/index.ts index d1d5599a..db28e097 100644 --- a/packages/timo-design-system/src/components/index.ts +++ b/packages/timo-design-system/src/components/index.ts @@ -1,4 +1,6 @@ export { Checkbox } from "@components/checkbox/Checkbox"; +export { Dropdown } from "@components/layout/dropdown/Dropdown"; +export { DropdownView } from "@components/dropdown-view/DropdownView"; export { Color } from "@components/tokens/color/Color"; export { Typography } from "@components/tokens/typography/Typography"; export { TagIcon } from "@components/tag/tag-icon/TagIcon"; 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..f210eb96 100644 --- a/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx +++ b/packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx @@ -8,13 +8,16 @@ import { useRef, useState, type ButtonHTMLAttributes, + type HTMLAttributes, type ReactNode, + type RefObject, } from "react"; interface DropdownContextValue { isOpen: boolean; toggle: () => void; close: () => void; + triggerRef: RefObject; } const DropdownContext = createContext(null); @@ -39,6 +42,7 @@ export interface DropdownProps { const DropdownRoot = ({ children, className }: DropdownProps) => { const [isOpen, setIsOpen] = useState(false); const rootRef = useRef(null); + const triggerRef = useRef(null); useEffect(() => { if (!isOpen) return; @@ -52,6 +56,7 @@ const DropdownRoot = ({ children, className }: DropdownProps) => { const handleEscapeKeydown = (event: KeyboardEvent) => { if (event.key === "Escape") { setIsOpen(false); + triggerRef.current?.focus(); } }; @@ -68,7 +73,7 @@ const DropdownRoot = ({ children, className }: DropdownProps) => { const close = () => setIsOpen(false); return ( - +
{children}
@@ -78,32 +83,42 @@ const DropdownRoot = ({ children, className }: DropdownProps) => { export type DropdownTriggerProps = ButtonHTMLAttributes; -const DropdownTrigger = ({ className, ...rest }: DropdownTriggerProps) => { - const { isOpen, toggle } = useDropdownContext(); +const DropdownTrigger = ({ + className, + onClick, + ...rest +}: DropdownTriggerProps) => { + const { isOpen, toggle, triggerRef } = useDropdownContext(); return (