diff --git a/web/src/components/originui/timeline.tsx b/web/src/components/originui/timeline.tsx new file mode 100644 index 000000000..8b4bbea42 --- /dev/null +++ b/web/src/components/originui/timeline.tsx @@ -0,0 +1,209 @@ +'use client'; + +import { cn } from '@/lib/utils'; +import { Slot } from '@radix-ui/react-slot'; +import * as React from 'react'; + +// Types +type TimelineContextValue = { + activeStep: number; + setActiveStep: (step: number) => void; +}; + +// Context +const TimelineContext = React.createContext( + undefined, +); + +const useTimeline = () => { + const context = React.useContext(TimelineContext); + if (!context) { + throw new Error('useTimeline must be used within a Timeline'); + } + return context; +}; + +// Components +interface TimelineProps extends React.HTMLAttributes { + defaultValue?: number; + value?: number; + onValueChange?: (value: number) => void; + orientation?: 'horizontal' | 'vertical'; +} + +function Timeline({ + defaultValue = 1, + value, + onValueChange, + orientation = 'vertical', + className, + ...props +}: TimelineProps) { + const [activeStep, setInternalStep] = React.useState(defaultValue); + + const setActiveStep = React.useCallback( + (step: number) => { + if (value === undefined) { + setInternalStep(step); + } + onValueChange?.(step); + }, + [value, onValueChange], + ); + + const currentStep = value ?? activeStep; + + return ( + +
+ + ); +} + +// TimelineContent +function TimelineContent({ + className, + ...props +}: React.HTMLAttributes) { + return ( +
+ ); +} + +// TimelineDate +interface TimelineDateProps extends React.HTMLAttributes { + asChild?: boolean; +} + +function TimelineDate({ + asChild = false, + className, + ...props +}: TimelineDateProps) { + const Comp = asChild ? Slot : 'time'; + + return ( + + ); +} + +// TimelineHeader +function TimelineHeader({ + className, + ...props +}: React.HTMLAttributes) { + return ( +
+ ); +} + +// TimelineIndicator +interface TimelineIndicatorProps extends React.HTMLAttributes { + asChild?: boolean; +} + +function TimelineIndicator({ + asChild = false, + className, + children, + ...props +}: TimelineIndicatorProps) { + return ( + + ); +} + +// TimelineItem +interface TimelineItemProps extends React.HTMLAttributes { + step: number; +} + +function TimelineItem({ step, className, ...props }: TimelineItemProps) { + const { activeStep } = useTimeline(); + + return ( +
+ ); +} + +// TimelineSeparator +function TimelineSeparator({ + className, + ...props +}: React.HTMLAttributes) { + return ( +