noph-ui 0.46.0 → 0.47.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 (66) hide show
  1. package/README.md +2 -1
  2. package/dist/button/ButtonGroup.svelte +1 -3
  3. package/dist/date-picker/DockedDatePicker.svelte +2 -3
  4. package/dist/date-picker/DockedDateTimePicker.svelte +2 -3
  5. package/dist/date-picker/dateUtils.d.ts +2 -13
  6. package/dist/date-picker/dateUtils.js +4 -66
  7. package/dist/date-picker/formatters.d.ts +4 -0
  8. package/dist/date-picker/formatters.js +26 -0
  9. package/dist/date-picker/timeUtils.d.ts +34 -0
  10. package/dist/date-picker/timeUtils.js +139 -0
  11. package/dist/dialog/Dialog.svelte +4 -2
  12. package/dist/dialog/types.d.ts +1 -0
  13. package/dist/fab/FabMenu.svelte +1 -2
  14. package/dist/form-reset.d.ts +2 -1
  15. package/dist/form-reset.js +2 -2
  16. package/dist/icons/KeyboardIcon.svelte +5 -0
  17. package/dist/icons/KeyboardIcon.svelte.d.ts +26 -0
  18. package/dist/icons/ScheduleIcon.svelte +5 -0
  19. package/dist/icons/ScheduleIcon.svelte.d.ts +26 -0
  20. package/dist/icons/index.d.ts +2 -0
  21. package/dist/icons/index.js +2 -0
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.js +1 -0
  24. package/dist/keyboard-nav.js +3 -2
  25. package/dist/list/index.d.ts +1 -0
  26. package/dist/list/index.js +1 -0
  27. package/dist/media.d.ts +4 -0
  28. package/dist/media.js +4 -0
  29. package/dist/menu/Menu.svelte +2 -1
  30. package/dist/progress/CircularProgress.svelte +9 -9
  31. package/dist/progress/LinearProgress.svelte +1 -2
  32. package/dist/ripple/Ripple.svelte +7 -8
  33. package/dist/search/Search.svelte +14 -4
  34. package/dist/search/Search.svelte.d.ts +1 -1
  35. package/dist/select/Select.svelte +9 -10
  36. package/dist/select/VirtualList.svelte +12 -13
  37. package/dist/sheet/Sheet.svelte +4 -1
  38. package/dist/sheet/types.d.ts +1 -0
  39. package/dist/slider/Slider.svelte +5 -6
  40. package/dist/tabs/Tabs.svelte +21 -14
  41. package/dist/time-picker/ClockDial.svelte +528 -0
  42. package/dist/time-picker/ClockDial.svelte.d.ts +4 -0
  43. package/dist/time-picker/DockedTimePicker.svelte +378 -0
  44. package/dist/time-picker/DockedTimePicker.svelte.d.ts +7 -0
  45. package/dist/time-picker/PeriodSelector.svelte +156 -0
  46. package/dist/time-picker/PeriodSelector.svelte.d.ts +4 -0
  47. package/dist/time-picker/TimeInputs.svelte +233 -0
  48. package/dist/time-picker/TimeInputs.svelte.d.ts +4 -0
  49. package/dist/time-picker/TimePickerDialog.svelte +229 -0
  50. package/dist/time-picker/TimePickerDialog.svelte.d.ts +7 -0
  51. package/dist/time-picker/TimePickerPanel.svelte +203 -0
  52. package/dist/time-picker/TimePickerPanel.svelte.d.ts +4 -0
  53. package/dist/time-picker/TimeSelectors.svelte +157 -0
  54. package/dist/time-picker/TimeSelectors.svelte.d.ts +4 -0
  55. package/dist/time-picker/index.d.ts +4 -0
  56. package/dist/time-picker/index.js +4 -0
  57. package/dist/time-picker/timePickerState.svelte.d.ts +27 -0
  58. package/dist/time-picker/timePickerState.svelte.js +63 -0
  59. package/dist/time-picker/types.d.ts +148 -0
  60. package/dist/time-picker/types.js +1 -0
  61. package/dist/tooltip/Tooltip.svelte +2 -4
  62. package/dist/types.d.ts +1 -0
  63. package/dist/types.js +1 -0
  64. package/dist/validity.d.ts +2 -0
  65. package/dist/validity.js +4 -0
  66. package/package.json +1 -1
@@ -0,0 +1,528 @@
1
+ <script lang="ts">
2
+ import {
3
+ formatMinutes,
4
+ formatTwoDigits,
5
+ isMinuteWithin,
6
+ MINUTES_IN_HOUR,
7
+ snapToStep,
8
+ } from '../date-picker/timeUtils.js'
9
+ import { tick } from 'svelte'
10
+ import type { ClockDialProps } from './types.js'
11
+
12
+ let {
13
+ value,
14
+ selection = 'hour',
15
+ hour12 = false,
16
+ minuteStep = 1,
17
+ min,
18
+ max,
19
+ locale,
20
+ disabled = false,
21
+ element = $bindable(),
22
+ isTimeEnabled,
23
+ selectHourLabel = 'Select hour',
24
+ selectMinuteLabel = 'Select minute',
25
+ hourOptionLabel = (hourValue, total) =>
26
+ `${hourValue} ${hourValue === '1' ? 'hour' : 'hours'} of ${total}`,
27
+ minuteOptionLabel = (minuteValue, total) =>
28
+ `${minuteValue} ${minuteValue === '1' ? 'minute' : 'minutes'} of ${total}`,
29
+ onselect,
30
+ onselectionend,
31
+ ...attributes
32
+ }: ClockDialProps = $props()
33
+
34
+ const OUTER_RATIO = 0.454
35
+ const INNER_RATIO = 0.3021
36
+ const DRAG_THRESHOLD = 3
37
+ const FULL_CIRCLE = 360
38
+
39
+ let tracking = $state(false)
40
+ let announcement = $state('')
41
+ let handleElement = $state<HTMLDivElement>()
42
+
43
+ let step = $derived(Math.max(1, Math.trunc(minuteStep) || 1))
44
+ let hour = $derived(Math.floor(value / MINUTES_IN_HOUR))
45
+ let minute = $derived(value % MINUTES_IN_HOUR)
46
+ let isPm = $derived(hour >= 12)
47
+
48
+ const usable = (minutes: number) =>
49
+ isMinuteWithin(minutes, min, max) && (isTimeEnabled?.(minutes) ?? true)
50
+
51
+ const hourReachable = (candidate: number) => {
52
+ for (let each = 0; each < MINUTES_IN_HOUR; each += step) {
53
+ if (usable(candidate * MINUTES_IN_HOUR + each)) return true
54
+ }
55
+ return false
56
+ }
57
+
58
+ /** Minutes for an hour ring position, keeping the minute that is already selected. */
59
+ const hourMinutes = (position: number) =>
60
+ (hour12 ? (position % 12) + (isPm ? 12 : 0) : position) * MINUTES_IN_HOUR + minute
61
+
62
+ interface DialOption {
63
+ /** Minutes since midnight this option selects. */
64
+ minutes: number
65
+ /** Ring position, 0 at the top going clockwise. */
66
+ position: number
67
+ /** How many positions the ring has. */
68
+ positions: number
69
+ label?: string
70
+ inner: boolean
71
+ disabled: boolean
72
+ accessibleLabel: string
73
+ }
74
+
75
+ let options = $derived.by<DialOption[]>(() => {
76
+ if (selection === 'hour') {
77
+ const total = hour12 ? 12 : 24
78
+ return Array.from({ length: total }, (_, index) => {
79
+ const minutes = hourMinutes(index)
80
+ const inner = !hour12 && index >= 12
81
+ const shown = hour12 ? index % 12 || 12 : index
82
+ const label = hour12 ? `${shown}` : index === 0 ? '00' : `${index}`
83
+ return {
84
+ minutes,
85
+ position: index % 12,
86
+ positions: 12,
87
+ label,
88
+ inner,
89
+ disabled: !hourReachable(hour12 ? (index % 12) + (isPm ? 12 : 0) : index),
90
+ accessibleLabel: hourOptionLabel(`${shown}`, total),
91
+ }
92
+ })
93
+ }
94
+ const positions = Math.floor(MINUTES_IN_HOUR / step)
95
+ const total = positions
96
+ return Array.from({ length: positions }, (_, index) => {
97
+ const each = index * step
98
+ const minutes = hour * MINUTES_IN_HOUR + each
99
+ return {
100
+ minutes,
101
+ position: (each / MINUTES_IN_HOUR) * 12,
102
+ positions: 12,
103
+ // Only every fifth minute carries a number, as on a real clock face.
104
+ label: each % 5 === 0 ? formatTwoDigits(each, locale) : undefined,
105
+ inner: false,
106
+ disabled: !usable(minutes),
107
+ accessibleLabel: minuteOptionLabel(`${each}`, total),
108
+ }
109
+ })
110
+ })
111
+
112
+ /** The option the handle is resting on, or the nearest one when it sits between two. */
113
+ let selectedIndex = $derived.by(() => {
114
+ let nearest = -1
115
+ let distance = Infinity
116
+ options.forEach((option, index) => {
117
+ const gap = Math.abs(option.minutes - value)
118
+ if (gap < distance) {
119
+ distance = gap
120
+ nearest = index
121
+ }
122
+ })
123
+ return nearest
124
+ })
125
+
126
+ let tabStopIndex = $derived.by(() => {
127
+ if (selectedIndex >= 0 && !options[selectedIndex].disabled) return selectedIndex
128
+ const offered = options.findIndex((option) => !option.disabled)
129
+ return offered >= 0 ? offered : Math.max(selectedIndex, 0)
130
+ })
131
+
132
+ const angleOf = (position: number, positions: number) => (position / positions) * FULL_CIRCLE - 90
133
+
134
+ const ratioFor = (inner: boolean) => (inner ? INNER_RATIO : OUTER_RATIO)
135
+
136
+ /**
137
+ * How far the numbers and the handle sit from the centre. The handle's own radius comes off the
138
+ * ring, the way Compose reads the ratios, which keeps a 48dp handle inside the 256dp dial instead
139
+ * of hanging over its edge.
140
+ */
141
+ const radiusOf = (inner: boolean) =>
142
+ `calc(var(--_size) * ${ratioFor(inner)} - var(--_handle) / 2)`
143
+
144
+ /** The angle the handle points at, from the live value rather than the nearest labelled option. */
145
+ let handleAngle = $derived(
146
+ selection === 'hour' ? angleOf(hour % 12, 12) : angleOf((minute / MINUTES_IN_HOUR) * 12, 12),
147
+ )
148
+
149
+ let handleInner = $derived(selection === 'hour' && !hour12 && hour >= 12)
150
+
151
+ // Rotating to the nearest equivalent angle keeps 11 -> 12 going forwards by 30 degrees instead of
152
+ // spinning back 330. The running total is a plain variable so reading it creates no dependency.
153
+ let unwrapped = 0
154
+ let rotation = $state(0)
155
+
156
+ $effect(() => {
157
+ const target = handleAngle
158
+ const delta = ((((target - unwrapped) % FULL_CIRCLE) + FULL_CIRCLE * 1.5) % FULL_CIRCLE) - 180
159
+ unwrapped += delta
160
+ rotation = unwrapped
161
+ })
162
+
163
+ const geometry = () => {
164
+ const rect = element?.getBoundingClientRect()
165
+ if (!rect) return undefined
166
+ // Measured rather than assumed, so the ratios hold at any root font size or dial size.
167
+ const handleRadius = (handleElement?.offsetWidth ?? 0) / 2
168
+ return {
169
+ cx: rect.left + rect.width / 2,
170
+ cy: rect.top + rect.height / 2,
171
+ radius: (inner: boolean) => rect.width * ratioFor(inner) - handleRadius,
172
+ }
173
+ }
174
+
175
+ /** Reads a pointer position as minutes since midnight. */
176
+ const fromPointer = (clientX: number, clientY: number, gesture: 'tap' | 'drag') => {
177
+ const box = geometry()
178
+ if (!box) return undefined
179
+ const dx = clientX - box.cx
180
+ const dy = clientY - box.cy
181
+ const turn = ((Math.atan2(dy, dx) + Math.PI / 2) / (2 * Math.PI) + 1) % 1
182
+
183
+ if (selection === 'hour') {
184
+ const position = Math.round(turn * 12) % 12
185
+ if (hour12) return hourMinutes(position)
186
+ const midpoint = (box.radius(false) + box.radius(true)) / 2
187
+ const inner = Math.hypot(dx, dy) < midpoint
188
+ return (position + (inner ? 12 : 0)) * MINUTES_IN_HOUR + minute
189
+ }
190
+
191
+ // A tap lands on a whole five minutes, the way the numbers read; a drag keeps the full step.
192
+ const snap = gesture === 'tap' ? Math.max(step, 5) : step
193
+ const raw = Math.round(turn * MINUTES_IN_HOUR) % MINUTES_IN_HOUR
194
+ return snapToStep(hour * MINUTES_IN_HOUR + raw, snap)
195
+ }
196
+
197
+ const announce = () => {
198
+ announcement = formatMinutes(value, locale, hour12)
199
+ }
200
+
201
+ let pointerId: number | undefined
202
+ let origin: { x: number; y: number } | undefined
203
+ let moved = false
204
+
205
+ // Capturing keeps the drag alive once the finger leaves the dial. It throws when the pointer is
206
+ // already gone, which is nothing to act on, so both directions are guarded.
207
+ const capture = (id: number, hold: boolean) => {
208
+ try {
209
+ if (hold) element?.setPointerCapture(id)
210
+ else element?.releasePointerCapture(id)
211
+ } catch {
212
+ // The pointer ended on its own; there is nothing left to capture or release.
213
+ }
214
+ }
215
+
216
+ const handlePointerdown = (event: PointerEvent) => {
217
+ if (disabled || event.button !== 0) return
218
+ event.preventDefault()
219
+ pointerId = event.pointerId
220
+ origin = { x: event.clientX, y: event.clientY }
221
+ moved = false
222
+ capture(event.pointerId, true)
223
+ }
224
+
225
+ const handlePointermove = (event: PointerEvent) => {
226
+ if (disabled || pointerId !== event.pointerId || !origin) return
227
+ if (!moved && Math.hypot(event.clientX - origin.x, event.clientY - origin.y) < DRAG_THRESHOLD) {
228
+ return
229
+ }
230
+ moved = true
231
+ tracking = true
232
+ const next = fromPointer(event.clientX, event.clientY, 'drag')
233
+ if (next !== undefined) onselect?.(next)
234
+ }
235
+
236
+ const handlePointerup = (event: PointerEvent) => {
237
+ if (disabled || pointerId !== event.pointerId) return
238
+ capture(event.pointerId, false)
239
+ const next = fromPointer(event.clientX, event.clientY, moved ? 'drag' : 'tap')
240
+ if (next !== undefined) onselect?.(next)
241
+ pointerId = undefined
242
+ origin = undefined
243
+ tracking = false
244
+ moved = false
245
+ announce()
246
+ onselectionend?.('pointer')
247
+ }
248
+
249
+ const handlePointercancel = (event: PointerEvent) => {
250
+ if (pointerId !== event.pointerId) return
251
+ capture(event.pointerId, false)
252
+ pointerId = undefined
253
+ origin = undefined
254
+ tracking = false
255
+ moved = false
256
+ }
257
+
258
+ const focusOption = async (index: number) => {
259
+ const target = options[index]
260
+ if (!target) return
261
+ onselect?.(target.minutes)
262
+ announcement = formatMinutes(target.minutes, locale, hour12)
263
+ // The buttons are keyed by their minute, so let the list settle before reaching for one.
264
+ await tick()
265
+ element
266
+ ?.querySelector<HTMLElement>(`.np-clock-dial-option[data-minutes="${target.minutes}"]`)
267
+ ?.focus()
268
+ }
269
+
270
+ const stepBy = (delta: number) => {
271
+ const from = tabStopIndex
272
+ const count = options.length
273
+ focusOption((((from + delta) % count) + count) % count)
274
+ }
275
+
276
+ const handleKeydown = (event: KeyboardEvent) => {
277
+ if (disabled) return
278
+ const keys: Record<string, () => void> = {
279
+ ArrowRight: () => stepBy(1),
280
+ ArrowUp: () => stepBy(1),
281
+ ArrowLeft: () => stepBy(-1),
282
+ ArrowDown: () => stepBy(-1),
283
+ PageUp: () => stepBy(5),
284
+ PageDown: () => stepBy(-5),
285
+ Home: () => focusOption(0),
286
+ End: () => focusOption(options.length - 1),
287
+ }
288
+ const action = keys[event.key]
289
+ if (!action) return
290
+ event.preventDefault()
291
+ action()
292
+ }
293
+ </script>
294
+
295
+ <div
296
+ {...attributes}
297
+ bind:this={element}
298
+ role="listbox"
299
+ aria-label={selection === 'hour' ? selectHourLabel : selectMinuteLabel}
300
+ aria-disabled={disabled || undefined}
301
+ class={['np-clock-dial', tracking && 'np-tracking', disabled && 'np-disabled', attributes.class]}
302
+ onpointerdown={handlePointerdown}
303
+ onpointermove={handlePointermove}
304
+ onpointerup={handlePointerup}
305
+ onpointercancel={handlePointercancel}
306
+ onkeydown={handleKeydown}
307
+ >
308
+ <div class="np-clock-dial-selector" style="rotate: {rotation}deg">
309
+ <div class="np-clock-dial-track" style="--_r: {radiusOf(handleInner)}"></div>
310
+ <div
311
+ bind:this={handleElement}
312
+ class="np-clock-dial-handle"
313
+ style="--_r: {radiusOf(handleInner)}"
314
+ ></div>
315
+ </div>
316
+ <div class="np-clock-dial-center"></div>
317
+
318
+ {#each options as option, index (option.minutes)}
319
+ {@const angle = (angleOf(option.position, option.positions) * Math.PI) / 180}
320
+ {@const radius = radiusOf(option.inner)}
321
+ <button
322
+ type="button"
323
+ role="option"
324
+ {disabled}
325
+ data-minutes={option.minutes}
326
+ aria-selected={index === selectedIndex}
327
+ aria-disabled={option.disabled || undefined}
328
+ aria-label={option.accessibleLabel}
329
+ tabindex={tabStopIndex === index ? 0 : -1}
330
+ class={[
331
+ 'np-clock-dial-option',
332
+ option.minutes === value && 'selected',
333
+ !option.label && 'np-unlabelled',
334
+ ]}
335
+ style="--_x: calc({radius} * {Math.cos(angle).toFixed(5)}); --_y: calc({radius} * {Math.sin(
336
+ angle,
337
+ ).toFixed(5)})"
338
+ onclick={() => {
339
+ if (option.disabled) return
340
+ onselect?.(option.minutes)
341
+ announcement = formatMinutes(option.minutes, locale, hour12)
342
+ onselectionend?.('keyboard')
343
+ }}
344
+ >
345
+ <span aria-hidden="true">{option.label ?? ''}</span>
346
+ </button>
347
+ {/each}
348
+
349
+ <span class="np-clock-dial-sr-only" aria-live="polite">{announcement}</span>
350
+ </div>
351
+
352
+ <style>
353
+ .np-clock-dial {
354
+ --_size: var(--np-time-picker-clock-dial-size, 16rem);
355
+ --_handle: 3rem;
356
+ position: relative;
357
+ box-sizing: border-box;
358
+ width: var(--_size);
359
+ height: var(--_size);
360
+ flex: none;
361
+ border-radius: var(--np-time-picker-clock-dial-container-shape, var(--np-shape-corner-full));
362
+ background-color: var(
363
+ --np-time-picker-clock-dial-container-color,
364
+ var(--np-color-surface-container-highest)
365
+ );
366
+ touch-action: none;
367
+ -webkit-tap-highlight-color: transparent;
368
+ cursor: pointer;
369
+ }
370
+
371
+ .np-clock-dial:focus-visible {
372
+ outline: none;
373
+ }
374
+
375
+ .np-clock-dial.np-disabled {
376
+ cursor: default;
377
+ }
378
+
379
+ .np-clock-dial-center,
380
+ .np-clock-dial-handle,
381
+ .np-clock-dial-track {
382
+ background-color: var(--np-time-picker-clock-dial-selector-color, var(--np-color-primary));
383
+ }
384
+
385
+ .np-clock-dial-center {
386
+ position: absolute;
387
+ left: 50%;
388
+ top: 50%;
389
+ width: 0.5rem;
390
+ height: 0.5rem;
391
+ margin: -0.25rem 0 0 -0.25rem;
392
+ border-radius: var(--np-shape-corner-full);
393
+ }
394
+
395
+ /*
396
+ * A point at the centre rather than a box over the whole dial: rotating a 256dp box would push its
397
+ * corners some 47px past every edge and make the panel scroll, even though nothing is drawn there.
398
+ */
399
+ .np-clock-dial-selector {
400
+ position: absolute;
401
+ left: 50%;
402
+ top: 50%;
403
+ width: 0;
404
+ height: 0;
405
+ }
406
+
407
+ .np-clock-dial-track {
408
+ position: absolute;
409
+ left: 0;
410
+ top: 0;
411
+ height: 0.125rem;
412
+ width: var(--_r);
413
+ transform-origin: 0 50%;
414
+ translate: 0 -0.0625rem;
415
+ }
416
+
417
+ .np-clock-dial-handle {
418
+ position: absolute;
419
+ left: 0;
420
+ top: 0;
421
+ width: var(--_handle);
422
+ height: var(--_handle);
423
+ margin: calc(var(--_handle) / -2) 0 0 calc(var(--_handle) / -2);
424
+ border-radius: var(--np-shape-corner-full);
425
+ translate: var(--_r) 0;
426
+ }
427
+
428
+ .np-clock-dial-option {
429
+ font: inherit;
430
+ position: absolute;
431
+ left: 50%;
432
+ top: 50%;
433
+ display: flex;
434
+ align-items: center;
435
+ justify-content: center;
436
+ box-sizing: border-box;
437
+ width: var(--_handle);
438
+ height: var(--_handle);
439
+ margin: calc(var(--_handle) / -2) 0 0 calc(var(--_handle) / -2);
440
+ padding: 0;
441
+ border: 0;
442
+ border-radius: var(--np-shape-corner-full);
443
+ background: none;
444
+ translate: var(--_x) var(--_y);
445
+ font-size: 1rem;
446
+ line-height: 1.5rem;
447
+ font-variant-numeric: tabular-nums;
448
+ color: var(--np-time-picker-clock-dial-label-color, var(--np-color-on-surface));
449
+ /* The dial itself reads every pointer position, so the numbers only exist for the keyboard. */
450
+ pointer-events: none;
451
+ }
452
+
453
+ .np-clock-dial-option.np-unlabelled {
454
+ width: 1.5rem;
455
+ height: 1.5rem;
456
+ margin: -0.75rem 0 0 -0.75rem;
457
+ }
458
+
459
+ .np-clock-dial-option.selected {
460
+ color: var(--np-time-picker-clock-dial-selected-label-color, var(--np-color-on-primary));
461
+ }
462
+
463
+ .np-clock-dial-option[aria-disabled='true'] {
464
+ color: color-mix(in srgb, var(--np-color-on-surface) 38%, transparent);
465
+ }
466
+
467
+ .np-clock-dial-option:focus-visible {
468
+ outline: 3px solid var(--np-color-secondary);
469
+ outline-offset: 2px;
470
+ }
471
+
472
+ .np-clock-dial-sr-only {
473
+ position: absolute;
474
+ width: 1px;
475
+ height: 1px;
476
+ margin: -1px;
477
+ padding: 0;
478
+ overflow: hidden;
479
+ clip-path: inset(50%);
480
+ white-space: nowrap;
481
+ border: 0;
482
+ }
483
+
484
+ @media (prefers-reduced-motion: no-preference) {
485
+ .np-clock-dial-selector {
486
+ transition: rotate var(--np-motion-expressive-default-spatial);
487
+ }
488
+
489
+ .np-clock-dial-track {
490
+ transition: width var(--np-motion-expressive-default-spatial);
491
+ }
492
+
493
+ .np-clock-dial-handle {
494
+ transition: translate var(--np-motion-expressive-default-spatial);
495
+ }
496
+
497
+ .np-clock-dial-option {
498
+ transition: color var(--np-motion-expressive-fast-effects);
499
+ }
500
+ }
501
+
502
+ /* While a finger is down the handle has to follow it exactly, so nothing may lag behind. */
503
+ .np-clock-dial.np-tracking .np-clock-dial-selector,
504
+ .np-clock-dial.np-tracking .np-clock-dial-track,
505
+ .np-clock-dial.np-tracking .np-clock-dial-handle {
506
+ transition: none;
507
+ }
508
+
509
+ @media (forced-colors: active) {
510
+ .np-clock-dial {
511
+ border: 1px solid CanvasText;
512
+ }
513
+
514
+ .np-clock-dial-center,
515
+ .np-clock-dial-handle,
516
+ .np-clock-dial-track {
517
+ background-color: Highlight;
518
+ }
519
+
520
+ .np-clock-dial-option.selected {
521
+ color: HighlightText;
522
+ }
523
+
524
+ .np-clock-dial-option[aria-disabled='true'] {
525
+ color: GrayText;
526
+ }
527
+ }
528
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { ClockDialProps } from './types.ts';
2
+ declare const ClockDial: import("svelte").Component<ClockDialProps, {}, "element">;
3
+ type ClockDial = ReturnType<typeof ClockDial>;
4
+ export default ClockDial;