@human-kit/svelte-components 1.0.0-alpha.3 → 1.0.0-alpha.4

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 (110) hide show
  1. package/dist/FOCUS_STATE_CONTRACT.md +12 -0
  2. package/dist/calendar/body-cell/README.md +15 -0
  3. package/dist/calendar/grid/README.md +13 -0
  4. package/dist/calendar/grid-body/README.md +13 -0
  5. package/dist/calendar/grid-header/README.md +13 -0
  6. package/dist/calendar/header-cell/README.md +14 -0
  7. package/dist/calendar/heading/README.md +13 -0
  8. package/dist/calendar/root/README.md +24 -0
  9. package/dist/calendar/trigger-next/README.md +14 -0
  10. package/dist/calendar/trigger-previous/README.md +14 -0
  11. package/dist/clock/README.md +75 -0
  12. package/dist/clock/axis/README.md +24 -0
  13. package/dist/clock/axis/clock-axis.svelte +37 -0
  14. package/dist/clock/axis/clock-axis.svelte.d.ts +8 -0
  15. package/dist/clock/hooks/use-wheel-scroll.svelte.d.ts +16 -0
  16. package/dist/clock/hooks/use-wheel-scroll.svelte.js +336 -0
  17. package/dist/clock/index.d.ts +10 -0
  18. package/dist/clock/index.js +10 -0
  19. package/dist/clock/index.parts.d.ts +4 -0
  20. package/dist/clock/index.parts.js +4 -0
  21. package/dist/clock/root/README.md +38 -0
  22. package/dist/clock/root/clock-root-test.svelte +62 -0
  23. package/dist/clock/root/clock-root-test.svelte.d.ts +14 -0
  24. package/dist/clock/root/clock-root.svelte +329 -0
  25. package/dist/clock/root/clock-root.svelte.d.ts +25 -0
  26. package/dist/clock/root/context.d.ts +22 -0
  27. package/dist/clock/root/context.js +15 -0
  28. package/dist/clock/root/resolve-visible-columns.d.ts +7 -0
  29. package/dist/clock/root/resolve-visible-columns.js +16 -0
  30. package/dist/clock/root/time-utils.d.ts +48 -0
  31. package/dist/clock/root/time-utils.js +314 -0
  32. package/dist/clock/root/wheel-options.d.ts +17 -0
  33. package/dist/clock/root/wheel-options.js +63 -0
  34. package/dist/clock/wheel-column/README.md +25 -0
  35. package/dist/clock/wheel-column/clock-wheel-column-bindable-test.svelte +16 -0
  36. package/dist/clock/wheel-column/clock-wheel-column-bindable-test.svelte.d.ts +3 -0
  37. package/dist/clock/wheel-column/clock-wheel-column-custom-snippet-test.svelte +29 -0
  38. package/dist/clock/wheel-column/clock-wheel-column-custom-snippet-test.svelte.d.ts +6 -0
  39. package/dist/clock/wheel-column/clock-wheel-column-default-height-test.svelte +11 -0
  40. package/dist/clock/wheel-column/clock-wheel-column-default-height-test.svelte.d.ts +3 -0
  41. package/dist/clock/wheel-column/clock-wheel-column-test.svelte +38 -0
  42. package/dist/clock/wheel-column/clock-wheel-column-test.svelte.d.ts +12 -0
  43. package/dist/clock/wheel-column/clock-wheel-column-tp-test.svelte +38 -0
  44. package/dist/clock/wheel-column/clock-wheel-column-tp-test.svelte.d.ts +12 -0
  45. package/dist/clock/wheel-column/clock-wheel-column-untagged-snippet-test.svelte +29 -0
  46. package/dist/clock/wheel-column/clock-wheel-column-untagged-snippet-test.svelte.d.ts +6 -0
  47. package/dist/clock/wheel-column/clock-wheel-column.svelte +499 -0
  48. package/dist/clock/wheel-column/clock-wheel-column.svelte.d.ts +17 -0
  49. package/dist/clock/wheel-item/README.md +17 -0
  50. package/dist/clock/wheel-item/clock-wheel-item.svelte +49 -0
  51. package/dist/clock/wheel-item/clock-wheel-item.svelte.d.ts +17 -0
  52. package/dist/datepicker/TODO.md +2 -2
  53. package/dist/datepicker/calendar/README.md +19 -0
  54. package/dist/datepicker/input/README.md +15 -0
  55. package/dist/datepicker/popover/README.md +20 -0
  56. package/dist/datepicker/root/README.md +38 -0
  57. package/dist/datepicker/segment/README.md +14 -0
  58. package/dist/datepicker/trigger/README.md +14 -0
  59. package/dist/index.d.ts +5 -0
  60. package/dist/index.js +5 -0
  61. package/dist/primitives/focus-trap.js +11 -12
  62. package/dist/primitives/input-modality.js +10 -1
  63. package/dist/timepicker/IMPLEMENTATION_PLAN.md +254 -0
  64. package/dist/timepicker/README.md +97 -0
  65. package/dist/timepicker/TODO.md +86 -0
  66. package/dist/timepicker/clock/README.md +14 -0
  67. package/dist/timepicker/clock/time-picker-clock-test.svelte +45 -0
  68. package/dist/timepicker/clock/time-picker-clock-test.svelte.d.ts +11 -0
  69. package/dist/timepicker/clock/time-picker-clock.svelte +65 -0
  70. package/dist/timepicker/clock/time-picker-clock.svelte.d.ts +10 -0
  71. package/dist/timepicker/index.d.ts +14 -0
  72. package/dist/timepicker/index.js +14 -0
  73. package/dist/timepicker/index.parts.d.ts +8 -0
  74. package/dist/timepicker/index.parts.js +8 -0
  75. package/dist/timepicker/input/README.md +15 -0
  76. package/dist/timepicker/input/time-picker-input-forwarding-test.svelte +40 -0
  77. package/dist/timepicker/input/time-picker-input-forwarding-test.svelte.d.ts +3 -0
  78. package/dist/timepicker/input/time-picker-input.svelte +109 -0
  79. package/dist/timepicker/input/time-picker-input.svelte.d.ts +11 -0
  80. package/dist/timepicker/internal/strict-props.d.ts +4 -0
  81. package/dist/timepicker/internal/strict-props.js +51 -0
  82. package/dist/timepicker/popover/README.md +20 -0
  83. package/dist/timepicker/popover/time-picker-popover-unsafe-props-test.svelte +22 -0
  84. package/dist/timepicker/popover/time-picker-popover-unsafe-props-test.svelte.d.ts +3 -0
  85. package/dist/timepicker/popover/time-picker-popover.svelte +89 -0
  86. package/dist/timepicker/popover/time-picker-popover.svelte.d.ts +7 -0
  87. package/dist/timepicker/root/README.md +42 -0
  88. package/dist/timepicker/root/context.d.ts +51 -0
  89. package/dist/timepicker/root/context.js +15 -0
  90. package/dist/timepicker/root/time-picker-12h-test.svelte +22 -0
  91. package/dist/timepicker/root/time-picker-12h-test.svelte.d.ts +3 -0
  92. package/dist/timepicker/root/time-picker-bindable-test.svelte +25 -0
  93. package/dist/timepicker/root/time-picker-bindable-test.svelte.d.ts +3 -0
  94. package/dist/timepicker/root/time-picker-empty-test.svelte +20 -0
  95. package/dist/timepicker/root/time-picker-empty-test.svelte.d.ts +3 -0
  96. package/dist/timepicker/root/time-picker-root.svelte +625 -0
  97. package/dist/timepicker/root/time-picker-root.svelte.d.ts +28 -0
  98. package/dist/timepicker/root/time-picker-test.svelte +72 -0
  99. package/dist/timepicker/root/time-picker-test.svelte.d.ts +15 -0
  100. package/dist/timepicker/root/time-utils.d.ts +1 -0
  101. package/dist/timepicker/root/time-utils.js +3 -0
  102. package/dist/timepicker/segment/README.md +14 -0
  103. package/dist/timepicker/segment/time-picker-segment.svelte +365 -0
  104. package/dist/timepicker/segment/time-picker-segment.svelte.d.ts +9 -0
  105. package/dist/timepicker/trigger/README.md +14 -0
  106. package/dist/timepicker/trigger/time-picker-trigger-forwarding-test.svelte +35 -0
  107. package/dist/timepicker/trigger/time-picker-trigger-forwarding-test.svelte.d.ts +3 -0
  108. package/dist/timepicker/trigger/time-picker-trigger.svelte +122 -0
  109. package/dist/timepicker/trigger/time-picker-trigger.svelte.d.ts +9 -0
  110. package/package.json +11 -1
@@ -0,0 +1,314 @@
1
+ const timeValuePattern = /^(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$/;
2
+ export function createEmptyTimePickerDraft() {
3
+ return {
4
+ hour: '',
5
+ minute: '',
6
+ second: '',
7
+ dayPeriod: ''
8
+ };
9
+ }
10
+ export function isValidTimePickerValue(value) {
11
+ return typeof value === 'string' && timeValuePattern.test(value);
12
+ }
13
+ export function parseTimePickerValue(value) {
14
+ if (!isValidTimePickerValue(value))
15
+ return null;
16
+ const [hourPart, minutePart, secondPart] = value.split(':');
17
+ return {
18
+ hour: Number(hourPart),
19
+ minute: Number(minutePart),
20
+ second: Number(secondPart ?? '0')
21
+ };
22
+ }
23
+ function pad2(value) {
24
+ return value.toString().padStart(2, '0');
25
+ }
26
+ function formatDraftTwoDigits(value) {
27
+ const numeric = Number(value);
28
+ if (!Number.isFinite(numeric))
29
+ return value;
30
+ return pad2(Math.trunc(numeric));
31
+ }
32
+ function getDayPeriodPlaceholder(locale) {
33
+ try {
34
+ const formatter = new Intl.DateTimeFormat(locale, {
35
+ hour: 'numeric',
36
+ hourCycle: 'h12',
37
+ timeZone: 'UTC'
38
+ });
39
+ const dayPeriod = formatter
40
+ .formatToParts(new Date(Date.UTC(2024, 0, 1, 9, 0, 0)))
41
+ .find((part) => part.type === 'dayPeriod')
42
+ ?.value?.trim();
43
+ return dayPeriod && dayPeriod.length > 0 ? dayPeriod : 'AM';
44
+ }
45
+ catch {
46
+ return 'AM';
47
+ }
48
+ }
49
+ export function formatTimePickerValue(parts, granularity) {
50
+ const hour = pad2(parts.hour);
51
+ if (granularity === 'hour') {
52
+ return `${hour}:00`;
53
+ }
54
+ const minute = pad2(parts.minute);
55
+ if (granularity === 'minute') {
56
+ return `${hour}:${minute}`;
57
+ }
58
+ return `${hour}:${minute}:${pad2(parts.second)}`;
59
+ }
60
+ export function toDraftFromTimeValue(value, hourCycle) {
61
+ const parsed = parseTimePickerValue(value);
62
+ if (!parsed)
63
+ return createEmptyTimePickerDraft();
64
+ if (hourCycle === 24) {
65
+ return {
66
+ hour: String(parsed.hour),
67
+ minute: String(parsed.minute),
68
+ second: String(parsed.second),
69
+ dayPeriod: ''
70
+ };
71
+ }
72
+ const isPm = parsed.hour >= 12;
73
+ const hour12 = parsed.hour % 12 || 12;
74
+ return {
75
+ hour: String(hour12),
76
+ minute: String(parsed.minute),
77
+ second: String(parsed.second),
78
+ dayPeriod: isPm ? 'PM' : 'AM'
79
+ };
80
+ }
81
+ export function getRequiredSegments(granularity, hourCycle) {
82
+ const required = ['hour'];
83
+ if (granularity !== 'hour') {
84
+ required.push('minute');
85
+ }
86
+ if (granularity === 'second') {
87
+ required.push('second');
88
+ }
89
+ if (hourCycle === 12) {
90
+ required.push('dayPeriod');
91
+ }
92
+ return required;
93
+ }
94
+ export function normalizeSegmentNumberInput(value, maxDigits = 2) {
95
+ const digits = value.replace(/\D+/g, '').slice(0, maxDigits);
96
+ if (!digits)
97
+ return '';
98
+ const numeric = Number(digits);
99
+ if (!Number.isFinite(numeric))
100
+ return '';
101
+ return String(numeric);
102
+ }
103
+ export function isSegmentValueEmpty(value) {
104
+ return !value || value.trim().length === 0;
105
+ }
106
+ export function buildTimePartsFromDraft(draft, granularity, hourCycle) {
107
+ const hourRaw = draft.hour.trim();
108
+ const minuteRaw = draft.minute.trim();
109
+ const secondRaw = draft.second.trim();
110
+ const dayPeriodRaw = draft.dayPeriod.trim().toUpperCase();
111
+ if (!hourRaw)
112
+ return null;
113
+ if (granularity !== 'hour' && !minuteRaw)
114
+ return null;
115
+ if (granularity === 'second' && !secondRaw)
116
+ return null;
117
+ if (hourCycle === 12 && !dayPeriodRaw)
118
+ return null;
119
+ const hourNumeric = Number(hourRaw);
120
+ if (!Number.isInteger(hourNumeric))
121
+ return null;
122
+ let hour24 = hourNumeric;
123
+ if (hourCycle === 12) {
124
+ if (hourNumeric < 1 || hourNumeric > 12)
125
+ return null;
126
+ if (dayPeriodRaw !== 'AM' && dayPeriodRaw !== 'PM')
127
+ return null;
128
+ hour24 = hourNumeric % 12;
129
+ if (dayPeriodRaw === 'PM') {
130
+ hour24 += 12;
131
+ }
132
+ }
133
+ else if (hourNumeric < 0 || hourNumeric > 23) {
134
+ return null;
135
+ }
136
+ const minute = granularity === 'hour' ? 0 : Number(minuteRaw);
137
+ if (!Number.isInteger(minute) || minute < 0 || minute > 59)
138
+ return null;
139
+ const second = granularity === 'second' ? Number(secondRaw) : 0;
140
+ if (!Number.isInteger(second) || second < 0 || second > 59)
141
+ return null;
142
+ return {
143
+ hour: hour24,
144
+ minute,
145
+ second
146
+ };
147
+ }
148
+ export function compareTimeParts(left, right) {
149
+ const leftTotal = left.hour * 3600 + left.minute * 60 + left.second;
150
+ const rightTotal = right.hour * 3600 + right.minute * 60 + right.second;
151
+ if (leftTotal < rightTotal)
152
+ return -1;
153
+ if (leftTotal > rightTotal)
154
+ return 1;
155
+ return 0;
156
+ }
157
+ function truncateToGranularity(parts, granularity) {
158
+ if (granularity === 'hour')
159
+ return { hour: parts.hour, minute: 0, second: 0 };
160
+ if (granularity === 'minute')
161
+ return { hour: parts.hour, minute: parts.minute, second: 0 };
162
+ return parts;
163
+ }
164
+ export function isTimeOutOfRange(value, minValue, maxValue, granularity = 'second') {
165
+ const currentRaw = parseTimePickerValue(value);
166
+ if (!currentRaw)
167
+ return true;
168
+ const current = truncateToGranularity(currentRaw, granularity);
169
+ if (minValue) {
170
+ const minRaw = parseTimePickerValue(minValue);
171
+ if (minRaw) {
172
+ const min = truncateToGranularity(minRaw, granularity);
173
+ if (compareTimeParts(current, min) < 0)
174
+ return true;
175
+ }
176
+ }
177
+ if (maxValue) {
178
+ const maxRaw = parseTimePickerValue(maxValue);
179
+ if (maxRaw) {
180
+ const max = truncateToGranularity(maxRaw, granularity);
181
+ if (compareTimeParts(current, max) > 0)
182
+ return true;
183
+ }
184
+ }
185
+ return false;
186
+ }
187
+ function clamp(value, min, max) {
188
+ return Math.min(max, Math.max(min, value));
189
+ }
190
+ export function clampToStep(value, step, min, max) {
191
+ if (!Number.isFinite(value))
192
+ return min;
193
+ const safeStep = Number.isFinite(step) && step > 0 ? step : 1;
194
+ const clamped = clamp(value, min, max);
195
+ const stepped = Math.round(clamped / safeStep) * safeStep;
196
+ return clamp(stepped, min, max);
197
+ }
198
+ function wrapCircular(value, min, max) {
199
+ const range = max - min + 1;
200
+ if (range <= 0)
201
+ return min;
202
+ let normalized = (value - min) % range;
203
+ if (normalized < 0)
204
+ normalized += range;
205
+ return normalized + min;
206
+ }
207
+ export function adjustSegmentWithStep(currentValue, type, delta, options) {
208
+ let min = 0;
209
+ let max = 59;
210
+ let step = 1;
211
+ if (type === 'hour') {
212
+ if (options.hourCycle === 12) {
213
+ min = 1;
214
+ max = 12;
215
+ }
216
+ else {
217
+ min = 0;
218
+ max = 23;
219
+ }
220
+ step = Math.max(1, options.hourStep);
221
+ }
222
+ if (type === 'minute') {
223
+ min = 0;
224
+ max = 59;
225
+ step = Math.max(1, options.minuteStep);
226
+ }
227
+ if (type === 'second') {
228
+ min = 0;
229
+ max = 59;
230
+ step = Math.max(1, options.secondStep);
231
+ }
232
+ const current = Number(currentValue || String(min));
233
+ const base = Number.isFinite(current) ? current : min;
234
+ const next = wrapCircular(base + step * delta, min, max);
235
+ return String(next);
236
+ }
237
+ export function buildTimePickerSegments(params) {
238
+ const { locale, hourCycle, granularity, draft, formatter } = params;
239
+ const formatOptions = {
240
+ hour: 'numeric',
241
+ minute: granularity !== 'hour' ? '2-digit' : undefined,
242
+ second: granularity === 'second' ? '2-digit' : undefined,
243
+ hourCycle: hourCycle === 12 ? 'h12' : 'h23',
244
+ timeZone: 'UTC'
245
+ };
246
+ const sampleDate = new Date(Date.UTC(2024, 0, 1, 14, 30, 45));
247
+ const resolvedFormatter = formatter ?? new Intl.DateTimeFormat(locale, formatOptions);
248
+ const parts = resolvedFormatter.formatToParts(sampleDate);
249
+ return parts
250
+ .map((part) => {
251
+ if (part.type === 'literal') {
252
+ return {
253
+ type: 'literal',
254
+ text: part.value,
255
+ isPlaceholder: false
256
+ };
257
+ }
258
+ if (part.type === 'hour') {
259
+ const isPlaceholder = isSegmentValueEmpty(draft.hour);
260
+ const hourText = hourCycle === 24 && draft.hour.trim() === '0' ? '00' : draft.hour;
261
+ return {
262
+ type: 'hour',
263
+ text: isPlaceholder ? (hourCycle === 12 ? 'hh' : 'HH') : hourText,
264
+ isPlaceholder
265
+ };
266
+ }
267
+ if (part.type === 'minute' && granularity !== 'hour') {
268
+ const isPlaceholder = isSegmentValueEmpty(draft.minute);
269
+ return {
270
+ type: 'minute',
271
+ text: isPlaceholder ? 'mm' : formatDraftTwoDigits(draft.minute),
272
+ isPlaceholder
273
+ };
274
+ }
275
+ if (part.type === 'second' && granularity === 'second') {
276
+ const isPlaceholder = isSegmentValueEmpty(draft.second);
277
+ return {
278
+ type: 'second',
279
+ text: isPlaceholder ? 'ss' : formatDraftTwoDigits(draft.second),
280
+ isPlaceholder
281
+ };
282
+ }
283
+ if (part.type === 'dayPeriod' && hourCycle === 12) {
284
+ const isPlaceholder = isSegmentValueEmpty(draft.dayPeriod);
285
+ return {
286
+ type: 'dayPeriod',
287
+ text: isPlaceholder ? getDayPeriodPlaceholder(locale) : draft.dayPeriod,
288
+ isPlaceholder
289
+ };
290
+ }
291
+ return null;
292
+ })
293
+ .filter((part) => part !== null);
294
+ }
295
+ export function getEditableSegmentOrder(segments) {
296
+ return segments
297
+ .filter((segment) => segment.type !== 'literal')
298
+ .map((segment) => segment.type);
299
+ }
300
+ export function getSegmentLabel(type, locale) {
301
+ try {
302
+ const displayNames = new Intl.DisplayNames([locale], { type: 'dateTimeField' });
303
+ return displayNames.of(type) ?? type;
304
+ }
305
+ catch {
306
+ if (type === 'hour')
307
+ return 'Hour';
308
+ if (type === 'minute')
309
+ return 'Minute';
310
+ if (type === 'second')
311
+ return 'Second';
312
+ return 'Day period';
313
+ }
314
+ }
@@ -0,0 +1,17 @@
1
+ import type { TimePickerDraft, TimePickerEditableSegmentType, TimePickerGranularity, TimePickerHourCycle, TimePickerTimeValue } from './time-utils';
2
+ export type WheelOption = {
3
+ value: string;
4
+ label: string;
5
+ disabled: boolean;
6
+ };
7
+ export declare function buildWheelOptions(params: {
8
+ type: TimePickerEditableSegmentType;
9
+ granularity: TimePickerGranularity;
10
+ hourCycle: TimePickerHourCycle;
11
+ hourStep: number;
12
+ minuteStep: number;
13
+ secondStep: number;
14
+ hasRangeBounds: boolean;
15
+ getCandidateFromPartial: (partial: Partial<TimePickerDraft>) => TimePickerTimeValue | null;
16
+ isOutOfRange: (value: TimePickerTimeValue) => boolean;
17
+ }): WheelOption[];
@@ -0,0 +1,63 @@
1
+ export function buildWheelOptions(params) {
2
+ const { type, hourCycle, hourStep, minuteStep, secondStep, hasRangeBounds, getCandidateFromPartial, isOutOfRange } = params;
3
+ const options = [];
4
+ if (type === 'dayPeriod') {
5
+ for (const option of ['AM', 'PM']) {
6
+ const disabled = hasRangeBounds
7
+ ? (() => {
8
+ const candidate = getCandidateFromPartial({ dayPeriod: option });
9
+ return candidate ? isOutOfRange(candidate) : false;
10
+ })()
11
+ : false;
12
+ options.push({
13
+ value: option,
14
+ label: option,
15
+ disabled
16
+ });
17
+ }
18
+ return options;
19
+ }
20
+ let min = 0;
21
+ let max = 59;
22
+ let step = 1;
23
+ if (type === 'hour') {
24
+ if (hourCycle === 12) {
25
+ min = 1;
26
+ max = 12;
27
+ }
28
+ else {
29
+ min = 0;
30
+ max = 23;
31
+ }
32
+ step = Math.max(1, hourStep);
33
+ }
34
+ else if (type === 'minute') {
35
+ min = 0;
36
+ max = 59;
37
+ step = Math.max(1, minuteStep);
38
+ }
39
+ else if (type === 'second') {
40
+ min = 0;
41
+ max = 59;
42
+ step = Math.max(1, secondStep);
43
+ }
44
+ for (let current = min; current <= max; current += step) {
45
+ const valueString = String(current);
46
+ const disabled = hasRangeBounds
47
+ ? (() => {
48
+ const candidate = getCandidateFromPartial(type === 'hour'
49
+ ? { hour: valueString }
50
+ : type === 'minute'
51
+ ? { minute: valueString }
52
+ : { second: valueString });
53
+ return candidate ? isOutOfRange(candidate) : false;
54
+ })()
55
+ : false;
56
+ options.push({
57
+ value: valueString,
58
+ label: String(current).padStart(2, '0'),
59
+ disabled
60
+ });
61
+ }
62
+ return options;
63
+ }
@@ -0,0 +1,25 @@
1
+ # Clock WheelColumn
2
+
3
+ ## API reference
4
+
5
+ ### Clock.WheelColumn
6
+
7
+ Name: `Clock.WheelColumn`
8
+ Description: Scrollable spinbutton column for a single editable segment (`hour`, `minute`, `second`, `dayPeriod`).
9
+
10
+ | Prop | Type | Default | Description |
11
+ | -------------- | ---------------------------------------------------------------- | --------------- | ----------------------------------------------------------- |
12
+ | `type` | `'hour' \| 'minute' \| 'second' \| 'dayPeriod'` | `required` | Segment represented by this wheel column. |
13
+ | `children` | `Snippet<[{ value: string; label: string; disabled: boolean }]>` | `undefined` | Optional custom item renderer for each wheel option. |
14
+ | `class` | `string` | `'h-55'` | CSS class names for the spinbutton column. |
15
+ | `aria-label` | `string` | `segment label` | Overrides the computed accessible column label. |
16
+ | `...restProps` | `HTMLAttributes<HTMLDivElement>` | `-` | Additional attributes forwarded to the column root element. |
17
+
18
+ ### Context utilities
19
+
20
+ Name: `useClockContext`
21
+ Description: Reads shared state and operations from `Clock.Root`.
22
+
23
+ | Prop | Type | Default | Description |
24
+ | ----------------- | -------------------- | ------- | ------------------------------------------------ |
25
+ | `useClockContext` | `() => ClockContext` | `-` | Returns context and throws outside `Clock.Root`. |
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import * as Clock from '../index.parts';
3
+
4
+ let value = $state<string | null>('14:30');
5
+ </script>
6
+
7
+ <Clock.Root bind:value hourCycle={24} class="flex gap-2">
8
+ {#snippet column(col)}
9
+ <Clock.WheelColumn type={col.type} class="h-44 w-16" />
10
+ {/snippet}
11
+ </Clock.Root>
12
+
13
+ <p data-testid="bind-value">{value}</p>
14
+ <button type="button" data-testid="set-value-16-45" onclick={() => (value = '16:45')}
15
+ >Set value</button
16
+ >
@@ -0,0 +1,3 @@
1
+ declare const ClockWheelColumnBindableTest: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type ClockWheelColumnBindableTest = ReturnType<typeof ClockWheelColumnBindableTest>;
3
+ export default ClockWheelColumnBindableTest;
@@ -0,0 +1,29 @@
1
+ <script lang="ts">
2
+ import * as Clock from '../index.parts';
3
+
4
+ type Props = {
5
+ defaultValue?: string;
6
+ };
7
+
8
+ let { defaultValue = '14:30' }: Props = $props();
9
+
10
+ let selectedValue = $state<string | null>('');
11
+ </script>
12
+
13
+ <Clock.Root
14
+ {defaultValue}
15
+ onChange={(nextValue) => {
16
+ selectedValue = nextValue;
17
+ }}
18
+ class="flex gap-2"
19
+ >
20
+ {#snippet column(col)}
21
+ <Clock.WheelColumn type={col.type} class="h-44 w-16">
22
+ {#snippet children(option)}
23
+ <Clock.WheelItem type={col.type} {option} class="custom-wheel-item" />
24
+ {/snippet}
25
+ </Clock.WheelColumn>
26
+ {/snippet}
27
+ </Clock.Root>
28
+
29
+ <p data-testid="clock-value">{selectedValue}</p>
@@ -0,0 +1,6 @@
1
+ type Props = {
2
+ defaultValue?: string;
3
+ };
4
+ declare const ClockWheelColumnCustomSnippetTest: import("svelte").Component<Props, {}, "">;
5
+ type ClockWheelColumnCustomSnippetTest = ReturnType<typeof ClockWheelColumnCustomSnippetTest>;
6
+ export default ClockWheelColumnCustomSnippetTest;
@@ -0,0 +1,11 @@
1
+ <script lang="ts">
2
+ import * as Clock from '../index.parts';
3
+
4
+ let value = $state<string | null>('14:30');
5
+ </script>
6
+
7
+ <Clock.Root bind:value hourCycle={24} class="flex gap-2">
8
+ {#snippet column(col)}
9
+ <Clock.WheelColumn type={col.type} class="w-16" />
10
+ {/snippet}
11
+ </Clock.Root>
@@ -0,0 +1,3 @@
1
+ declare const ClockWheelColumnDefaultHeightTest: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type ClockWheelColumnDefaultHeightTest = ReturnType<typeof ClockWheelColumnDefaultHeightTest>;
3
+ export default ClockWheelColumnDefaultHeightTest;
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ import { untrack } from 'svelte';
3
+ import * as Clock from '../index.parts';
4
+ import type { ClockColumnInfo } from '../root/resolve-visible-columns';
5
+
6
+ type Props = {
7
+ defaultValue?: string;
8
+ defaultOpen?: boolean;
9
+ hourCycle?: 12 | 24;
10
+ granularity?: 'hour' | 'minute' | 'second';
11
+ minValue?: string;
12
+ maxValue?: string;
13
+ useSnippet?: boolean;
14
+ };
15
+
16
+ let {
17
+ defaultValue = '14:30',
18
+ hourCycle = 24,
19
+ granularity = 'minute',
20
+ minValue,
21
+ maxValue,
22
+ useSnippet = false
23
+ }: Props = $props();
24
+
25
+ let value = $state<string | null>(untrack(() => defaultValue));
26
+ </script>
27
+
28
+ {#if useSnippet}
29
+ <Clock.Root bind:value {hourCycle} {granularity} {minValue} {maxValue} class="flex gap-2">
30
+ {#snippet column(col: ClockColumnInfo)}
31
+ <div data-testid="panel-column" data-type={col.type}>{col.label}</div>
32
+ {/snippet}
33
+ </Clock.Root>
34
+ {:else}
35
+ <Clock.Root bind:value {hourCycle} {granularity} {minValue} {maxValue} class="flex gap-2" />
36
+ {/if}
37
+
38
+ <p data-testid="clock-value">{value}</p>
@@ -0,0 +1,12 @@
1
+ type Props = {
2
+ defaultValue?: string;
3
+ defaultOpen?: boolean;
4
+ hourCycle?: 12 | 24;
5
+ granularity?: 'hour' | 'minute' | 'second';
6
+ minValue?: string;
7
+ maxValue?: string;
8
+ useSnippet?: boolean;
9
+ };
10
+ declare const ClockWheelColumnTest: import("svelte").Component<Props, {}, "">;
11
+ type ClockWheelColumnTest = ReturnType<typeof ClockWheelColumnTest>;
12
+ export default ClockWheelColumnTest;
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ import { untrack } from 'svelte';
3
+ import * as Clock from '../index.parts';
4
+ import type { ClockColumnInfo } from '../root/resolve-visible-columns';
5
+
6
+ type Props = {
7
+ defaultValue?: string;
8
+ defaultOpen?: boolean;
9
+ hourCycle?: 12 | 24;
10
+ granularity?: 'hour' | 'minute' | 'second';
11
+ minValue?: string;
12
+ maxValue?: string;
13
+ useSnippet?: boolean;
14
+ };
15
+
16
+ let {
17
+ defaultValue = '14:30',
18
+ hourCycle = 24,
19
+ granularity = 'minute',
20
+ minValue,
21
+ maxValue,
22
+ useSnippet = false
23
+ }: Props = $props();
24
+
25
+ let value = $state<string | null>(untrack(() => defaultValue));
26
+ </script>
27
+
28
+ {#if useSnippet}
29
+ <Clock.Root bind:value {hourCycle} {granularity} {minValue} {maxValue} class="flex gap-2">
30
+ {#snippet column(col: ClockColumnInfo)}
31
+ <div data-testid="panel-column" data-type={col.type}>{col.label}</div>
32
+ {/snippet}
33
+ </Clock.Root>
34
+ {:else}
35
+ <Clock.Root bind:value {hourCycle} {granularity} {minValue} {maxValue} class="flex gap-2" />
36
+ {/if}
37
+
38
+ <p data-testid="clock-value">{value}</p>
@@ -0,0 +1,12 @@
1
+ type Props = {
2
+ defaultValue?: string;
3
+ defaultOpen?: boolean;
4
+ hourCycle?: 12 | 24;
5
+ granularity?: 'hour' | 'minute' | 'second';
6
+ minValue?: string;
7
+ maxValue?: string;
8
+ useSnippet?: boolean;
9
+ };
10
+ declare const ClockWheelColumnTpTest: import("svelte").Component<Props, {}, "">;
11
+ type ClockWheelColumnTpTest = ReturnType<typeof ClockWheelColumnTpTest>;
12
+ export default ClockWheelColumnTpTest;
@@ -0,0 +1,29 @@
1
+ <script lang="ts">
2
+ import * as Clock from '../index.parts';
3
+
4
+ type Props = {
5
+ defaultValue?: string;
6
+ };
7
+
8
+ let { defaultValue = '14:30' }: Props = $props();
9
+
10
+ let selectedValue = $state<string | null>('');
11
+ </script>
12
+
13
+ <Clock.Root
14
+ {defaultValue}
15
+ onChange={(nextValue) => {
16
+ selectedValue = nextValue;
17
+ }}
18
+ class="flex gap-2"
19
+ >
20
+ {#snippet column(col)}
21
+ <Clock.WheelColumn type={col.type} class="h-44 w-16">
22
+ {#snippet children(option)}
23
+ <div data-testid="untagged-wheel-option">{option.label}</div>
24
+ {/snippet}
25
+ </Clock.WheelColumn>
26
+ {/snippet}
27
+ </Clock.Root>
28
+
29
+ <p data-testid="clock-value">{selectedValue}</p>
@@ -0,0 +1,6 @@
1
+ type Props = {
2
+ defaultValue?: string;
3
+ };
4
+ declare const ClockWheelColumnUntaggedSnippetTest: import("svelte").Component<Props, {}, "">;
5
+ type ClockWheelColumnUntaggedSnippetTest = ReturnType<typeof ClockWheelColumnUntaggedSnippetTest>;
6
+ export default ClockWheelColumnUntaggedSnippetTest;