@urbicon-ui/blocks 6.9.0 → 6.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
3
  import type { JourneyTimelineSlots, JourneyTimelineVariants } from './journey-timeline.variants.js';
4
- /** Lifecycle status of a journey node — drives marker colour and glyph. */
5
- export type JourneyStatus = 'complete' | 'active' | 'pending' | 'blocked' | 'skipped';
4
+ /** Lifecycle status of a journey node — drives marker colour and title tone. */
5
+ export type JourneyStatus = 'complete' | 'active' | 'pending' | 'attention' | 'blocked' | 'skipped';
6
6
  /** A single node (waypoint) on a {@link JourneyTimelineProps | JourneyTimeline}. */
7
7
  export interface JourneyNode {
8
8
  /** Stable unique identifier — used as the focus key and for `{#each}` keying. */
@@ -10,27 +10,59 @@ export interface JourneyNode {
10
10
  /** Node title, always visible next to the marker. */
11
11
  title: string;
12
12
  /**
13
- * Lifecycle status. Maps to a semantic marker colour + glyph:
14
- * `complete` (success), `active` (primary ), `pending` (empty circle),
15
- * `blocked` (danger ), `skipped` (muted −).
13
+ * Lifecycle status. Maps to a semantic dot marker + title tone:
14
+ * `complete` (success), `active` (primary, ringed), `pending` (hollow),
15
+ * `attention` (warning — worth a look, does not block), `blocked` (danger),
16
+ * `skipped` (muted). The status is also announced through a visually-hidden
17
+ * label.
16
18
  */
17
19
  status: JourneyStatus;
18
- /** Short status line shown below the title while collapsed. */
20
+ /** Short context line shown below the title. */
19
21
  subtitle?: string;
20
22
  /**
21
- * When `false`, the node is a pure waypoint: it renders a marker + labels but
22
- * cannot receive focus, does not expand, and is skipped by keyboard navigation.
23
+ * Label on the chronicle axis (the meta rail left of the markers) a time,
24
+ * date, version, actor…. The rail renders as soon as any node provides `meta`
25
+ * (or the `meta` snippet is set). Vertical orientation renders it as a
26
+ * right-aligned column; horizontal as a date row above the spine.
27
+ */
28
+ meta?: string;
29
+ /**
30
+ * Line style of the connector *leaving* this node — lets the connector carry
31
+ * meaning (e.g. solid = ride, dashed/dotted = transfer, walk, gap in the
32
+ * record). @default 'solid'
33
+ */
34
+ connector?: 'solid' | 'dashed' | 'dotted';
35
+ /**
36
+ * Label for the segment between this node and the next (a duration, transport
37
+ * mode, "3 days in transit"…). Rendered along the connector; ignored on the
38
+ * last node.
39
+ */
40
+ segmentLabel?: string;
41
+ /**
42
+ * When `false`, the node is a pure waypoint: it renders marker + labels but
43
+ * cannot receive focus, shows no detail, and is skipped by keyboard navigation.
23
44
  * @default true
24
45
  */
25
46
  focusable?: boolean;
26
47
  }
27
48
  /**
28
- * @description Connected timeline whose markers *are* the progress indicator and
29
- * where exactly one focusable node expands to reveal rich per-step detail (the
30
- * "journey" / travel-log pattern). Data-driven via `items`; the focused node's
31
- * body is rendered through the `node` snippet. For a compact progress indicator
32
- * without a detail container use `Stepper`; for peer views without sequence use
33
- * `Tab`.
49
+ * @description Retrospective chronicle timeline (focus + context): an ordered
50
+ * record of what happened / where things stand shipment tracking, audit
51
+ * trails, travel logs, billing runs. Exactly one focusable node is in focus and
52
+ * shows rich detail (`node` snippet); the rest stay quiet, compact context
53
+ * rows. The chronicle axis is first-class: per-node `meta` (time/date/actor)
54
+ * renders on a meta rail, connectors carry meaning (`solid | dashed | dotted`)
55
+ * and `segmentLabel` annotates the stretch between nodes. Rows extend without
56
+ * forking the layout: a `marker` snippet puts glyphs inside the status dots, a
57
+ * `trailing` snippet adds badges/help/actions beside each header (outside the
58
+ * button — safe for interactive elements), and the `attention` status flags
59
+ * optional-but-noteworthy rows. Detail placement is configurable:
60
+ * `detail="inline"` expands in place (vertical default); `detail="panel"`
61
+ * renders a stable readout beside (wide) or docked below (narrow) the rail —
62
+ * horizontal always uses the panel. Use `Stepper` for a prospective
63
+ * wizard/progress indicator and `Tab` for switching between peer views;
64
+ * JourneyTimeline is read-only observation of a sequence, not process control
65
+ * or navigation.
34
66
  *
35
67
  * @tag navigation
36
68
  * @tag display
@@ -39,26 +71,27 @@ export interface JourneyNode {
39
71
  * @related Accordion
40
72
  * @stability beta
41
73
  *
42
- * @example Vertical journey with inline detail
74
+ * @example Vertical chronicle with inline detail
43
75
  * ```svelte
44
76
  * <script lang="ts">
45
77
  * import { JourneyTimeline, type JourneyNode } from '@urbicon-ui/blocks';
46
- * const stages: JourneyNode[] = [
47
- * { id: 'draft', title: 'Draft', status: 'complete', subtitle: 'Sent 3 Jun' },
48
- * { id: 'review', title: 'Review', status: 'active', subtitle: 'In progress' },
49
- * { id: 'approve', title: 'Approval', status: 'pending' }
78
+ * const run: JourneyNode[] = [
79
+ * { id: 'readings', title: 'Meter readings', status: 'complete', meta: '3 Jun', segmentLabel: '2 days · validation' },
80
+ * { id: 'validate', title: 'Validation', status: 'complete', meta: '5 Jun', connector: 'dashed', segmentLabel: 'manual review' },
81
+ * { id: 'statement', title: 'Statements', status: 'active', meta: '6 Jun' },
82
+ * { id: 'dispatch', title: 'Dispatch', status: 'pending' }
50
83
  * ];
51
- * let focusId = $state('review');
84
+ * let focusId = $state('statement');
52
85
  * </script>
53
86
  *
54
- * <JourneyTimeline items={stages} bind:focusId>
87
+ * <JourneyTimeline items={run} bind:focusId>
55
88
  * {#snippet node(item)}
56
89
  * <p>Details for {item.title}…</p>
57
90
  * {/snippet}
58
91
  * </JourneyTimeline>
59
92
  * ```
60
93
  *
61
- * @example Horizontal lifecycle with a shared detail panel + scroll-spy off
94
+ * @example Horizontal lifecycle detail renders in the shared panel
62
95
  * ```svelte
63
96
  * <JourneyTimeline items={phases} orientation="horizontal" onFocusChange={(id) => track(id)}>
64
97
  * {#snippet node(item)}
@@ -70,36 +103,59 @@ export interface JourneyNode {
70
103
  export interface JourneyTimelineProps extends Pick<JourneyTimelineVariants, 'orientation' | 'size'>, Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
71
104
  /** The ordered journey nodes. */
72
105
  items: JourneyNode[];
73
- /** Layout direction. Vertical expands detail inline; horizontal into a panel below the rail. @default 'vertical' */
106
+ /** Layout direction. @default 'vertical' */
74
107
  orientation?: 'vertical' | 'horizontal';
75
108
  /** Marker + label scale. @default 'md' */
76
109
  size?: 'sm' | 'md' | 'lg';
77
110
  /**
78
- * The expanded (focused) node id. Supports `bind:focusId`. When omitted the
79
- * component is uncontrolled and falls back to `defaultFocusId`, then the first
80
- * `active` node, then the first focusable node.
111
+ * Where the focused node's detail renders. `inline` expands in place inside
112
+ * the rail; `panel` renders a stable readout beside the rail on wide
113
+ * viewports, docked to the viewport bottom on narrow ones. Horizontal
114
+ * orientation always uses the panel and ignores `inline` (DEV warning).
115
+ * @default 'inline' (vertical) / 'panel' (horizontal)
116
+ */
117
+ detail?: 'inline' | 'panel';
118
+ /**
119
+ * The focused node id. Supports `bind:focusId`. When omitted the component is
120
+ * uncontrolled and falls back to `defaultFocusId`, then the first `active`
121
+ * node, then the first focusable node.
81
122
  */
82
123
  focusId?: string;
83
124
  /** Initial focused node id in uncontrolled mode. Ignored once `focusId` is bound. */
84
125
  defaultFocusId?: string;
85
- /**
86
- * When `true`, the focus follows the node scrolled to the top of the viewport
87
- * (the travel-log feel), driven by an IntersectionObserver. Reduced-motion safe.
88
- * @default false
89
- */
90
- scrollSpy?: boolean;
91
- /** Fires when the focused node changes (click, keyboard, or scroll-spy). */
126
+ /** Fires when the focused node changes (click or keyboard). */
92
127
  onFocusChange?: (id: string) => void;
93
- /** Renders the body of the focused node. Receives the focused `JourneyNode`. */
128
+ /** Renders the detail of the focused node. Receives the focused `JourneyNode`. */
94
129
  node?: Snippet<[JourneyNode]>;
130
+ /**
131
+ * Rich override for the meta rail — receives each `JourneyNode` and replaces
132
+ * the plain `item.meta` text (e.g. planned + actual time with a Badge).
133
+ */
134
+ meta?: Snippet<[JourneyNode]>;
135
+ /**
136
+ * Custom content *inside* each status dot — a glyph, count or icon. The dot
137
+ * keeps its status colour, shape and size contract (scale it via
138
+ * `slotClasses.marker`, e.g. `size-5`). Markers stay decorative
139
+ * (`aria-hidden`); the status is still announced through the hidden label.
140
+ */
141
+ marker?: Snippet<[JourneyNode]>;
142
+ /**
143
+ * End-of-row content beside each node's header — status badges, a help
144
+ * affordance, quick actions. Renders *outside* the trigger button (a sibling
145
+ * in the header row), so interactive elements are valid HTML and activating
146
+ * them never moves the focused node. Right-aligned in vertical orientation,
147
+ * appended to the label pill in horizontal.
148
+ */
149
+ trailing?: Snippet<[JourneyNode]>;
95
150
  /** Extra classes merged onto the root element. */
96
151
  class?: string;
97
152
  /** Remove all default tv() classes. */
98
153
  unstyled?: boolean;
99
154
  /**
100
- * Per-slot class overrides. Slots: base | rail | node | trigger | marker |
101
- * connectorColumn | connector | labelGroup | title | subtitle | body | detail |
102
- * detailInner | detailContent | panel
155
+ * Per-slot class overrides. Slots: base | rail | node | metaColumn | meta |
156
+ * markerColumn | marker | connector | content | card | header | trigger |
157
+ * trailing | labelGroup | title | subtitle | segment | detail | detailInner |
158
+ * detailContent | panel
103
159
  */
104
160
  slotClasses?: Partial<Record<JourneyTimelineSlots, string>>;
105
161
  /**