@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,79 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLButtonAttributes } from 'svelte/elements';
4
+ import {
5
+ emitControlChange,
6
+ highlightControl,
7
+ revealControl,
8
+ } from './control-dom.js';
9
+ import type { ControlInteractionOptions } from './control-interaction.js';
10
+ import { tryGetControlInteractionContext } from './control-interaction-context.js';
11
+ import { useControlRegistration } from './use-control-registration.svelte.js';
12
+
13
+ export interface Props extends Omit<HTMLButtonAttributes, 'class'> {
14
+ pressed?: boolean;
15
+ class?: string;
16
+ interaction?: ControlInteractionOptions | false;
17
+ children?: Snippet;
18
+ }
19
+ let {
20
+ pressed = $bindable(false),
21
+ id,
22
+ name,
23
+ disabled = false,
24
+ class: className = '',
25
+ interaction,
26
+ children,
27
+ onclick,
28
+ 'aria-label': ariaLabel,
29
+ ...rest
30
+ }: Props = $props();
31
+ const instanceId = $props.id();
32
+ const interactionContext = tryGetControlInteractionContext();
33
+ let buttonEl = $state<HTMLButtonElement | null>(null);
34
+ const resolvedId = $derived(id ?? `smrt-toggle-button-${instanceId}`);
35
+ const controlId = $derived(
36
+ interaction === false ? undefined : (interaction?.id ?? name ?? resolvedId),
37
+ );
38
+ function setPressed(next: unknown) {
39
+ pressed = Boolean(next);
40
+ if (buttonEl) emitControlChange(buttonEl);
41
+ }
42
+ function handleClick(event: MouseEvent & { currentTarget: HTMLButtonElement }) {
43
+ pressed = !pressed;
44
+ onclick?.(event);
45
+ }
46
+ useControlRegistration(() => {
47
+ const element = buttonEl;
48
+ if (!element || interaction === false) return false;
49
+ return {
50
+ controlId,
51
+ subject: interaction?.subject,
52
+ metadata: {
53
+ kind: 'toggle-button',
54
+ label: ariaLabel ?? (element.textContent?.trim() || undefined),
55
+ description: interaction?.description,
56
+ sensitivity: interaction?.sensitivity ?? 'public',
57
+ readable: interaction?.readable,
58
+ writable: interaction?.writable,
59
+ },
60
+ getValue: () => pressed,
61
+ setValue: setPressed,
62
+ clear: () => setPressed(false),
63
+ focus: () => element.focus(),
64
+ reveal: () => revealControl(element),
65
+ highlight: (durationMs) => highlightControl(element, durationMs),
66
+ getState: () => ({ disabled: element.disabled }),
67
+ };
68
+ });
69
+ </script>
70
+ <button bind:this={buttonEl} id={resolvedId} type="button" {name} {disabled} class="toggle-button {className}" class:pressed
71
+ aria-pressed={pressed} aria-label={ariaLabel} data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}
72
+ onclick={handleClick} {...rest}>{@render children?.()}</button>
73
+ <style>
74
+ .toggle-button { display: inline-flex; align-items: center; justify-content: center; min-height: 2.5rem; padding: 0 var(--smrt-spacing-4); border: 1px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-full); background: var(--smrt-color-surface); color: var(--smrt-color-on-surface); cursor: pointer; }
75
+ .toggle-button.pressed { background: var(--smrt-color-secondary-container); color: var(--smrt-color-on-secondary-container); border-color: var(--smrt-color-secondary); }
76
+ .toggle-button:focus-visible { outline: 2px solid var(--smrt-color-primary); outline-offset: 2px; }
77
+ .toggle-button:disabled { opacity: .5; cursor: not-allowed; }
78
+ .toggle-button[data-smrt-highlighted='true'] { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; }
79
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLButtonAttributes } from 'svelte/elements';
3
+ import type { ControlInteractionOptions } from './control-interaction.js';
4
+ export interface Props extends Omit<HTMLButtonAttributes, 'class'> {
5
+ pressed?: boolean;
6
+ class?: string;
7
+ interaction?: ControlInteractionOptions | false;
8
+ children?: Snippet;
9
+ }
10
+ declare const ToggleButton: import("svelte").Component<Props, {}, "pressed">;
11
+ type ToggleButton = ReturnType<typeof ToggleButton>;
12
+ export default ToggleButton;
13
+ //# sourceMappingURL=ToggleButton.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToggleButton.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/ToggleButton.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAM5D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAK1E,MAAM,WAAW,KAAM,SAAQ,IAAI,CAAC,oBAAoB,EAAE,OAAO,CAAC;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAiED,QAAA,MAAM,YAAY,kDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,29 @@
1
+ <script lang="ts">
2
+ import Combobox from '../Combobox.svelte';
3
+ import type { ControlInteractionRegistry } from '../control-interaction.js';
4
+ import Form from '../Form.svelte';
5
+ import Listbox from '../Listbox.svelte';
6
+ import MultiSelect from '../MultiSelect.svelte';
7
+ import TagsInput from '../TagsInput.svelte';
8
+
9
+ interface Props {
10
+ registry: ControlInteractionRegistry;
11
+ }
12
+ let { registry }: Props = $props();
13
+ const countries = [
14
+ { value: 'ca', label: 'Canada' },
15
+ { value: 'us', label: 'United States' },
16
+ { value: 'mx', label: 'Mexico' },
17
+ ];
18
+ let country = $state('');
19
+ let channels = $state<Array<string | number>>([]);
20
+ let tags = $state<string[]>([]);
21
+ let region = $state<string | number>('west');
22
+ </script>
23
+
24
+ <Form formId="profile" interactionRegistry={registry} aria-label="Profile">
25
+ <Combobox name="country" label="Country" options={countries} bind:value={country} />
26
+ <MultiSelect name="channels" label="Channels" options={[{ value: 'email', label: 'Email' }, { value: 'sms', label: 'SMS' }]} bind:values={channels} />
27
+ <TagsInput name="topics" label="Topics" bind:values={tags} />
28
+ <Listbox name="region" label="Region" options={[{ value: 'west', label: 'West' }, { value: 'east', label: 'East' }]} bind:value={region} />
29
+ </Form>
@@ -0,0 +1,8 @@
1
+ import type { ControlInteractionRegistry } from '../control-interaction.js';
2
+ interface Props {
3
+ registry: ControlInteractionRegistry;
4
+ }
5
+ declare const CompositeControls: import("svelte").Component<Props, {}, "">;
6
+ type CompositeControls = ReturnType<typeof CompositeControls>;
7
+ export default CompositeControls;
8
+ //# sourceMappingURL=composite-controls.fixture.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composite-controls.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/composite-controls.fixture.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAO5E,UAAU,KAAK;IACb,QAAQ,EAAE,0BAA0B,CAAC;CACtC;AA8BD,QAAA,MAAM,iBAAiB,2CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { render, screen } from '@testing-library/svelte';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { expectNoA11yViolations } from '../../../test-support/a11y';
5
+ import { createControlInteractionRegistry } from '../control-interaction.js';
6
+ import Fixture from './composite-controls.fixture.svelte';
7
+ describe('composite controls', () => {
8
+ it('supports searchable, multiple, tag, and listbox selection', async () => {
9
+ const registry = createControlInteractionRegistry();
10
+ render(Fixture, { props: { registry } });
11
+ const country = screen.getByRole('combobox', { name: 'Country' });
12
+ await userEvent.type(country, 'Can');
13
+ await userEvent.click(screen.getByRole('option', { name: 'Canada' }));
14
+ expect(country).toHaveValue('Canada');
15
+ await userEvent.click(screen.getByRole('button', { name: /Channels.*Select options/ }));
16
+ await userEvent.click(screen.getByRole('option', { name: 'Email' }));
17
+ expect(screen.getByRole('option', { name: 'Email' })).toHaveAttribute('aria-selected', 'true');
18
+ await userEvent.type(screen.getByRole('textbox', { name: 'Topics' }), 'svelte{Enter}');
19
+ expect(screen.getByText('svelte')).toBeInTheDocument();
20
+ await userEvent.click(screen.getByRole('option', { name: 'East' }));
21
+ expect(screen.getByRole('option', { name: 'East' })).toHaveAttribute('aria-selected', 'true');
22
+ });
23
+ it('registers stable controls for agent adapters', () => {
24
+ const registry = createControlInteractionRegistry();
25
+ render(Fixture, { props: { registry } });
26
+ expect(registry.list('profile').map((item) => item.identity.controlId)).toEqual(['country', 'channels', 'topics', 'region']);
27
+ });
28
+ it('is axe-clean', async () => {
29
+ const registry = createControlInteractionRegistry();
30
+ const { container } = render(Fixture, { props: { registry } });
31
+ await expectNoA11yViolations(container);
32
+ });
33
+ });
@@ -0,0 +1,95 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { createControlInteractionRegistry, } from '../control-interaction.js';
3
+ const identity = {
4
+ formId: 'profile',
5
+ controlId: 'display-name',
6
+ };
7
+ describe('control interaction registry', () => {
8
+ it('discovers serializable metadata and current state', () => {
9
+ let value = 'Ada';
10
+ const registry = createControlInteractionRegistry();
11
+ registry.register({
12
+ identity,
13
+ metadata: { kind: 'text', label: 'Display name' },
14
+ getValue: () => value,
15
+ setValue: (next) => {
16
+ value = String(next);
17
+ },
18
+ getState: () => ({ valid: true }),
19
+ });
20
+ expect(registry.list('profile')).toEqual([
21
+ expect.objectContaining({
22
+ identity,
23
+ metadata: expect.objectContaining({
24
+ kind: 'text',
25
+ label: 'Display name',
26
+ capabilities: expect.arrayContaining(['read', 'stage', 'apply']),
27
+ }),
28
+ state: expect.objectContaining({ value: 'Ada', valid: true }),
29
+ }),
30
+ ]);
31
+ });
32
+ it('stages without mutation, requires agent consent to apply, and supports undo', async () => {
33
+ let value = 'Ada';
34
+ const setValue = vi.fn((next) => {
35
+ value = String(next);
36
+ });
37
+ const registry = createControlInteractionRegistry();
38
+ registry.register({
39
+ identity,
40
+ metadata: { kind: 'text', label: 'Display name' },
41
+ getValue: () => value,
42
+ setValue,
43
+ });
44
+ await registry.execute({ action: 'stage', identity, value: 'Grace' }, { source: 'agent' });
45
+ expect(value).toBe('Ada');
46
+ expect(registry.get(identity)?.state.stagedValue).toBe('Grace');
47
+ const denied = await registry.execute({ action: 'apply', identity }, { source: 'agent' });
48
+ expect(denied).toMatchObject({ ok: false, reason: 'consent_required' });
49
+ const applied = await registry.execute({ action: 'apply', identity }, { source: 'agent', confirmed: true });
50
+ expect(applied.ok).toBe(true);
51
+ expect(value).toBe('Grace');
52
+ await registry.execute({ action: 'undo', identity }, { source: 'agent', confirmed: true });
53
+ expect(value).toBe('Ada');
54
+ });
55
+ it('redacts and denies secret controls by default', async () => {
56
+ let value = 'token';
57
+ const registry = createControlInteractionRegistry();
58
+ registry.register({
59
+ identity,
60
+ metadata: { kind: 'password', sensitivity: 'secret' },
61
+ getValue: () => value,
62
+ setValue: (next) => {
63
+ value = String(next);
64
+ },
65
+ });
66
+ const snapshot = registry.get(identity);
67
+ expect(snapshot?.state).toMatchObject({
68
+ value: undefined,
69
+ valueRedacted: true,
70
+ });
71
+ expect(snapshot?.metadata.capabilities).not.toEqual(expect.arrayContaining(['read', 'stage', 'apply', 'clear', 'undo']));
72
+ const result = await registry.execute({ action: 'stage', identity, value: 'replacement' }, { source: 'agent' });
73
+ expect(result).toMatchObject({ ok: false, reason: 'sensitive_control' });
74
+ expect(value).toBe('token');
75
+ });
76
+ it('runs focus, reveal, and highlight without coupling to a DOM implementation', async () => {
77
+ const focus = vi.fn();
78
+ const reveal = vi.fn();
79
+ const highlight = vi.fn();
80
+ const registry = createControlInteractionRegistry();
81
+ registry.register({
82
+ identity,
83
+ metadata: { kind: 'text' },
84
+ focus,
85
+ reveal,
86
+ highlight,
87
+ });
88
+ await registry.execute({ action: 'focus', identity });
89
+ await registry.execute({ action: 'reveal', identity });
90
+ await registry.execute({ action: 'highlight', identity, durationMs: 400 });
91
+ expect(focus).toHaveBeenCalledOnce();
92
+ expect(reveal).toHaveBeenCalledOnce();
93
+ expect(highlight).toHaveBeenCalledWith(400);
94
+ });
95
+ });
@@ -0,0 +1,39 @@
1
+ <script lang="ts">
2
+ import Checkbox from '../Checkbox.svelte';
3
+ import type { ControlInteractionRegistry } from '../control-interaction.js';
4
+ import Form from '../Form.svelte';
5
+ import Radio from '../Radio.svelte';
6
+ import RadioGroup from '../RadioGroup.svelte';
7
+ import RangeSlider from '../RangeSlider.svelte';
8
+ import SegmentedControl from '../SegmentedControl.svelte';
9
+ import Slider from '../Slider.svelte';
10
+ import Switch from '../Switch.svelte';
11
+
12
+ interface Props {
13
+ registry: ControlInteractionRegistry;
14
+ }
15
+ let { registry }: Props = $props();
16
+ let accepted = $state(false);
17
+ let notifications = $state(false);
18
+ let role = $state('editor');
19
+ let volume = $state(30);
20
+ let range = $state({ min: 20, max: 80 });
21
+ let view = $state<string | number>('grid');
22
+ </script>
23
+
24
+ <Form formId="settings" interactionRegistry={registry} aria-label="Settings">
25
+ <Checkbox name="accepted" label="Accept terms" bind:checked={accepted} />
26
+ <Switch name="notifications" label="Notifications" bind:checked={notifications} />
27
+ <RadioGroup name="role" label="Role" bind:value={role}>
28
+ <Radio value="editor" label="Editor" />
29
+ <Radio value="viewer" label="Viewer" />
30
+ </RadioGroup>
31
+ <Slider name="volume" label="Volume" bind:value={volume} />
32
+ <RangeSlider name="price" label="Price" bind:value={range} />
33
+ <SegmentedControl
34
+ name="view"
35
+ label="View"
36
+ options={[{ value: 'grid', label: 'Grid' }, { value: 'list', label: 'List' }]}
37
+ bind:value={view}
38
+ />
39
+ </Form>
@@ -0,0 +1,8 @@
1
+ import type { ControlInteractionRegistry } from '../control-interaction.js';
2
+ interface Props {
3
+ registry: ControlInteractionRegistry;
4
+ }
5
+ declare const CoreControls: import("svelte").Component<Props, {}, "">;
6
+ type CoreControls = ReturnType<typeof CoreControls>;
7
+ export default CoreControls;
8
+ //# sourceMappingURL=core-controls.fixture.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core-controls.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/core-controls.fixture.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAU5E,UAAU,KAAK;IACb,QAAQ,EAAE,0BAA0B,CAAC;CACtC;AAmCD,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,90 @@
1
+ import { render, screen } from '@testing-library/svelte';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { expectNoA11yViolations } from '../../../test-support/a11y';
5
+ import Progress from '../../feedback/Progress.svelte';
6
+ import Spinner from '../../feedback/Spinner.svelte';
7
+ import { createControlInteractionRegistry } from '../control-interaction.js';
8
+ import ErrorSummary from '../ErrorSummary.svelte';
9
+ import Fixture from './core-controls.fixture.svelte';
10
+ describe('core controls', () => {
11
+ it('provides distinct checkbox, switch, radio, slider, range, and segmented semantics', async () => {
12
+ const registry = createControlInteractionRegistry();
13
+ render(Fixture, { props: { registry } });
14
+ await userEvent.click(screen.getByRole('checkbox', { name: 'Accept terms' }));
15
+ expect(screen.getByRole('checkbox', { name: 'Accept terms' })).toBeChecked();
16
+ await userEvent.click(screen.getByRole('switch', { name: 'Notifications' }));
17
+ expect(screen.getByRole('switch', { name: 'Notifications' })).toBeChecked();
18
+ await userEvent.click(screen.getByRole('radio', { name: 'Viewer' }));
19
+ expect(screen.getByRole('radio', { name: 'Viewer' })).toBeChecked();
20
+ expect(screen.getAllByRole('slider')).toHaveLength(3);
21
+ await userEvent.click(screen.getByRole('radio', { name: 'List' }));
22
+ expect(screen.getByRole('radio', { name: 'List' })).toHaveAttribute('aria-checked', 'true');
23
+ });
24
+ it('registers all controls and lets an agent stage before confirmed apply', async () => {
25
+ const registry = createControlInteractionRegistry();
26
+ render(Fixture, { props: { registry } });
27
+ expect(registry.list('settings').map((item) => item.identity.controlId)).toEqual(expect.arrayContaining([
28
+ 'accepted',
29
+ 'notifications',
30
+ 'role',
31
+ 'volume',
32
+ 'price',
33
+ 'view',
34
+ ]));
35
+ await registry.execute({
36
+ action: 'stage',
37
+ identity: { formId: 'settings', controlId: 'volume' },
38
+ value: 55,
39
+ }, { source: 'agent' });
40
+ expect(screen.getByRole('slider', { name: 'Volume' })).toHaveValue('30');
41
+ await registry.execute({
42
+ action: 'apply',
43
+ identity: { formId: 'settings', controlId: 'volume' },
44
+ }, { source: 'agent', confirmed: true });
45
+ expect(screen.getByRole('slider', { name: 'Volume' })).toHaveValue('55');
46
+ });
47
+ it('is axe-clean as a composed form', async () => {
48
+ const registry = createControlInteractionRegistry();
49
+ const { container } = render(Fixture, { props: { registry } });
50
+ await expectNoA11yViolations(container);
51
+ });
52
+ it('focuses the usable control inside an error-summary target', async () => {
53
+ const { container } = render(ErrorSummary, {
54
+ props: {
55
+ errors: [
56
+ {
57
+ controlId: 'profile-photo',
58
+ label: 'Profile photo',
59
+ message: 'Choose a file',
60
+ },
61
+ ],
62
+ },
63
+ });
64
+ const wrapper = document.createElement('div');
65
+ wrapper.dataset.smrtControl = 'profile-photo';
66
+ wrapper.scrollIntoView = () => undefined;
67
+ const hidden = document.createElement('input');
68
+ hidden.type = 'hidden';
69
+ const file = document.createElement('input');
70
+ file.type = 'file';
71
+ wrapper.append(hidden, file);
72
+ container.append(wrapper);
73
+ await userEvent.click(screen.getByRole('button', { name: 'Profile photo: Choose a file' }));
74
+ expect(file).toHaveFocus();
75
+ });
76
+ });
77
+ describe('progress feedback', () => {
78
+ it('distinguishes determinate and indeterminate progress', async () => {
79
+ const { rerender } = render(Progress, {
80
+ props: { label: 'Import', value: 40, showValue: true },
81
+ });
82
+ expect(screen.getByRole('progressbar', { name: 'Import' })).toHaveAttribute('aria-valuenow', '40');
83
+ await rerender({ label: 'Import', value: undefined });
84
+ expect(screen.getByRole('progressbar', { name: 'Import' })).not.toHaveAttribute('aria-valuenow');
85
+ });
86
+ it('announces a spinner label', () => {
87
+ render(Spinner, { props: { label: 'Saving changes' } });
88
+ expect(screen.getByRole('status')).toHaveTextContent('Saving changes');
89
+ });
90
+ });
@@ -0,0 +1,6 @@
1
+ /** DOM adapter helpers shared by addressable control Implementations. */
2
+ export declare function focusControl(node: HTMLElement): void;
3
+ export declare function revealControl(node: HTMLElement): void;
4
+ export declare function highlightControl(node: HTMLElement, durationMs?: number): void;
5
+ export declare function emitControlChange(node: HTMLElement): void;
6
+ //# sourceMappingURL=control-dom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-dom.d.ts","sourceRoot":"","sources":["../../../src/components/forms/control-dom.ts"],"names":[],"mappings":"AAAA,yEAAyE;AAYzE,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAKpD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAMrD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,SAAO,GAAG,IAAI,CAK3E;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAMzD"}
@@ -0,0 +1,38 @@
1
+ /** DOM adapter helpers shared by addressable control Implementations. */
2
+ const focusableControlSelector = [
3
+ 'input:not([type="hidden"]):not(:disabled)',
4
+ 'select:not(:disabled)',
5
+ 'textarea:not(:disabled)',
6
+ 'button:not(:disabled)',
7
+ 'a[href]',
8
+ '[tabindex]:not([tabindex="-1"]):not([type="hidden"])',
9
+ '[contenteditable="true"]',
10
+ ].join(',');
11
+ export function focusControl(node) {
12
+ const target = node.matches(focusableControlSelector)
13
+ ? node
14
+ : node.querySelector(focusableControlSelector);
15
+ target?.focus();
16
+ }
17
+ export function revealControl(node) {
18
+ node.scrollIntoView({
19
+ block: 'center',
20
+ inline: 'nearest',
21
+ behavior: 'smooth',
22
+ });
23
+ }
24
+ export function highlightControl(node, durationMs = 1600) {
25
+ node.dataset.smrtHighlighted = 'true';
26
+ globalThis.setTimeout(() => {
27
+ if (node.isConnected)
28
+ delete node.dataset.smrtHighlighted;
29
+ }, durationMs);
30
+ }
31
+ export function emitControlChange(node) {
32
+ queueMicrotask(() => {
33
+ if (!node.isConnected)
34
+ return;
35
+ node.dispatchEvent(new Event('input', { bubbles: true }));
36
+ node.dispatchEvent(new Event('change', { bubbles: true }));
37
+ });
38
+ }
@@ -0,0 +1,9 @@
1
+ import type { ControlInteractionRegistry } from './control-interaction.js';
2
+ export interface ControlInteractionContextValue {
3
+ readonly formId: string;
4
+ readonly registry: ControlInteractionRegistry;
5
+ }
6
+ export declare function setControlInteractionContext(context: ControlInteractionContextValue): void;
7
+ export declare function tryGetControlInteractionContext(): ControlInteractionContextValue | undefined;
8
+ export declare function getControlInteractionContext(): ControlInteractionContextValue;
9
+ //# sourceMappingURL=control-interaction-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-interaction-context.d.ts","sourceRoot":"","sources":["../../../src/components/forms/control-interaction-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAI3E,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,0BAA0B,CAAC;CAC/C;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,8BAA8B,GACtC,IAAI,CAEN;AAED,wBAAgB,+BAA+B,IAC3C,8BAA8B,GAC9B,SAAS,CAIZ;AAED,wBAAgB,4BAA4B,IAAI,8BAA8B,CAQ7E"}
@@ -0,0 +1,15 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ const CONTROL_INTERACTION_KEY = Symbol('smrt-control-interaction');
3
+ export function setControlInteractionContext(context) {
4
+ setContext(CONTROL_INTERACTION_KEY, context);
5
+ }
6
+ export function tryGetControlInteractionContext() {
7
+ return getContext(CONTROL_INTERACTION_KEY);
8
+ }
9
+ export function getControlInteractionContext() {
10
+ const context = tryGetControlInteractionContext();
11
+ if (!context) {
12
+ throw new Error('Control interaction context not found. Wrap controls in <Form>.');
13
+ }
14
+ return context;
15
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Transport-neutral interaction model for addressable form controls.
3
+ *
4
+ * The registry deliberately knows nothing about chat, agents, WebMCP, or the
5
+ * DOM. Controls register serializable metadata plus small imperative handles;
6
+ * adapters translate voice/chat/tutorial requests into commands.
7
+ */
8
+ export type ControlKind = 'text' | 'email' | 'password' | 'number' | 'date' | 'time' | 'datetime' | 'textarea' | 'select' | 'listbox' | 'combobox' | 'multi-select' | 'tags-input' | 'checkbox' | 'radio-group' | 'switch' | 'toggle-button' | 'slider' | 'range-slider' | 'segmented-control' | 'file' | 'custom';
9
+ export type ControlSensitivity = 'public' | 'personal' | 'sensitive' | 'secret';
10
+ export type ControlCapability = 'read' | 'focus' | 'reveal' | 'highlight' | 'explain' | 'validate' | 'stage' | 'apply' | 'clear' | 'undo';
11
+ export interface ControlSubject {
12
+ type: string;
13
+ id: string;
14
+ label?: string;
15
+ }
16
+ /** Stable address for a control, following AdminShell's Tool Identity pattern. */
17
+ export interface ControlIdentity {
18
+ formId: string;
19
+ controlId: string;
20
+ subject?: ControlSubject;
21
+ }
22
+ export interface ControlOption {
23
+ value: string | number;
24
+ label: string;
25
+ disabled?: boolean;
26
+ }
27
+ export interface ControlConstraints {
28
+ required?: boolean;
29
+ min?: number | string;
30
+ max?: number | string;
31
+ step?: number | string;
32
+ minLength?: number;
33
+ maxLength?: number;
34
+ pattern?: string;
35
+ }
36
+ /** Optional declarative metadata shared by every control primitive. */
37
+ export interface ControlInteractionOptions {
38
+ /** Stable id inside the enclosing form. Defaults to name, then DOM id. */
39
+ id?: string;
40
+ description?: string;
41
+ sensitivity?: ControlSensitivity;
42
+ readable?: boolean;
43
+ writable?: boolean;
44
+ subject?: ControlSubject;
45
+ }
46
+ export interface ControlMetadata {
47
+ kind: ControlKind;
48
+ label?: string;
49
+ description?: string;
50
+ sensitivity?: ControlSensitivity;
51
+ readable?: boolean;
52
+ writable?: boolean;
53
+ constraints?: ControlConstraints;
54
+ options?: ControlOption[];
55
+ unit?: string;
56
+ capabilities?: ControlCapability[];
57
+ }
58
+ export interface ControlRuntimeState {
59
+ disabled?: boolean;
60
+ readonly?: boolean;
61
+ valid?: boolean;
62
+ validationMessage?: string;
63
+ }
64
+ export interface ControlSnapshot {
65
+ identity: ControlIdentity;
66
+ metadata: ControlMetadata;
67
+ state: ControlRuntimeState & {
68
+ value?: unknown;
69
+ valueRedacted: boolean;
70
+ stagedValue?: unknown;
71
+ stagedValueRedacted?: boolean;
72
+ };
73
+ }
74
+ export interface ControlRegistration {
75
+ identity: ControlIdentity;
76
+ metadata: ControlMetadata;
77
+ getValue?: () => unknown;
78
+ setValue?: (value: unknown) => void | Promise<void>;
79
+ clear?: () => void | Promise<void>;
80
+ focus?: () => void | Promise<void>;
81
+ reveal?: () => void | Promise<void>;
82
+ highlight?: (durationMs?: number) => void | Promise<void>;
83
+ validate?: () => boolean | Promise<boolean>;
84
+ getState?: () => ControlRuntimeState;
85
+ }
86
+ export type ControlCommandAction = 'focus' | 'reveal' | 'highlight' | 'explain' | 'validate' | 'stage' | 'apply' | 'clear' | 'undo';
87
+ export type ControlCommand = {
88
+ action: 'focus' | 'reveal' | 'explain' | 'validate' | 'undo';
89
+ identity: ControlIdentity;
90
+ } | {
91
+ action: 'highlight';
92
+ identity: ControlIdentity;
93
+ durationMs?: number;
94
+ } | {
95
+ action: 'stage';
96
+ identity: ControlIdentity;
97
+ value: unknown;
98
+ } | {
99
+ action: 'apply';
100
+ identity: ControlIdentity;
101
+ value?: unknown;
102
+ } | {
103
+ action: 'clear';
104
+ identity: ControlIdentity;
105
+ };
106
+ export type ControlCommandSource = 'user' | 'voice' | 'agent' | 'tutorial' | 'test';
107
+ export interface ControlCommandContext {
108
+ source: ControlCommandSource;
109
+ /** Agent mutations require this explicit consent signal by default. */
110
+ confirmed?: boolean;
111
+ actorId?: string;
112
+ sessionId?: string;
113
+ }
114
+ export interface ControlPolicyDecision {
115
+ allowed: boolean;
116
+ reason?: string;
117
+ }
118
+ export type ControlInteractionPolicy = (command: ControlCommand, context: ControlCommandContext, snapshot: ControlSnapshot) => ControlPolicyDecision | Promise<ControlPolicyDecision>;
119
+ export interface ControlCommandResult {
120
+ ok: boolean;
121
+ action: ControlCommandAction;
122
+ identity: ControlIdentity;
123
+ snapshot?: ControlSnapshot;
124
+ reason?: string;
125
+ }
126
+ export interface ControlInteractionEvent {
127
+ type: 'registered' | 'unregistered' | 'staged' | 'command';
128
+ identity: ControlIdentity;
129
+ command?: ControlCommand;
130
+ context?: ControlCommandContext;
131
+ result?: ControlCommandResult;
132
+ timestamp: number;
133
+ }
134
+ export interface ControlInteractionRegistry {
135
+ register(registration: ControlRegistration): () => void;
136
+ unregister(identity: ControlIdentity): void;
137
+ list(formId?: string): ControlSnapshot[];
138
+ get(identity: ControlIdentity): ControlSnapshot | undefined;
139
+ execute(command: ControlCommand, context?: ControlCommandContext): Promise<ControlCommandResult>;
140
+ subscribe(listener: (event: ControlInteractionEvent) => void): () => void;
141
+ }
142
+ export interface CreateControlInteractionRegistryOptions {
143
+ policy?: ControlInteractionPolicy;
144
+ }
145
+ /** Create an isolated registry; apps choose where and how it is exposed. */
146
+ export declare function createControlInteractionRegistry(options?: CreateControlInteractionRegistryOptions): ControlInteractionRegistry;
147
+ //# sourceMappingURL=control-interaction.d.ts.map