@dorsk/tsumikit 0.38.1 → 0.39.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.
Files changed (143) hide show
  1. package/dist/autoresize.d.ts +2 -0
  2. package/dist/autoresize.js +15 -1
  3. package/dist/components/atoms/Artwork.svelte +19 -18
  4. package/dist/components/atoms/Artwork.svelte.d.ts +3 -2
  5. package/dist/components/atoms/Badge.svelte +60 -26
  6. package/dist/components/atoms/Badge.svelte.d.ts +14 -3
  7. package/dist/components/atoms/Button.svelte +30 -12
  8. package/dist/components/atoms/Button.svelte.d.ts +4 -2
  9. package/dist/components/atoms/Card.svelte +47 -30
  10. package/dist/components/atoms/Card.svelte.d.ts +12 -3
  11. package/dist/components/atoms/Checkbox.svelte +16 -2
  12. package/dist/components/atoms/Checkbox.svelte.d.ts +2 -0
  13. package/dist/components/atoms/Dot.svelte +17 -11
  14. package/dist/components/atoms/Dot.svelte.d.ts +5 -2
  15. package/dist/components/atoms/Gauge.svelte +19 -18
  16. package/dist/components/atoms/Gauge.svelte.d.ts +3 -2
  17. package/dist/components/atoms/Heading.svelte +20 -19
  18. package/dist/components/atoms/Heading.svelte.d.ts +3 -2
  19. package/dist/components/atoms/Icon.svelte +33 -13
  20. package/dist/components/atoms/Icon.svelte.d.ts +17 -2
  21. package/dist/components/atoms/Input.svelte +17 -3
  22. package/dist/components/atoms/Input.svelte.d.ts +2 -1
  23. package/dist/components/atoms/Kbd.svelte +25 -3
  24. package/dist/components/atoms/Kbd.svelte.d.ts +5 -1
  25. package/dist/components/atoms/Link.svelte +16 -12
  26. package/dist/components/atoms/Link.svelte.d.ts +6 -2
  27. package/dist/components/atoms/Progress.svelte +119 -4
  28. package/dist/components/atoms/Progress.svelte.d.ts +28 -2
  29. package/dist/components/atoms/Scrim.svelte +7 -2
  30. package/dist/components/atoms/Scrim.svelte.d.ts +2 -0
  31. package/dist/components/atoms/SegmentedProgress.svelte +14 -2
  32. package/dist/components/atoms/SegmentedProgress.svelte.d.ts +5 -2
  33. package/dist/components/atoms/Select.svelte +18 -6
  34. package/dist/components/atoms/Select.svelte.d.ts +3 -3
  35. package/dist/components/atoms/Skeleton.svelte +12 -11
  36. package/dist/components/atoms/Skeleton.svelte.d.ts +3 -2
  37. package/dist/components/atoms/Slider.svelte +45 -3
  38. package/dist/components/atoms/Slider.svelte.d.ts +7 -0
  39. package/dist/components/atoms/Spinner.svelte +2 -0
  40. package/dist/components/atoms/Spinner.svelte.d.ts +2 -0
  41. package/dist/components/atoms/Switch.svelte +70 -18
  42. package/dist/components/atoms/Switch.svelte.d.ts +5 -1
  43. package/dist/components/atoms/Text.svelte +39 -26
  44. package/dist/components/atoms/Text.svelte.d.ts +7 -3
  45. package/dist/components/atoms/Textarea.svelte +47 -10
  46. package/dist/components/atoms/Textarea.svelte.d.ts +10 -3
  47. package/dist/components/layouts/AppShell.svelte +7 -3
  48. package/dist/components/layouts/AppShell.svelte.d.ts +2 -0
  49. package/dist/components/layouts/AutoGrid.svelte +4 -0
  50. package/dist/components/layouts/AutoGrid.svelte.d.ts +2 -0
  51. package/dist/components/layouts/Cluster.svelte +62 -1
  52. package/dist/components/layouts/Cluster.svelte.d.ts +9 -0
  53. package/dist/components/layouts/NavItem.svelte +22 -1
  54. package/dist/components/layouts/NavItem.svelte.d.ts +5 -0
  55. package/dist/components/layouts/NavSection.svelte +4 -1
  56. package/dist/components/layouts/NavSection.svelte.d.ts +2 -0
  57. package/dist/components/layouts/ResizablePanel.svelte +14 -3
  58. package/dist/components/layouts/ResizablePanel.svelte.d.ts +5 -0
  59. package/dist/components/layouts/Stack.svelte +41 -0
  60. package/dist/components/layouts/Stack.svelte.d.ts +9 -0
  61. package/dist/components/layouts/Toolbar.svelte +122 -0
  62. package/dist/components/layouts/Toolbar.svelte.d.ts +22 -0
  63. package/dist/components/molecules/AttachmentList.svelte +59 -0
  64. package/dist/components/molecules/AttachmentList.svelte.d.ts +13 -0
  65. package/dist/components/molecules/Breadcrumb.svelte +6 -2
  66. package/dist/components/molecules/Breadcrumb.svelte.d.ts +2 -0
  67. package/dist/components/molecules/Callout.svelte +10 -4
  68. package/dist/components/molecules/Callout.svelte.d.ts +1 -1
  69. package/dist/components/molecules/CapBar.svelte +5 -1
  70. package/dist/components/molecules/ChatBubble.svelte +158 -0
  71. package/dist/components/molecules/ChatBubble.svelte.d.ts +26 -0
  72. package/dist/components/molecules/CodeBlock.svelte +36 -3
  73. package/dist/components/molecules/CodeBlock.svelte.d.ts +8 -0
  74. package/dist/components/molecules/Composer.svelte +195 -0
  75. package/dist/components/molecules/Composer.svelte.d.ts +27 -0
  76. package/dist/components/molecules/ConfirmModal.svelte +15 -5
  77. package/dist/components/molecules/ConfirmModal.svelte.d.ts +5 -1
  78. package/dist/components/molecules/Dropzone.svelte +9 -3
  79. package/dist/components/molecules/Dropzone.svelte.d.ts +2 -0
  80. package/dist/components/molecules/EmptyState.svelte +9 -5
  81. package/dist/components/molecules/EmptyState.svelte.d.ts +3 -1
  82. package/dist/components/molecules/Field.svelte +7 -1
  83. package/dist/components/molecules/Field.svelte.d.ts +2 -0
  84. package/dist/components/molecules/FileButton.svelte +2 -1
  85. package/dist/components/molecules/FileButton.svelte.d.ts +2 -1
  86. package/dist/components/molecules/FilterInput.svelte +13 -3
  87. package/dist/components/molecules/FilterInput.svelte.d.ts +4 -1
  88. package/dist/components/molecules/IconButton.svelte +32 -7
  89. package/dist/components/molecules/IconButton.svelte.d.ts +9 -1
  90. package/dist/components/molecules/KeyValue.svelte +4 -1
  91. package/dist/components/molecules/KeyValue.svelte.d.ts +2 -1
  92. package/dist/components/molecules/Menu.svelte +50 -5
  93. package/dist/components/molecules/Menu.svelte.d.ts +10 -1
  94. package/dist/components/molecules/Meter.svelte +91 -0
  95. package/dist/components/molecules/Meter.svelte.d.ts +29 -0
  96. package/dist/components/molecules/Metric.svelte +134 -8
  97. package/dist/components/molecules/Metric.svelte.d.ts +20 -2
  98. package/dist/components/molecules/Modal.svelte +26 -12
  99. package/dist/components/molecules/Modal.svelte.d.ts +7 -1
  100. package/dist/components/molecules/Pagination.svelte +25 -1
  101. package/dist/components/molecules/Pagination.svelte.d.ts +9 -2
  102. package/dist/components/molecules/Popover.svelte +14 -3
  103. package/dist/components/molecules/Popover.svelte.d.ts +5 -0
  104. package/dist/components/molecules/Prose.svelte +140 -0
  105. package/dist/components/molecules/Prose.svelte.d.ts +13 -0
  106. package/dist/components/molecules/RadioGroup.svelte +42 -2
  107. package/dist/components/molecules/RadioGroup.svelte.d.ts +4 -1
  108. package/dist/components/molecules/SectionHeader.svelte +5 -2
  109. package/dist/components/molecules/SectionHeader.svelte.d.ts +1 -1
  110. package/dist/components/molecules/SegmentedControl.svelte +36 -1
  111. package/dist/components/molecules/SegmentedControl.svelte.d.ts +11 -1
  112. package/dist/components/molecules/SelectButton.svelte +64 -4
  113. package/dist/components/molecules/SelectButton.svelte.d.ts +18 -1
  114. package/dist/components/molecules/Tabs.svelte +21 -3
  115. package/dist/components/molecules/Tabs.svelte.d.ts +6 -0
  116. package/dist/components/molecules/Timestamp.svelte +13 -6
  117. package/dist/components/molecules/Timestamp.svelte.d.ts +4 -0
  118. package/dist/components/molecules/Toaster.svelte +54 -38
  119. package/dist/components/molecules/Toaster.svelte.d.ts +5 -1
  120. package/dist/components/molecules/Toggle.svelte +2 -1
  121. package/dist/components/molecules/Toggle.svelte.d.ts +2 -1
  122. package/dist/components/molecules/Tooltip.svelte +32 -5
  123. package/dist/components/molecules/Tooltip.svelte.d.ts +9 -0
  124. package/dist/components/molecules/Truncate.svelte +156 -18
  125. package/dist/components/molecules/Truncate.svelte.d.ts +17 -1
  126. package/dist/components/molecules/WorkingDir.svelte +6 -0
  127. package/dist/components/molecules/WorkingDir.svelte.d.ts +2 -0
  128. package/dist/components/organisms/DataTable.svelte +38 -10
  129. package/dist/components/organisms/DataTable.svelte.d.ts +4 -0
  130. package/dist/components/organisms/FilterSearchBar.svelte +9 -2
  131. package/dist/components/organisms/FilterSearchBar.svelte.d.ts +4 -1
  132. package/dist/index.d.ts +10 -2
  133. package/dist/index.js +8 -1
  134. package/dist/size.d.ts +2 -0
  135. package/dist/size.js +1 -0
  136. package/dist/styles/tokens.css +3 -0
  137. package/dist/timestamp.d.ts +2 -2
  138. package/dist/timestamp.js +4 -4
  139. package/dist/tone.d.ts +1 -1
  140. package/dist/tone.js +1 -1
  141. package/dist/truncate.d.ts +13 -1
  142. package/dist/truncate.js +39 -0
  143. package/package.json +5 -3
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import type { ControlSize } from '../../size';
3
+ import { canonicalTone, type Tone } from '../../tone';
2
4
  // Progress bar. Determinate when `value` is a number (0..max); omit `value`
3
5
  // (or pass `indeterminate`) for an indeterminate animation. Uses
4
6
  // role="progressbar" with the right aria-value* attributes. Token-styled.
@@ -16,8 +18,17 @@
16
18
  size = 'md',
17
19
  gradient = false,
18
20
  striped = false,
21
+ grow = false,
22
+ shrink = true,
23
+ block = false,
19
24
  indeterminate: indeterminateProp = false,
20
- class: klass = ''
25
+ markers = [],
26
+ toneAt,
27
+ color,
28
+ showValue = false,
29
+ format = (v: number, m: number) => `${Math.round((v / m) * 100)}%`,
30
+ class: klass = '',
31
+ ...rest
21
32
  }: {
22
33
  value?: number;
23
34
  max?: number;
@@ -25,27 +36,57 @@
25
36
  // Fill colour. `accent` is the default brand fill; the semantic tones
26
37
  // retint the bar for severity (e.g. usage meters going warm/hot).
27
38
  // `ok` and `success` are aliases.
28
- tone?: 'accent' | 'ok' | 'success' | 'warn' | 'danger';
39
+ tone?: Tone;
29
40
  // Track height. `sm` is a thin ~5px track for inline rows.
30
- size?: 'sm' | 'md';
41
+ size?: ControlSize;
42
+ /** Fill the free space of a flex row (`flex: 1 1 0`). */
43
+ grow?: boolean;
44
+ /** `false` pins the box (`flex: none`) so a flex row cannot squeeze it. */
45
+ shrink?: boolean;
46
+ /** Full-width block. */
47
+ block?: boolean;
31
48
  // Accent→teal gradient fill (overrides the flat tone colour).
32
49
  gradient?: boolean;
33
50
  // Diagonal stripes; animated in indeterminate mode.
34
51
  striped?: boolean;
35
52
  // Force indeterminate mode even when a `value` is supplied.
36
53
  indeterminate?: boolean;
54
+ /** Thin vertical lines at `at / max` (caps, thresholds). */
55
+ markers?: { at: number; label?: string; tone?: Tone }[];
56
+ /** Percent thresholds that auto-pick warn/danger; an explicit `tone` other
57
+ * than the default `accent` wins. */
58
+ toneAt?: { warn?: number; danger?: number };
59
+ /** Any CSS colour for the fill; wins over `tone`. */
60
+ color?: string;
61
+ /** Trailing numeric readout after the track. */
62
+ showValue?: boolean;
63
+ format?: (value: number, max: number) => string;
37
64
  class?: string;
65
+ [key: string]: unknown;
38
66
  } = $props();
39
67
 
40
68
  const pct = $derived(value == null ? 0 : Math.max(0, Math.min(100, (value / max) * 100)));
41
69
  const indeterminate = $derived(indeterminateProp || value == null);
42
- const toneClass = $derived(tone === 'ok' ? 'success' : tone);
70
+ const autoTone = $derived.by((): Tone => {
71
+ if (!toneAt || tone !== 'accent' || indeterminate) return tone;
72
+ if (toneAt.danger !== undefined && pct >= toneAt.danger) return 'danger';
73
+ if (toneAt.warn !== undefined && pct >= toneAt.warn) return 'warn';
74
+ return tone;
75
+ });
76
+ const toneClass = $derived(canonicalTone(autoTone) === 'ok' ? 'success' : canonicalTone(autoTone));
77
+ const markerPct = (at: number) => Math.max(0, Math.min(100, (at / max) * 100));
43
78
  </script>
44
79
 
80
+ {#snippet track()}
45
81
  <div
46
82
  data-tsu="Progress"
47
83
  class="progress tone-{toneClass} size-{size} {klass}"
84
+ class:grow={grow}
85
+ class:no-shrink={!shrink}
86
+ class:block={block}
48
87
  class:indeterminate
88
+ style:--fill={color}
89
+ {...rest}
49
90
  class:gradient
50
91
  class:striped
51
92
  role="progressbar"
@@ -55,10 +96,54 @@
55
96
  aria-valuenow={indeterminate ? undefined : value}
56
97
  >
57
98
  <div class="bar" style={indeterminate ? undefined : `width: ${pct}%`}></div>
99
+ {#each markers as m (m.at)}
100
+ <span
101
+ class="marker tone-{m.tone ? canonicalTone(m.tone) : 'text'}"
102
+ style="left: {markerPct(m.at)}%"
103
+ title={m.label}
104
+ aria-hidden="true"
105
+ ></span>
106
+ {/each}
58
107
  </div>
108
+ {/snippet}
109
+
110
+ {#if showValue}
111
+ <span class="progress-row" class:grow={grow} class:no-shrink={!shrink} class:block={block}>
112
+ {@render track()}
113
+ <span class="progress-value">{indeterminate || value == null ? '' : format(value, max)}</span>
114
+ </span>
115
+ {:else}
116
+ {@render track()}
117
+ {/if}
59
118
 
60
119
  <style>
120
+ .progress-row {
121
+ display: inline-flex;
122
+ align-items: center;
123
+ gap: var(--sp-2);
124
+ width: 100%;
125
+ }
126
+ .progress-value {
127
+ flex: none;
128
+ min-width: 3ch;
129
+ text-align: right;
130
+ font-size: var(--fs-xs);
131
+ font-variant-numeric: tabular-nums;
132
+ color: var(--text-muted);
133
+ }
134
+ .grow {
135
+ flex: 1 1 0;
136
+ min-width: 0;
137
+ }
138
+ .no-shrink {
139
+ flex: none;
140
+ }
141
+ .block {
142
+ display: flex;
143
+ width: 100%;
144
+ }
61
145
  .progress {
146
+ position: relative;
62
147
  width: 100%;
63
148
  height: 0.5rem;
64
149
  background: var(--bg-elevated-2);
@@ -74,9 +159,39 @@
74
159
  border-radius: inherit;
75
160
  transition: width 0.2s var(--ease);
76
161
  }
162
+ .marker {
163
+ position: absolute;
164
+ top: 0;
165
+ bottom: 0;
166
+ width: 2px;
167
+ transform: translateX(-50%);
168
+ background: var(--marker, var(--text));
169
+ pointer-events: none;
170
+ }
171
+ .marker.tone-ok {
172
+ --marker: var(--ok);
173
+ }
174
+ .marker.tone-warn {
175
+ --marker: var(--warn);
176
+ }
177
+ .marker.tone-danger {
178
+ --marker: var(--danger);
179
+ }
180
+ .marker.tone-info {
181
+ --marker: var(--info);
182
+ }
183
+ .marker.tone-accent {
184
+ --marker: var(--accent);
185
+ }
77
186
  .tone-success {
78
187
  --fill: var(--ok);
79
188
  }
189
+ .tone-info {
190
+ --fill: var(--info);
191
+ }
192
+ .tone-neutral {
193
+ --fill: var(--text-faint);
194
+ }
80
195
  .tone-warn {
81
196
  --fill: var(--warn);
82
197
  }
@@ -1,13 +1,39 @@
1
+ import type { ControlSize } from '../../size';
2
+ import { type Tone } from '../../tone';
1
3
  type $$ComponentProps = {
2
4
  value?: number;
3
5
  max?: number;
4
6
  label?: string;
5
- tone?: 'accent' | 'ok' | 'success' | 'warn' | 'danger';
6
- size?: 'sm' | 'md';
7
+ tone?: Tone;
8
+ size?: ControlSize;
9
+ /** Fill the free space of a flex row (`flex: 1 1 0`). */
10
+ grow?: boolean;
11
+ /** `false` pins the box (`flex: none`) so a flex row cannot squeeze it. */
12
+ shrink?: boolean;
13
+ /** Full-width block. */
14
+ block?: boolean;
7
15
  gradient?: boolean;
8
16
  striped?: boolean;
9
17
  indeterminate?: boolean;
18
+ /** Thin vertical lines at `at / max` (caps, thresholds). */
19
+ markers?: {
20
+ at: number;
21
+ label?: string;
22
+ tone?: Tone;
23
+ }[];
24
+ /** Percent thresholds that auto-pick warn/danger; an explicit `tone` other
25
+ * than the default `accent` wins. */
26
+ toneAt?: {
27
+ warn?: number;
28
+ danger?: number;
29
+ };
30
+ /** Any CSS colour for the fill; wins over `tone`. */
31
+ color?: string;
32
+ /** Trailing numeric readout after the track. */
33
+ showValue?: boolean;
34
+ format?: (value: number, max: number) => string;
10
35
  class?: string;
36
+ [key: string]: unknown;
11
37
  };
12
38
  declare const Progress: import("svelte").Component<$$ComponentProps, {}, "">;
13
39
  type Progress = ReturnType<typeof Progress>;
@@ -5,7 +5,9 @@
5
5
  onclose,
6
6
  hideBelow,
7
7
  z = 'var(--z-drawer)',
8
- label = 'Close'
8
+ label = 'Close',
9
+ class: klass = '',
10
+ style: styleProp = '',
9
11
  }: {
10
12
  /** Called on click anywhere on the scrim and on Escape (document-level). */
11
13
  onclose?: () => void;
@@ -16,6 +18,8 @@
16
18
  z?: string | number;
17
19
  /** Accessible name of the click target. */
18
20
  label?: string;
21
+ class?: string;
22
+ style?: string;
19
23
  } = $props();
20
24
 
21
25
  let hidden = $state(false);
@@ -49,7 +53,8 @@
49
53
  {#if !hidden}
50
54
  <button
51
55
  type="button"
52
- class="scrim"
56
+ class="scrim {klass}"
57
+ style={styleProp}
53
58
  style:z-index={z}
54
59
  aria-label={label}
55
60
  tabindex="-1"
@@ -8,6 +8,8 @@ type $$ComponentProps = {
8
8
  z?: string | number;
9
9
  /** Accessible name of the click target. */
10
10
  label?: string;
11
+ class?: string;
12
+ style?: string;
11
13
  };
12
14
  declare const Scrim: import("svelte").Component<$$ComponentProps, {}, "">;
13
15
  type Scrim = ReturnType<typeof Scrim>;
@@ -1,4 +1,5 @@
1
1
  <script lang="ts" module>
2
+ import type { Tone } from '../../tone';
2
3
  // One bar split into segments, each with its own tone. Two modes:
3
4
  // • segments (default): widths ∝ `max`, each filled value/max, thin gaps.
4
5
  // • stacked: one shared track, widths ∝ `value` (Σvalue = full width, or
@@ -10,7 +11,7 @@
10
11
  max: number;
11
12
  // Fill colour per segment. `ok` is an alias of `success`; `muted` renders a
12
13
  // faint fill for empty parts.
13
- tone?: 'accent' | 'success' | 'ok' | 'warn' | 'danger' | 'muted';
14
+ tone?: Tone | 'muted';
14
15
  // Native tooltip / accessible name / legend caption for the segment.
15
16
  label?: string;
16
17
  };
@@ -21,11 +22,14 @@
21
22
  ok: 'var(--ok)',
22
23
  warn: 'var(--warn)',
23
24
  danger: 'var(--danger)',
25
+ info: 'var(--info)',
26
+ neutral: 'var(--text-faint)',
24
27
  muted: 'var(--text-faint)',
25
28
  };
26
29
  </script>
27
30
 
28
31
  <script lang="ts">
32
+ import type { ControlSize } from '../../size';
29
33
  import type { Snippet } from 'svelte';
30
34
  import Dot from './Dot.svelte';
31
35
  import Text from './Text.svelte';
@@ -39,11 +43,12 @@
39
43
  max,
40
44
  legend = false,
41
45
  class: klass = '',
46
+ ...rest
42
47
  }: {
43
48
  segments: ProgressSegment[];
44
49
  label?: string;
45
50
  // Track height. `sm` is a thin ~5px track for inline rows.
46
- size?: 'sm' | 'md';
51
+ size?: ControlSize;
47
52
  mode?: 'segments' | 'stacked';
48
53
  // Space between segments in `segments` mode; a bare number is px.
49
54
  gap?: number | string;
@@ -53,6 +58,7 @@
53
58
  // `true` = 'below'. A snippet receives the segments for custom rendering.
54
59
  legend?: boolean | 'inline' | 'below' | Snippet<[ProgressSegment[]]>;
55
60
  class?: string;
61
+ [key: string]: unknown;
56
62
  } = $props();
57
63
 
58
64
  const stacked = $derived(mode === 'stacked');
@@ -90,6 +96,7 @@
90
96
  <div
91
97
  data-tsu="SegmentedProgress"
92
98
  class="segmented-progress stacked size-{size} {rootClass}"
99
+ {...rest}
93
100
  style="gap: {gapCss}"
94
101
  role="img"
95
102
  aria-label={stackedLabel}
@@ -107,6 +114,7 @@
107
114
  <div
108
115
  data-tsu="SegmentedProgress"
109
116
  class="segmented-progress size-{size} {rootClass}"
117
+ {...rest}
110
118
  style="gap: {gapCss}"
111
119
  role="progressbar"
112
120
  aria-label={label}
@@ -186,6 +194,10 @@
186
194
  .tone-danger {
187
195
  --fill: var(--danger);
188
196
  }
197
+ .tone-info {
198
+ --seg-fill: var(--info);
199
+ }
200
+ .tone-neutral,
189
201
  .tone-muted {
190
202
  --fill: var(--text-faint);
191
203
  }
@@ -1,20 +1,23 @@
1
+ import type { Tone } from '../../tone';
1
2
  export type ProgressSegment = {
2
3
  value: number;
3
4
  max: number;
4
- tone?: 'accent' | 'success' | 'ok' | 'warn' | 'danger' | 'muted';
5
+ tone?: Tone | 'muted';
5
6
  label?: string;
6
7
  };
7
8
  export declare const TONE_FILL: Record<NonNullable<ProgressSegment['tone']>, string>;
9
+ import type { ControlSize } from '../../size';
8
10
  import type { Snippet } from 'svelte';
9
11
  type $$ComponentProps = {
10
12
  segments: ProgressSegment[];
11
13
  label?: string;
12
- size?: 'sm' | 'md';
14
+ size?: ControlSize;
13
15
  mode?: 'segments' | 'stacked';
14
16
  gap?: number | string;
15
17
  max?: number;
16
18
  legend?: boolean | 'inline' | 'below' | Snippet<[ProgressSegment[]]>;
17
19
  class?: string;
20
+ [key: string]: unknown;
18
21
  };
19
22
  declare const SegmentedProgress: import("svelte").Component<$$ComponentProps, {}, "">;
20
23
  type SegmentedProgress = ReturnType<typeof SegmentedProgress>;
@@ -13,6 +13,7 @@
13
13
  </script>
14
14
 
15
15
  <script lang="ts">
16
+ import type { ControlSize } from '../../size';
16
17
  // Native <select> primitive. Owns its styling from theme tokens; supports
17
18
  // `bind:value` and passes through every native attribute. Options are either
18
19
  // slotted children (full control over <option> rendering) or an `options`
@@ -48,12 +49,11 @@
48
49
  variant?: 'default' | 'ghost' | 'embedded';
49
50
  /** Error state: danger border + aria-invalid. */
50
51
  invalid?: boolean;
51
- /** Compact inline form: smaller padding + font for dense toolbars/headers.
52
- * Deprecated alias for `size="sm"` (kept for backward compatibility). */
52
+ /** @deprecated use `size="sm"`; `size` wins when both are set. */
53
53
  compact?: boolean;
54
54
  /** Size scale matching Button/SegmentedControl: `sm` also adopts the shared
55
55
  * `--control-height-compact` toolbar height so it lines up with siblings. */
56
- size?: 'sm' | 'md';
56
+ size?: ControlSize;
57
57
  /** Draw the custom chevron (default). Set false for a bare inline select. */
58
58
  chevron?: boolean;
59
59
  /** `auto` sizes to the selected option instead of filling the row. */
@@ -74,7 +74,7 @@
74
74
  variant = 'default',
75
75
  invalid = false,
76
76
  compact = false,
77
- size = 'md',
77
+ size,
78
78
  chevron,
79
79
  width = 'full',
80
80
  grow = false,
@@ -94,7 +94,7 @@
94
94
 
95
95
  $effect(() => warnUnlabelled(el, 'Select'));
96
96
 
97
- const small = $derived(compact || size === 'sm');
97
+ const small = $derived((size ?? (compact ? 'sm' : 'md')) === 'sm');
98
98
  const showChevron = $derived(chevron ?? variant !== 'embedded');
99
99
  const selected = $derived(options?.find((o) => o.value === value));
100
100
  const hasFace = $derived(!!options && variant !== 'ghost');
@@ -138,6 +138,7 @@
138
138
  bind:this={el}
139
139
  class="select"
140
140
  class:compact={small}
141
+ class:select-lg={size === 'lg'}
141
142
  class:select-sm={size === 'sm'}
142
143
  class:w-auto={width === 'auto'}
143
144
  class:embedded={variant === 'embedded'}
@@ -187,7 +188,9 @@
187
188
  }
188
189
  .select {
189
190
  width: 100%;
190
- padding: var(--sp-3);
191
+ min-height: var(--control-height-default);
192
+ padding: var(--sp-2) var(--sp-3);
193
+ line-height: var(--lh-tight);
191
194
  background: var(--bg);
192
195
  border: 1px solid var(--border-strong);
193
196
  border-radius: var(--r-md);
@@ -205,6 +208,11 @@
205
208
  padding-right: var(--sp-3);
206
209
  }
207
210
  /* Compact inline form for dense headers/toolbars. */
211
+ .select.select-lg {
212
+ min-height: var(--control-height-large);
213
+ padding: var(--sp-3) var(--sp-4);
214
+ font-size: var(--fs-base);
215
+ }
208
216
  .select.compact {
209
217
  padding: var(--sp-1) var(--sp-2);
210
218
  font-size: var(--fs-xs);
@@ -229,6 +237,10 @@
229
237
  outline: none;
230
238
  border-color: var(--accent);
231
239
  }
240
+ .select:focus-visible {
241
+ outline: var(--focus-ring);
242
+ outline-offset: var(--focus-ring-offset);
243
+ }
232
244
  .select[aria-invalid='true'],
233
245
  .select[aria-invalid='true']:focus {
234
246
  border-color: var(--danger);
@@ -8,18 +8,18 @@ export type SelectOption = {
8
8
  hint?: string;
9
9
  disabled?: boolean;
10
10
  };
11
+ import type { ControlSize } from '../../size';
11
12
  import type { Snippet } from 'svelte';
12
13
  import type { HTMLSelectAttributes } from 'svelte/elements';
13
14
  type Props = Omit<HTMLSelectAttributes, 'size'> & {
14
15
  variant?: 'default' | 'ghost' | 'embedded';
15
16
  /** Error state: danger border + aria-invalid. */
16
17
  invalid?: boolean;
17
- /** Compact inline form: smaller padding + font for dense toolbars/headers.
18
- * Deprecated alias for `size="sm"` (kept for backward compatibility). */
18
+ /** @deprecated use `size="sm"`; `size` wins when both are set. */
19
19
  compact?: boolean;
20
20
  /** Size scale matching Button/SegmentedControl: `sm` also adopts the shared
21
21
  * `--control-height-compact` toolbar height so it lines up with siblings. */
22
- size?: 'sm' | 'md';
22
+ size?: ControlSize;
23
23
  /** Draw the custom chevron (default). Set false for a bare inline select. */
24
24
  chevron?: boolean;
25
25
  /** `auto` sizes to the selected option instead of filling the row. */
@@ -1,15 +1,8 @@
1
1
  <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
2
3
  // Shimmering placeholder bar(s) standing in for content that is still
3
4
  // loading. Decorative only — the surrounding region should carry aria-busy.
4
- let {
5
- width = '100%',
6
- height = '1em',
7
- lines = 1,
8
- circle = false,
9
- radius = 'var(--r-sm)',
10
- class: klass = '',
11
- ...rest
12
- }: {
5
+ type Own = {
13
6
  width?: string;
14
7
  height?: string;
15
8
  /** Stacked bars; the last one is shortened to read as a paragraph. */
@@ -18,8 +11,16 @@
18
11
  circle?: boolean;
19
12
  radius?: string;
20
13
  class?: string;
21
- [key: string]: unknown;
22
- } = $props();
14
+ };
15
+ let {
16
+ width = '100%',
17
+ height = '1em',
18
+ lines = 1,
19
+ circle = false,
20
+ radius = 'var(--r-sm)',
21
+ class: klass = '',
22
+ ...rest
23
+ }: Omit<HTMLAttributes<HTMLElement>, keyof Own> & Own = $props();
23
24
 
24
25
  const w = $derived(circle ? height : width);
25
26
  const r = $derived(circle ? '50%' : radius);
@@ -1,4 +1,5 @@
1
- type $$ComponentProps = {
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type Own = {
2
3
  width?: string;
3
4
  height?: string;
4
5
  /** Stacked bars; the last one is shortened to read as a paragraph. */
@@ -7,8 +8,8 @@ type $$ComponentProps = {
7
8
  circle?: boolean;
8
9
  radius?: string;
9
10
  class?: string;
10
- [key: string]: unknown;
11
11
  };
12
+ type $$ComponentProps = Omit<HTMLAttributes<HTMLElement>, keyof Own> & Own;
12
13
  declare const Skeleton: import("svelte").Component<$$ComponentProps, {}, "">;
13
14
  type Skeleton = ReturnType<typeof Skeleton>;
14
15
  export default Skeleton;
@@ -21,6 +21,8 @@
21
21
  'aria-invalid': ariaInvalid,
22
22
  invalid = false,
23
23
  ticks = false,
24
+ marks = [],
25
+ width,
24
26
  class: klass = '',
25
27
  el = $bindable(null),
26
28
  ...rest
@@ -37,6 +39,10 @@
37
39
  /** Visible dots at each step (segmented slider). `true` = one per `step`;
38
40
  * a number array places dots at those values. */
39
41
  ticks?: boolean | number[];
42
+ /** Clickable labelled tick row under the track. */
43
+ marks?: { value: number; label: string }[];
44
+ /** Fixed width (`flex: none`). */
45
+ width?: string;
40
46
  id?: string;
41
47
  'aria-describedby'?: string | null;
42
48
  'aria-invalid'?: HTMLInputAttributes['aria-invalid'];
@@ -61,7 +67,7 @@
61
67
  });
62
68
  </script>
63
69
 
64
- <div class="slider {klass}" class:has-ticks={tickValues.length > 0} style="--pct: {pct}%" data-tsu="Slider">
70
+ <div class="slider {klass}" class:has-ticks={tickValues.length > 0} class:has-marks={marks.length > 0} class:fixed={!!width} style="--pct: {pct}%; {width ? `width: ${width}` : ''}" data-tsu="Slider">
65
71
  <span class="range">
66
72
  <input
67
73
  bind:this={el}
@@ -87,9 +93,45 @@
87
93
  {#if showValue}
88
94
  <output for={id} class="slider-out">{format(Number(value))}</output>
89
95
  {/if}
96
+ {#if marks.length > 0}
97
+ <span class="marks" aria-hidden="true">
98
+ {#each marks as m (m.value)}
99
+ <button type="button" class="mark" class:on={Number(value) === m.value} style="--f: {frac(m.value)}" tabindex="-1" onclick={() => (value = m.value)}>{m.label}</button>
100
+ {/each}
101
+ </span>
102
+ {/if}
90
103
  </div>
91
104
 
92
105
  <style>
106
+ .slider.fixed {
107
+ flex: none;
108
+ }
109
+ .slider.has-marks {
110
+ flex-wrap: wrap;
111
+ row-gap: var(--sp-1);
112
+ }
113
+ .marks {
114
+ position: relative;
115
+ flex: 1 0 100%;
116
+ height: 1.2em;
117
+ font-size: var(--fs-xs);
118
+ color: var(--text-faint);
119
+ }
120
+ .mark {
121
+ position: absolute;
122
+ left: calc(var(--f) * 100%);
123
+ transform: translateX(-50%);
124
+ border: 0;
125
+ background: none;
126
+ padding: 0;
127
+ font: inherit;
128
+ color: inherit;
129
+ cursor: pointer;
130
+ white-space: nowrap;
131
+ }
132
+ .mark.on {
133
+ color: var(--text);
134
+ }
93
135
  .slider {
94
136
  display: flex;
95
137
  align-items: center;
@@ -160,11 +202,11 @@
160
202
  outline: none;
161
203
  }
162
204
  input[type='range']:focus-visible::-webkit-slider-thumb {
163
- outline: 2px solid var(--accent);
205
+ outline: var(--focus-ring);
164
206
  outline-offset: 2px;
165
207
  }
166
208
  input[type='range']:focus-visible::-moz-range-thumb {
167
- outline: 2px solid var(--accent);
209
+ outline: var(--focus-ring);
168
210
  outline-offset: 2px;
169
211
  }
170
212
  input[type='range']:disabled {
@@ -12,6 +12,13 @@ type $$ComponentProps = HTMLInputAttributes & {
12
12
  /** Visible dots at each step (segmented slider). `true` = one per `step`;
13
13
  * a number array places dots at those values. */
14
14
  ticks?: boolean | number[];
15
+ /** Clickable labelled tick row under the track. */
16
+ marks?: {
17
+ value: number;
18
+ label: string;
19
+ }[];
20
+ /** Fixed width (`flex: none`). */
21
+ width?: string;
15
22
  id?: string;
16
23
  'aria-describedby'?: string | null;
17
24
  'aria-invalid'?: HTMLInputAttributes['aria-invalid'];
@@ -15,6 +15,8 @@
15
15
  /** AT label announcing the busy state. Set to undefined to make the
16
16
  * spinner decorative when an ancestor already conveys the loading state. */
17
17
  label?: string;
18
+ class?: string;
19
+ style?: string;
18
20
  [key: string]: unknown;
19
21
  } = $props();
20
22
  </script>
@@ -4,6 +4,8 @@ type $$ComponentProps = {
4
4
  /** AT label announcing the busy state. Set to undefined to make the
5
5
  * spinner decorative when an ancestor already conveys the loading state. */
6
6
  label?: string;
7
+ class?: string;
8
+ style?: string;
7
9
  [key: string]: unknown;
8
10
  };
9
11
  declare const Spinner: import("svelte").Component<$$ComponentProps, {}, "">;