fiixes
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
Timeline,
|
||||
TimelineDate,
|
||||
TimelineHeader,
|
||||
TimelineIndicator,
|
||||
TimelineItem,
|
||||
TimelineSeparator,
|
||||
TimelineTitle,
|
||||
} from "components/ui/timeline"
|
||||
|
||||
const items = [
|
||||
{
|
||||
id: 1,
|
||||
date: "Mar 15, 2024",
|
||||
title: "Project Kickoff",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: "Mar 22, 2024",
|
||||
title: "Design Phase",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: "Apr 5, 2024",
|
||||
title: "Development Sprint",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
date: "Apr 19, 2024",
|
||||
title: "Testing & Deployment",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
date: "May 3, 2024",
|
||||
title: "User Training",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
date: "May 17, 2024",
|
||||
title: "Project Handover",
|
||||
},
|
||||
]
|
||||
|
||||
export default function Component() {
|
||||
return (
|
||||
<Timeline defaultValue={3}>
|
||||
{items.map((item) => (
|
||||
<TimelineItem
|
||||
key={item.id}
|
||||
step={item.id}
|
||||
className="w-[calc(50%-1.5rem)] odd:ms-auto even:text-right even:group-data-[orientation=vertical]/timeline:ms-0 even:group-data-[orientation=vertical]/timeline:me-8 even:group-data-[orientation=vertical]/timeline:[&_[data-slot=timeline-indicator]]:-right-6 even:group-data-[orientation=vertical]/timeline:[&_[data-slot=timeline-indicator]]:left-auto even:group-data-[orientation=vertical]/timeline:[&_[data-slot=timeline-indicator]]:translate-x-1/2 even:group-data-[orientation=vertical]/timeline:[&_[data-slot=timeline-separator]]:-right-6 even:group-data-[orientation=vertical]/timeline:[&_[data-slot=timeline-separator]]:left-auto even:group-data-[orientation=vertical]/timeline:[&_[data-slot=timeline-separator]]:translate-x-1/2"
|
||||
>
|
||||
<TimelineHeader>
|
||||
<TimelineSeparator />
|
||||
<TimelineDate>{item.date}</TimelineDate>
|
||||
<TimelineTitle>{item.title}</TimelineTitle>
|
||||
<TimelineIndicator />
|
||||
</TimelineHeader>
|
||||
</TimelineItem>
|
||||
))}
|
||||
</Timeline>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import {
|
||||
Timeline,
|
||||
TimelineContent,
|
||||
TimelineDate,
|
||||
TimelineHeader,
|
||||
TimelineIndicator,
|
||||
TimelineItem,
|
||||
TimelineSeparator,
|
||||
TimelineTitle,
|
||||
} from "components/ui/timeline"
|
||||
|
||||
const items = [
|
||||
{
|
||||
id: 1,
|
||||
date: "Mar 15, 2024",
|
||||
title: "Project Kickoff",
|
||||
description: "Initial team meeting.",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: "Mar 22, 2024",
|
||||
title: "Design Phase",
|
||||
description: "Completed wireframes.",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: "Apr 5, 2024",
|
||||
title: "Development Sprint",
|
||||
description: "Backend development.",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
date: "Apr 19, 2024",
|
||||
title: "Testing & Deployment",
|
||||
description: "Performance optimization.",
|
||||
},
|
||||
]
|
||||
|
||||
export default function Component() {
|
||||
return (
|
||||
<Timeline defaultValue={3} orientation="horizontal">
|
||||
{items.map((item) => (
|
||||
<TimelineItem key={item.id} step={item.id}>
|
||||
<TimelineHeader>
|
||||
<TimelineSeparator />
|
||||
<TimelineDate>{item.date}</TimelineDate>
|
||||
<TimelineTitle>{item.title}</TimelineTitle>
|
||||
<TimelineIndicator />
|
||||
</TimelineHeader>
|
||||
<TimelineContent>{item.description}</TimelineContent>
|
||||
</TimelineItem>
|
||||
))}
|
||||
</Timeline>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
Timeline,
|
||||
TimelineContent,
|
||||
TimelineDate,
|
||||
TimelineHeader,
|
||||
TimelineIndicator,
|
||||
TimelineItem,
|
||||
TimelineSeparator,
|
||||
TimelineTitle,
|
||||
} from "components/ui/timeline"
|
||||
|
||||
const items = [
|
||||
{
|
||||
id: 1,
|
||||
date: "Mar 15, 2024",
|
||||
title: "Project Kickoff",
|
||||
description: "Initial team meeting.",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: "Mar 22, 2024",
|
||||
title: "Design Phase",
|
||||
description: "Completed wireframes.",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: "Apr 5, 2024",
|
||||
title: "Development Sprint",
|
||||
description: "Backend development.",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
date: "Apr 19, 2024",
|
||||
title: "Testing & Deployment",
|
||||
description: "Performance optimization.",
|
||||
},
|
||||
]
|
||||
|
||||
export default function Component() {
|
||||
return (
|
||||
<Timeline defaultValue={3} orientation="horizontal">
|
||||
{items.map((item) => (
|
||||
<TimelineItem
|
||||
key={item.id}
|
||||
step={item.id}
|
||||
className="group-data-[orientation=horizontal]/timeline:mt-0"
|
||||
>
|
||||
<TimelineHeader>
|
||||
<TimelineSeparator className="group-data-[orientation=horizontal]/timeline:top-8" />
|
||||
<TimelineDate className="mb-10">{item.date}</TimelineDate>
|
||||
<TimelineTitle>{item.title}</TimelineTitle>
|
||||
<TimelineIndicator className="group-data-[orientation=horizontal]/timeline:top-8" />
|
||||
</TimelineHeader>
|
||||
<TimelineContent>{item.description}</TimelineContent>
|
||||
</TimelineItem>
|
||||
))}
|
||||
</Timeline>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Slot } from "radix-ui"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
// Types
|
||||
type TimelineContextValue = {
|
||||
activeStep: number
|
||||
setActiveStep: (step: number) => void
|
||||
}
|
||||
|
||||
// Context
|
||||
const TimelineContext = React.createContext<TimelineContextValue | undefined>(
|
||||
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<HTMLDivElement> {
|
||||
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 (
|
||||
<TimelineContext.Provider
|
||||
value={{ activeStep: currentStep, setActiveStep }}
|
||||
>
|
||||
<div
|
||||
data-slot="timeline"
|
||||
className={cn(
|
||||
"group/timeline flex data-[orientation=horizontal]:w-full data-[orientation=horizontal]:flex-row data-[orientation=vertical]:flex-col",
|
||||
className
|
||||
)}
|
||||
data-orientation={orientation}
|
||||
{...props}
|
||||
/>
|
||||
</TimelineContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
// TimelineContent
|
||||
function TimelineContent({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="timeline-content"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// TimelineDate
|
||||
interface TimelineDateProps extends React.HTMLAttributes<HTMLTimeElement> {
|
||||
asChild?: boolean
|
||||
}
|
||||
|
||||
function TimelineDate({
|
||||
asChild = false,
|
||||
className,
|
||||
...props
|
||||
}: TimelineDateProps) {
|
||||
const Comp = asChild ? Slot.Root : "time"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="timeline-date"
|
||||
className={cn(
|
||||
"text-muted-foreground mb-1 block text-xs font-medium group-data-[orientation=vertical]/timeline:max-sm:h-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// TimelineHeader
|
||||
function TimelineHeader({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div data-slot="timeline-header" className={cn(className)} {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
// TimelineIndicator
|
||||
interface TimelineIndicatorProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
asChild?: boolean
|
||||
}
|
||||
|
||||
function TimelineIndicator({
|
||||
asChild = false,
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: TimelineIndicatorProps) {
|
||||
return (
|
||||
<div
|
||||
data-slot="timeline-indicator"
|
||||
className={cn(
|
||||
"border-primary/20 group-data-completed/timeline-item:border-primary absolute size-4 rounded-full border-2 group-data-[orientation=horizontal]/timeline:-top-6 group-data-[orientation=horizontal]/timeline:left-0 group-data-[orientation=horizontal]/timeline:-translate-y-1/2 group-data-[orientation=vertical]/timeline:top-0 group-data-[orientation=vertical]/timeline:-left-6 group-data-[orientation=vertical]/timeline:-translate-x-1/2",
|
||||
className
|
||||
)}
|
||||
aria-hidden="true"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// TimelineItem
|
||||
interface TimelineItemProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
step: number
|
||||
}
|
||||
|
||||
function TimelineItem({ step, className, ...props }: TimelineItemProps) {
|
||||
const { activeStep } = useTimeline()
|
||||
|
||||
return (
|
||||
<div
|
||||
data-slot="timeline-item"
|
||||
className={cn(
|
||||
"group/timeline-item has-[+[data-completed]]:[&_[data-slot=timeline-separator]]:bg-primary relative flex flex-1 flex-col gap-0.5 group-data-[orientation=horizontal]/timeline:mt-8 group-data-[orientation=horizontal]/timeline:not-last:pe-8 group-data-[orientation=vertical]/timeline:ms-8 group-data-[orientation=vertical]/timeline:not-last:pb-12",
|
||||
className
|
||||
)}
|
||||
data-completed={step <= activeStep || undefined}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// TimelineSeparator
|
||||
function TimelineSeparator({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="timeline-separator"
|
||||
className={cn(
|
||||
"bg-primary/10 absolute self-start group-last/timeline-item:hidden group-data-[orientation=horizontal]/timeline:-top-6 group-data-[orientation=horizontal]/timeline:h-0.5 group-data-[orientation=horizontal]/timeline:w-[calc(100%-1rem-0.25rem)] group-data-[orientation=horizontal]/timeline:translate-x-4.5 group-data-[orientation=horizontal]/timeline:-translate-y-1/2 group-data-[orientation=vertical]/timeline:-left-6 group-data-[orientation=vertical]/timeline:h-[calc(100%-1rem-0.25rem)] group-data-[orientation=vertical]/timeline:w-0.5 group-data-[orientation=vertical]/timeline:-translate-x-1/2 group-data-[orientation=vertical]/timeline:translate-y-4.5",
|
||||
className
|
||||
)}
|
||||
aria-hidden="true"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// TimelineTitle
|
||||
function TimelineTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLHeadingElement>) {
|
||||
return (
|
||||
<h3
|
||||
data-slot="timeline-title"
|
||||
className={cn("text-sm font-medium", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Timeline,
|
||||
TimelineContent,
|
||||
TimelineDate,
|
||||
TimelineHeader,
|
||||
TimelineIndicator,
|
||||
TimelineItem,
|
||||
TimelineSeparator,
|
||||
TimelineTitle,
|
||||
}
|
||||
Reference in New Issue
Block a user