import type { Snippet } from 'svelte';
import type { HTMLAttributes } from 'svelte/elements';
import type { JourneyTimelineSlots, JourneyTimelineVariants } from './journey-timeline.variants.js';
/** Lifecycle status of a journey node — drives marker colour and title tone. */
export type JourneyStatus = 'complete' | 'active' | 'pending' | 'attention' | 'blocked' | 'skipped';
/** A single node (waypoint) on a {@link JourneyTimelineProps | JourneyTimeline}. */
export interface JourneyNode {
/** Stable unique identifier — used as the focus key and for `{#each}` keying. */
id: string;
/** Node title, always visible next to the marker. */
title: string;
/**
* Lifecycle status. Maps to a semantic dot marker + title tone:
* `complete` (success), `active` (primary, ringed), `pending` (hollow),
* `attention` (warning — worth a look, does not block), `blocked` (danger),
* `skipped` (muted). The status is also announced through a visually-hidden
* label.
*/
status: JourneyStatus;
/** Short context line shown below the title. */
subtitle?: string;
/**
* Label on the chronicle axis (the meta rail left of the markers) — a time,
* date, version, actor…. The rail renders as soon as any node provides `meta`
* (or the `meta` snippet is set). Vertical orientation renders it as a
* right-aligned column; horizontal as a date row above the spine.
*/
meta?: string;
/**
* Line style of the connector *leaving* this node — lets the connector carry
* meaning (e.g. solid = ride, dashed/dotted = transfer, walk, gap in the
* record). @default 'solid'
*/
connector?: 'solid' | 'dashed' | 'dotted';
/**
* Label for the segment between this node and the next (a duration, transport
* mode, "3 days in transit"…). Rendered along the connector; ignored on the
* last node.
*/
segmentLabel?: string;
/**
* When `false`, the node is a pure waypoint: it renders marker + labels but
* cannot receive focus, shows no detail, and is skipped by keyboard navigation.
* @default true
*/
focusable?: boolean;
}
/**
* @summary The record of what happened, in order — shipments, audits, itineraries.
* @description Retrospective chronicle timeline (focus + context): an ordered
* record of what happened / where things stand — shipment tracking, audit
* trails, travel logs, billing runs. Exactly one focusable node is in focus and
* shows rich detail (`node` snippet); the rest stay quiet, compact context
* rows. The chronicle axis is first-class: per-node `meta` (time/date/actor)
* renders on a meta rail, connectors carry meaning (`solid | dashed | dotted`)
* and `segmentLabel` annotates the stretch between nodes. Rows extend without
* forking the layout: a `marker` snippet puts glyphs inside the status dots, a
* `trailing` snippet adds badges/help/actions beside each header (outside the
* button — safe for interactive elements), and the `attention` status flags
* optional-but-noteworthy rows. Detail placement is configurable:
* `detail="inline"` expands in place (vertical default); `detail="panel"`
* renders a stable readout beside (wide) or docked below (narrow) the rail —
* horizontal always uses the panel. Use `Stepper` for a prospective
* wizard/progress indicator and `Tab` for switching between peer views;
* JourneyTimeline is read-only observation of a sequence, not process control
* or navigation.
*
* @tag navigation
* @tag display
* @related Stepper
* @related Tab
* @related Accordion
*
* @example Vertical chronicle with inline detail
* ```svelte
*
*
*
* {#snippet node(item)}
*
Details for {item.title}…
* {/snippet}
*
* ```
*
* @example Horizontal lifecycle — detail renders in the shared panel
* ```svelte
* track(id)}>
* {#snippet node(item)}
*
* {/snippet}
*
* ```
*/
export interface JourneyTimelineProps extends Pick, Omit, 'children'> {
/** The ordered journey nodes. */
items: JourneyNode[];
/** Layout direction. @default 'vertical' */
orientation?: 'vertical' | 'horizontal';
/** Marker + label scale. @default 'md' */
size?: 'sm' | 'md' | 'lg';
/**
* Where the focused node's detail renders. `inline` expands in place inside
* the rail; `panel` renders a stable readout — beside the rail on wide
* viewports, docked to the viewport bottom on narrow ones. Horizontal
* orientation always uses the panel and ignores `inline` (DEV warning).
* @default 'inline' (vertical) / 'panel' (horizontal)
* @summary Whether the focused step opens in place or in a side panel.
*/
detail?: 'inline' | 'panel';
/**
* The focused node id. Supports `bind:focusId`. When omitted the component is
* uncontrolled and falls back to `defaultFocusId`, then the first `active`
* node, then the first focusable node.
*/
focusId?: string;
/** Initial focused node id in uncontrolled mode. Ignored once `focusId` is bound. */
defaultFocusId?: string;
/** Fires when the focused node changes (click or keyboard). */
onFocusChange?: (id: string) => void;
/** Renders the detail of the focused node. Receives the focused `JourneyNode`. */
node?: Snippet<[JourneyNode]>;
/**
* Rich override for the meta rail — receives each `JourneyNode` and replaces
* the plain `item.meta` text (e.g. planned + actual time with a Badge).
*/
meta?: Snippet<[JourneyNode]>;
/**
* Custom content *inside* each status dot — a glyph, count or icon. The dot
* keeps its status colour, shape and size contract (scale it via
* `slotClasses.marker`, e.g. `size-5`). Markers stay decorative
* (`aria-hidden`); the status is still announced through the hidden label.
*/
marker?: Snippet<[JourneyNode]>;
/**
* End-of-row content beside each node's header — status badges, a help
* affordance, quick actions. Renders *outside* the trigger button (a sibling
* in the header row), so interactive elements are valid HTML and activating
* them never moves the focused node. Right-aligned in vertical orientation,
* appended to the label pill in horizontal.
*/
trailing?: Snippet<[JourneyNode]>;
/** Extra classes merged onto the root element. */
class?: string;
/** Remove all default tv() classes. */
unstyled?: boolean;
/**
* Per-slot class overrides. Slots: base | rail | node | metaColumn | meta |
* markerColumn | marker | connector | content | card | header | trigger |
* trailing | labelGroup | title | subtitle | segment | detail | detailInner |
* detailContent | panel
*/
slotClasses?: Partial>;
/**
* Apply a named preset registered via ``.
* Prefer this over `class` overrides when the requested look falls outside the
* semantic intent palette — presets keep hover/active/dark-mode logic coherent
* and make the custom look reusable across the project.
*/
preset?: string;
}
export { default as JourneyTimeline } from './JourneyTimeline.svelte';
export { type JourneyTimelineVariants, journeyTimelineVariants } from './journey-timeline.variants.js';