@happyvertical/smrt-ui 0.43.3 → 0.43.5

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 (89) hide show
  1. package/AGENTS.md +33 -0
  2. package/README.md +50 -4
  3. package/dist/components/forms/Checkbox.svelte +15 -6
  4. package/dist/components/forms/Checkbox.svelte.d.ts.map +1 -1
  5. package/dist/components/forms/Combobox.svelte +45 -10
  6. package/dist/components/forms/Combobox.svelte.d.ts.map +1 -1
  7. package/dist/components/forms/FilePicker.svelte +5 -2
  8. package/dist/components/forms/Form.svelte +57 -0
  9. package/dist/components/forms/Form.svelte.d.ts +2 -0
  10. package/dist/components/forms/Form.svelte.d.ts.map +1 -1
  11. package/dist/components/forms/Input.svelte +59 -13
  12. package/dist/components/forms/Input.svelte.d.ts.map +1 -1
  13. package/dist/components/forms/Listbox.svelte +33 -6
  14. package/dist/components/forms/Listbox.svelte.d.ts.map +1 -1
  15. package/dist/components/forms/MultiSelect.svelte +41 -19
  16. package/dist/components/forms/MultiSelect.svelte.d.ts.map +1 -1
  17. package/dist/components/forms/RadioGroup.svelte +14 -3
  18. package/dist/components/forms/RadioGroup.svelte.d.ts.map +1 -1
  19. package/dist/components/forms/RangeSlider.svelte +61 -6
  20. package/dist/components/forms/RangeSlider.svelte.d.ts.map +1 -1
  21. package/dist/components/forms/SegmentedControl.svelte +33 -6
  22. package/dist/components/forms/SegmentedControl.svelte.d.ts.map +1 -1
  23. package/dist/components/forms/Select.svelte +32 -9
  24. package/dist/components/forms/Select.svelte.d.ts.map +1 -1
  25. package/dist/components/forms/Slider.svelte +26 -6
  26. package/dist/components/forms/Slider.svelte.d.ts.map +1 -1
  27. package/dist/components/forms/StagedControlReview.svelte +637 -0
  28. package/dist/components/forms/StagedControlReview.svelte.d.ts +13 -0
  29. package/dist/components/forms/StagedControlReview.svelte.d.ts.map +1 -0
  30. package/dist/components/forms/Switch.svelte +16 -7
  31. package/dist/components/forms/Switch.svelte.d.ts.map +1 -1
  32. package/dist/components/forms/TagsInput.svelte +36 -8
  33. package/dist/components/forms/TagsInput.svelte.d.ts.map +1 -1
  34. package/dist/components/forms/Textarea.svelte +20 -8
  35. package/dist/components/forms/Textarea.svelte.d.ts.map +1 -1
  36. package/dist/components/forms/ToggleButton.svelte +20 -3
  37. package/dist/components/forms/ToggleButton.svelte.d.ts.map +1 -1
  38. package/dist/components/forms/__tests__/Form.test.js +106 -0
  39. package/dist/components/forms/__tests__/StagedControlReview.test.js +668 -0
  40. package/dist/components/forms/__tests__/composite-controls.fixture.svelte +22 -4
  41. package/dist/components/forms/__tests__/composite-controls.fixture.svelte.d.ts +6 -2
  42. package/dist/components/forms/__tests__/composite-controls.fixture.svelte.d.ts.map +1 -1
  43. package/dist/components/forms/__tests__/composite-controls.test.js +96 -1
  44. package/dist/components/forms/__tests__/composite-user-edit.fixture.svelte +41 -0
  45. package/dist/components/forms/__tests__/composite-user-edit.fixture.svelte.d.ts +13 -0
  46. package/dist/components/forms/__tests__/composite-user-edit.fixture.svelte.d.ts.map +1 -0
  47. package/dist/components/forms/__tests__/control-interaction.test.js +3323 -15
  48. package/dist/components/forms/__tests__/core-controls.fixture.svelte +16 -5
  49. package/dist/components/forms/__tests__/core-controls.fixture.svelte.d.ts +1 -0
  50. package/dist/components/forms/__tests__/core-controls.fixture.svelte.d.ts.map +1 -1
  51. package/dist/components/forms/__tests__/core-controls.test.js +223 -2
  52. package/dist/components/forms/__tests__/decimal-sliders.fixture.svelte +25 -0
  53. package/dist/components/forms/__tests__/decimal-sliders.fixture.svelte.d.ts +8 -0
  54. package/dist/components/forms/__tests__/decimal-sliders.fixture.svelte.d.ts.map +1 -0
  55. package/dist/components/forms/__tests__/select-interaction.fixture.svelte +25 -0
  56. package/dist/components/forms/__tests__/select-interaction.fixture.svelte.d.ts +9 -0
  57. package/dist/components/forms/__tests__/select-interaction.fixture.svelte.d.ts.map +1 -0
  58. package/dist/components/forms/__tests__/staged-review-fieldset.fixture.svelte +21 -0
  59. package/dist/components/forms/__tests__/staged-review-fieldset.fixture.svelte.d.ts +8 -0
  60. package/dist/components/forms/__tests__/staged-review-fieldset.fixture.svelte.d.ts.map +1 -0
  61. package/dist/components/forms/__tests__/staged-review-outer-fieldset.fixture.svelte +23 -0
  62. package/dist/components/forms/__tests__/staged-review-outer-fieldset.fixture.svelte.d.ts +8 -0
  63. package/dist/components/forms/__tests__/staged-review-outer-fieldset.fixture.svelte.d.ts.map +1 -0
  64. package/dist/components/forms/__tests__/staged-review.fixture.svelte +56 -0
  65. package/dist/components/forms/__tests__/staged-review.fixture.svelte.d.ts +12 -0
  66. package/dist/components/forms/__tests__/staged-review.fixture.svelte.d.ts.map +1 -0
  67. package/dist/components/forms/control-interaction-context.d.ts +9 -1
  68. package/dist/components/forms/control-interaction-context.d.ts.map +1 -1
  69. package/dist/components/forms/control-interaction-context.js +16 -0
  70. package/dist/components/forms/control-interaction.d.ts +97 -7
  71. package/dist/components/forms/control-interaction.d.ts.map +1 -1
  72. package/dist/components/forms/control-interaction.js +1019 -92
  73. package/dist/components/forms/control-value-validation.d.ts +19 -0
  74. package/dist/components/forms/control-value-validation.d.ts.map +1 -0
  75. package/dist/components/forms/control-value-validation.js +152 -0
  76. package/dist/components/forms/index.d.ts +3 -1
  77. package/dist/components/forms/index.d.ts.map +1 -1
  78. package/dist/components/forms/index.js +2 -1
  79. package/dist/components/forms/staged-control-review.d.ts +21 -0
  80. package/dist/components/forms/staged-control-review.d.ts.map +1 -0
  81. package/dist/components/forms/staged-control-review.js +1 -0
  82. package/dist/components/forms/use-control-registration.svelte.d.ts.map +1 -1
  83. package/dist/components/forms/use-control-registration.svelte.js +23 -4
  84. package/dist/i18n/strings.ui.d.ts +18 -0
  85. package/dist/i18n/strings.ui.d.ts.map +1 -1
  86. package/dist/i18n/strings.ui.js +19 -0
  87. package/dist/svelte/playground/InteractiveControlsPreview.svelte +3 -4
  88. package/dist/svelte/playground/InteractiveControlsPreview.svelte.d.ts.map +1 -1
  89. package/package.json +2 -2
package/AGENTS.md CHANGED
@@ -112,6 +112,39 @@ other text pairing clears WCAG AA in both schemes.
112
112
  interaction registry. Keep chat/voice transports outside this package;
113
113
  mutations must retain sensitivity checks and the stage → confirmed apply
114
114
  policy.
115
+ - Agent-originated form values remain proposals: the registry records their
116
+ provenance, timestamp, and revision, while `StagedControlReview` is the shared
117
+ review/apply surface mounted by both base and rich Forms. Agents can stage but
118
+ cannot self-confirm; only a local human handler submits confirmed apply,
119
+ discard, clear, or undo commands after the registry validates a trusted DOM
120
+ gesture while its event is actively dispatching. Retained events are rejected,
121
+ and the complete command or batch is snapshotted synchronously before gesture
122
+ consumption. Serialized or programmatic `source: 'user', confirmed: true`
123
+ input is not confirmation.
124
+ `StagedControlReview` marks its complete edited Apply value with
125
+ `reviewedValueIsCanonical: true`, but the marker grants no authority and is
126
+ honored only for a current staged entry under the registry's exact local
127
+ gesture proof. A control with proposal-relative preparation implements
128
+ `prepareReviewedValue` to validate/canonicalize the complete displayed value;
129
+ without that hook, marked edits still pass through ordinary `prepareValue`.
130
+ Only an unchanged value exactly equal to the stored staged canonical value can
131
+ reuse that trusted stored value without either hook.
132
+ Secret controls never accept staging, and sensitive/secret
133
+ values stay redacted in snapshots, events, policy callbacks, and review UI.
134
+ Batch execution is ordered best-effort and returns one result per command;
135
+ review-surface batch actions exclude stale and invalid proposals, which remain
136
+ visible with per-field feedback.
137
+ `executeBatch` is additive and optional for injected legacy registries; the
138
+ framework falls back to ordered `execute` calls without weakening the private
139
+ one-shot gesture proof on factory-created registries.
140
+ Rejecting async custom setters and clear handlers roll back by default;
141
+ controls that accept concurrent direct edits expose a monotonic revision and
142
+ user value from `getUserEditSnapshot()` so rollback restores newer human input;
143
+ fallible async setters expose an infallible `restoreValue()` path. Async hooks
144
+ issue nested commands through their optional final `ControlExtensionContext`;
145
+ same-control mutations are rejected there without timing out unrelated queued
146
+ commands. Context-aware setters implement the additive `setValueWithContext`
147
+ hook; legacy `setValue` is always invoked with exactly one argument.
115
148
 
116
149
  ## Gotchas
117
150
 
package/README.md CHANGED
@@ -99,10 +99,56 @@ without coupling controls to a transport:
99
99
  ```
100
100
 
101
101
  Controls publish serializable metadata, constraints, options, sensitivity, and
102
- capabilities. Adapters can focus, reveal, highlight, explain, validate, stage,
103
- apply, clear, or undo. Agent mutations are denied for secret/read-only controls
104
- and require explicit confirmation before apply, clear, or undo. Staging is
105
- separate so the UI can show a proposal before it changes user state.
102
+ capabilities. Adapters can focus, reveal, highlight, explain, validate, and
103
+ stage reviewable proposals. Agents cannot apply, discard, clear, or undo;
104
+ those value-changing actions require a trusted local gesture handled by the
105
+ framework review surface. Secret/read-only controls reject agent mutations.
106
+ Staging remains separate so proposals never change user state before review.
107
+ Custom local review controls can call `executeLocalControlCommand` or
108
+ `executeLocalControlBatch` synchronously from their DOM handlers; the registry
109
+ requires the event to still be actively dispatching, snapshots the complete
110
+ command, and consumes the gesture before authorizing a value-changing command.
111
+ Retaining an event for later use is rejected even when it remains trusted.
112
+ Serialized or programmatic `source: 'user', confirmed: true` input is never
113
+ confirmation. Sensitive and secret values, validation details, failures, and
114
+ events remain redacted from every public surface.
115
+ The shared review surface marks its complete edited Apply value with
116
+ `reviewedValueIsCanonical: true`. That marker carries no authority: the registry
117
+ honors it only for a current staged entry after validating the exact command
118
+ under an actively dispatching local gesture. Controls with non-idempotent
119
+ proposal preparation can implement `prepareReviewedValue(value)` to validate or
120
+ canonicalize the complete displayed value without re-applying proposal-relative
121
+ behavior. Controls without that hook route marked edits through their ordinary
122
+ `prepareValue`, so a generic marker never bypasses custom normalization or
123
+ rejection. An unchanged value exactly equal to the stored staged canonical value
124
+ uses that trusted stored value directly.
125
+ Registries expose optional `refresh(formId)` notification for hosts whose live
126
+ metadata or runtime-state getters change without a registration event; it
127
+ updates subscribers without discarding an internal staged proposal.
128
+ `executeBatch` is an additive optional registry method; Forms fall back to
129
+ ordered `execute` calls for older injected registries. Factory-created
130
+ registries retain the framework's private, one-shot gesture proof, while an
131
+ older custom registry remains responsible for its pre-existing execution
132
+ policy and accepts review actions only from a trusted browser event.
133
+ Custom controls whose clear operation is intentionally idempotent should return
134
+ `true` from `clear()` to affirm that the unchanged cleared value was accepted.
135
+ Async custom setters and clear handlers are rolled back when they reject. A
136
+ control that permits direct edits while an async mutation is pending can expose
137
+ `getUserEditSnapshot()` and update its revision and value only for direct user
138
+ edits so rollback restores newer human input even if the handler mutates again
139
+ before rejecting. A fallible async setter should also expose `restoreValue()` as
140
+ an infallible state restoration path that does not repeat the external workflow.
141
+ Async policy, validation, setter, clear, and restoration hooks receive an
142
+ optional final `ControlExtensionContext`. Hooks that need to issue another
143
+ control command should use `extension.execute()`; it rejects a mutation of the
144
+ same control immediately, while commands from independent callers remain in
145
+ the normal ordered queue regardless of how long the hook takes. Existing hooks
146
+ that omit the additional argument remain compatible. Setters retain their exact
147
+ legacy `setValue(value)` invocation; a setter that needs this context implements
148
+ the additive `setValueWithContext(value, extension)` hook instead. A hook must
149
+ not await a same-control mutation through a captured registry reference: that
150
+ call is indistinguishable from an independent caller in browser runtimes and,
151
+ like any hook that never settles, can hold the ordered queue indefinitely.
106
152
 
107
153
  ## DataTable controller
108
154
 
@@ -7,6 +7,11 @@ import {
7
7
  } from './control-dom.js';
8
8
  import type { ControlInteractionOptions } from './control-interaction.js';
9
9
  import { tryGetControlInteractionContext } from './control-interaction-context.js';
10
+ import {
11
+ booleanControlValue,
12
+ prepareBooleanControlValue,
13
+ validateNativeCheckedValue,
14
+ } from './control-value-validation.js';
10
15
  import { tryGetFormGroupContext } from './form-group-context.js';
11
16
  import { useControlRegistration } from './use-control-registration.svelte.js';
12
17
 
@@ -62,10 +67,7 @@ $effect(() => {
62
67
  });
63
68
 
64
69
  function setChecked(next: unknown) {
65
- checked =
66
- typeof next === 'string'
67
- ? ['true', '1', 'yes', 'on'].includes(next.toLowerCase())
68
- : Boolean(next);
70
+ checked = booleanControlValue(next);
69
71
  if (inputEl) emitControlChange(inputEl);
70
72
  }
71
73
 
@@ -91,15 +93,20 @@ useControlRegistration(() => {
91
93
  constraints: { required: required === true },
92
94
  },
93
95
  getValue: () => checked,
96
+ prepareValue: prepareBooleanControlValue,
94
97
  setValue: setChecked,
95
- clear: () => setChecked(false),
98
+ clear: () => {
99
+ setChecked(false);
100
+ return true;
101
+ },
96
102
  focus: () => element.focus(),
97
103
  reveal: () => revealControl(element),
98
104
  highlight: (durationMs) =>
99
105
  highlightControl(element.closest('label') ?? element, durationMs),
100
106
  validate: () => element.reportValidity(),
107
+ validateValue: (next) => validateNativeCheckedValue(element, next),
101
108
  getState: () => ({
102
- disabled: element.disabled,
109
+ disabled: element.matches(':disabled'),
103
110
  readonly: false,
104
111
  valid: element.validity.valid,
105
112
  validationMessage: element.validationMessage || undefined,
@@ -113,6 +120,8 @@ useControlRegistration(() => {
113
120
  class:checkbox--disabled={disabled}
114
121
  data-smrt-control={controlId}
115
122
  data-smrt-form={interactionContext?.formId}
123
+ data-smrt-subject-type={resolvedInteraction === false ? undefined : resolvedInteraction.subject?.type}
124
+ data-smrt-subject-id={resolvedInteraction === false ? undefined : resolvedInteraction.subject?.id}
116
125
  >
117
126
  <input
118
127
  bind:this={inputEl}
@@ -1 +1 @@
1
- {"version":3,"file":"Checkbox.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Checkbox.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,CAAC,mBAAmB,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;IAC/D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;CACjD;AA+GD,QAAA,MAAM,QAAQ,kDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"Checkbox.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Checkbox.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAM3D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAW1E,MAAM,WAAW,KACf,SAAQ,IAAI,CAAC,mBAAmB,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;IAC/D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;CACjD;AAkHD,QAAA,MAAM,QAAQ,kDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -1,10 +1,21 @@
1
1
  <script lang="ts">
2
- import { highlightControl, revealControl } from './control-dom.js';
2
+ import {
3
+ emitControlChange,
4
+ highlightControl,
5
+ revealControl,
6
+ } from './control-dom.js';
3
7
  import type {
4
8
  ControlInteractionOptions,
5
9
  ControlOption,
6
10
  } from './control-interaction.js';
7
- import { tryGetControlInteractionContext } from './control-interaction-context.js';
11
+ import {
12
+ recordControlUserEdit,
13
+ tryGetControlInteractionContext,
14
+ } from './control-interaction-context.js';
15
+ import {
16
+ matchingOption,
17
+ prepareTextControlValue,
18
+ } from './control-value-validation.js';
8
19
  import { useControlRegistration } from './use-control-registration.svelte.js';
9
20
  export interface Props {
10
21
  options: ControlOption[];
@@ -49,18 +60,25 @@ const filtered = $derived(
49
60
  const controlId = $derived(
50
61
  interaction === false ? undefined : (interaction?.id ?? name ?? inputId),
51
62
  );
52
- function commit(option: ControlOption) {
63
+ function commit(option: ControlOption, userEdit = false) {
53
64
  if (option.disabled || disabled) return;
65
+ const changed = String(option.value) !== value;
54
66
  value = String(option.value);
55
67
  query = option.label;
56
68
  open = false;
57
69
  onvaluechange?.(value);
70
+ if (userEdit && changed) {
71
+ recordControlUserEdit(
72
+ interactionContext,
73
+ controlId,
74
+ interaction === false ? undefined : interaction?.subject,
75
+ );
76
+ if (rootEl) emitControlChange(rootEl);
77
+ }
58
78
  }
59
79
  function setValue(next: unknown) {
60
80
  const candidate = String(next ?? '');
61
- const option = options.find(
62
- (item) => String(item.value) === candidate || item.label === candidate,
63
- );
81
+ const option = matchingOption(options, candidate, true);
64
82
  if (option) commit(option);
65
83
  else if (allowCustom) {
66
84
  value = candidate;
@@ -68,6 +86,13 @@ function setValue(next: unknown) {
68
86
  onvaluechange?.(candidate);
69
87
  }
70
88
  }
89
+ function prepareValue(next: unknown) {
90
+ const candidate = prepareTextControlValue(next);
91
+ const option = matchingOption(options, candidate, true);
92
+ if (option) return String(option.value);
93
+ if (allowCustom) return candidate;
94
+ return candidate;
95
+ }
71
96
  function handleInput(event: Event & { currentTarget: HTMLInputElement }) {
72
97
  query = event.currentTarget.value;
73
98
  open = true;
@@ -96,7 +121,7 @@ function handleKeydown(event: KeyboardEvent) {
96
121
  enabled[(position - 1 + enabled.length) % enabled.length] ?? 0;
97
122
  } else if (event.key === 'Enter' && open && filtered[activeIndex]) {
98
123
  event.preventDefault();
99
- commit(filtered[activeIndex]);
124
+ commit(filtered[activeIndex], true);
100
125
  } else if (event.key === 'Escape') {
101
126
  open = false;
102
127
  }
@@ -138,30 +163,40 @@ useControlRegistration(() => {
138
163
  options,
139
164
  },
140
165
  getValue: () => value,
166
+ prepareValue,
141
167
  setValue,
142
168
  clear: () => {
143
169
  value = '';
144
170
  query = '';
145
171
  onvaluechange?.('');
172
+ return true;
146
173
  },
147
174
  focus: () => input.focus(),
148
175
  reveal: () => revealControl(root),
149
176
  highlight: (durationMs) => highlightControl(root, durationMs),
150
177
  validate: () => input.reportValidity(),
178
+ validateValue: (next) => {
179
+ const candidate = String(next ?? '');
180
+ const option = matchingOption(options, candidate, true);
181
+ if (option) return option.disabled !== true;
182
+ return allowCustom && (!required || candidate.length > 0);
183
+ },
151
184
  getState: () => ({
152
- disabled,
185
+ disabled: input.matches(':disabled'),
153
186
  valid: input.validity.valid,
154
187
  validationMessage: input.validationMessage || undefined,
155
188
  }),
156
189
  };
157
190
  });
158
191
  </script>
159
- <div bind:this={rootEl} class="combobox {className}" data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}>
192
+ <div bind:this={rootEl} class="combobox {className}" data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}
193
+ data-smrt-subject-type={interaction === false ? undefined : interaction?.subject?.type}
194
+ data-smrt-subject-id={interaction === false ? undefined : interaction?.subject?.id}>
160
195
  <label for={inputId}>{label}</label><input bind:this={inputEl} id={inputId} {name} role="combobox" autocomplete="off" {placeholder} {disabled} {required} value={query}
161
196
  aria-expanded={open} aria-controls={listId} aria-autocomplete="list" aria-activedescendant={open && filtered[activeIndex] ? `${listId}-${activeIndex}` : undefined}
162
197
  onfocus={() => open = true} oninput={handleInput} onkeydown={handleKeydown} />
163
198
  {#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}
199
+ aria-selected={String(option.value) === value} class:active={index === activeIndex} disabled={option.disabled} onpointerdown={(event) => event.preventDefault()} onclick={() => commit(option, true)}>{option.label}</button>{/each}</div>{/if}
165
200
  </div>
166
201
  <style>
167
202
  .combobox { position: relative; display: grid; gap: var(--smrt-spacing-1); color: var(--smrt-color-on-surface); }
@@ -1 +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"}
1
+ {"version":3,"file":"Combobox.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Combobox.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,yBAAyB,EACzB,aAAa,EACd,MAAM,0BAA0B,CAAC;AAWlC,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;AA+KD,QAAA,MAAM,QAAQ,gDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -92,7 +92,7 @@ useControlRegistration(() => {
92
92
  highlight: (durationMs) => highlightControl(root, durationMs),
93
93
  validate: () => input.reportValidity(),
94
94
  getState: () => ({
95
- disabled: input.disabled,
95
+ disabled: input.matches(':disabled'),
96
96
  valid: input.validity.valid,
97
97
  validationMessage: input.validationMessage || undefined,
98
98
  }),
@@ -100,7 +100,10 @@ useControlRegistration(() => {
100
100
  });
101
101
  </script>
102
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; }}
103
+ data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}
104
+ data-smrt-subject-type={interaction === false ? undefined : interaction?.subject?.type}
105
+ data-smrt-subject-id={interaction === false ? undefined : interaction?.subject?.id}
106
+ ondragenter={(event) => { event.preventDefault(); dragging = true; }}
104
107
  ondragover={(event) => event.preventDefault()} ondragleave={() => dragging = false} ondrop={handleDrop}>
105
108
  <input bind:this={inputEl} id={resolvedId} type="file" {name} {accept} {multiple} {disabled} {required} onchange={handleChange} {...rest} />
106
109
  <span class="label">{label}</span><span class="description">{files.length ? `${files.length} file${files.length === 1 ? '' : 's'} selected` : description}</span>
@@ -25,6 +25,7 @@ import {
25
25
  createControlInteractionRegistry,
26
26
  } from './control-interaction.js';
27
27
  import { setControlInteractionContext } from './control-interaction-context.js';
28
+ import StagedControlReview from './StagedControlReview.svelte';
28
29
 
29
30
  export interface Props extends Omit<HTMLFormAttributes, 'class'> {
30
31
  class?: string;
@@ -35,6 +36,8 @@ export interface Props extends Omit<HTMLFormAttributes, 'class'> {
35
36
  interactionRegistry?: ControlInteractionRegistry;
36
37
  /** Receives registry lifecycle and command events. */
37
38
  oninteraction?: (event: ControlInteractionEvent) => void;
39
+ /** Render the built-in human review surface for staged changes. */
40
+ stagedReview?: boolean;
38
41
  children: Snippet;
39
42
  }
40
43
 
@@ -44,9 +47,13 @@ let {
44
47
  formId,
45
48
  interactionRegistry,
46
49
  oninteraction,
50
+ stagedReview = true,
47
51
  id,
48
52
  name,
49
53
  onsubmit,
54
+ onclick,
55
+ oninput,
56
+ onchange,
50
57
  children,
51
58
  ...rest
52
59
  }: Props = $props();
@@ -58,6 +65,7 @@ const resolvedFormId = $derived(formId ?? id ?? name ?? generatedFormId);
58
65
  const resolvedInteractionRegistry = $derived(
59
66
  interactionRegistry ?? localInteractionRegistry,
60
67
  );
68
+ let formElement = $state<HTMLFormElement | null>(null);
61
69
 
62
70
  setControlInteractionContext({
63
71
  get formId() {
@@ -85,17 +93,66 @@ function handleSubmit(event: SubmitEvent & { currentTarget: HTMLFormElement }) {
85
93
  if (preventDefault) event.preventDefault();
86
94
  onsubmit?.(event);
87
95
  }
96
+
97
+ function recordDirectUserEdit(event: Event) {
98
+ if (!event.isTrusted) return;
99
+ const target = event.target;
100
+ if (!(target instanceof Element)) return;
101
+ const control = target.closest<HTMLElement>('[data-smrt-control]');
102
+ const controlId = control?.dataset.smrtControl;
103
+ if (!controlId) return;
104
+ resolvedInteractionRegistry.recordUserEdit?.({
105
+ formId: resolvedFormId,
106
+ controlId,
107
+ subject:
108
+ control.dataset.smrtSubjectType && control.dataset.smrtSubjectId
109
+ ? {
110
+ type: control.dataset.smrtSubjectType,
111
+ id: control.dataset.smrtSubjectId,
112
+ }
113
+ : undefined,
114
+ });
115
+ }
116
+
117
+ function handleInput(event: Event & { currentTarget: HTMLFormElement }) {
118
+ try {
119
+ oninput?.(event);
120
+ } finally {
121
+ // Consumer handlers may synchronously normalize the bound value. Capture
122
+ // that final human value even when the handler throws.
123
+ recordDirectUserEdit(event);
124
+ }
125
+ }
126
+
127
+ function handleChange(event: Event & { currentTarget: HTMLFormElement }) {
128
+ try {
129
+ onchange?.(event);
130
+ } finally {
131
+ // Match input semantics for controls that commit via change.
132
+ recordDirectUserEdit(event);
133
+ }
134
+ }
88
135
  </script>
89
136
 
90
137
  <form
138
+ bind:this={formElement}
91
139
  id={id}
92
140
  name={name}
93
141
  class="form {className}"
94
142
  data-smrt-form={resolvedFormId}
95
143
  onsubmit={handleSubmit}
144
+ {onclick}
145
+ oninput={handleInput}
146
+ onchange={handleChange}
96
147
  {...rest}
97
148
  >
98
149
  {@render children()}
150
+ <StagedControlReview
151
+ registry={resolvedInteractionRegistry}
152
+ formId={resolvedFormId}
153
+ {formElement}
154
+ summary={stagedReview}
155
+ />
99
156
  </form>
100
157
 
101
158
  <!--
@@ -28,6 +28,8 @@ export interface Props extends Omit<HTMLFormAttributes, 'class'> {
28
28
  interactionRegistry?: ControlInteractionRegistry;
29
29
  /** Receives registry lifecycle and command events. */
30
30
  oninteraction?: (event: ControlInteractionEvent) => void;
31
+ /** Render the built-in human review surface for staged changes. */
32
+ stagedReview?: boolean;
31
33
  children: Snippet;
32
34
  }
33
35
  declare const Form: import("svelte").Component<Props, {
@@ -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;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
+ {"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;AAKlC,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,mEAAmE;IACnE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAgHD,QAAA,MAAM,IAAI;kCA9D0B,0BAA0B;qBAIvC,MAAM;MA0DqB,CAAC;AACnD,KAAK,IAAI,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;AACpC,eAAe,IAAI,CAAC"}
@@ -10,7 +10,12 @@ import type {
10
10
  ControlKind,
11
11
  } from './control-interaction.js';
12
12
  import { tryGetControlInteractionContext } from './control-interaction-context.js';
13
+ import {
14
+ prepareNumberControlValue,
15
+ prepareTextControlValue,
16
+ } from './control-value-validation.js';
13
17
  import { tryGetFormGroupContext } from './form-group-context.js';
18
+ import { useControlRegistration } from './use-control-registration.svelte.js';
14
19
 
15
20
  export interface Props extends Omit<HTMLInputAttributes, 'class' | 'value'> {
16
21
  value?: string | number;
@@ -83,23 +88,54 @@ function kindFromType(): ControlKind {
83
88
 
84
89
  function setControlValue(next: unknown) {
85
90
  value =
86
- type === 'number' || type === 'range' ? Number(next) : String(next ?? '');
91
+ type === 'number' || type === 'range'
92
+ ? next === '' || next === null || next === undefined
93
+ ? ''
94
+ : Number(next)
95
+ : String(next ?? '');
87
96
  if (inputEl) emitControlChange(inputEl);
88
97
  }
89
98
 
90
- $effect(() => {
91
- const context = controlInteraction;
99
+ function validateControlValue(next: unknown) {
100
+ if (!inputEl) return true;
101
+ if (
102
+ (type === 'number' || type === 'range') &&
103
+ next !== '' &&
104
+ next !== null &&
105
+ next !== undefined &&
106
+ !Number.isFinite(typeof next === 'number' ? next : Number(next))
107
+ ) {
108
+ return { valid: false, message: 'invalid_number' };
109
+ }
110
+ const candidate = inputEl.cloneNode() as HTMLInputElement;
111
+ const proposedValue = String(next ?? '');
112
+ try {
113
+ candidate.value = proposedValue;
114
+ } catch {
115
+ return { valid: false, message: 'invalid_value' };
116
+ }
117
+ // Native inputs sanitize some assignments without making them invalid: an
118
+ // optional invalid date/time becomes empty and a range value can clamp to a
119
+ // bound. A staged proposal must never be marked applicable when the setter
120
+ // would apply a different value than the one reviewed.
121
+ if (candidate.value !== proposedValue) {
122
+ return { valid: false, message: 'invalid_value' };
123
+ }
124
+ return {
125
+ valid: candidate.checkValidity(),
126
+ message: candidate.validationMessage || undefined,
127
+ };
128
+ }
129
+
130
+ useControlRegistration(() => {
92
131
  const element = inputEl;
93
132
  const controlId = resolvedControlId;
94
133
  const options = resolvedInteraction;
95
- if (!context || !element || !controlId || options === false) return;
134
+ if (!element || !controlId || options === false) return false;
96
135
 
97
- return context.registry.register({
98
- identity: {
99
- formId: context.formId,
100
- controlId,
101
- subject: options.subject,
102
- },
136
+ return {
137
+ controlId,
138
+ subject: options.subject,
103
139
  metadata: {
104
140
  kind: kindFromType(),
105
141
  label: formGroup?.().label ?? ariaLabel ?? undefined,
@@ -119,19 +155,27 @@ $effect(() => {
119
155
  },
120
156
  },
121
157
  getValue: () => value,
158
+ prepareValue: (next) =>
159
+ type === 'number' || type === 'range'
160
+ ? prepareNumberControlValue(next)
161
+ : prepareTextControlValue(next),
122
162
  setValue: setControlValue,
123
- clear: () => setControlValue(''),
163
+ clear: () => {
164
+ setControlValue('');
165
+ return true;
166
+ },
124
167
  focus: () => element.focus(),
125
168
  reveal: () => revealControl(element),
126
169
  highlight: (durationMs) => highlightControl(element, durationMs),
127
170
  validate: () => element.reportValidity(),
171
+ validateValue: validateControlValue,
128
172
  getState: () => ({
129
- disabled: element.disabled,
173
+ disabled: element.matches(':disabled'),
130
174
  readonly: element.readOnly,
131
175
  valid: element.validity.valid,
132
176
  validationMessage: element.validationMessage || undefined,
133
177
  }),
134
- });
178
+ };
135
179
  });
136
180
 
137
181
  export function focus(): void {
@@ -167,6 +211,8 @@ export function getElement(): HTMLInputElement | null {
167
211
  class="input {className}"
168
212
  data-smrt-control={resolvedControlId}
169
213
  data-smrt-form={controlInteraction?.formId}
214
+ data-smrt-subject-type={resolvedInteraction === false ? undefined : resolvedInteraction.subject?.type}
215
+ data-smrt-subject-id={resolvedInteraction === false ? undefined : resolvedInteraction.subject?.id}
170
216
  {...rest}
171
217
  />
172
218
 
@@ -1 +1 @@
1
- {"version":3,"file":"Input.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Input.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAM3D,OAAO,KAAK,EACV,yBAAyB,EAE1B,MAAM,0BAA0B,CAAC;AAKlC,MAAM,WAAW,KAAM,SAAQ,IAAI,CAAC,mBAAmB,EAAE,OAAO,GAAG,OAAO,CAAC;IACzE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;CACjD;AAmJD,QAAA,MAAM,KAAK;iBAvBQ,IAAI;kBAIH,IAAI;6BAIS,MAAM,KAAG,IAAI;sBAItB,gBAAgB,GAAG,IAAI;WAWI,CAAC;AACpD,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AACtC,eAAe,KAAK,CAAC"}
1
+ {"version":3,"file":"Input.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Input.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAM3D,OAAO,KAAK,EACV,yBAAyB,EAE1B,MAAM,0BAA0B,CAAC;AAUlC,MAAM,WAAW,KAAM,SAAQ,IAAI,CAAC,mBAAmB,EAAE,OAAO,GAAG,OAAO,CAAC;IACzE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;CACjD;AA4LD,QAAA,MAAM,KAAK;iBAvBQ,IAAI;kBAIH,IAAI;6BAIS,MAAM,KAAG,IAAI;sBAItB,gBAAgB,GAAG,IAAI;WAWI,CAAC;AACpD,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AACtC,eAAe,KAAK,CAAC"}
@@ -1,10 +1,21 @@
1
1
  <script lang="ts">
2
- import { highlightControl, revealControl } from './control-dom.js';
2
+ import {
3
+ emitControlChange,
4
+ highlightControl,
5
+ revealControl,
6
+ } from './control-dom.js';
3
7
  import type {
4
8
  ControlInteractionOptions,
5
9
  ControlOption,
6
10
  } from './control-interaction.js';
7
- import { tryGetControlInteractionContext } from './control-interaction-context.js';
11
+ import {
12
+ recordControlUserEdit,
13
+ tryGetControlInteractionContext,
14
+ } from './control-interaction-context.js';
15
+ import {
16
+ prepareEnabledOptionValue,
17
+ validatesEnabledOption,
18
+ } from './control-value-validation.js';
8
19
  import { useControlRegistration } from './use-control-registration.svelte.js';
9
20
  export interface Props {
10
21
  options: ControlOption[];
@@ -35,11 +46,20 @@ const controlId = $derived(
35
46
  ? undefined
36
47
  : (interaction?.id ?? name ?? `listbox-${instanceId}`),
37
48
  );
38
- function select(next: unknown) {
49
+ function select(next: unknown, userEdit = false) {
39
50
  const option = options.find((item) => String(item.value) === String(next));
40
51
  if (!option || option.disabled || disabled) return;
52
+ const changed = !Object.is(value, option.value);
41
53
  value = option.value;
42
54
  onvaluechange?.(option.value);
55
+ if (userEdit && changed) {
56
+ recordControlUserEdit(
57
+ interactionContext,
58
+ controlId,
59
+ interaction === false ? undefined : interaction?.subject,
60
+ );
61
+ if (rootEl) emitControlChange(rootEl);
62
+ }
43
63
  }
44
64
  function move(event: KeyboardEvent, index: number) {
45
65
  let next = index;
@@ -73,21 +93,28 @@ useControlRegistration(() => {
73
93
  options,
74
94
  },
75
95
  getValue: () => value,
96
+ prepareValue: (next) => prepareEnabledOptionValue(options, next),
76
97
  setValue: select,
77
98
  clear: () => {
78
99
  value = undefined;
100
+ return true;
79
101
  },
80
102
  focus: () => optionEls.find((item) => item && !item.disabled)?.focus(),
81
103
  reveal: () => revealControl(root),
82
104
  highlight: (durationMs) => highlightControl(root, durationMs),
83
- getState: () => ({ disabled }),
105
+ validateValue: (next) => validatesEnabledOption(options, next),
106
+ getState: () => ({
107
+ disabled: disabled || root.closest('fieldset:disabled') !== null,
108
+ }),
84
109
  };
85
110
  });
86
111
  </script>
87
- <div bind:this={rootEl} class="listbox {className}" role="listbox" aria-label={label} aria-disabled={disabled} data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}>
112
+ <div bind:this={rootEl} class="listbox {className}" role="listbox" aria-label={label} aria-disabled={disabled} data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}
113
+ data-smrt-subject-type={interaction === false ? undefined : interaction?.subject?.type}
114
+ data-smrt-subject-id={interaction === false ? undefined : interaction?.subject?.id}>
88
115
  {#each options as option, index (option.value)}<button bind:this={optionEls[index]} type="button" role="option" aria-selected={value === option.value}
89
116
  disabled={disabled || option.disabled} tabindex={value === option.value || (value === undefined && index === 0) ? 0 : -1}
90
- onkeydown={(event) => move(event, index)} onclick={() => select(option.value)}>{option.label}</button>{/each}
117
+ onkeydown={(event) => move(event, index)} onclick={() => select(option.value, true)}>{option.label}</button>{/each}
91
118
  </div>
92
119
  <style>
93
120
  .listbox { display: grid; padding: var(--smrt-spacing-1); border: 1px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface); }
@@ -1 +1 @@
1
- {"version":3,"file":"Listbox.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Listbox.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,GAAG,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAkFD,QAAA,MAAM,OAAO,gDAAwC,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"Listbox.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Listbox.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,yBAAyB,EACzB,aAAa,EACd,MAAM,0BAA0B,CAAC;AAWlC,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAiGD,QAAA,MAAM,OAAO,gDAAwC,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}