@happyvertical/smrt-ui 0.38.26 → 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 (205) hide show
  1. package/AGENTS.md +23 -7
  2. package/README.md +132 -0
  3. package/dist/components/data/CollectionList.svelte +106 -0
  4. package/dist/components/data/CollectionList.svelte.d.ts +49 -0
  5. package/dist/components/data/CollectionList.svelte.d.ts.map +1 -0
  6. package/dist/components/data/CollectionToolbar.svelte +72 -0
  7. package/dist/components/data/CollectionToolbar.svelte.d.ts +20 -0
  8. package/dist/components/data/CollectionToolbar.svelte.d.ts.map +1 -0
  9. package/dist/components/data/DataTable.svelte +107 -17
  10. package/dist/components/data/DataTable.svelte.d.ts +2 -2
  11. package/dist/components/data/DataTable.svelte.d.ts.map +1 -1
  12. package/dist/components/data/__tests__/DataTable.test.js +61 -1
  13. package/dist/components/data/index.d.ts +2 -0
  14. package/dist/components/data/index.d.ts.map +1 -1
  15. package/dist/components/data/index.js +2 -0
  16. package/dist/components/data/types.d.ts +33 -0
  17. package/dist/components/data/types.d.ts.map +1 -1
  18. package/dist/components/feedback/Alert.svelte +53 -0
  19. package/dist/components/feedback/Alert.svelte.d.ts +14 -0
  20. package/dist/components/feedback/Alert.svelte.d.ts.map +1 -0
  21. package/dist/components/feedback/Drawer.svelte +34 -0
  22. package/dist/components/feedback/Drawer.svelte.d.ts +18 -0
  23. package/dist/components/feedback/Drawer.svelte.d.ts.map +1 -0
  24. package/dist/components/feedback/Meter.svelte +33 -0
  25. package/dist/components/feedback/Meter.svelte.d.ts +16 -0
  26. package/dist/components/feedback/Meter.svelte.d.ts.map +1 -0
  27. package/dist/components/feedback/Modal.svelte +9 -5
  28. package/dist/components/feedback/Modal.svelte.d.ts +2 -2
  29. package/dist/components/feedback/Modal.svelte.d.ts.map +1 -1
  30. package/dist/components/feedback/Progress.svelte +61 -0
  31. package/dist/components/feedback/Progress.svelte.d.ts +14 -0
  32. package/dist/components/feedback/Progress.svelte.d.ts.map +1 -0
  33. package/dist/components/feedback/Spinner.svelte +17 -0
  34. package/dist/components/feedback/Spinner.svelte.d.ts +9 -0
  35. package/dist/components/feedback/Spinner.svelte.d.ts.map +1 -0
  36. package/dist/components/feedback/ToastViewport.svelte +48 -0
  37. package/dist/components/feedback/ToastViewport.svelte.d.ts +10 -0
  38. package/dist/components/feedback/ToastViewport.svelte.d.ts.map +1 -0
  39. package/dist/components/feedback/__tests__/toast.test.js +49 -0
  40. package/dist/components/feedback/index.d.ts +8 -0
  41. package/dist/components/feedback/index.d.ts.map +1 -1
  42. package/dist/components/feedback/index.js +10 -3
  43. package/dist/components/feedback/toast.d.ts +28 -0
  44. package/dist/components/feedback/toast.d.ts.map +1 -0
  45. package/dist/components/feedback/toast.js +58 -0
  46. package/dist/components/forms/Checkbox.svelte +149 -0
  47. package/dist/components/forms/Checkbox.svelte.d.ts +13 -0
  48. package/dist/components/forms/Checkbox.svelte.d.ts.map +1 -0
  49. package/dist/components/forms/Combobox.svelte +172 -0
  50. package/dist/components/forms/Combobox.svelte.d.ts +18 -0
  51. package/dist/components/forms/Combobox.svelte.d.ts.map +1 -0
  52. package/dist/components/forms/DatePicker.svelte +25 -0
  53. package/dist/components/forms/DatePicker.svelte.d.ts +12 -0
  54. package/dist/components/forms/DatePicker.svelte.d.ts.map +1 -0
  55. package/dist/components/forms/ErrorSummary.svelte +32 -0
  56. package/dist/components/forms/ErrorSummary.svelte.d.ts +11 -0
  57. package/dist/components/forms/ErrorSummary.svelte.d.ts.map +1 -0
  58. package/dist/components/forms/Fieldset.svelte +32 -0
  59. package/dist/components/forms/Fieldset.svelte.d.ts +13 -0
  60. package/dist/components/forms/Fieldset.svelte.d.ts.map +1 -0
  61. package/dist/components/forms/FilePicker.svelte +116 -0
  62. package/dist/components/forms/FilePicker.svelte.d.ts +19 -0
  63. package/dist/components/forms/FilePicker.svelte.d.ts.map +1 -0
  64. package/dist/components/forms/Form.svelte +55 -2
  65. package/dist/components/forms/Form.svelte.d.ts +11 -1
  66. package/dist/components/forms/Form.svelte.d.ts.map +1 -1
  67. package/dist/components/forms/FormGroup.svelte +19 -2
  68. package/dist/components/forms/FormGroup.svelte.d.ts +3 -0
  69. package/dist/components/forms/FormGroup.svelte.d.ts.map +1 -1
  70. package/dist/components/forms/Input.svelte +127 -0
  71. package/dist/components/forms/Input.svelte.d.ts +9 -1
  72. package/dist/components/forms/Input.svelte.d.ts.map +1 -1
  73. package/dist/components/forms/InputGroup.svelte +17 -0
  74. package/dist/components/forms/InputGroup.svelte.d.ts +11 -0
  75. package/dist/components/forms/InputGroup.svelte.d.ts.map +1 -0
  76. package/dist/components/forms/Listbox.svelte +97 -0
  77. package/dist/components/forms/Listbox.svelte.d.ts +15 -0
  78. package/dist/components/forms/Listbox.svelte.d.ts.map +1 -0
  79. package/dist/components/forms/MultiSelect.svelte +159 -0
  80. package/dist/components/forms/MultiSelect.svelte.d.ts +16 -0
  81. package/dist/components/forms/MultiSelect.svelte.d.ts.map +1 -0
  82. package/dist/components/forms/Radio.svelte +34 -0
  83. package/dist/components/forms/Radio.svelte.d.ts +10 -0
  84. package/dist/components/forms/Radio.svelte.d.ts.map +1 -0
  85. package/dist/components/forms/RadioGroup.svelte +130 -0
  86. package/dist/components/forms/RadioGroup.svelte.d.ts +19 -0
  87. package/dist/components/forms/RadioGroup.svelte.d.ts.map +1 -0
  88. package/dist/components/forms/RangeSlider.svelte +137 -0
  89. package/dist/components/forms/RangeSlider.svelte.d.ts +21 -0
  90. package/dist/components/forms/RangeSlider.svelte.d.ts.map +1 -0
  91. package/dist/components/forms/SegmentedControl.svelte +87 -0
  92. package/dist/components/forms/SegmentedControl.svelte.d.ts +17 -0
  93. package/dist/components/forms/SegmentedControl.svelte.d.ts.map +1 -0
  94. package/dist/components/forms/Select.svelte +84 -3
  95. package/dist/components/forms/Select.svelte.d.ts +6 -1
  96. package/dist/components/forms/Select.svelte.d.ts.map +1 -1
  97. package/dist/components/forms/Slider.svelte +131 -0
  98. package/dist/components/forms/Slider.svelte.d.ts +18 -0
  99. package/dist/components/forms/Slider.svelte.d.ts.map +1 -0
  100. package/dist/components/forms/Switch.svelte +128 -0
  101. package/dist/components/forms/Switch.svelte.d.ts +14 -0
  102. package/dist/components/forms/Switch.svelte.d.ts.map +1 -0
  103. package/dist/components/forms/TagsInput.svelte +106 -0
  104. package/dist/components/forms/TagsInput.svelte.d.ts +17 -0
  105. package/dist/components/forms/TagsInput.svelte.d.ts.map +1 -0
  106. package/dist/components/forms/Textarea.svelte +84 -3
  107. package/dist/components/forms/Textarea.svelte.d.ts +6 -1
  108. package/dist/components/forms/Textarea.svelte.d.ts.map +1 -1
  109. package/dist/components/forms/TimePicker.svelte +25 -0
  110. package/dist/components/forms/TimePicker.svelte.d.ts +12 -0
  111. package/dist/components/forms/TimePicker.svelte.d.ts.map +1 -0
  112. package/dist/components/forms/ToggleButton.svelte +79 -0
  113. package/dist/components/forms/ToggleButton.svelte.d.ts +13 -0
  114. package/dist/components/forms/ToggleButton.svelte.d.ts.map +1 -0
  115. package/dist/components/forms/__tests__/composite-controls.fixture.svelte +29 -0
  116. package/dist/components/forms/__tests__/composite-controls.fixture.svelte.d.ts +8 -0
  117. package/dist/components/forms/__tests__/composite-controls.fixture.svelte.d.ts.map +1 -0
  118. package/dist/components/forms/__tests__/composite-controls.test.js +33 -0
  119. package/dist/components/forms/__tests__/control-interaction.test.js +95 -0
  120. package/dist/components/forms/__tests__/core-controls.fixture.svelte +39 -0
  121. package/dist/components/forms/__tests__/core-controls.fixture.svelte.d.ts +8 -0
  122. package/dist/components/forms/__tests__/core-controls.fixture.svelte.d.ts.map +1 -0
  123. package/dist/components/forms/__tests__/core-controls.test.js +90 -0
  124. package/dist/components/forms/control-dom.d.ts +6 -0
  125. package/dist/components/forms/control-dom.d.ts.map +1 -0
  126. package/dist/components/forms/control-dom.js +38 -0
  127. package/dist/components/forms/control-interaction-context.d.ts +9 -0
  128. package/dist/components/forms/control-interaction-context.d.ts.map +1 -0
  129. package/dist/components/forms/control-interaction-context.js +15 -0
  130. package/dist/components/forms/control-interaction.d.ts +147 -0
  131. package/dist/components/forms/control-interaction.d.ts.map +1 -0
  132. package/dist/components/forms/control-interaction.js +265 -0
  133. package/dist/components/forms/form-group-context.d.ts +7 -0
  134. package/dist/components/forms/form-group-context.d.ts.map +1 -1
  135. package/dist/components/forms/index.d.ts +22 -1
  136. package/dist/components/forms/index.d.ts.map +1 -1
  137. package/dist/components/forms/index.js +21 -1
  138. package/dist/components/forms/radio-group-context.d.ts +12 -0
  139. package/dist/components/forms/radio-group-context.d.ts.map +1 -0
  140. package/dist/components/forms/radio-group-context.js +11 -0
  141. package/dist/components/forms/types.d.ts +14 -0
  142. package/dist/components/forms/types.d.ts.map +1 -0
  143. package/dist/components/forms/types.js +1 -0
  144. package/dist/components/forms/use-control-registration.svelte.d.ts +8 -0
  145. package/dist/components/forms/use-control-registration.svelte.d.ts.map +1 -0
  146. package/dist/components/forms/use-control-registration.svelte.js +15 -0
  147. package/dist/components/ui/Accordion.svelte +44 -0
  148. package/dist/components/ui/Accordion.svelte.d.ts +12 -0
  149. package/dist/components/ui/Accordion.svelte.d.ts.map +1 -0
  150. package/dist/components/ui/AccordionItem.svelte +42 -0
  151. package/dist/components/ui/AccordionItem.svelte.d.ts +11 -0
  152. package/dist/components/ui/AccordionItem.svelte.d.ts.map +1 -0
  153. package/dist/components/ui/Disclosure.svelte +50 -0
  154. package/dist/components/ui/Disclosure.svelte.d.ts +13 -0
  155. package/dist/components/ui/Disclosure.svelte.d.ts.map +1 -0
  156. package/dist/components/ui/Dropdown.svelte +5 -7
  157. package/dist/components/ui/Dropdown.svelte.d.ts +9 -0
  158. package/dist/components/ui/Dropdown.svelte.d.ts.map +1 -1
  159. package/dist/components/ui/Popover.svelte +102 -0
  160. package/dist/components/ui/Popover.svelte.d.ts +16 -0
  161. package/dist/components/ui/Popover.svelte.d.ts.map +1 -0
  162. package/dist/components/ui/__tests__/interaction-primitives.fixture.svelte +13 -0
  163. package/dist/components/ui/__tests__/interaction-primitives.fixture.svelte.d.ts +19 -0
  164. package/dist/components/ui/__tests__/interaction-primitives.fixture.svelte.d.ts.map +1 -0
  165. package/dist/components/ui/__tests__/interaction-primitives.test.js +31 -0
  166. package/dist/components/ui/accordion-context.d.ts +7 -0
  167. package/dist/components/ui/accordion-context.d.ts.map +1 -0
  168. package/dist/components/ui/accordion-context.js +11 -0
  169. package/dist/components/ui/index.d.ts +5 -1
  170. package/dist/components/ui/index.d.ts.map +1 -1
  171. package/dist/components/ui/index.js +5 -1
  172. package/dist/index.d.ts +1 -1
  173. package/dist/index.d.ts.map +1 -1
  174. package/dist/playground.d.ts +2 -0
  175. package/dist/playground.d.ts.map +1 -0
  176. package/dist/playground.js +1 -0
  177. package/dist/svelte/__tests__/playground.test.js +18 -0
  178. package/dist/svelte/playground/BaseControlsPreview.svelte +233 -0
  179. package/dist/svelte/playground/BaseControlsPreview.svelte.d.ts +4 -0
  180. package/dist/svelte/playground/BaseControlsPreview.svelte.d.ts.map +1 -0
  181. package/dist/svelte/playground/CollectionsPreview.svelte +59 -0
  182. package/dist/svelte/playground/CollectionsPreview.svelte.d.ts +4 -0
  183. package/dist/svelte/playground/CollectionsPreview.svelte.d.ts.map +1 -0
  184. package/dist/svelte/playground/DataTablePreview.svelte +247 -0
  185. package/dist/svelte/playground/DataTablePreview.svelte.d.ts +4 -0
  186. package/dist/svelte/playground/DataTablePreview.svelte.d.ts.map +1 -0
  187. package/dist/svelte/playground/FeedbackPreview.svelte +51 -0
  188. package/dist/svelte/playground/FeedbackPreview.svelte.d.ts +4 -0
  189. package/dist/svelte/playground/FeedbackPreview.svelte.d.ts.map +1 -0
  190. package/dist/svelte/playground/InteractiveControlsPreview.svelte +109 -0
  191. package/dist/svelte/playground/InteractiveControlsPreview.svelte.d.ts +4 -0
  192. package/dist/svelte/playground/InteractiveControlsPreview.svelte.d.ts.map +1 -0
  193. package/dist/svelte/playground.d.ts +20 -0
  194. package/dist/svelte/playground.d.ts.map +1 -0
  195. package/dist/svelte/playground.js +65 -0
  196. package/dist/theme/index.d.ts +6 -4
  197. package/dist/theme/index.d.ts.map +1 -1
  198. package/dist/theme/index.js +6 -7
  199. package/dist/themes/context.svelte.js +1 -1
  200. package/dist/themes/index.d.ts +1 -1
  201. package/dist/themes/index.js +1 -1
  202. package/dist/themes/smrt/index.js +1 -1
  203. package/dist/types-generic.d.ts +7 -1
  204. package/dist/types-generic.d.ts.map +1 -1
  205. package/package.json +23 -12
@@ -0,0 +1,131 @@
1
+ <script lang="ts">
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+ import {
4
+ emitControlChange,
5
+ highlightControl,
6
+ revealControl,
7
+ } from './control-dom.js';
8
+ import type { ControlInteractionOptions } from './control-interaction.js';
9
+ import { tryGetControlInteractionContext } from './control-interaction-context.js';
10
+ import { tryGetFormGroupContext } from './form-group-context.js';
11
+ import { useControlRegistration } from './use-control-registration.svelte.js';
12
+ export interface Props
13
+ extends Omit<
14
+ HTMLInputAttributes,
15
+ 'type' | 'class' | 'value' | 'min' | 'max' | 'step'
16
+ > {
17
+ value?: number;
18
+ min?: number;
19
+ max?: number;
20
+ step?: number;
21
+ label?: string;
22
+ unit?: string;
23
+ showInput?: boolean;
24
+ formatValue?: (value: number) => string;
25
+ class?: string;
26
+ interaction?: ControlInteractionOptions | false;
27
+ }
28
+ let {
29
+ value = $bindable(0),
30
+ min = 0,
31
+ max = 100,
32
+ step = 1,
33
+ label,
34
+ unit,
35
+ showInput = true,
36
+ formatValue = (next) => `${next}${unit ?? ''}`,
37
+ id,
38
+ name,
39
+ disabled = false,
40
+ class: className = '',
41
+ interaction,
42
+ onchange,
43
+ ...rest
44
+ }: Props = $props();
45
+ const instanceId = $props.id();
46
+ const formGroup = tryGetFormGroupContext();
47
+ const interactionContext = tryGetControlInteractionContext();
48
+ let rangeEl = $state<HTMLInputElement | null>(null);
49
+ const resolvedId = $derived(
50
+ id ?? formGroup?.().inputId ?? `smrt-slider-${instanceId}`,
51
+ );
52
+ const resolvedInteraction = $derived.by(() => {
53
+ const inherited = formGroup?.().interaction;
54
+ if (interaction === false || inherited === false) return false;
55
+ return { ...(inherited ?? {}), ...(interaction ?? {}) };
56
+ });
57
+ const controlId = $derived(
58
+ resolvedInteraction === false
59
+ ? undefined
60
+ : (resolvedInteraction.id ?? name ?? resolvedId),
61
+ );
62
+ const percent = $derived(max === min ? 0 : ((value - min) / (max - min)) * 100);
63
+ function clamp(next: number) {
64
+ return Math.min(max, Math.max(min, Math.round(next / step) * step));
65
+ }
66
+ function setValue(next: unknown) {
67
+ const parsed = Number(next);
68
+ if (!Number.isFinite(parsed)) return;
69
+ value = clamp(parsed);
70
+ if (rangeEl) emitControlChange(rangeEl);
71
+ }
72
+ function handleInput(event: Event & { currentTarget: HTMLInputElement }) {
73
+ value = Number(event.currentTarget.value);
74
+ }
75
+ function handleChange(event: Event & { currentTarget: HTMLInputElement }) {
76
+ value = Number(event.currentTarget.value);
77
+ onchange?.(event);
78
+ }
79
+ useControlRegistration(() => {
80
+ const element = rangeEl;
81
+ const options = resolvedInteraction;
82
+ if (!element || options === false) return false;
83
+ return {
84
+ controlId,
85
+ subject: options.subject,
86
+ metadata: {
87
+ kind: 'slider',
88
+ label: formGroup?.().label ?? label,
89
+ description: options.description ?? formGroup?.().description,
90
+ sensitivity: options.sensitivity ?? 'public',
91
+ readable: options.readable,
92
+ writable: options.writable,
93
+ constraints: { min, max, step },
94
+ unit,
95
+ },
96
+ getValue: () => value,
97
+ setValue,
98
+ clear: () => setValue(min),
99
+ focus: () => element.focus(),
100
+ reveal: () => revealControl(element),
101
+ highlight: (durationMs) =>
102
+ highlightControl(element.closest('.slider') ?? element, durationMs),
103
+ validate: () => element.reportValidity(),
104
+ getState: () => ({
105
+ disabled: element.disabled,
106
+ valid: element.validity.valid,
107
+ validationMessage: element.validationMessage || undefined,
108
+ }),
109
+ };
110
+ });
111
+ </script>
112
+ <div class="slider {className}" data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}>
113
+ {#if label}<div class="slider__header"><label for={resolvedId}>{label}</label><output for={resolvedId}>{formatValue(value)}</output></div>{/if}
114
+ <div class="slider__controls">
115
+ <input bind:this={rangeEl} id={resolvedId} type="range" {name} {min} {max} {step} {disabled} value={value}
116
+ style={`--smrt-slider-percent: ${percent}%`} aria-valuetext={formatValue(value)} aria-describedby={formGroup?.().describedBy}
117
+ oninput={handleInput} onchange={handleChange} {...rest} />
118
+ {#if showInput}<input class="slider__number" type="number" {min} {max} {step} {disabled} value={value}
119
+ aria-label={label ? `${label} value` : 'Slider value'} onchange={(event) => setValue(event.currentTarget.value)} />{/if}
120
+ </div>
121
+ </div>
122
+ <style>
123
+ .slider { display: grid; gap: var(--smrt-spacing-2); color: var(--smrt-color-on-surface); }
124
+ .slider__header, .slider__controls { display: flex; align-items: center; gap: var(--smrt-spacing-3); }
125
+ .slider__header { justify-content: space-between; font: var(--smrt-typography-label-large-font); }
126
+ output { color: var(--smrt-color-primary); font-variant-numeric: tabular-nums; }
127
+ input[type='range'] { flex: 1; min-width: 8rem; accent-color: var(--smrt-color-primary); cursor: pointer; }
128
+ .slider__number { width: 5.5rem; padding: var(--smrt-spacing-2); border: 1px solid var(--smrt-color-outline-variant); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface); color: var(--smrt-color-on-surface); }
129
+ input:focus-visible { outline: 2px solid var(--smrt-color-primary); outline-offset: 3px; }
130
+ .slider[data-smrt-highlighted='true'] { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; border-radius: var(--smrt-radius-small); }
131
+ </style>
@@ -0,0 +1,18 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ import type { ControlInteractionOptions } from './control-interaction.js';
3
+ export interface Props extends Omit<HTMLInputAttributes, 'type' | 'class' | 'value' | 'min' | 'max' | 'step'> {
4
+ value?: number;
5
+ min?: number;
6
+ max?: number;
7
+ step?: number;
8
+ label?: string;
9
+ unit?: string;
10
+ showInput?: boolean;
11
+ formatValue?: (value: number) => string;
12
+ class?: string;
13
+ interaction?: ControlInteractionOptions | false;
14
+ }
15
+ declare const Slider: import("svelte").Component<Props, {}, "value">;
16
+ type Slider = ReturnType<typeof Slider>;
17
+ export default Slider;
18
+ //# sourceMappingURL=Slider.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Slider.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Slider.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAM3D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAK1E,MAAM,WAAW,KACf,SAAQ,IAAI,CACV,mBAAmB,EACnB,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CACpD;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;CACjD;AAwGD,QAAA,MAAM,MAAM,gDAAwC,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
@@ -0,0 +1,128 @@
1
+ <script lang="ts">
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+ import {
4
+ emitControlChange,
5
+ highlightControl,
6
+ revealControl,
7
+ } from './control-dom.js';
8
+ import type { ControlInteractionOptions } from './control-interaction.js';
9
+ import { tryGetControlInteractionContext } from './control-interaction-context.js';
10
+ import { tryGetFormGroupContext } from './form-group-context.js';
11
+ import { useControlRegistration } from './use-control-registration.svelte.js';
12
+
13
+ export interface Props
14
+ extends Omit<
15
+ HTMLInputAttributes,
16
+ 'type' | 'class' | 'checked' | 'role' | 'size'
17
+ > {
18
+ checked?: boolean;
19
+ label?: string;
20
+ labelPosition?: 'left' | 'right';
21
+ size?: 'sm' | 'md' | 'lg';
22
+ class?: string;
23
+ interaction?: ControlInteractionOptions | false;
24
+ }
25
+
26
+ let {
27
+ checked = $bindable(false),
28
+ label,
29
+ labelPosition = 'right',
30
+ size = 'md',
31
+ id,
32
+ name,
33
+ value,
34
+ disabled = false,
35
+ required = false,
36
+ class: className = '',
37
+ interaction,
38
+ onchange,
39
+ 'aria-label': ariaLabel,
40
+ ...rest
41
+ }: Props = $props();
42
+ const instanceId = $props.id();
43
+ const formGroup = tryGetFormGroupContext();
44
+ const interactionContext = tryGetControlInteractionContext();
45
+ let inputEl = $state<HTMLInputElement | null>(null);
46
+ const resolvedId = $derived(
47
+ id ?? formGroup?.().inputId ?? `smrt-switch-${instanceId}`,
48
+ );
49
+ const resolvedInteraction = $derived.by(() => {
50
+ const inherited = formGroup?.().interaction;
51
+ if (interaction === false || inherited === false) return false;
52
+ return { ...(inherited ?? {}), ...(interaction ?? {}) };
53
+ });
54
+ const controlId = $derived(
55
+ resolvedInteraction === false
56
+ ? undefined
57
+ : (resolvedInteraction.id ?? name ?? resolvedId),
58
+ );
59
+ function setChecked(next: unknown) {
60
+ checked =
61
+ typeof next === 'string'
62
+ ? ['true', '1', 'yes', 'on'].includes(next.toLowerCase())
63
+ : Boolean(next);
64
+ if (inputEl) emitControlChange(inputEl);
65
+ }
66
+ function handleChange(event: Event & { currentTarget: HTMLInputElement }) {
67
+ checked = event.currentTarget.checked;
68
+ onchange?.(event);
69
+ }
70
+ useControlRegistration(() => {
71
+ const element = inputEl;
72
+ const options = resolvedInteraction;
73
+ if (!element || options === false) return false;
74
+ return {
75
+ controlId,
76
+ subject: options.subject,
77
+ metadata: {
78
+ kind: 'switch',
79
+ label: formGroup?.().label ?? label ?? ariaLabel ?? undefined,
80
+ description: options.description ?? formGroup?.().description,
81
+ sensitivity: options.sensitivity ?? 'public',
82
+ readable: options.readable,
83
+ writable: options.writable,
84
+ constraints: { required: required === true },
85
+ },
86
+ getValue: () => checked,
87
+ setValue: setChecked,
88
+ clear: () => setChecked(false),
89
+ focus: () => element.focus(),
90
+ reveal: () => revealControl(element),
91
+ highlight: (durationMs) =>
92
+ highlightControl(element.closest('label') ?? element, durationMs),
93
+ validate: () => element.reportValidity(),
94
+ getState: () => ({
95
+ disabled: element.disabled,
96
+ valid: element.validity.valid,
97
+ validationMessage: element.validationMessage || undefined,
98
+ }),
99
+ };
100
+ });
101
+ </script>
102
+
103
+ <label class="switch switch--{size} {className}" class:switch--disabled={disabled} data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}>
104
+ {#if label && labelPosition === 'left'}<span>{label}</span>{/if}
105
+ <span class="switch__control">
106
+ <input bind:this={inputEl} id={resolvedId} type="checkbox" role="switch" {name} {value} {disabled} {required} checked={checked}
107
+ aria-label={ariaLabel ?? label ?? formGroup?.().label} aria-describedby={formGroup?.().describedBy}
108
+ aria-invalid={formGroup?.().invalid ? 'true' : undefined} onchange={handleChange} {...rest} />
109
+ <span class="switch__track" aria-hidden="true"><span class="switch__thumb"></span></span>
110
+ </span>
111
+ {#if label && labelPosition === 'right'}<span>{label}</span>{/if}
112
+ </label>
113
+
114
+ <style>
115
+ .switch { display: inline-flex; align-items: center; gap: var(--smrt-spacing-2, .5rem); cursor: pointer; color: var(--smrt-color-on-surface); }
116
+ .switch--disabled { opacity: .5; cursor: not-allowed; }
117
+ .switch__control { position: relative; display: inline-flex; }
118
+ input { position: absolute; width: 1px; height: 1px; opacity: 0; }
119
+ .switch__track { width: 2.75rem; height: 1.5rem; padding: 2px; border-radius: var(--smrt-radius-full); background: var(--smrt-color-surface-container-highest); border: 1px solid var(--smrt-color-outline); transition: background var(--smrt-duration-short2); }
120
+ .switch__thumb { display: block; width: 1.125rem; height: 1.125rem; border-radius: 50%; background: var(--smrt-color-outline); transition: transform var(--smrt-duration-short2), background var(--smrt-duration-short2); }
121
+ input:checked + .switch__track { background: var(--smrt-color-primary); border-color: var(--smrt-color-primary); }
122
+ input:checked + .switch__track .switch__thumb { transform: translateX(1.25rem); background: var(--smrt-color-on-primary); }
123
+ input:focus-visible + .switch__track { outline: 2px solid var(--smrt-color-primary); outline-offset: 3px; }
124
+ .switch--sm { font-size: .8rem; transform-origin: left center; }
125
+ .switch--lg .switch__track { transform: scale(1.15); margin-inline: .2rem; }
126
+ .switch[data-smrt-highlighted='true'] { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; border-radius: var(--smrt-radius-full); }
127
+ @media (prefers-reduced-motion: reduce) { .switch__track, .switch__thumb { transition: none; } }
128
+ </style>
@@ -0,0 +1,14 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ import type { ControlInteractionOptions } from './control-interaction.js';
3
+ export interface Props extends Omit<HTMLInputAttributes, 'type' | 'class' | 'checked' | 'role' | 'size'> {
4
+ checked?: boolean;
5
+ label?: string;
6
+ labelPosition?: 'left' | 'right';
7
+ size?: 'sm' | 'md' | 'lg';
8
+ class?: string;
9
+ interaction?: ControlInteractionOptions | false;
10
+ }
11
+ declare const Switch: import("svelte").Component<Props, {}, "checked">;
12
+ type Switch = ReturnType<typeof Switch>;
13
+ export default Switch;
14
+ //# sourceMappingURL=Switch.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Switch.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Switch.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAM3D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAM1E,MAAM,WAAW,KACf,SAAQ,IAAI,CACV,mBAAmB,EACnB,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAC/C;IACD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;CACjD;AAoGD,QAAA,MAAM,MAAM,kDAAwC,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
@@ -0,0 +1,106 @@
1
+ <script lang="ts">
2
+ import { highlightControl, revealControl } from './control-dom.js';
3
+ import type { ControlInteractionOptions } from './control-interaction.js';
4
+ import { tryGetControlInteractionContext } from './control-interaction-context.js';
5
+ import { useControlRegistration } from './use-control-registration.svelte.js';
6
+ export interface Props {
7
+ values?: string[];
8
+ label: string;
9
+ name?: string;
10
+ placeholder?: string;
11
+ disabled?: boolean;
12
+ maxTags?: number;
13
+ allowDuplicates?: boolean;
14
+ interaction?: ControlInteractionOptions | false;
15
+ onvalueschange?: (values: string[]) => void;
16
+ class?: string;
17
+ }
18
+ let {
19
+ values = $bindable([]),
20
+ label,
21
+ name,
22
+ placeholder = 'Add a tag',
23
+ disabled = false,
24
+ maxTags,
25
+ allowDuplicates = false,
26
+ interaction,
27
+ onvalueschange,
28
+ class: className = '',
29
+ }: Props = $props();
30
+ const instanceId = $props.id();
31
+ const inputId = `smrt-tags-${instanceId}`;
32
+ const interactionContext = tryGetControlInteractionContext();
33
+ let rootEl = $state<HTMLDivElement | null>(null);
34
+ let inputEl = $state<HTMLInputElement | null>(null);
35
+ let draft = $state('');
36
+ const controlId = $derived(
37
+ interaction === false ? undefined : (interaction?.id ?? name ?? inputId),
38
+ );
39
+ function commit(raw: string) {
40
+ const tag = raw.trim();
41
+ if (
42
+ !tag ||
43
+ disabled ||
44
+ (maxTags !== undefined && values.length >= maxTags) ||
45
+ (!allowDuplicates && values.includes(tag))
46
+ )
47
+ return;
48
+ values = [...values, tag];
49
+ draft = '';
50
+ onvalueschange?.(values);
51
+ }
52
+ function remove(index: number) {
53
+ values = values.filter((_, candidate) => candidate !== index);
54
+ onvalueschange?.(values);
55
+ }
56
+ function setValues(next: unknown) {
57
+ if (!Array.isArray(next)) return;
58
+ values = next
59
+ .map(String)
60
+ .filter((tag, index, all) => allowDuplicates || all.indexOf(tag) === index)
61
+ .slice(0, maxTags);
62
+ onvalueschange?.(values);
63
+ }
64
+ function handleKeydown(event: KeyboardEvent) {
65
+ if (event.key === 'Enter' || event.key === ',') {
66
+ event.preventDefault();
67
+ commit(draft);
68
+ } else if (event.key === 'Backspace' && !draft && values.length)
69
+ remove(values.length - 1);
70
+ }
71
+ useControlRegistration(() => {
72
+ const root = rootEl;
73
+ const input = inputEl;
74
+ if (!root || !input || interaction === false) return false;
75
+ return {
76
+ controlId,
77
+ subject: interaction?.subject,
78
+ metadata: {
79
+ kind: 'tags-input',
80
+ label,
81
+ description: interaction?.description,
82
+ sensitivity: interaction?.sensitivity ?? 'public',
83
+ readable: interaction?.readable,
84
+ writable: interaction?.writable,
85
+ },
86
+ getValue: () => [...values],
87
+ setValue: setValues,
88
+ clear: () => setValues([]),
89
+ focus: () => input.focus(),
90
+ reveal: () => revealControl(root),
91
+ highlight: (durationMs) => highlightControl(root, durationMs),
92
+ getState: () => ({ disabled }),
93
+ };
94
+ });
95
+ </script>
96
+ <div class="tags-field {className}"><label for={inputId}>{label}</label><div bind:this={rootEl} class="tags" data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}>
97
+ {#each values as tag, index}<span class="tag">{tag}<button type="button" disabled={disabled} aria-label={`Remove ${tag}`} onclick={() => remove(index)}>×</button><input type="hidden" name={name ? `${name}[]` : undefined} value={tag} /></span>{/each}
98
+ <input bind:this={inputEl} id={inputId} {placeholder} {disabled} bind:value={draft} onkeydown={handleKeydown} onblur={() => commit(draft)} />
99
+ </div></div>
100
+ <style>
101
+ .tags-field { display: grid; gap: var(--smrt-spacing-1); color: var(--smrt-color-on-surface); } label { font: var(--smrt-typography-label-large-font); }
102
+ .tags { display: flex; flex-wrap: wrap; gap: var(--smrt-spacing-1); min-height: 2.5rem; padding: var(--smrt-spacing-1); border: 1px solid var(--smrt-color-outline-variant); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface); }
103
+ .tag { display: inline-flex; align-items: center; gap: var(--smrt-spacing-1); padding: var(--smrt-spacing-1) var(--smrt-spacing-2); border-radius: var(--smrt-radius-full); background: var(--smrt-color-secondary-container); color: var(--smrt-color-on-secondary-container); }
104
+ .tag button { border: 0; background: transparent; color: inherit; cursor: pointer; } .tags > input { flex: 1; min-width: 8rem; border: 0; outline: 0; background: transparent; color: inherit; }
105
+ :global(.tags[data-smrt-highlighted='true']) { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; }
106
+ </style>
@@ -0,0 +1,17 @@
1
+ import type { ControlInteractionOptions } from './control-interaction.js';
2
+ export interface Props {
3
+ values?: string[];
4
+ label: string;
5
+ name?: string;
6
+ placeholder?: string;
7
+ disabled?: boolean;
8
+ maxTags?: number;
9
+ allowDuplicates?: boolean;
10
+ interaction?: ControlInteractionOptions | false;
11
+ onvalueschange?: (values: string[]) => void;
12
+ class?: string;
13
+ }
14
+ declare const TagsInput: import("svelte").Component<Props, {}, "values">;
15
+ type TagsInput = ReturnType<typeof TagsInput>;
16
+ export default TagsInput;
17
+ //# sourceMappingURL=TagsInput.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TagsInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/TagsInput.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAI1E,MAAM,WAAW,KAAK;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA6FD,QAAA,MAAM,SAAS,iDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -1,11 +1,19 @@
1
1
  <script lang="ts">
2
2
  import type { HTMLTextareaAttributes } from 'svelte/elements';
3
+ import {
4
+ emitControlChange,
5
+ highlightControl,
6
+ revealControl,
7
+ } from './control-dom.js';
8
+ import type { ControlInteractionOptions } from './control-interaction.js';
9
+ import { tryGetControlInteractionContext } from './control-interaction-context.js';
3
10
  import { tryGetFormGroupContext } from './form-group-context.js';
4
11
 
5
12
  export interface Props extends Omit<HTMLTextareaAttributes, 'class' | 'value'> {
6
13
  value?: string;
7
14
  rows?: number;
8
15
  class?: string;
16
+ interaction?: ControlInteractionOptions | false;
9
17
  }
10
18
 
11
19
  let {
@@ -18,6 +26,8 @@ let {
18
26
  name,
19
27
  rows = 4,
20
28
  class: className = '',
29
+ interaction,
30
+ 'aria-label': ariaLabel,
21
31
  'aria-describedby': ariaDescribedby,
22
32
  'aria-invalid': ariaInvalid,
23
33
  ...rest
@@ -25,6 +35,8 @@ let {
25
35
 
26
36
  // Inherit id / hint+error association / error state from a wrapping FormGroup.
27
37
  const formGroup = tryGetFormGroupContext();
38
+ const controlInteraction = tryGetControlInteractionContext();
39
+ let textareaEl = $state<HTMLTextAreaElement | null>(null);
28
40
  const resolvedId = $derived(id ?? formGroup?.().inputId);
29
41
  const resolvedDescribedBy = $derived(
30
42
  ariaDescribedby ?? formGroup?.().describedBy,
@@ -32,20 +44,84 @@ const resolvedDescribedBy = $derived(
32
44
  const resolvedInvalid = $derived(
33
45
  ariaInvalid ?? (formGroup?.().invalid ? 'true' : undefined),
34
46
  );
47
+ const resolvedInteraction = $derived.by(() => {
48
+ const inherited = formGroup?.().interaction;
49
+ if (interaction === false || inherited === false) return false;
50
+ return { ...(inherited ?? {}), ...(interaction ?? {}) };
51
+ });
52
+ const resolvedControlId = $derived(
53
+ resolvedInteraction === false
54
+ ? undefined
55
+ : (resolvedInteraction.id ?? name ?? resolvedId),
56
+ );
57
+
58
+ function setControlValue(next: unknown) {
59
+ value = String(next ?? '');
60
+ if (textareaEl) emitControlChange(textareaEl);
61
+ }
62
+
63
+ $effect(() => {
64
+ const context = controlInteraction;
65
+ const element = textareaEl;
66
+ const controlId = resolvedControlId;
67
+ const options = resolvedInteraction;
68
+ if (!context || !element || !controlId || options === false) return;
69
+ return context.registry.register({
70
+ identity: { formId: context.formId, controlId, subject: options.subject },
71
+ metadata: {
72
+ kind: 'textarea',
73
+ label: formGroup?.().label ?? ariaLabel ?? undefined,
74
+ description: options.description ?? formGroup?.().description,
75
+ sensitivity: options.sensitivity ?? 'public',
76
+ readable: options.readable,
77
+ writable: options.writable,
78
+ constraints: {
79
+ required: required === true,
80
+ minLength: element.minLength >= 0 ? element.minLength : undefined,
81
+ maxLength: element.maxLength >= 0 ? element.maxLength : undefined,
82
+ },
83
+ },
84
+ getValue: () => value,
85
+ setValue: setControlValue,
86
+ clear: () => setControlValue(''),
87
+ focus: () => element.focus(),
88
+ reveal: () => revealControl(element),
89
+ highlight: (durationMs) => highlightControl(element, durationMs),
90
+ validate: () => element.reportValidity(),
91
+ getState: () => ({
92
+ disabled: element.disabled,
93
+ readonly: element.readOnly,
94
+ valid: element.validity.valid,
95
+ validationMessage: element.validationMessage || undefined,
96
+ }),
97
+ });
98
+ });
99
+
100
+ export function focus(): void {
101
+ textareaEl?.focus();
102
+ }
103
+
104
+ export function getElement(): HTMLTextAreaElement | null {
105
+ return textareaEl;
106
+ }
35
107
  </script>
36
108
 
37
- <textarea
109
+ <textarea
110
+ bind:this={textareaEl}
38
111
  id={resolvedId}
39
112
  bind:value
40
113
  {placeholder}
41
114
  {disabled}
42
115
  {readonly}
43
116
  {required}
44
- {name}
117
+ {name}
118
+ aria-label={ariaLabel}
45
119
  {rows}
46
120
  aria-describedby={resolvedDescribedBy}
47
121
  aria-invalid={resolvedInvalid}
48
- class="textarea {className}"
122
+ class="textarea {className}"
123
+ data-smrt-control={resolvedControlId}
124
+ data-smrt-form={controlInteraction?.formId}
49
125
  {...rest}
50
126
  ></textarea>
51
127
 
@@ -73,6 +149,11 @@ const resolvedInvalid = $derived(
73
149
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--smrt-color-primary, #005ac1) 10%, transparent);
74
150
  }
75
151
 
152
+ .textarea[data-smrt-highlighted='true'] {
153
+ outline: 3px solid var(--smrt-color-tertiary, #7d5260);
154
+ outline-offset: 3px;
155
+ }
156
+
76
157
  .textarea:disabled {
77
158
  background-color: var(--smrt-color-surface-container-high, #f3f4f6);
78
159
  cursor: not-allowed;
@@ -1,10 +1,15 @@
1
1
  import type { HTMLTextareaAttributes } from 'svelte/elements';
2
+ import type { ControlInteractionOptions } from './control-interaction.js';
2
3
  export interface Props extends Omit<HTMLTextareaAttributes, 'class' | 'value'> {
3
4
  value?: string;
4
5
  rows?: number;
5
6
  class?: string;
7
+ interaction?: ControlInteractionOptions | false;
6
8
  }
7
- declare const Textarea: import("svelte").Component<Props, {}, "value">;
9
+ declare const Textarea: import("svelte").Component<Props, {
10
+ focus: () => void;
11
+ getElement: () => HTMLTextAreaElement | null;
12
+ }, "value">;
8
13
  type Textarea = ReturnType<typeof Textarea>;
9
14
  export default Textarea;
10
15
  //# sourceMappingURL=Textarea.svelte.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Textarea.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Textarea.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAI9D,MAAM,WAAW,KAAM,SAAQ,IAAI,CAAC,sBAAsB,EAAE,OAAO,GAAG,OAAO,CAAC;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAsCD,QAAA,MAAM,QAAQ,gDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"Textarea.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Textarea.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAM9D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAK1E,MAAM,WAAW,KAAM,SAAQ,IAAI,CAAC,sBAAsB,EAAE,OAAO,GAAG,OAAO,CAAC;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;CACjD;AAyGD,QAAA,MAAM,QAAQ;iBAfK,IAAI;sBAIC,mBAAmB,GAAG,IAAI;WAWI,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,25 @@
1
+ <script lang="ts">
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+ import type { ControlInteractionOptions } from './control-interaction.js';
4
+ import Input from './Input.svelte';
5
+ export interface Props
6
+ extends Omit<HTMLInputAttributes, 'type' | 'value' | 'class'> {
7
+ value?: string;
8
+ label: string;
9
+ class?: string;
10
+ interaction?: ControlInteractionOptions | false;
11
+ }
12
+ let {
13
+ value = $bindable(''),
14
+ label,
15
+ id,
16
+ name,
17
+ class: className = '',
18
+ interaction,
19
+ ...rest
20
+ }: Props = $props();
21
+ const instanceId = $props.id();
22
+ const resolvedId = $derived(id ?? `smrt-time-${instanceId}`);
23
+ </script>
24
+ <label class="picker {className}" for={resolvedId}><span>{label}</span><Input id={resolvedId} {name} type="time" aria-label={label} {interaction} bind:value {...rest} /></label>
25
+ <style>.picker { display: grid; gap: var(--smrt-spacing-1); color: var(--smrt-color-on-surface); font: var(--smrt-typography-label-large-font); }</style>
@@ -0,0 +1,12 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ import type { ControlInteractionOptions } from './control-interaction.js';
3
+ export interface Props extends Omit<HTMLInputAttributes, 'type' | 'value' | 'class'> {
4
+ value?: string;
5
+ label: string;
6
+ class?: string;
7
+ interaction?: ControlInteractionOptions | false;
8
+ }
9
+ declare const TimePicker: import("svelte").Component<Props, {}, "value">;
10
+ type TimePicker = ReturnType<typeof TimePicker>;
11
+ export default TimePicker;
12
+ //# sourceMappingURL=TimePicker.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimePicker.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/TimePicker.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAG1E,MAAM,WAAW,KACf,SAAQ,IAAI,CAAC,mBAAmB,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;CACjD;AAuBD,QAAA,MAAM,UAAU,gDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}