@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,172 @@
1
+ <script lang="ts">
2
+ import { highlightControl, revealControl } from './control-dom.js';
3
+ import type {
4
+ ControlInteractionOptions,
5
+ ControlOption,
6
+ } from './control-interaction.js';
7
+ import { tryGetControlInteractionContext } from './control-interaction-context.js';
8
+ import { useControlRegistration } from './use-control-registration.svelte.js';
9
+ export interface Props {
10
+ options: ControlOption[];
11
+ value?: string;
12
+ label: string;
13
+ name?: string;
14
+ placeholder?: string;
15
+ disabled?: boolean;
16
+ required?: boolean;
17
+ allowCustom?: boolean;
18
+ interaction?: ControlInteractionOptions | false;
19
+ onvaluechange?: (value: string) => void;
20
+ class?: string;
21
+ }
22
+ let {
23
+ options,
24
+ value = $bindable(''),
25
+ label,
26
+ name,
27
+ placeholder,
28
+ disabled = false,
29
+ required = false,
30
+ allowCustom = false,
31
+ interaction,
32
+ onvaluechange,
33
+ class: className = '',
34
+ }: Props = $props();
35
+ const instanceId = $props.id();
36
+ const inputId = `smrt-combobox-${instanceId}`;
37
+ const listId = `${inputId}-list`;
38
+ const interactionContext = tryGetControlInteractionContext();
39
+ let rootEl = $state<HTMLDivElement | null>(null);
40
+ let inputEl = $state<HTMLInputElement | null>(null);
41
+ let open = $state(false);
42
+ let query = $state(value);
43
+ let activeIndex = $state(0);
44
+ const filtered = $derived(
45
+ options.filter((option) =>
46
+ option.label.toLowerCase().includes(query.toLowerCase()),
47
+ ),
48
+ );
49
+ const controlId = $derived(
50
+ interaction === false ? undefined : (interaction?.id ?? name ?? inputId),
51
+ );
52
+ function commit(option: ControlOption) {
53
+ if (option.disabled || disabled) return;
54
+ value = String(option.value);
55
+ query = option.label;
56
+ open = false;
57
+ onvaluechange?.(value);
58
+ }
59
+ function setValue(next: unknown) {
60
+ const candidate = String(next ?? '');
61
+ const option = options.find(
62
+ (item) => String(item.value) === candidate || item.label === candidate,
63
+ );
64
+ if (option) commit(option);
65
+ else if (allowCustom) {
66
+ value = candidate;
67
+ query = candidate;
68
+ onvaluechange?.(candidate);
69
+ }
70
+ }
71
+ function handleInput(event: Event & { currentTarget: HTMLInputElement }) {
72
+ query = event.currentTarget.value;
73
+ open = true;
74
+ activeIndex = 0;
75
+ if (allowCustom) {
76
+ value = query;
77
+ onvaluechange?.(value);
78
+ }
79
+ }
80
+ function handleKeydown(event: KeyboardEvent) {
81
+ if (event.key === 'ArrowDown') {
82
+ event.preventDefault();
83
+ open = true;
84
+ const enabled = filtered
85
+ .map((option, index) => (option.disabled ? -1 : index))
86
+ .filter((index) => index >= 0);
87
+ const position = enabled.indexOf(activeIndex);
88
+ activeIndex = enabled[(position + 1) % enabled.length] ?? 0;
89
+ } else if (event.key === 'ArrowUp') {
90
+ event.preventDefault();
91
+ const enabled = filtered
92
+ .map((option, index) => (option.disabled ? -1 : index))
93
+ .filter((index) => index >= 0);
94
+ const position = enabled.indexOf(activeIndex);
95
+ activeIndex =
96
+ enabled[(position - 1 + enabled.length) % enabled.length] ?? 0;
97
+ } else if (event.key === 'Enter' && open && filtered[activeIndex]) {
98
+ event.preventDefault();
99
+ commit(filtered[activeIndex]);
100
+ } else if (event.key === 'Escape') {
101
+ open = false;
102
+ }
103
+ }
104
+ $effect(() => {
105
+ if (!open) return;
106
+ const dismissPointer = (event: PointerEvent) => {
107
+ if (rootEl && !rootEl.contains(event.target as Node)) open = false;
108
+ };
109
+ const dismissFocus = (event: FocusEvent) => {
110
+ if (rootEl && !rootEl.contains(event.target as Node)) open = false;
111
+ };
112
+ document.addEventListener('pointerdown', dismissPointer, true);
113
+ document.addEventListener('focusin', dismissFocus, true);
114
+ return () => {
115
+ document.removeEventListener('pointerdown', dismissPointer, true);
116
+ document.removeEventListener('focusin', dismissFocus, true);
117
+ };
118
+ });
119
+ $effect(() => {
120
+ const option = options.find((item) => String(item.value) === value);
121
+ if (option && document.activeElement !== inputEl) query = option.label;
122
+ });
123
+ useControlRegistration(() => {
124
+ const root = rootEl;
125
+ const input = inputEl;
126
+ if (!root || !input || interaction === false) return false;
127
+ return {
128
+ controlId,
129
+ subject: interaction?.subject,
130
+ metadata: {
131
+ kind: 'combobox',
132
+ label,
133
+ description: interaction?.description,
134
+ sensitivity: interaction?.sensitivity ?? 'public',
135
+ readable: interaction?.readable,
136
+ writable: interaction?.writable,
137
+ constraints: { required },
138
+ options,
139
+ },
140
+ getValue: () => value,
141
+ setValue,
142
+ clear: () => {
143
+ value = '';
144
+ query = '';
145
+ onvaluechange?.('');
146
+ },
147
+ focus: () => input.focus(),
148
+ reveal: () => revealControl(root),
149
+ highlight: (durationMs) => highlightControl(root, durationMs),
150
+ validate: () => input.reportValidity(),
151
+ getState: () => ({
152
+ disabled,
153
+ valid: input.validity.valid,
154
+ validationMessage: input.validationMessage || undefined,
155
+ }),
156
+ };
157
+ });
158
+ </script>
159
+ <div bind:this={rootEl} class="combobox {className}" data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}>
160
+ <label for={inputId}>{label}</label><input bind:this={inputEl} id={inputId} {name} role="combobox" autocomplete="off" {placeholder} {disabled} {required} value={query}
161
+ aria-expanded={open} aria-controls={listId} aria-autocomplete="list" aria-activedescendant={open && filtered[activeIndex] ? `${listId}-${activeIndex}` : undefined}
162
+ onfocus={() => open = true} oninput={handleInput} onkeydown={handleKeydown} />
163
+ {#if open && filtered.length}<div id={listId} class="options" role="listbox">{#each filtered as option, index (option.value)}<button id={`${listId}-${index}`} type="button" role="option"
164
+ aria-selected={String(option.value) === value} class:active={index === activeIndex} disabled={option.disabled} onpointerdown={(event) => event.preventDefault()} onclick={() => commit(option)}>{option.label}</button>{/each}</div>{/if}
165
+ </div>
166
+ <style>
167
+ .combobox { position: relative; display: grid; gap: var(--smrt-spacing-1); color: var(--smrt-color-on-surface); }
168
+ label { font: var(--smrt-typography-label-large-font); } input { width: 100%; padding: var(--smrt-spacing-2) var(--smrt-spacing-3); border: 1px solid var(--smrt-color-outline-variant); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface); color: inherit; }
169
+ input:focus { outline: 2px solid var(--smrt-color-primary); outline-offset: 1px; } .options { position: absolute; z-index: var(--smrt-z-index-dropdown); top: 100%; left: 0; right: 0; display: grid; padding: var(--smrt-spacing-1); border: 1px solid var(--smrt-color-outline-variant); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface-container); box-shadow: var(--smrt-elevation-2); }
170
+ .options button { padding: var(--smrt-spacing-2) var(--smrt-spacing-3); border: 0; border-radius: var(--smrt-radius-extra-small); background: transparent; color: var(--smrt-color-on-surface); text-align: left; } .options button.active { background: var(--smrt-color-secondary-container); }
171
+ :global(.combobox[data-smrt-highlighted='true']) { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; }
172
+ </style>
@@ -0,0 +1,18 @@
1
+ import type { ControlInteractionOptions, ControlOption } from './control-interaction.js';
2
+ export interface Props {
3
+ options: ControlOption[];
4
+ value?: string;
5
+ label: string;
6
+ name?: string;
7
+ placeholder?: string;
8
+ disabled?: boolean;
9
+ required?: boolean;
10
+ allowCustom?: boolean;
11
+ interaction?: ControlInteractionOptions | false;
12
+ onvaluechange?: (value: string) => void;
13
+ class?: string;
14
+ }
15
+ declare const Combobox: import("svelte").Component<Props, {}, "value">;
16
+ type Combobox = ReturnType<typeof Combobox>;
17
+ export default Combobox;
18
+ //# sourceMappingURL=Combobox.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Combobox.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Combobox.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,yBAAyB,EACzB,aAAa,EACd,MAAM,0BAA0B,CAAC;AAIlC,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAwJD,QAAA,MAAM,QAAQ,gDAAwC,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-date-${instanceId}`);
23
+ </script>
24
+ <label class="picker {className}" for={resolvedId}><span>{label}</span><Input id={resolvedId} {name} type="date" 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 DatePicker: import("svelte").Component<Props, {}, "value">;
10
+ type DatePicker = ReturnType<typeof DatePicker>;
11
+ export default DatePicker;
12
+ //# sourceMappingURL=DatePicker.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DatePicker.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/DatePicker.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"}
@@ -0,0 +1,32 @@
1
+ <script lang="ts">
2
+ import { focusControl } from './control-dom.js';
3
+ import type { FormError } from './types.js';
4
+ export interface Props {
5
+ errors: FormError[];
6
+ title?: string;
7
+ onselect?: (controlId: string) => void;
8
+ class?: string;
9
+ }
10
+ let {
11
+ errors,
12
+ title = 'Please correct the following',
13
+ onselect,
14
+ class: className = '',
15
+ }: Props = $props();
16
+ const instanceId = $props.id();
17
+ const titleId = `error-summary-${instanceId}`;
18
+ function select(error: FormError) {
19
+ const target = document.querySelector<HTMLElement>(
20
+ `[data-smrt-control="${CSS.escape(error.controlId)}"]`,
21
+ );
22
+ target?.scrollIntoView({ block: 'center' });
23
+ if (target) focusControl(target);
24
+ onselect?.(error.controlId);
25
+ }
26
+ </script>
27
+ {#if errors.length > 0}<section class="error-summary {className}" role="alert" aria-labelledby={titleId}><h2 id={titleId}>{title}</h2><ul>{#each errors as error}<li><button type="button" onclick={() => select(error)}>{error.label ? `${error.label}: ` : ''}{error.message}</button></li>{/each}</ul></section>{/if}
28
+ <style>
29
+ .error-summary { padding: var(--smrt-spacing-4); border-inline-start: 4px solid var(--smrt-color-error); border-radius: var(--smrt-radius-small); background: var(--smrt-color-error-container); color: var(--smrt-color-on-error-container); }
30
+ h2 { margin: 0 0 var(--smrt-spacing-2); font: var(--smrt-typography-title-small-font); } ul { margin: 0; padding-inline-start: var(--smrt-spacing-5); }
31
+ button { padding: 0; border: 0; background: transparent; color: inherit; text-decoration: underline; cursor: pointer; }
32
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { FormError } from './types.js';
2
+ export interface Props {
3
+ errors: FormError[];
4
+ title?: string;
5
+ onselect?: (controlId: string) => void;
6
+ class?: string;
7
+ }
8
+ declare const ErrorSummary: import("svelte").Component<Props, {}, "">;
9
+ type ErrorSummary = ReturnType<typeof ErrorSummary>;
10
+ export default ErrorSummary;
11
+ //# sourceMappingURL=ErrorSummary.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorSummary.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/ErrorSummary.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA2BD,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,32 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLFieldsetAttributes } from 'svelte/elements';
4
+ export interface Props extends Omit<HTMLFieldsetAttributes, 'class'> {
5
+ legend: string;
6
+ description?: string;
7
+ error?: string;
8
+ class?: string;
9
+ children: Snippet;
10
+ }
11
+ let {
12
+ legend,
13
+ description,
14
+ error,
15
+ class: className = '',
16
+ children,
17
+ ...rest
18
+ }: Props = $props();
19
+ const instanceId = $props.id();
20
+ const descriptionId = $derived(
21
+ description ? `fieldset-${instanceId}-description` : undefined,
22
+ );
23
+ const errorId = $derived(error ? `fieldset-${instanceId}-error` : undefined);
24
+ </script>
25
+ <fieldset class="fieldset {className}" aria-describedby={[descriptionId, errorId].filter(Boolean).join(' ') || undefined} {...rest}>
26
+ <legend>{legend}</legend>{#if description}<p id={descriptionId} class="description">{description}</p>{/if}<div class="content">{@render children()}</div>{#if error}<p id={errorId} class="error" role="alert">{error}</p>{/if}
27
+ </fieldset>
28
+ <style>
29
+ .fieldset { margin: 0; padding: var(--smrt-spacing-4); border: 1px solid var(--smrt-color-outline-variant); border-radius: var(--smrt-radius-medium); }
30
+ legend { padding: 0 var(--smrt-spacing-2); color: var(--smrt-color-on-surface); font: var(--smrt-typography-title-small-font); }
31
+ .description, .error { margin: 0 0 var(--smrt-spacing-3); font: var(--smrt-typography-body-small-font); color: var(--smrt-color-on-surface-variant); } .error { margin: var(--smrt-spacing-3) 0 0; color: var(--smrt-color-error); }
32
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLFieldsetAttributes } from 'svelte/elements';
3
+ export interface Props extends Omit<HTMLFieldsetAttributes, 'class'> {
4
+ legend: string;
5
+ description?: string;
6
+ error?: string;
7
+ class?: string;
8
+ children: Snippet;
9
+ }
10
+ declare const Fieldset: import("svelte").Component<Props, {}, "">;
11
+ type Fieldset = ReturnType<typeof Fieldset>;
12
+ export default Fieldset;
13
+ //# sourceMappingURL=Fieldset.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Fieldset.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Fieldset.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAE9D,MAAM,WAAW,KAAM,SAAQ,IAAI,CAAC,sBAAsB,EAAE,OAAO,CAAC;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;CACnB;AA0BD,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,116 @@
1
+ <script lang="ts">
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+ import { highlightControl, revealControl } from './control-dom.js';
4
+ import type { ControlInteractionOptions } from './control-interaction.js';
5
+ import { tryGetControlInteractionContext } from './control-interaction-context.js';
6
+ import { useControlRegistration } from './use-control-registration.svelte.js';
7
+ export interface Props
8
+ extends Omit<HTMLInputAttributes, 'type' | 'class' | 'value' | 'files'> {
9
+ files?: File[];
10
+ label?: string;
11
+ description?: string;
12
+ dropzone?: boolean;
13
+ class?: string;
14
+ interaction?: ControlInteractionOptions | false;
15
+ onchangefiles?: (files: File[]) => void;
16
+ }
17
+ let {
18
+ files = $bindable([]),
19
+ label = 'Choose files',
20
+ description = 'Drop files here or browse',
21
+ dropzone = false,
22
+ id,
23
+ name,
24
+ accept,
25
+ multiple = false,
26
+ disabled = false,
27
+ required = false,
28
+ class: className = '',
29
+ interaction,
30
+ onchange,
31
+ onchangefiles,
32
+ ...rest
33
+ }: Props = $props();
34
+ const instanceId = $props.id();
35
+ const interactionContext = tryGetControlInteractionContext();
36
+ let inputEl = $state<HTMLInputElement | null>(null);
37
+ let rootEl = $state<HTMLLabelElement | null>(null);
38
+ const resolvedId = $derived(id ?? `smrt-file-${instanceId}`);
39
+ const controlId = $derived(
40
+ interaction === false ? undefined : (interaction?.id ?? name ?? resolvedId),
41
+ );
42
+ let dragging = $state(false);
43
+ function setFiles(next: FileList | File[], syncInput = true) {
44
+ files = Array.from(next).slice(0, multiple ? undefined : 1);
45
+ if (syncInput && inputEl && typeof DataTransfer !== 'undefined') {
46
+ const transfer = new DataTransfer();
47
+ for (const file of files) transfer.items.add(file);
48
+ inputEl.files = transfer.files;
49
+ }
50
+ onchangefiles?.(files);
51
+ }
52
+ function handleChange(event: Event & { currentTarget: HTMLInputElement }) {
53
+ setFiles(event.currentTarget.files ?? [], false);
54
+ onchange?.(event);
55
+ }
56
+ function handleDrop(event: DragEvent) {
57
+ event.preventDefault();
58
+ dragging = false;
59
+ if (!disabled && event.dataTransfer?.files)
60
+ setFiles(event.dataTransfer.files);
61
+ }
62
+ export function open(): void {
63
+ if (!disabled) inputEl?.click();
64
+ }
65
+ export function clear(): void {
66
+ files = [];
67
+ if (inputEl) inputEl.value = '';
68
+ onchangefiles?.([]);
69
+ }
70
+ export function getElement(): HTMLInputElement | null {
71
+ return inputEl;
72
+ }
73
+ useControlRegistration(() => {
74
+ const input = inputEl;
75
+ const root = rootEl;
76
+ if (!input || !root || interaction === false) return false;
77
+ return {
78
+ controlId,
79
+ subject: interaction?.subject,
80
+ metadata: {
81
+ kind: 'file',
82
+ label,
83
+ description: interaction?.description ?? description,
84
+ sensitivity: interaction?.sensitivity ?? 'sensitive',
85
+ readable: false,
86
+ writable: false,
87
+ constraints: { required: required === true },
88
+ capabilities: ['focus', 'reveal', 'highlight', 'explain', 'validate'],
89
+ },
90
+ focus: () => input.focus(),
91
+ reveal: () => revealControl(root),
92
+ highlight: (durationMs) => highlightControl(root, durationMs),
93
+ validate: () => input.reportValidity(),
94
+ getState: () => ({
95
+ disabled: input.disabled,
96
+ valid: input.validity.valid,
97
+ validationMessage: input.validationMessage || undefined,
98
+ }),
99
+ };
100
+ });
101
+ </script>
102
+ <label bind:this={rootEl} for={resolvedId} class="file-picker {className}" class:dropzone class:dragging class:disabled
103
+ data-smrt-control={controlId} data-smrt-form={interactionContext?.formId} ondragenter={(event) => { event.preventDefault(); dragging = true; }}
104
+ ondragover={(event) => event.preventDefault()} ondragleave={() => dragging = false} ondrop={handleDrop}>
105
+ <input bind:this={inputEl} id={resolvedId} type="file" {name} {accept} {multiple} {disabled} {required} onchange={handleChange} {...rest} />
106
+ <span class="label">{label}</span><span class="description">{files.length ? `${files.length} file${files.length === 1 ? '' : 's'} selected` : description}</span>
107
+ </label>
108
+ <style>
109
+ .file-picker { display: inline-flex; flex-direction: column; gap: var(--smrt-spacing-1); padding: var(--smrt-spacing-3) var(--smrt-spacing-4); 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); cursor: pointer; }
110
+ .file-picker.dropzone { width: 100%; min-height: 8rem; align-items: center; justify-content: center; border-style: dashed; text-align: center; }
111
+ .file-picker.dragging { border-color: var(--smrt-color-primary); background: var(--smrt-color-primary-container); }
112
+ .file-picker.disabled { opacity: .5; cursor: not-allowed; } input { position: absolute; width: 1px; height: 1px; opacity: 0; }
113
+ input:focus-visible ~ .label { outline: 2px solid var(--smrt-color-primary); outline-offset: 3px; }
114
+ .label { font: var(--smrt-typography-label-large-font); } .description { font: var(--smrt-typography-body-small-font); color: var(--smrt-color-on-surface-variant); }
115
+ .file-picker[data-smrt-highlighted='true'] { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; }
116
+ </style>
@@ -0,0 +1,19 @@
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' | 'files'> {
4
+ files?: File[];
5
+ label?: string;
6
+ description?: string;
7
+ dropzone?: boolean;
8
+ class?: string;
9
+ interaction?: ControlInteractionOptions | false;
10
+ onchangefiles?: (files: File[]) => void;
11
+ }
12
+ declare const FilePicker: import("svelte").Component<Props, {
13
+ open: () => void;
14
+ clear: () => void;
15
+ getElement: () => HTMLInputElement | null;
16
+ }, "files">;
17
+ type FilePicker = ReturnType<typeof FilePicker>;
18
+ export default FilePicker;
19
+ //# sourceMappingURL=FilePicker.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilePicker.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/FilePicker.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAI1E,MAAM,WAAW,KACf,SAAQ,IAAI,CAAC,mBAAmB,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IACvE,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;CACzC;AAqGD,QAAA,MAAM,UAAU;gBAhDE,IAAI;iBAGH,IAAI;sBAKC,gBAAgB,GAAG,IAAI;WAwCS,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -19,28 +19,82 @@
19
19
  */
20
20
  import type { Snippet } from 'svelte';
21
21
  import type { HTMLFormAttributes } from 'svelte/elements';
22
+ import {
23
+ type ControlInteractionEvent,
24
+ type ControlInteractionRegistry,
25
+ createControlInteractionRegistry,
26
+ } from './control-interaction.js';
27
+ import { setControlInteractionContext } from './control-interaction-context.js';
22
28
 
23
29
  export interface Props extends Omit<HTMLFormAttributes, 'class'> {
24
30
  class?: string;
25
31
  preventDefault?: boolean;
32
+ /** Stable form scope used by voice/chat/tutorial adapters. */
33
+ formId?: string;
34
+ /** Supply a registry to coordinate multiple forms or inspect commands. */
35
+ interactionRegistry?: ControlInteractionRegistry;
36
+ /** Receives registry lifecycle and command events. */
37
+ oninteraction?: (event: ControlInteractionEvent) => void;
26
38
  children: Snippet;
27
39
  }
28
40
 
29
41
  let {
30
42
  class: className = '',
31
43
  preventDefault = true,
44
+ formId,
45
+ interactionRegistry,
46
+ oninteraction,
47
+ id,
48
+ name,
32
49
  onsubmit,
33
50
  children,
34
51
  ...rest
35
52
  }: Props = $props();
36
53
 
54
+ const instanceId = $props.id();
55
+ const generatedFormId = `smrt-form-${instanceId}`;
56
+ const localInteractionRegistry = createControlInteractionRegistry();
57
+ const resolvedFormId = $derived(formId ?? id ?? name ?? generatedFormId);
58
+ const resolvedInteractionRegistry = $derived(
59
+ interactionRegistry ?? localInteractionRegistry,
60
+ );
61
+
62
+ setControlInteractionContext({
63
+ get formId() {
64
+ return resolvedFormId;
65
+ },
66
+ get registry() {
67
+ return resolvedInteractionRegistry;
68
+ },
69
+ });
70
+
71
+ $effect(() => {
72
+ if (!oninteraction) return;
73
+ return resolvedInteractionRegistry.subscribe(oninteraction);
74
+ });
75
+
76
+ export function getInteractionRegistry(): ControlInteractionRegistry {
77
+ return resolvedInteractionRegistry;
78
+ }
79
+
80
+ export function getFormId(): string {
81
+ return resolvedFormId;
82
+ }
83
+
37
84
  function handleSubmit(event: SubmitEvent & { currentTarget: HTMLFormElement }) {
38
85
  if (preventDefault) event.preventDefault();
39
86
  onsubmit?.(event);
40
87
  }
41
88
  </script>
42
89
 
43
- <form class="form {className}" onsubmit={handleSubmit} {...rest}>
90
+ <form
91
+ id={id}
92
+ name={name}
93
+ class="form {className}"
94
+ data-smrt-form={resolvedFormId}
95
+ onsubmit={handleSubmit}
96
+ {...rest}
97
+ >
44
98
  {@render children()}
45
99
  </form>
46
100
 
@@ -50,4 +104,3 @@ function handleSubmit(event: SubmitEvent & { currentTarget: HTMLFormElement }) {
50
104
  with a consumer's single-class layout override (e.g. `:global(.x){display:flex}`)
51
105
  and can win by stylesheet order. The `form` class stays as a stable hook.
52
106
  -->
53
-
@@ -18,12 +18,22 @@
18
18
  */
19
19
  import type { Snippet } from 'svelte';
20
20
  import type { HTMLFormAttributes } from 'svelte/elements';
21
+ import { type ControlInteractionEvent, type ControlInteractionRegistry } from './control-interaction.js';
21
22
  export interface Props extends Omit<HTMLFormAttributes, 'class'> {
22
23
  class?: string;
23
24
  preventDefault?: boolean;
25
+ /** Stable form scope used by voice/chat/tutorial adapters. */
26
+ formId?: string;
27
+ /** Supply a registry to coordinate multiple forms or inspect commands. */
28
+ interactionRegistry?: ControlInteractionRegistry;
29
+ /** Receives registry lifecycle and command events. */
30
+ oninteraction?: (event: ControlInteractionEvent) => void;
24
31
  children: Snippet;
25
32
  }
26
- declare const Form: import("svelte").Component<Props, {}, "">;
33
+ declare const Form: import("svelte").Component<Props, {
34
+ getInteractionRegistry: () => ControlInteractionRegistry;
35
+ getFormId: () => string;
36
+ }, "">;
27
37
  type Form = ReturnType<typeof Form>;
28
38
  export default Form;
29
39
  //# sourceMappingURL=Form.svelte.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Form.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Form.svelte.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,MAAM,WAAW,KAAM,SAAQ,IAAI,CAAC,kBAAkB,EAAE,OAAO,CAAC;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;CACnB;AA8BD,QAAA,MAAM,IAAI,2CAAwC,CAAC;AACnD,KAAK,IAAI,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;AACpC,eAAe,IAAI,CAAC"}
1
+ {"version":3,"file":"Form.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Form.svelte.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,0BAA0B,EAEhC,MAAM,0BAA0B,CAAC;AAIlC,MAAM,WAAW,KAAM,SAAQ,IAAI,CAAC,kBAAkB,EAAE,OAAO,CAAC;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,mBAAmB,CAAC,EAAE,0BAA0B,CAAC;IACjD,sDAAsD;IACtD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACzD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAkED,QAAA,MAAM,IAAI;kCAtB0B,0BAA0B;qBAIvC,MAAM;MAkBqB,CAAC;AACnD,KAAK,IAAI,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;AACpC,eAAe,IAAI,CAAC"}
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
+ import type { ControlInteractionOptions } from './control-interaction.js';
3
4
  import {
4
5
  type FormGroupContextValue,
5
6
  nextFieldId,
@@ -12,14 +13,25 @@ export interface Props {
12
13
  error?: string;
13
14
  hint?: string;
14
15
  required?: boolean;
16
+ /** Agent/tutorial interaction metadata inherited by the wrapped control. */
17
+ interaction?: ControlInteractionOptions | false;
15
18
  children: Snippet;
16
19
  }
17
20
 
18
- const { label, id, error, hint, required = false, children }: Props = $props();
21
+ const {
22
+ label,
23
+ id,
24
+ error,
25
+ hint,
26
+ required = false,
27
+ interaction,
28
+ children,
29
+ }: Props = $props();
19
30
 
20
31
  // Stable id so the label's `for` and the wrapped input's `id` agree even when
21
32
  // the consumer doesn't pass one.
22
- const fallbackId = nextFieldId();
33
+ const instanceId = $props.id();
34
+ const fallbackId = `smrt-field-${instanceId}`;
23
35
  const fieldId = $derived(id ?? fallbackId);
24
36
  const hintId = $derived(hint && !error ? `${fieldId}-hint` : undefined);
25
37
  const errorId = $derived(error ? `${fieldId}-error` : undefined);
@@ -34,6 +46,11 @@ setFormGroupContext(
34
46
  inputId: fieldId,
35
47
  describedBy,
36
48
  invalid: !!error,
49
+ label,
50
+ description: hint,
51
+ required,
52
+ error,
53
+ interaction,
37
54
  }),
38
55
  );
39
56
  </script>
@@ -1,10 +1,13 @@
1
1
  import type { Snippet } from 'svelte';
2
+ import type { ControlInteractionOptions } from './control-interaction.js';
2
3
  export interface Props {
3
4
  label: string;
4
5
  id?: string;
5
6
  error?: string;
6
7
  hint?: string;
7
8
  required?: boolean;
9
+ /** Agent/tutorial interaction metadata inherited by the wrapped control. */
10
+ interaction?: ControlInteractionOptions | false;
8
11
  children: Snippet;
9
12
  }
10
13
  declare const FormGroup: import("svelte").Component<Props, {}, "">;