entasis 0.13.4 → 0.13.6

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.
Files changed (83) hide show
  1. package/dist/components/Address/Address.svelte +44 -0
  2. package/dist/components/Address/Address.svelte.d.ts +4 -0
  3. package/dist/components/Address/address.d.ts +99 -0
  4. package/dist/components/Address/address.google.d.ts +89 -0
  5. package/dist/components/Address/address.google.fixtures.json +808 -0
  6. package/dist/components/Address/address.google.js +184 -0
  7. package/dist/components/Address/address.js +109 -0
  8. package/dist/components/Address/address.mcp.d.ts +1 -0
  9. package/dist/components/Address/address.mcp.js +100 -0
  10. package/dist/components/Address/address.props.d.ts +26 -0
  11. package/dist/components/Address/address.props.js +1 -0
  12. package/dist/components/Address/address.provider.d.ts +19 -0
  13. package/dist/components/Address/address.provider.js +69 -0
  14. package/dist/components/Address/address.theme.d.ts +63 -0
  15. package/dist/components/Address/address.theme.js +42 -0
  16. package/dist/components/Address/index.d.ts +6 -0
  17. package/dist/components/Address/index.js +5 -0
  18. package/dist/components/Code/Code.svelte +7 -2
  19. package/dist/components/Code/CodeTheme.svelte +12 -1
  20. package/dist/components/Code/code.highlighter.js +2 -2
  21. package/dist/components/Code/code.mcp.d.ts +1 -1
  22. package/dist/components/Code/code.mcp.js +3 -1
  23. package/dist/components/Code/code.theme.js +6 -5
  24. package/dist/components/Form/AddressInput/AddressInput.svelte +482 -0
  25. package/dist/components/Form/AddressInput/AddressInput.svelte.d.ts +4 -0
  26. package/dist/components/Form/AddressInput/addressInput.mcp.d.ts +1 -0
  27. package/dist/components/Form/AddressInput/addressInput.mcp.js +110 -0
  28. package/dist/components/Form/AddressInput/addressInput.props.d.ts +72 -0
  29. package/dist/components/Form/AddressInput/addressInput.props.js +1 -0
  30. package/dist/components/Form/AddressInput/addressInput.theme.d.ts +63 -0
  31. package/dist/components/Form/AddressInput/addressInput.theme.js +55 -0
  32. package/dist/components/Form/AddressInput/index.d.ts +3 -0
  33. package/dist/components/Form/AddressInput/index.js +2 -0
  34. package/dist/components/Form/Field/field.d.ts +5 -2
  35. package/dist/components/Form/Field/field.state.svelte.js +4 -0
  36. package/dist/components/Form/Field/index.d.ts +1 -1
  37. package/dist/components/Form/Field/schemas.js +8 -0
  38. package/dist/components/Form/Form/FormInputRenderer.svelte +6 -0
  39. package/dist/components/Form/Form/form.d.ts +6 -0
  40. package/dist/components/Form/Form/form.mcp.d.ts +1 -1
  41. package/dist/components/Form/Form/form.mcp.js +1 -1
  42. package/dist/components/Form/Form/form.state.svelte.d.ts +142 -11
  43. package/dist/components/Form/Form/form.state.svelte.js +2 -0
  44. package/dist/components/Form/Form/visibility.d.ts +186 -20
  45. package/dist/components/Form/MultiStepForm/multiStepForm.state.svelte.d.ts +136 -0
  46. package/dist/components/Form/SignatureInput/SignatureInput.svelte +327 -0
  47. package/dist/components/Form/SignatureInput/SignatureInput.svelte.d.ts +4 -0
  48. package/dist/components/Form/SignatureInput/index.d.ts +4 -0
  49. package/dist/components/Form/SignatureInput/index.js +2 -0
  50. package/dist/components/Form/SignatureInput/signatureInput.mcp.d.ts +1 -0
  51. package/dist/components/Form/SignatureInput/signatureInput.mcp.js +74 -0
  52. package/dist/components/Form/SignatureInput/signatureInput.props.d.ts +29 -0
  53. package/dist/components/Form/SignatureInput/signatureInput.props.js +1 -0
  54. package/dist/components/Form/SignatureInput/signatureInput.strokes.d.ts +77 -0
  55. package/dist/components/Form/SignatureInput/signatureInput.strokes.js +149 -0
  56. package/dist/components/Form/SignatureInput/signatureInput.theme.d.ts +99 -0
  57. package/dist/components/Form/SignatureInput/signatureInput.theme.js +70 -0
  58. package/dist/components/Sidebar/sidebar-layout.js +3 -1
  59. package/dist/components/Timeline/Timeline.svelte +1 -1
  60. package/dist/components/Timeline/TimelineEntry.svelte +16 -2
  61. package/dist/components/Timeline/index.d.ts +1 -1
  62. package/dist/components/Timeline/timeline.items.d.ts +1 -1
  63. package/dist/components/Timeline/timeline.items.js +7 -2
  64. package/dist/components/Timeline/timeline.mcp.d.ts +1 -1
  65. package/dist/components/Timeline/timeline.mcp.js +35 -3
  66. package/dist/components/Timeline/timeline.props.d.ts +17 -1
  67. package/dist/components/Timeline/timeline.theme.d.ts +36 -0
  68. package/dist/components/Timeline/timeline.theme.js +27 -4
  69. package/dist/generated/componentAliases.d.ts +3 -0
  70. package/dist/generated/componentAliases.js +3 -0
  71. package/dist/generated/componentContract.d.ts +47 -2
  72. package/dist/generated/componentContract.js +114 -0
  73. package/dist/generated/componentMcpRegistry.js +6 -0
  74. package/dist/i18n/ar.js +18 -0
  75. package/dist/i18n/de.js +18 -0
  76. package/dist/i18n/en.d.ts +18 -0
  77. package/dist/i18n/en.js +18 -0
  78. package/dist/i18n/es.js +18 -0
  79. package/dist/i18n/fr.js +18 -0
  80. package/dist/i18n/ja.js +18 -0
  81. package/dist/i18n/pt.js +18 -0
  82. package/dist/i18n/zh.js +18 -0
  83. package/package.json +25 -1
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Strokes of a signature and the images they export to. Pure geometry: no DOM except the
3
+ * canvas context a caller passes in, so it runs the same in a test, on the pad and on export.
4
+ */
5
+ /** One sampled pointer position, in CSS pixels of the pad. */
6
+ export type SignaturePoint = {
7
+ x: number;
8
+ y: number;
9
+ /** Pen pressure from 0 to 1. Mouse and touch report 0.5. */
10
+ pressure: number;
11
+ };
12
+ /** One continuous line, from a pointer down to its pointer up. */
13
+ export type SignatureStroke = readonly SignaturePoint[];
14
+ export type SignatureBounds = {
15
+ x: number;
16
+ y: number;
17
+ width: number;
18
+ height: number;
19
+ };
20
+ /** Exported image format: a PNG or an SVG, each as a data URL. */
21
+ export type SignatureFormat = 'png' | 'svg';
22
+ export type SignatureRenderOptions = {
23
+ /** CSS color of the ink. */
24
+ color: string;
25
+ /** Base line width in CSS pixels, at a pressure of 0.5. */
26
+ width: number;
27
+ };
28
+ /** Width of a segment drawn at `pressure`: half the base at no pressure, 1.5 times at full. */
29
+ export declare const strokeWidthAt: (pressure: number, width: number) => number;
30
+ /**
31
+ * The segments a stroke is drawn as: quadratic curves through the midpoints of its samples, so
32
+ * the line is smooth however coarse the pointer events were. Each segment carries the pressure it
33
+ * is drawn at, so a pen's line thickens and thins along the way.
34
+ */
35
+ export declare function strokeSegments(stroke: SignatureStroke): {
36
+ from: SignaturePoint;
37
+ control: SignaturePoint;
38
+ to: SignaturePoint;
39
+ pressure: number;
40
+ }[];
41
+ /** Draws `strokes` on a 2D context already scaled to CSS pixels. A tap draws a dot. */
42
+ export declare function drawStrokes(context: CanvasRenderingContext2D, strokes: readonly SignatureStroke[], { color, width }: SignatureRenderOptions): void;
43
+ /** The box the ink covers, line width included, or null when there is no ink. */
44
+ export declare function strokesBounds(strokes: readonly SignatureStroke[], width: number): SignatureBounds | null;
45
+ /** One drawable piece of a stroke: a dot for a tap, or a path at one line width. */
46
+ export type SignatureShape = {
47
+ kind: 'dot';
48
+ x: number;
49
+ y: number;
50
+ r: number;
51
+ } | {
52
+ kind: 'path';
53
+ d: string;
54
+ width: number;
55
+ };
56
+ /**
57
+ * The shapes a stroke draws as, in SVG path syntax, shifted by `offset`. Consecutive segments
58
+ * at the same width share one path, so a mouse or finger stroke is a single path and only a
59
+ * pen's changing pressure splits it.
60
+ */
61
+ export declare function strokeShapes(stroke: SignatureStroke, width: number, offset?: {
62
+ x: number;
63
+ y: number;
64
+ }): SignatureShape[];
65
+ /** A standalone SVG of the strokes, cropped to the ink plus `padding`. */
66
+ export declare function strokesToSvg(strokes: readonly SignatureStroke[], { color, width, padding }: SignatureRenderOptions & {
67
+ padding: number;
68
+ }): string | null;
69
+ /**
70
+ * Exports the strokes as a data URL cropped to the ink plus `padding`: an SVG, or a PNG drawn at
71
+ * `scale` device pixels per CSS pixel so it stays sharp in a document. Null when there is no ink.
72
+ */
73
+ export declare function exportStrokes(strokes: readonly SignatureStroke[], { format, color, width, padding, scale }: SignatureRenderOptions & {
74
+ format: SignatureFormat;
75
+ padding: number;
76
+ scale: number;
77
+ }): string | null;
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Strokes of a signature and the images they export to. Pure geometry: no DOM except the
3
+ * canvas context a caller passes in, so it runs the same in a test, on the pad and on export.
4
+ */
5
+ /** Width of a segment drawn at `pressure`: half the base at no pressure, 1.5 times at full. */
6
+ export const strokeWidthAt = (pressure, width) => width * (0.5 + Math.min(Math.max(pressure, 0), 1));
7
+ const midpoint = (a, b) => ({
8
+ x: (a.x + b.x) / 2,
9
+ y: (a.y + b.y) / 2,
10
+ pressure: (a.pressure + b.pressure) / 2
11
+ });
12
+ /**
13
+ * The segments a stroke is drawn as: quadratic curves through the midpoints of its samples, so
14
+ * the line is smooth however coarse the pointer events were. Each segment carries the pressure it
15
+ * is drawn at, so a pen's line thickens and thins along the way.
16
+ */
17
+ export function strokeSegments(stroke) {
18
+ const segments = [];
19
+ if (stroke.length < 2)
20
+ return segments;
21
+ let from = stroke[0];
22
+ for (let index = 1; index < stroke.length - 1; index++) {
23
+ const control = stroke[index];
24
+ const to = midpoint(control, stroke[index + 1]);
25
+ segments.push({ from, control, to, pressure: control.pressure });
26
+ from = to;
27
+ }
28
+ const last = stroke[stroke.length - 1];
29
+ segments.push({ from, control: last, to: last, pressure: last.pressure });
30
+ return segments;
31
+ }
32
+ /** Draws `strokes` on a 2D context already scaled to CSS pixels. A tap draws a dot. */
33
+ export function drawStrokes(context, strokes, { color, width }) {
34
+ context.save();
35
+ context.strokeStyle = color;
36
+ context.fillStyle = color;
37
+ context.lineCap = 'round';
38
+ context.lineJoin = 'round';
39
+ for (const stroke of strokes) {
40
+ if (stroke.length === 1) {
41
+ const [dot] = stroke;
42
+ context.beginPath();
43
+ context.arc(dot.x, dot.y, strokeWidthAt(dot.pressure, width) / 2, 0, Math.PI * 2);
44
+ context.fill();
45
+ continue;
46
+ }
47
+ for (const segment of strokeSegments(stroke)) {
48
+ context.beginPath();
49
+ context.lineWidth = strokeWidthAt(segment.pressure, width);
50
+ context.moveTo(segment.from.x, segment.from.y);
51
+ context.quadraticCurveTo(segment.control.x, segment.control.y, segment.to.x, segment.to.y);
52
+ context.stroke();
53
+ }
54
+ }
55
+ context.restore();
56
+ }
57
+ /** The box the ink covers, line width included, or null when there is no ink. */
58
+ export function strokesBounds(strokes, width) {
59
+ let minX = Infinity;
60
+ let minY = Infinity;
61
+ let maxX = -Infinity;
62
+ let maxY = -Infinity;
63
+ for (const stroke of strokes) {
64
+ for (const point of stroke) {
65
+ const half = strokeWidthAt(point.pressure, width) / 2;
66
+ minX = Math.min(minX, point.x - half);
67
+ minY = Math.min(minY, point.y - half);
68
+ maxX = Math.max(maxX, point.x + half);
69
+ maxY = Math.max(maxY, point.y + half);
70
+ }
71
+ }
72
+ if (minX === Infinity)
73
+ return null;
74
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
75
+ }
76
+ const round = (value) => Math.round(value * 100) / 100;
77
+ /**
78
+ * The shapes a stroke draws as, in SVG path syntax, shifted by `offset`. Consecutive segments
79
+ * at the same width share one path, so a mouse or finger stroke is a single path and only a
80
+ * pen's changing pressure splits it.
81
+ */
82
+ export function strokeShapes(stroke, width, offset = { x: 0, y: 0 }) {
83
+ const x = (value) => round(value - offset.x);
84
+ const y = (value) => round(value - offset.y);
85
+ if (stroke.length === 1) {
86
+ const [dot] = stroke;
87
+ return [
88
+ { kind: 'dot', x: x(dot.x), y: y(dot.y), r: round(strokeWidthAt(dot.pressure, width) / 2) }
89
+ ];
90
+ }
91
+ const shapes = [];
92
+ for (const segment of strokeSegments(stroke)) {
93
+ const segmentWidth = round(strokeWidthAt(segment.pressure, width));
94
+ const curve = `Q${x(segment.control.x)} ${y(segment.control.y)} ${x(segment.to.x)} ${y(segment.to.y)}`;
95
+ const last = shapes.at(-1);
96
+ if (last?.kind === 'path' && last.width === segmentWidth)
97
+ last.d += curve;
98
+ else
99
+ shapes.push({
100
+ kind: 'path',
101
+ d: `M${x(segment.from.x)} ${y(segment.from.y)}${curve}`,
102
+ width: segmentWidth
103
+ });
104
+ }
105
+ return shapes;
106
+ }
107
+ /** A standalone SVG of the strokes, cropped to the ink plus `padding`. */
108
+ export function strokesToSvg(strokes, { color, width, padding }) {
109
+ const bounds = strokesBounds(strokes, width);
110
+ if (!bounds)
111
+ return null;
112
+ const offset = { x: bounds.x - padding, y: bounds.y - padding };
113
+ const boxWidth = round(bounds.width + padding * 2);
114
+ const boxHeight = round(bounds.height + padding * 2);
115
+ const shapes = strokes
116
+ .flatMap((stroke) => strokeShapes(stroke, width, offset))
117
+ .map((shape) => shape.kind === 'dot'
118
+ ? `<circle cx="${shape.x}" cy="${shape.y}" r="${shape.r}" fill="${color}"/>`
119
+ : `<path d="${shape.d}" stroke-width="${shape.width}"/>`);
120
+ return [
121
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${boxWidth}" height="${boxHeight}" viewBox="0 0 ${boxWidth} ${boxHeight}">`,
122
+ `<g fill="none" stroke="${color}" stroke-linecap="round" stroke-linejoin="round">`,
123
+ shapes.join(''),
124
+ '</g></svg>'
125
+ ].join('');
126
+ }
127
+ /**
128
+ * Exports the strokes as a data URL cropped to the ink plus `padding`: an SVG, or a PNG drawn at
129
+ * `scale` device pixels per CSS pixel so it stays sharp in a document. Null when there is no ink.
130
+ */
131
+ export function exportStrokes(strokes, { format, color, width, padding, scale }) {
132
+ if (format === 'svg') {
133
+ const svg = strokesToSvg(strokes, { color, width, padding });
134
+ return svg ? `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}` : null;
135
+ }
136
+ const bounds = strokesBounds(strokes, width);
137
+ if (!bounds || typeof document === 'undefined')
138
+ return null;
139
+ const canvas = document.createElement('canvas');
140
+ canvas.width = Math.ceil((bounds.width + padding * 2) * scale);
141
+ canvas.height = Math.ceil((bounds.height + padding * 2) * scale);
142
+ const context = canvas.getContext('2d');
143
+ if (!context)
144
+ return null;
145
+ context.scale(scale, scale);
146
+ context.translate(padding - bounds.x, padding - bounds.y);
147
+ drawStrokes(context, strokes, { color, width });
148
+ return canvas.toDataURL('image/png');
149
+ }
@@ -0,0 +1,99 @@
1
+ import { type InferComponentTheme } from '../../../utils/cva/index.js';
2
+ export declare const signatureInputTheme: {
3
+ frame: (props?: import("../../../utils/cva/types.ts").CVAClassProp | undefined) => string;
4
+ pad: (props?: (import("../../../utils/cva/types.ts").CVAVariantSchema<{
5
+ size: {
6
+ small: string;
7
+ normal: string;
8
+ large: string;
9
+ };
10
+ interactive: {
11
+ true: string;
12
+ false: string;
13
+ };
14
+ disabled: {
15
+ true: string;
16
+ false: string;
17
+ };
18
+ hasError: {
19
+ true: string;
20
+ false: string;
21
+ };
22
+ }> & import("../../../utils/cva/types.ts").CVAClassProp) | undefined) => string;
23
+ ink: (props?: import("../../../utils/cva/types.ts").CVAClassProp | undefined) => string;
24
+ image: (props?: import("../../../utils/cva/types.ts").CVAClassProp | undefined) => string;
25
+ placeholder: (props?: (import("../../../utils/cva/types.ts").CVAVariantSchema<{
26
+ size: {
27
+ small: string;
28
+ normal: string;
29
+ large: string;
30
+ };
31
+ }> & import("../../../utils/cva/types.ts").CVAClassProp) | undefined) => string;
32
+ actions: (props?: import("../../../utils/cva/types.ts").CVAClassProp | undefined) => string;
33
+ };
34
+ export type SignatureInputTheme = typeof signatureInputTheme;
35
+ export type SignatureInputThemeProps = InferComponentTheme<SignatureInputTheme>;
36
+ export declare const setSignatureInputTheme: (theme: InferComponentTheme<{
37
+ frame: (props?: import("../../../utils/cva/types.ts").CVAClassProp | undefined) => string;
38
+ pad: (props?: (import("../../../utils/cva/types.ts").CVAVariantSchema<{
39
+ size: {
40
+ small: string;
41
+ normal: string;
42
+ large: string;
43
+ };
44
+ interactive: {
45
+ true: string;
46
+ false: string;
47
+ };
48
+ disabled: {
49
+ true: string;
50
+ false: string;
51
+ };
52
+ hasError: {
53
+ true: string;
54
+ false: string;
55
+ };
56
+ }> & import("../../../utils/cva/types.ts").CVAClassProp) | undefined) => string;
57
+ ink: (props?: import("../../../utils/cva/types.ts").CVAClassProp | undefined) => string;
58
+ image: (props?: import("../../../utils/cva/types.ts").CVAClassProp | undefined) => string;
59
+ placeholder: (props?: (import("../../../utils/cva/types.ts").CVAVariantSchema<{
60
+ size: {
61
+ small: string;
62
+ normal: string;
63
+ large: string;
64
+ };
65
+ }> & import("../../../utils/cva/types.ts").CVAClassProp) | undefined) => string;
66
+ actions: (props?: import("../../../utils/cva/types.ts").CVAClassProp | undefined) => string;
67
+ }>) => void;
68
+ export declare const useSignatureInputTheme: import("../../../utils/cva/theme.ts").UseComponentTheme<{
69
+ frame: (props?: import("../../../utils/cva/types.ts").CVAClassProp | undefined) => string;
70
+ pad: (props?: (import("../../../utils/cva/types.ts").CVAVariantSchema<{
71
+ size: {
72
+ small: string;
73
+ normal: string;
74
+ large: string;
75
+ };
76
+ interactive: {
77
+ true: string;
78
+ false: string;
79
+ };
80
+ disabled: {
81
+ true: string;
82
+ false: string;
83
+ };
84
+ hasError: {
85
+ true: string;
86
+ false: string;
87
+ };
88
+ }> & import("../../../utils/cva/types.ts").CVAClassProp) | undefined) => string;
89
+ ink: (props?: import("../../../utils/cva/types.ts").CVAClassProp | undefined) => string;
90
+ image: (props?: import("../../../utils/cva/types.ts").CVAClassProp | undefined) => string;
91
+ placeholder: (props?: (import("../../../utils/cva/types.ts").CVAVariantSchema<{
92
+ size: {
93
+ small: string;
94
+ normal: string;
95
+ large: string;
96
+ };
97
+ }> & import("../../../utils/cva/types.ts").CVAClassProp) | undefined) => string;
98
+ actions: (props?: import("../../../utils/cva/types.ts").CVAClassProp | undefined) => string;
99
+ }>;
@@ -0,0 +1,70 @@
1
+ import { cva, setComponentTheme, useComponentTheme } from '../../../utils/cva/index.js';
2
+ // Holds the pad and, over its corner, the buttons: they cannot sit inside the pad, whose image
3
+ // role would hide them from assistive technology.
4
+ const defaultFrame = cva({
5
+ base: 'relative w-full min-w-0'
6
+ });
7
+ // The pad is the control: it takes focus, so its own ring shows keyboard focus, and the strokes
8
+ // are drawn in its text color (an SVG in `currentColor`), so they follow the theme.
9
+ const defaultPad = cva({
10
+ base: 'relative w-full min-w-0 touch-none overflow-hidden rounded-md border border-neutral-muted bg-surface-raised text-neutral outline-none select-none focus-visible:ring-2 focus-visible:ring-focus/50',
11
+ variants: {
12
+ size: {
13
+ small: 'h-28',
14
+ normal: 'h-36',
15
+ large: 'h-44'
16
+ },
17
+ interactive: {
18
+ true: 'cursor-crosshair',
19
+ false: ''
20
+ },
21
+ disabled: {
22
+ true: 'cursor-not-allowed opacity-50',
23
+ false: ''
24
+ },
25
+ hasError: {
26
+ true: 'border-danger focus-visible:ring-danger/50',
27
+ false: ''
28
+ }
29
+ },
30
+ defaultVariants: {
31
+ size: 'normal',
32
+ interactive: true,
33
+ disabled: false,
34
+ hasError: false
35
+ }
36
+ });
37
+ const defaultInk = cva({
38
+ base: 'pointer-events-none absolute inset-0 size-full overflow-visible'
39
+ });
40
+ // A value set from outside (a saved signature) shows as an image, scaled to fit the pad.
41
+ const defaultImage = cva({
42
+ base: 'pointer-events-none absolute inset-0 size-full object-contain p-md'
43
+ });
44
+ // The line to sign on and its hint: decorative, gone as soon as there is ink.
45
+ const defaultPlaceholder = cva({
46
+ base: 'pointer-events-none absolute inset-x-[var(--space-xl)] bottom-1/4 flex items-end gap-sm border-b border-dashed border-neutral-muted pb-xs text-neutral/45',
47
+ variants: {
48
+ size: {
49
+ small: 'text-xs',
50
+ normal: 'text-sm',
51
+ large: 'text-base'
52
+ }
53
+ },
54
+ defaultVariants: {
55
+ size: 'normal'
56
+ }
57
+ });
58
+ const defaultActions = cva({
59
+ base: 'absolute end-[var(--space-xs)] top-[var(--space-xs)] flex items-center gap-xs'
60
+ });
61
+ export const signatureInputTheme = {
62
+ frame: defaultFrame,
63
+ pad: defaultPad,
64
+ ink: defaultInk,
65
+ image: defaultImage,
66
+ placeholder: defaultPlaceholder,
67
+ actions: defaultActions
68
+ };
69
+ export const setSignatureInputTheme = setComponentTheme('signature-input');
70
+ export const useSignatureInputTheme = useComponentTheme('signature-input', signatureInputTheme);
@@ -59,6 +59,8 @@ export function getSidebarPanelPeekClass(variant) {
59
59
  * ones for floating and split (the panel's own gutter spaces the pair).
60
60
  */
61
61
  export function getSidebarActivityBarContainerClass(side, frame, variant) {
62
- return cx('inset-y-0 z-30 hidden w-[var(--sidebar-activity-offset,3rem)] md:flex', frame === 'viewport' ? 'fixed h-window' : 'absolute h-full', side === 'left' ? 'left-0' : 'right-0', variant === 'inset' && 'py-md', (variant === 'floating' || variant === 'split') &&
62
+ // Above the panel in every state, a peek (`z-40`) included: an edge-revealed panel slides in
63
+ // from past the screen edge and must come out from under the rail, never across it.
64
+ return cx('inset-y-0 z-[45] hidden w-[var(--sidebar-activity-offset,3rem)] md:flex', frame === 'viewport' ? 'fixed h-window' : 'absolute h-full', side === 'left' ? 'left-0' : 'right-0', variant === 'inset' && 'py-md', (variant === 'floating' || variant === 'split') &&
63
65
  (side === 'left' ? 'py-md pl-md' : 'py-md pr-md'));
64
66
  }
@@ -15,7 +15,7 @@
15
15
  size = 'normal',
16
16
  density = 'normal',
17
17
  color = 'neutral',
18
- connectorColor = 'neutral',
18
+ connectorColor,
19
19
  showConnectors = true,
20
20
  scrollFade = true,
21
21
  i18n,
@@ -1,4 +1,5 @@
1
1
  <script lang="ts" generics="Item extends TimelineItem = TimelineItem">
2
+ import { checkIcon } from '../Icons/check.js';
2
3
  import Slot from '../Slot/Slot.svelte';
3
4
  import Spinner from '../Spinner/Spinner.svelte';
4
5
  import type { Density, Sizes } from '../../types/theme.js';
@@ -64,6 +65,8 @@
64
65
  data-density={density}
65
66
  data-first={entry.isFirst ? 'true' : undefined}
66
67
  data-last={entry.isLast ? 'true' : undefined}
68
+ data-progress={entry.item.progress}
69
+ aria-current={entry.item.progress === 'current' ? 'step' : undefined}
67
70
  class={classes.item({
68
71
  orientation: entry.orientation,
69
72
  placement,
@@ -107,9 +110,11 @@
107
110
  <span
108
111
  data-slot="timeline-connector"
109
112
  data-color={entry.connectorColor}
113
+ data-progress={entry.item.progress}
110
114
  class={classes.connector({
111
115
  orientation: entry.orientation,
112
- color: entry.connectorColor
116
+ color: entry.connectorColor,
117
+ progress: entry.item.progress ?? 'none'
113
118
  })}
114
119
  ></span>
115
120
  {/if}
@@ -182,7 +187,12 @@
182
187
  <span
183
188
  data-slot="timeline-marker"
184
189
  data-color={entry.color}
185
- class={classes.marker({ size, color: entry.color })}
190
+ data-progress={entry.item.loading ? undefined : entry.item.progress}
191
+ class={classes.marker({
192
+ size,
193
+ color: entry.color,
194
+ progress: entry.item.loading ? 'none' : (entry.item.progress ?? 'none')
195
+ })}
186
196
  >
187
197
  {#if entry.item.loading}
188
198
  <span data-slot="timeline-loading" class={classes.loading({ size })}>
@@ -190,6 +200,10 @@
190
200
  </span>
191
201
  {:else if entry.item.icon}
192
202
  <Slot render={entry.item.icon} />
203
+ {:else if entry.item.progress === 'done'}
204
+ {@render checkIcon()}
205
+ {:else if entry.item.progress === 'upcoming'}
206
+ <span data-slot="timeline-step-number">{entry.index + 1}</span>
193
207
  {:else}
194
208
  <span data-slot="timeline-dot"></span>
195
209
  {/if}
@@ -1,3 +1,3 @@
1
1
  export { default as Timeline } from './Timeline.svelte';
2
- export type { TimelineItem, TimelineItemPayload, TimelineOrientation, TimelinePlacement, TimelineProps, TimelineSide, TimelineVariant } from './timeline.props.js';
2
+ export type { TimelineItem, TimelineItemPayload, TimelineOrientation, TimelinePlacement, TimelineProgress, TimelineProps, TimelineSide, TimelineVariant } from './timeline.props.js';
3
3
  export { timelineTheme, setTimelineTheme, useTimelineTheme, type TimelineTheme, type TimelineThemeProps } from './timeline.theme.js';
@@ -11,4 +11,4 @@ export type ResolvedTimelineItem<Item extends TimelineItem> = Readonly<{
11
11
  isFirst: boolean;
12
12
  isLast: boolean;
13
13
  }>;
14
- export declare function resolveTimelineItems<Item extends TimelineItem>(items: readonly Item[], orientation: TimelineOrientation, placement: TimelinePlacement, color: Colors, connectorColor: Colors): readonly ResolvedTimelineItem<Item>[];
14
+ export declare function resolveTimelineItems<Item extends TimelineItem>(items: readonly Item[], orientation: TimelineOrientation, placement: TimelinePlacement, color: Colors, connectorColor: Colors | undefined): readonly ResolvedTimelineItem<Item>[];
@@ -17,6 +17,7 @@ export function resolveTimelineItems(items, orientation, placement, color, conne
17
17
  const side = placement === 'alternate'
18
18
  ? (timelineItem.side ?? (index % 2 === 0 ? 'end' : 'start'))
19
19
  : placement;
20
+ const itemColor = timelineItem.color ?? color;
20
21
  let key = `index:${index}`;
21
22
  if (explicitId !== undefined) {
22
23
  key = `id:${typeof explicitId}:${explicitId}`;
@@ -27,8 +28,12 @@ export function resolveTimelineItems(items, orientation, placement, color, conne
27
28
  index,
28
29
  side,
29
30
  orientation,
30
- color: timelineItem.color ?? color,
31
- connectorColor: timelineItem.connectorColor ?? connectorColor,
31
+ color: itemColor,
32
+ // A progress band reads as one stroke: its connectors follow the step's own color
33
+ // unless a connector color is set on the item or the root.
34
+ connectorColor: timelineItem.connectorColor ??
35
+ connectorColor ??
36
+ (timelineItem.progress ? itemColor : 'neutral'),
32
37
  isFirst: index === 0,
33
38
  isLast: index === items.length - 1
34
39
  };
@@ -1 +1 @@
1
- export declare const timelineDescription = "\n# Timeline\n\nRender an ordered sequence of dated or descriptive events on a vertical or horizontal axis. Timeline owns the semantic list, item surfaces, axis anchors, markers, connectors, responsive alternate collapse, and horizontal overflow behavior. Input order is always render order.\n\n## Basic usage\n\n```svelte\n<script lang=\"ts\">\n import { Timeline, type TimelineItem } from './index.ts';\n\n const items: TimelineItem[] = [\n {\n id: 'placed',\n date: 'Mar 15, 2024',\n datetime: '2024-03-15',\n title: 'Order placed',\n description: 'Your order has been received.'\n },\n {\n id: 'payment',\n date: 'Mar 16, 2024',\n datetime: '2024-03-16',\n title: 'Payment confirmed',\n color: 'success'\n }\n ];\n</script>\n\n<Timeline {items} />\n```\n\nThe default configuration is a vertical timeline with all content on the logical end side of the axis.\n\n## Props\n\n### Data and layout\n\n- **items**: `readonly Item[]` (required) \u2014 entries in display order. `Item` must extend `TimelineItem`.\n- **orientation**: `'vertical' | 'horizontal'` (default: `'vertical'`) \u2014 direction of the sequence axis.\n- **placement**: `'start' | 'end' | 'alternate'` (default: `'end'`) \u2014 logical side used for item surfaces. Alternate placement starts on `end`, then alternates by array index.\n- **variant**: `'ghost' | 'card' | 'outline' | 'soft'` (default: `'ghost'`) \u2014 global surface treatment for all entries.\n- **size**: `'small' | 'normal' | 'large'` (default: `'normal'`) \u2014 title and description typography, marker and icon scale, and loading Spinner scale.\n- **density**: `'compact' | 'normal' | 'comfortable'` (default: `'normal'`) \u2014 item gaps, surface padding, connector spacing, and horizontal item minimum width.\n- **color**: Entasis semantic color (default: `'neutral'`) \u2014 default marker and outline or soft surface accent.\n- **connectorColor**: Entasis semantic color (default: `'neutral'`) \u2014 default outgoing connector color.\n- **showConnectors**: `boolean` (default: `true`) \u2014 shows connector segments between markers.\n- **scrollFade**: `boolean` (default: `true`) \u2014 applies the shared logical horizontal scroll fade only while a horizontal timeline actually overflows.\n- **i18n**: `Partial<Messages>` \u2014 per-instance translations merged over the global i18n catalog.\n\n### Custom rendering\n\n- **item**: `Snippet<[TimelineItemPayload<Item>]>` \u2014 replaces the content inside each Timeline-owned variant surface.\n- **marker**: `Snippet<[TimelineItemPayload<Item>]>` \u2014 replaces the marker visual inside the Timeline-owned axis anchor.\n- **opposite**: `Snippet<[TimelineItemPayload<Item>]>` \u2014 replaces the opposite track in alternate placement. Its default renderer is the item date. TypeScript rejects this prop with fixed `start` or `end` placement.\n\n### Root element\n\n- **ref**: `HTMLOListElement | null` (bindable) \u2014 reference to the root `<ol>`.\n- **class**: `string` \u2014 additional root classes.\n- **theme**: `TimelineThemeProps` \u2014 per-instance overrides for Timeline theme parts.\n- Native ordered-list attributes and Svelte attachments are forwarded to the root.\n\n## Item shape\n\n```ts\ntype TimelineItem = {\n id?: string | number;\n title: Slot;\n date?: Slot;\n datetime?: string;\n description?: Slot;\n icon?: Slot;\n loading?: boolean;\n color?: Colors;\n connectorColor?: Colors;\n side?: 'start' | 'end';\n};\n```\n\n- `title` is required. `title`, `date`, `description`, and `icon` accept a string or snippet.\n- `id` is optional. Use a stable explicit ID when entries can reorder; otherwise Timeline uses the array index as identity.\n- `datetime` adds the machine-readable value to a default `<time>` element when `date` is present.\n- `icon` replaces the default dot inside the default marker.\n- `loading` replaces the default marker visual with a color- and size-aware Spinner.\n- `color` overrides the root color for this marker and its outline or soft surface.\n- `connectorColor` overrides the root color for this item's outgoing connector. It has no effect on the final item.\n- `side` overrides alternate parity for one item. It is not valid with fixed placement.\n\n## Placement and dates\n\nFixed `start` and `end` placement keeps every surface on one logical side. The default content renders the date before the title inside each surface.\n\n`placement=\"alternate\"` creates an opposite track and starts the first item on `end`. Its default opposite renderer displays the date. Set `item.side` to place a specific alternate item on `start` or `end` without changing the array order.\n\n```svelte\n<Timeline items={milestones} placement=\"alternate\" variant=\"card\" />\n```\n\nTimeline treats dates as display content. It does not parse, format, compare, or sort them. It also has no completed, current, or pending status model. Put status meaning in the title or description and use semantic color or a custom marker only as supporting presentation.\n\n## Generic snippets and defaults\n\nApplication item types can extend `TimelineItem`. The extended type remains available as `payload.item` in all three renderer snippets.\n\n```svelte\n<script lang=\"ts\">\n import { Timeline, type TimelineItem } from './index.ts';\n\n type Release = TimelineItem & {\n version: string;\n href: string;\n };\n\n const releases: Release[] = [\n { id: 'v2', title: 'Version 2 released', version: '2.0.0', href: '/releases/v2' }\n ];\n</script>\n\n<Timeline items={releases}>\n {#snippet item({ item, defaultContent })}\n <a href={item.href} class=\"block\">\n {@render defaultContent()}\n <span class=\"text-neutral/70 text-xs\">{item.version}</span>\n </a>\n {/snippet}\n</Timeline>\n```\n\nEvery snippet receives:\n\n```ts\ntype TimelineItemPayload<Item extends TimelineItem> = Readonly<{\n item: Item;\n index: number;\n side: 'start' | 'end';\n orientation: 'vertical' | 'horizontal';\n color: Colors;\n connectorColor: Colors;\n isFirst: boolean;\n isLast: boolean;\n defaultContent: Snippet;\n defaultMarker: Snippet;\n defaultOpposite: Snippet;\n}>;\n```\n\nCall `defaultContent()`, `defaultMarker()`, or `defaultOpposite()` to wrap or extend the matching default renderer. A custom `marker` snippet that does not call `defaultMarker()` owns its complete marker visual, including loading presentation. Timeline still owns the `<ol>`, each `<li>`, axis and connector geometry, marker anchor, opposite track, and variant surface.\n\n## Responsive alternate layout\n\nA vertical alternate timeline uses its own inline-size container. Below `40rem`, it moves the axis to the logical start edge, places every main surface on the end side, and places opposite content before the main surface. DOM and input order do not change. Per-item `side` values remain in the payload, but their visual side effect is suspended while the layout is collapsed.\n\n## Horizontal overflow\n\nHorizontal entries stay on one axis and use density-controlled minimum widths. They do not wrap or compress; the root uses native horizontal scrolling. Timeline measures real overflow with `ResizeObserver`, applies `scroll-fade-x` only when needed, and makes the ordered list keyboard-focusable only while it overflows. The fade and native scrolling use logical inline direction and work in LTR and RTL layouts.\n\n## Variants\n\n- `ghost`: no surface chrome.\n- `card`: neutral raised surface with a quiet ring and shadow.\n- `outline`: transparent surface with a ring in the resolved item color.\n- `soft`: muted surface in the resolved item color.\n\nThe variant is global. Timeline does not support per-item variant overrides.\n\n## Runtime validation\n\nTimeline throws a descriptive `TypeError` when:\n\n- two items have the same explicit `id`;\n- an item supplies `side` while placement is fixed to `start` or `end`.\n\nThe ID check preserves number and string identity, so `1` and `'1'` are different IDs. TypeScript separately rejects `opposite` with fixed placement.\n\n## Accessibility\n\n- The root is an ordered list and every event is a list item.\n- Axis anchors, markers, and connectors are decorative and hidden from assistive technology. Do not use marker color or shape as the only status signal.\n- A default loading marker has a localized polite status announcement outside the decorative axis.\n- A default date with `datetime` renders as a semantic `<time>` element.\n- An overflowing horizontal timeline becomes a native keyboard scroll region; a non-overflowing timeline adds no tab stop.\n\n## Theme parts\n\n`root`, `item`, `opposite`, `axis`, `connector`, `marker`, `content`, `date`, `titleRow`, `title`, `description`, and `loading`.\n\nTimeline is display-only. It has no selection, navigation, event callbacks, animation state, bindable controller, date processing, or built-in progress status.\n";
1
+ export declare const timelineDescription = "\n# Timeline\n\nRender an ordered sequence of dated or descriptive events on a vertical or horizontal axis. Timeline owns the semantic list, item surfaces, axis anchors, markers, connectors, responsive alternate collapse, and horizontal overflow behavior. Input order is always render order.\n\n## Basic usage\n\n```svelte\n<script lang=\"ts\">\n import { Timeline, type TimelineItem } from './index.ts';\n\n const items: TimelineItem[] = [\n {\n id: 'placed',\n date: 'Mar 15, 2024',\n datetime: '2024-03-15',\n title: 'Order placed',\n description: 'Your order has been received.'\n },\n {\n id: 'payment',\n date: 'Mar 16, 2024',\n datetime: '2024-03-16',\n title: 'Payment confirmed',\n color: 'success'\n }\n ];\n</script>\n\n<Timeline {items} />\n```\n\nThe default configuration is a vertical timeline with all content on the logical end side of the axis.\n\n## Props\n\n### Data and layout\n\n- **items**: `readonly Item[]` (required) \u2014 entries in display order. `Item` must extend `TimelineItem`.\n- **orientation**: `'vertical' | 'horizontal'` (default: `'vertical'`) \u2014 direction of the sequence axis.\n- **placement**: `'start' | 'end' | 'alternate'` (default: `'end'`) \u2014 logical side used for item surfaces. Alternate placement starts on `end`, then alternates by array index.\n- **variant**: `'ghost' | 'card' | 'outline' | 'soft'` (default: `'ghost'`) \u2014 global surface treatment for all entries.\n- **size**: `'small' | 'normal' | 'large'` (default: `'normal'`) \u2014 title and description typography, marker and icon scale, and loading Spinner scale.\n- **density**: `'compact' | 'normal' | 'comfortable'` (default: `'normal'`) \u2014 item gaps, surface padding, connector spacing, and horizontal item minimum width.\n- **color**: Entasis semantic color (default: `'neutral'`) \u2014 default marker and outline or soft surface accent.\n- **connectorColor**: Entasis semantic color \u2014 default outgoing connector color. When omitted, an item with `progress` uses its own resolved color for its connector, and every other item uses `'neutral'`.\n- **showConnectors**: `boolean` (default: `true`) \u2014 shows connector segments between markers.\n- **scrollFade**: `boolean` (default: `true`) \u2014 applies the shared logical horizontal scroll fade only while a horizontal timeline actually overflows.\n- **i18n**: `Partial<Messages>` \u2014 per-instance translations merged over the global i18n catalog.\n\n### Custom rendering\n\n- **item**: `Snippet<[TimelineItemPayload<Item>]>` \u2014 replaces the content inside each Timeline-owned variant surface.\n- **marker**: `Snippet<[TimelineItemPayload<Item>]>` \u2014 replaces the marker visual inside the Timeline-owned axis anchor.\n- **opposite**: `Snippet<[TimelineItemPayload<Item>]>` \u2014 replaces the opposite track in alternate placement. Its default renderer is the item date. TypeScript rejects this prop with fixed `start` or `end` placement.\n\n### Root element\n\n- **ref**: `HTMLOListElement | null` (bindable) \u2014 reference to the root `<ol>`.\n- **class**: `string` \u2014 additional root classes.\n- **theme**: `TimelineThemeProps` \u2014 per-instance overrides for Timeline theme parts.\n- Native ordered-list attributes and Svelte attachments are forwarded to the root.\n\n## Item shape\n\n```ts\ntype TimelineItem = {\n id?: string | number;\n title: Slot;\n date?: Slot;\n datetime?: string;\n description?: Slot;\n icon?: Slot;\n loading?: boolean;\n color?: Colors;\n connectorColor?: Colors;\n progress?: 'done' | 'current' | 'upcoming';\n side?: 'start' | 'end';\n};\n```\n\n- `title` is required. `title`, `date`, `description`, and `icon` accept a string or snippet.\n- `id` is optional. Use a stable explicit ID when entries can reorder; otherwise Timeline uses the array index as identity.\n- `datetime` adds the machine-readable value to a default `<time>` element when `date` is present.\n- `icon` replaces the default dot inside the default marker.\n- `loading` replaces the default marker visual with a color- and size-aware Spinner.\n- `color` overrides the root color for this marker and its outline or soft surface.\n- `connectorColor` overrides the root color for this item's outgoing connector. It has no effect on the final item.\n- `side` overrides alternate parity for one item. It is not valid with fixed placement.\n- `progress` marks a step of a process (`TimelineProgress`). See Step progress below.\n\n## Placement and dates\n\nFixed `start` and `end` placement keeps every surface on one logical side. The default content renders the date before the title inside each surface.\n\n`placement=\"alternate\"` creates an opposite track and starts the first item on `end`. Its default opposite renderer displays the date. Set `item.side` to place a specific alternate item on `start` or `end` without changing the array order.\n\n```svelte\n<Timeline items={milestones} placement=\"alternate\" variant=\"card\" />\n```\n\nTimeline treats dates as display content. It does not parse, format, compare, or sort them.\n\n## Step progress\n\nGive each item a `progress` to draw a process's progress band, such as a file's six stages, so a reader sees at a glance what is done, what is under way, and what is to come:\n\n```svelte\n<script lang=\"ts\">\n import { Timeline, type TimelineItem } from './index.ts';\n\n const steps: TimelineItem[] = [\n { id: 'opened', title: 'Opened', progress: 'done' },\n { id: 'documents', title: 'Documents', progress: 'done' },\n { id: 'appraisal', title: 'Appraisal', progress: 'current' },\n { id: 'report', title: 'Report', progress: 'upcoming' }\n ];\n</script>\n\n<Timeline items={steps} orientation=\"horizontal\" color=\"primary\" />\n```\n\n- `done`: the marker is filled with the item color and carries a check. Its outgoing connector is solid.\n- `current`: the marker is a ring in the item color around a point. The `<li>` gets `aria-current=\"step\"`. Its outgoing connector is faint.\n- `upcoming`: the marker is a quiet neutral ring carrying the step number (the item's position, from 1). Its outgoing connector is faint.\n- Markers keep the size's marker scale. Progress connectors are 2px thick instead of the hairline, so the done part of the band stands out.\n- An item `icon` replaces the check, the point, or the number, and keeps the progress ring and fill. `loading` replaces the whole marker with the Spinner.\n- `progress` is per item: Timeline does not derive it from an index, so a host may mark any combination.\n- The step's state is not announced by the markers, which stay decorative. Put it in the item's own text (for example \"Step 3 of 6, done\") or an `item` snippet. `aria-current=\"step\"` on the current item is the only state Timeline adds.\n- The `marker` snippet still replaces every marker: read `payload.item.progress` and call `defaultMarker()` for the steps it leaves alone. Theme overrides reach the `progress` variant of the `marker` and `connector` parts, for example `theme={{ connector: { progress: { done: 'bg-success' } } }}`.\n- Styling hooks: `data-progress` on the item, its marker and its outgoing connector.\n\n## Generic snippets and defaults\n\nApplication item types can extend `TimelineItem`. The extended type remains available as `payload.item` in all three renderer snippets.\n\n```svelte\n<script lang=\"ts\">\n import { Timeline, type TimelineItem } from './index.ts';\n\n type Release = TimelineItem & {\n version: string;\n href: string;\n };\n\n const releases: Release[] = [\n { id: 'v2', title: 'Version 2 released', version: '2.0.0', href: '/releases/v2' }\n ];\n</script>\n\n<Timeline items={releases}>\n {#snippet item({ item, defaultContent })}\n <a href={item.href} class=\"block\">\n {@render defaultContent()}\n <span class=\"text-neutral/70 text-xs\">{item.version}</span>\n </a>\n {/snippet}\n</Timeline>\n```\n\nEvery snippet receives:\n\n```ts\ntype TimelineItemPayload<Item extends TimelineItem> = Readonly<{\n item: Item;\n index: number;\n side: 'start' | 'end';\n orientation: 'vertical' | 'horizontal';\n color: Colors;\n connectorColor: Colors;\n isFirst: boolean;\n isLast: boolean;\n defaultContent: Snippet;\n defaultMarker: Snippet;\n defaultOpposite: Snippet;\n}>;\n```\n\nCall `defaultContent()`, `defaultMarker()`, or `defaultOpposite()` to wrap or extend the matching default renderer. A custom `marker` snippet that does not call `defaultMarker()` owns its complete marker visual, including loading presentation. Timeline still owns the `<ol>`, each `<li>`, axis and connector geometry, marker anchor, opposite track, and variant surface.\n\n## Responsive alternate layout\n\nA vertical alternate timeline uses its own inline-size container. Below `40rem`, it moves the axis to the logical start edge, places every main surface on the end side, and places opposite content before the main surface. DOM and input order do not change. Per-item `side` values remain in the payload, but their visual side effect is suspended while the layout is collapsed.\n\n## Horizontal overflow\n\nHorizontal entries stay on one axis and use density-controlled minimum widths. They do not wrap or compress; the root uses native horizontal scrolling. Timeline measures real overflow with `ResizeObserver`, applies `scroll-fade-x` only when needed, and makes the ordered list keyboard-focusable only while it overflows. The fade and native scrolling use logical inline direction and work in LTR and RTL layouts.\n\n## Variants\n\n- `ghost`: no surface chrome.\n- `card`: neutral raised surface with a quiet ring and shadow.\n- `outline`: transparent surface with a ring in the resolved item color.\n- `soft`: muted surface in the resolved item color.\n\nThe variant is global. Timeline does not support per-item variant overrides.\n\n## Runtime validation\n\nTimeline throws a descriptive `TypeError` when:\n\n- two items have the same explicit `id`;\n- an item supplies `side` while placement is fixed to `start` or `end`.\n\nThe ID check preserves number and string identity, so `1` and `'1'` are different IDs. TypeScript separately rejects `opposite` with fixed placement.\n\n## Accessibility\n\n- The root is an ordered list and every event is a list item.\n- Axis anchors, markers, and connectors are decorative and hidden from assistive technology. Do not use marker color or shape as the only status signal.\n- An item with `progress: 'current'` carries `aria-current=\"step\"`.\n- A default loading marker has a localized polite status announcement outside the decorative axis.\n- A default date with `datetime` renders as a semantic `<time>` element.\n- An overflowing horizontal timeline becomes a native keyboard scroll region; a non-overflowing timeline adds no tab stop.\n\n## Theme parts\n\n`root`, `item`, `opposite`, `axis`, `connector`, `marker`, `content`, `date`, `titleRow`, `title`, `description`, and `loading`.\n\nTimeline is display-only. It has no selection, navigation, event callbacks, animation state, bindable controller, or date processing. Step `progress` is presentation the host sets per item, not a state Timeline advances.\n";
@@ -43,7 +43,7 @@ The default configuration is a vertical timeline with all content on the logical
43
43
  - **size**: \`'small' | 'normal' | 'large'\` (default: \`'normal'\`) — title and description typography, marker and icon scale, and loading Spinner scale.
44
44
  - **density**: \`'compact' | 'normal' | 'comfortable'\` (default: \`'normal'\`) — item gaps, surface padding, connector spacing, and horizontal item minimum width.
45
45
  - **color**: Entasis semantic color (default: \`'neutral'\`) — default marker and outline or soft surface accent.
46
- - **connectorColor**: Entasis semantic color (default: \`'neutral'\`) — default outgoing connector color.
46
+ - **connectorColor**: Entasis semantic color — default outgoing connector color. When omitted, an item with \`progress\` uses its own resolved color for its connector, and every other item uses \`'neutral'\`.
47
47
  - **showConnectors**: \`boolean\` (default: \`true\`) — shows connector segments between markers.
48
48
  - **scrollFade**: \`boolean\` (default: \`true\`) — applies the shared logical horizontal scroll fade only while a horizontal timeline actually overflows.
49
49
  - **i18n**: \`Partial<Messages>\` — per-instance translations merged over the global i18n catalog.
@@ -74,6 +74,7 @@ type TimelineItem = {
74
74
  loading?: boolean;
75
75
  color?: Colors;
76
76
  connectorColor?: Colors;
77
+ progress?: 'done' | 'current' | 'upcoming';
77
78
  side?: 'start' | 'end';
78
79
  };
79
80
  \`\`\`
@@ -86,6 +87,7 @@ type TimelineItem = {
86
87
  - \`color\` overrides the root color for this marker and its outline or soft surface.
87
88
  - \`connectorColor\` overrides the root color for this item's outgoing connector. It has no effect on the final item.
88
89
  - \`side\` overrides alternate parity for one item. It is not valid with fixed placement.
90
+ - \`progress\` marks a step of a process (\`TimelineProgress\`). See Step progress below.
89
91
 
90
92
  ## Placement and dates
91
93
 
@@ -97,7 +99,36 @@ Fixed \`start\` and \`end\` placement keeps every surface on one logical side. T
97
99
  <Timeline items={milestones} placement="alternate" variant="card" />
98
100
  \`\`\`
99
101
 
100
- Timeline treats dates as display content. It does not parse, format, compare, or sort them. It also has no completed, current, or pending status model. Put status meaning in the title or description and use semantic color or a custom marker only as supporting presentation.
102
+ Timeline treats dates as display content. It does not parse, format, compare, or sort them.
103
+
104
+ ## Step progress
105
+
106
+ Give each item a \`progress\` to draw a process's progress band, such as a file's six stages, so a reader sees at a glance what is done, what is under way, and what is to come:
107
+
108
+ \`\`\`svelte
109
+ <script lang="ts">
110
+ import { Timeline, type TimelineItem } from './index.ts';
111
+
112
+ const steps: TimelineItem[] = [
113
+ { id: 'opened', title: 'Opened', progress: 'done' },
114
+ { id: 'documents', title: 'Documents', progress: 'done' },
115
+ { id: 'appraisal', title: 'Appraisal', progress: 'current' },
116
+ { id: 'report', title: 'Report', progress: 'upcoming' }
117
+ ];
118
+ </script>
119
+
120
+ <Timeline items={steps} orientation="horizontal" color="primary" />
121
+ \`\`\`
122
+
123
+ - \`done\`: the marker is filled with the item color and carries a check. Its outgoing connector is solid.
124
+ - \`current\`: the marker is a ring in the item color around a point. The \`<li>\` gets \`aria-current="step"\`. Its outgoing connector is faint.
125
+ - \`upcoming\`: the marker is a quiet neutral ring carrying the step number (the item's position, from 1). Its outgoing connector is faint.
126
+ - Markers keep the size's marker scale. Progress connectors are 2px thick instead of the hairline, so the done part of the band stands out.
127
+ - An item \`icon\` replaces the check, the point, or the number, and keeps the progress ring and fill. \`loading\` replaces the whole marker with the Spinner.
128
+ - \`progress\` is per item: Timeline does not derive it from an index, so a host may mark any combination.
129
+ - The step's state is not announced by the markers, which stay decorative. Put it in the item's own text (for example "Step 3 of 6, done") or an \`item\` snippet. \`aria-current="step"\` on the current item is the only state Timeline adds.
130
+ - The \`marker\` snippet still replaces every marker: read \`payload.item.progress\` and call \`defaultMarker()\` for the steps it leaves alone. Theme overrides reach the \`progress\` variant of the \`marker\` and \`connector\` parts, for example \`theme={{ connector: { progress: { done: 'bg-success' } } }}\`.
131
+ - Styling hooks: \`data-progress\` on the item, its marker and its outgoing connector.
101
132
 
102
133
  ## Generic snippets and defaults
103
134
 
@@ -177,6 +208,7 @@ The ID check preserves number and string identity, so \`1\` and \`'1'\` are diff
177
208
 
178
209
  - The root is an ordered list and every event is a list item.
179
210
  - Axis anchors, markers, and connectors are decorative and hidden from assistive technology. Do not use marker color or shape as the only status signal.
211
+ - An item with \`progress: 'current'\` carries \`aria-current="step"\`.
180
212
  - A default loading marker has a localized polite status announcement outside the decorative axis.
181
213
  - A default date with \`datetime\` renders as a semantic \`<time>\` element.
182
214
  - An overflowing horizontal timeline becomes a native keyboard scroll region; a non-overflowing timeline adds no tab stop.
@@ -185,5 +217,5 @@ The ID check preserves number and string identity, so \`1\` and \`'1'\` are diff
185
217
 
186
218
  \`root\`, \`item\`, \`opposite\`, \`axis\`, \`connector\`, \`marker\`, \`content\`, \`date\`, \`titleRow\`, \`title\`, \`description\`, and \`loading\`.
187
219
 
188
- Timeline is display-only. It has no selection, navigation, event callbacks, animation state, bindable controller, date processing, or built-in progress status.
220
+ Timeline is display-only. It has no selection, navigation, event callbacks, animation state, bindable controller, or date processing. Step \`progress\` is presentation the host sets per item, not a state Timeline advances.
189
221
  `;