diff --git a/packages/timo-design-system/src/components/index.ts b/packages/timo-design-system/src/components/index.ts index 7917669..986513a 100644 --- a/packages/timo-design-system/src/components/index.ts +++ b/packages/timo-design-system/src/components/index.ts @@ -5,3 +5,4 @@ export { Tag } from "@components/tag/Tag"; export { PriorityIcon } from "@components/priority-icon/PriorityIcon"; export { CreateButton } from "@components/button/create-button/CreateButton"; export { TodayBadge } from "@components/badge/today-badge/TodayBadge"; +export { TogglePanel } from "@components/toggle-panel/TogglePanel"; diff --git a/packages/timo-design-system/src/components/toggle-panel/TogglePanel.stories.tsx b/packages/timo-design-system/src/components/toggle-panel/TogglePanel.stories.tsx new file mode 100644 index 0000000..0f39c36 --- /dev/null +++ b/packages/timo-design-system/src/components/toggle-panel/TogglePanel.stories.tsx @@ -0,0 +1,114 @@ +import { + TogglePanel, + TogglePanelValue, +} from "@components/toggle-panel/TogglePanel"; +import { useEffect, useState } from "react"; + +import type { Meta, StoryObj } from "@storybook/react"; + +const meta = { + title: "Components/TogglePanel", + component: TogglePanel, + parameters: { + layout: "centered", + }, + args: { + value: "timebox", + onChange: () => {}, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const PlaygroundTogglePanel = ({ + width, + ...args +}: React.ComponentProps & { width: string }) => { + const [value, setValue] = useState(args.value); + + useEffect(() => { + setValue(args.value); + }, [args.value]); + + return ( +
+ +
+ ); +}; + +export const Default: Story = { + render: (args) => , +}; + +export const Big: Story = { + args: { + value: "timer", + }, + render: (args) => , +}; + +const TogglePanelWithPanel = ( + args: React.ComponentProps, +) => { + const [value, setValue] = useState(args.value); + + useEffect(() => { + setValue(args.value); + }, [args.value]); + + return ( +
+ + + +
+ ); +}; + +export const WithPanel: Story = { + render: (args) => , +}; + +export const AllSizes: Story = { + render: () => ( +
+
+

Default (268px)

+
+ {}} /> +
+
+
+

Big (404px)

+
+ {}} /> +
+
+
+ ), +}; diff --git a/packages/timo-design-system/src/components/toggle-panel/TogglePanel.tsx b/packages/timo-design-system/src/components/toggle-panel/TogglePanel.tsx new file mode 100644 index 0000000..3ac34ec --- /dev/null +++ b/packages/timo-design-system/src/components/toggle-panel/TogglePanel.tsx @@ -0,0 +1,85 @@ +import { cn } from "@lib"; +import { useId, useRef } from "react"; + +import type { KeyboardEvent, RefObject } from "react"; + +export type TogglePanelValue = "timebox" | "timer"; + +export interface TogglePanelProps { + id?: string; + value: TogglePanelValue; + onChange: (value: TogglePanelValue) => void; + timeboxControls?: string; + timerControls?: string; +} + +interface TogglePanelOption { + value: TogglePanelValue; + label: string; + ref: RefObject; + controls?: string; +} + +export const TogglePanel = ({ + id: idProp, + value, + onChange, + timeboxControls, + timerControls, +}: TogglePanelProps) => { + const generatedId = useId(); + const id = idProp ?? generatedId; + const timeboxRef = useRef(null); + const timerRef = useRef(null); + + const options: TogglePanelOption[] = [ + { + value: "timebox", + label: "Timebox", + ref: timeboxRef, + controls: timeboxControls, + }, + { value: "timer", label: "Timer", ref: timerRef, controls: timerControls }, + ]; + + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return; + + event.preventDefault(); + const next = options.find((option) => option.value !== value); + if (!next) return; + onChange(next.value); + next.ref.current?.focus(); + }; + + return ( +
+ {options.map((option) => ( + + ))} +
+ ); +};