80 lines
2.1 KiB
Vue
80 lines
2.1 KiB
Vue
|
|
<script setup lang="ts">
|
||
|
|
import type { HTMLAttributes } from "vue"
|
||
|
|
import type { TimelineItemVariants } from "."
|
||
|
|
import { Check, Circle } from "lucide-vue-next"
|
||
|
|
import { cn } from "@/lib/utils"
|
||
|
|
import { timelineItemVariants } from "."
|
||
|
|
|
||
|
|
const props = withDefaults(
|
||
|
|
defineProps<{
|
||
|
|
title: string
|
||
|
|
date?: string
|
||
|
|
time?: string
|
||
|
|
state?: TimelineItemVariants["state"]
|
||
|
|
class?: HTMLAttributes["class"]
|
||
|
|
}>(),
|
||
|
|
{ state: "pending" },
|
||
|
|
)
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<template>
|
||
|
|
<div
|
||
|
|
role="listitem"
|
||
|
|
data-slot="timeline-item"
|
||
|
|
:data-state="props.state"
|
||
|
|
:class="
|
||
|
|
cn(
|
||
|
|
'relative flex gap-4 pb-6 last:pb-0',
|
||
|
|
timelineItemVariants({ state: props.state }),
|
||
|
|
props.class,
|
||
|
|
)
|
||
|
|
"
|
||
|
|
>
|
||
|
|
<!-- Connector line (hidden on last item) -->
|
||
|
|
<div
|
||
|
|
aria-hidden
|
||
|
|
class="timeline-item-connector absolute left-[11px] top-6 h-[calc(100%-0.5rem)] w-px bg-border"
|
||
|
|
/>
|
||
|
|
|
||
|
|
<!-- Status indicator -->
|
||
|
|
<div
|
||
|
|
:class="
|
||
|
|
cn(
|
||
|
|
'relative z-10 flex shrink-0 items-center justify-center rounded-full border-2 transition-colors',
|
||
|
|
props.state === 'completed' &&
|
||
|
|
'border-primary bg-primary text-primary-foreground',
|
||
|
|
props.state === 'pending' && 'border-border bg-background',
|
||
|
|
props.state === 'current' && 'border-primary bg-primary/10 text-primary',
|
||
|
|
)
|
||
|
|
"
|
||
|
|
:style="{ width: '24px', height: '24px' }"
|
||
|
|
>
|
||
|
|
<Check v-if="props.state === 'completed'" class="size-3.5" :stroke-width="3" />
|
||
|
|
<Circle
|
||
|
|
v-else
|
||
|
|
class="size-2.5 fill-current"
|
||
|
|
stroke="none"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Content -->
|
||
|
|
<div class="flex min-w-0 flex-1 flex-col gap-0.5 pt-0.5">
|
||
|
|
<div class="flex items-start justify-between gap-4">
|
||
|
|
<span class="text-sm font-medium leading-tight">
|
||
|
|
{{ props.title }}
|
||
|
|
</span>
|
||
|
|
<span
|
||
|
|
v-if="props.time"
|
||
|
|
class="shrink-0 text-xs text-muted-foreground"
|
||
|
|
>
|
||
|
|
{{ props.time }}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<span v-if="props.date" class="text-xs text-muted-foreground">
|
||
|
|
{{ props.date }}
|
||
|
|
</span>
|
||
|
|
<slot />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</template>
|