@happyvertical/smrt-ui 0.55.3 → 0.55.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.
package/AGENTS.md CHANGED
@@ -17,7 +17,7 @@ SMRT has one shared set of UI primitives, split across two packages by concern:
17
17
  `Tooltip`, `Skeleton`, `Tree`, `Pagination`, `DataTable`, `ContentList`, … — **plus the
18
18
  Provider-free base FORM primitives** under `./forms` (`Form`, `Input`,
19
19
  `Select`, `Textarea`, `Checkbox`, `Switch`, `RadioGroup`, `Slider`,
20
- `RangeSlider`, `Combobox`, `MultiSelect`, `TagsInput`, `FilePicker`,
20
+ `RangeSlider`, `Combobox`, `RelationInput`, `MultiSelect`, `TagsInput`, `FilePicker`,
21
21
  `FormGroup`), relocated here in #1589's
22
22
  deferred-forms phase so domain packages can adopt them without pulling in the
23
23
  smrt-svelte Provider or closing a build-graph cycle. These are dependency-free:
package/README.md CHANGED
@@ -32,7 +32,7 @@ pnpm add @happyvertical/smrt-ui
32
32
  | Area | Components |
33
33
  | --- | --- |
34
34
  | Fields | `Form`, `Field`/`FormGroup`, `Fieldset`, `InputGroup`, `ErrorSummary`, `FormActionBar` |
35
- | Text and structured input | `Input`, `Textarea`, `Select`, `Combobox`, `Listbox`, `MultiSelect`, `TagsInput`, `SearchInput` |
35
+ | Text and structured input | `Input`, `Textarea`, `Select`, `Combobox`, `RelationInput`, `Listbox`, `MultiSelect`, `TagsInput`, `SearchInput` |
36
36
  | Choices | `Checkbox`, `RadioGroup`/`Radio`, `Switch`, `Toggle`, `ToggleButton`, `SegmentedControl` |
37
37
  | Values and files | `Slider`, `RangeSlider`, `DatePicker`, `TimePicker`, `FilePicker` |
38
38
  | Capture | `CameraCapture`, `SignaturePad` |
@@ -130,6 +130,50 @@ controls with no name or an empty name are omitted. Disabled controls, including
130
130
  those inside disabled fieldsets, are omitted by native `FormData`. Native form
131
131
  reset restores the initial selection and its label, unless reset is canceled.
132
132
 
133
+ ### RelationInput
134
+
135
+ `RelationInput` (`/forms`) moved here from `@happyvertical/smrt-svelte/forms` in #3637 (originally #3600); smrt-svelte keeps a deprecated re-export. Labels are props (`createLabel`, `clearLabel`, `loadingText`, ...); there is no message catalog.
136
+
137
+ Searchable single-select for relation (foreign-key) fields, a thin wrapper over
138
+ `Combobox`. It is presentation only: the caller supplies the data,
139
+ so the same lookup an assistant uses to turn "Acme" into a record id backs the
140
+ picker. Option shape is `RelationOption` — `{ id, label, detail? }`.
141
+
142
+ - **Props.** `name`, `label`, `value` (bindable id, `''` = none), `required`,
143
+ `disabled`, `error`, `placeholder`, `description`, `search(query)`, optional
144
+ `resolve(id)` and `onCreate(query)`, plus `debounceMs` (250), `interaction`,
145
+ `onchange` and overridable text (`createLabel`, `clearLabel`, `loadingText`,
146
+ `emptyText`, `errorText`, `resultsText`).
147
+ - **`search`** runs with `''` when the list opens and then debounced per
148
+ keystroke; a response for a superseded query is dropped. A rejected search
149
+ shows `errorText` and the next keystroke retries.
150
+ - **`resolve`** supplies the label of a `value` that has not been searched yet
151
+ (initial value, parent rebind, form reset). Without it such a value shows an
152
+ empty field, never the raw id. A `null` result also leaves the field empty.
153
+ - **`onCreate`** adds a "New ..." button; it receives the text most recently
154
+ searched for and may return the created `RelationOption`, which is selected.
155
+ - **Form integration** is through `Combobox`'s control registration (`Form`
156
+ provides the registry), not a `FieldDefinition`: agents see a `combobox`
157
+ control whose value is the id. The form posts the id under `name`.
158
+ - **Known limit.** An agent staging a value through the registered control can
159
+ only choose among the options last listed (the registry matches against
160
+ `options`; `allowCustom` is off), so it cannot write an id it has not
161
+ searched. It fails closed. Writing a resolved id needs an async hook on
162
+ `Combobox` and is tracked separately.
163
+ - **Stale options.** Typing clears the listed options until the new search
164
+ answers, so Enter or a click can never pick a record for an older query.
165
+ - **Clear button** shows only when not `required`, not `disabled`, and a value
166
+ is set; it returns focus to the field.
167
+ - **Accessibility** is the combobox pattern (`role=combobox`, `aria-expanded`,
168
+ `aria-activedescendant`, listbox/option, Arrow/Enter/Escape) plus a polite
169
+ `role=status` live region announcing searching / "N results" / no matches,
170
+ `aria-busy` while fetching, and `aria-invalid` + `aria-describedby` for
171
+ `error` and `description`.
172
+ - Options render `detail` under the label, so the option's accessible name
173
+ includes it. The `Combobox` additions that make this possible (`filter`,
174
+ `onquery`, `status`, `busy`, `invalid`, `describedby`, `optionContent`) are
175
+ generic and default to the previous behaviour.
176
+
133
177
  ### MultiSelect form submission
134
178
 
135
179
  A named `MultiSelect` submits one hidden native input per selected option value,
@@ -0,0 +1,378 @@
1
+ <script lang="ts">
2
+ import { untrack } from 'svelte';
3
+ import Button from '../ui/Button.svelte';
4
+ import Combobox from './Combobox.svelte';
5
+ import { emitControlChange } from './control-dom.js';
6
+ import type { ControlInteractionOptions } from './control-interaction.js';
7
+ import {
8
+ recordControlUserEdit,
9
+ tryGetControlInteractionContext,
10
+ } from './control-interaction-context.js';
11
+ import type { RelationOption } from './types.js';
12
+
13
+ /**
14
+ * RelationInput - searchable single-select for relation (foreign key) fields.
15
+ *
16
+ * Presentation only: the caller supplies `search` (and optionally `resolve`),
17
+ * so the same lookup an assistant uses to turn "Acme" into a record id backs
18
+ * the picker. Built on the smrt-ui `Combobox`, which supplies the ARIA 1.2
19
+ * combobox pattern (arrow keys, Enter, Escape, `aria-activedescendant`) and
20
+ * registers with the rich Form's control registry. The form posts the
21
+ * committed id under `name`, never the visible label.
22
+ *
23
+ * @example
24
+ * ```svelte
25
+ * <RelationInput
26
+ * name="customerId"
27
+ * label="Customer"
28
+ * bind:value={customerId}
29
+ * search={(q) => customers.search(q)}
30
+ * resolve={(id) => customers.byId(id)}
31
+ * />
32
+ * ```
33
+ */
34
+ export interface Props {
35
+ /** Field name; the selected id posts under it. */
36
+ name: string;
37
+ /** Visible label. */
38
+ label: string;
39
+ /** Selected record id (bindable). Empty string means no selection. */
40
+ value?: string;
41
+ /** Required field: hides the clear button and flags an empty value. */
42
+ required?: boolean;
43
+ /** Disabled state. */
44
+ disabled?: boolean;
45
+ /** Validation error message, shown below the field. */
46
+ error?: string;
47
+ /** Placeholder shown when nothing is selected. */
48
+ placeholder?: string;
49
+ /** Hint announced with the field. */
50
+ description?: string;
51
+ /** Look up records matching the text; called with '' when the list opens. */
52
+ search: (query: string) => Promise<RelationOption[]>;
53
+ /** Look up the current value's label when it has not been searched yet. */
54
+ resolve?: (id: string) => Promise<{ id: string; label: string } | null>;
55
+ /**
56
+ * "New ..." action. Receives the text most recently searched for. May
57
+ * return the created record, which is then selected.
58
+ */
59
+ onCreate?: (
60
+ query: string,
61
+ ) => void | RelationOption | Promise<void | RelationOption>;
62
+ /** Milliseconds to wait after typing before searching. */
63
+ debounceMs?: number;
64
+ /** Combobox interaction options (stable id, subject) for agents. */
65
+ interaction?: ControlInteractionOptions | false;
66
+ /** Text for the "New ..." button. */
67
+ createLabel?: string;
68
+ /** Accessible name for the clear button. */
69
+ clearLabel?: string;
70
+ /** Status text while searching. */
71
+ loadingText?: string;
72
+ /** Status text when nothing matches. */
73
+ emptyText?: string;
74
+ /** Status text when the search fails. */
75
+ errorText?: string;
76
+ /** Status text announcing how many results are listed. */
77
+ resultsText?: (count: number) => string;
78
+ /** Called when the selected id changes ('' when cleared). */
79
+ onchange?: (value: string) => void;
80
+ }
81
+
82
+ let {
83
+ name,
84
+ label,
85
+ value = $bindable(''),
86
+ required = false,
87
+ disabled = false,
88
+ error,
89
+ placeholder,
90
+ description,
91
+ search,
92
+ resolve,
93
+ onCreate,
94
+ debounceMs = 250,
95
+ interaction,
96
+ createLabel,
97
+ clearLabel,
98
+ loadingText = 'Searching…',
99
+ emptyText = 'No matches',
100
+ errorText = 'Search failed. Try again.',
101
+ resultsText = (count: number) =>
102
+ `${count} ${count === 1 ? 'result' : 'results'} available`,
103
+ onchange,
104
+ }: Props = $props();
105
+
106
+ const instanceId = $props.id();
107
+ const errorId = `${instanceId}-error`;
108
+ const hintId = `${instanceId}-hint`;
109
+ const interactionContext = tryGetControlInteractionContext();
110
+
111
+ let rootEl = $state<HTMLDivElement | null>(null);
112
+ let results = $state<RelationOption[]>([]);
113
+ let loading = $state(false);
114
+ let failed = $state(false);
115
+ let selectedLabel = $state('');
116
+ let creating = $state(false);
117
+
118
+ // Plain bookkeeping, deliberately not reactive.
119
+ let labelledId = '';
120
+ let resolvingId = '';
121
+ let searchSeq = 0;
122
+ let lastQuery = '';
123
+ let timer: ReturnType<typeof setTimeout> | undefined;
124
+
125
+ const options = $derived(
126
+ results.map((item) => ({ value: item.id, label: item.label })),
127
+ );
128
+ const detailById = $derived(
129
+ new Map(results.map((item) => [item.id, item.detail])),
130
+ );
131
+ const status = $derived(
132
+ loading
133
+ ? loadingText
134
+ : failed
135
+ ? errorText
136
+ : results.length === 0
137
+ ? emptyText
138
+ : resultsText(results.length),
139
+ );
140
+ const describedby = $derived(
141
+ [error ? errorId : '', description ? hintId : ''].filter(Boolean).join(' ') ||
142
+ undefined,
143
+ );
144
+ const controlId = $derived(
145
+ interaction === false ? undefined : (interaction?.id ?? name),
146
+ );
147
+ const canClear = $derived(!required && !disabled && value !== '');
148
+ const newLabel = $derived(createLabel ?? `New ${label.toLowerCase()}`);
149
+
150
+ async function runSearch(query: string) {
151
+ const seq = ++searchSeq;
152
+ loading = true;
153
+ failed = false;
154
+ try {
155
+ const found = await search(query);
156
+ if (seq !== searchSeq) return;
157
+ results = found;
158
+ } catch (err) {
159
+ if (seq !== searchSeq) return;
160
+ results = [];
161
+ failed = true;
162
+ console.warn('[smrt-ui] RelationInput search failed', {
163
+ error: String(err),
164
+ });
165
+ } finally {
166
+ if (seq === searchSeq) loading = false;
167
+ }
168
+ }
169
+
170
+ function handleQuery(query: string) {
171
+ clearTimeout(timer);
172
+ lastQuery = query;
173
+ if (query === '') {
174
+ // Opening the list: no typing to wait for.
175
+ void runSearch('');
176
+ return;
177
+ }
178
+ // Show "Searching" straight away and drop any in-flight older request. The
179
+ // listed options answered an older query, so none stay selectable (Enter or
180
+ // a click would otherwise pick a record that no longer matches the text).
181
+ searchSeq++;
182
+ loading = true;
183
+ failed = false;
184
+ results = [];
185
+ timer = setTimeout(() => void runSearch(query), debounceMs);
186
+ }
187
+
188
+ function handleValueChange(next: string) {
189
+ lastQuery = '';
190
+ const hit = results.find((item) => item.id === next);
191
+ if (hit) {
192
+ selectedLabel = hit.label;
193
+ labelledId = next;
194
+ }
195
+ onchange?.(next);
196
+ }
197
+
198
+ function focusInput() {
199
+ rootEl?.querySelector<HTMLInputElement>('input[role="combobox"]')?.focus();
200
+ }
201
+
202
+ function clear() {
203
+ if (!canClear) return;
204
+ value = '';
205
+ selectedLabel = '';
206
+ labelledId = '';
207
+ lastQuery = '';
208
+ onchange?.('');
209
+ recordControlUserEdit(
210
+ interactionContext,
211
+ controlId,
212
+ interaction === false ? undefined : interaction?.subject,
213
+ );
214
+ if (rootEl) emitControlChange(rootEl);
215
+ focusInput();
216
+ }
217
+
218
+ async function create() {
219
+ if (!onCreate || creating || disabled) return;
220
+ // The field has already lost focus (and reset its text) by now, so use the
221
+ // text most recently searched for.
222
+ const typed = lastQuery;
223
+ creating = true;
224
+ try {
225
+ const made = await onCreate(typed);
226
+ if (made && typeof made === 'object') {
227
+ results = [made, ...results.filter((item) => item.id !== made.id)];
228
+ selectedLabel = made.label;
229
+ labelledId = made.id;
230
+ value = made.id;
231
+ onchange?.(made.id);
232
+ recordControlUserEdit(
233
+ interactionContext,
234
+ controlId,
235
+ interaction === false ? undefined : interaction?.subject,
236
+ );
237
+ if (rootEl) emitControlChange(rootEl);
238
+ }
239
+ } catch (err) {
240
+ console.warn('[smrt-ui] RelationInput onCreate failed', {
241
+ error: String(err),
242
+ });
243
+ } finally {
244
+ creating = false;
245
+ }
246
+ }
247
+
248
+ // Keep the visible label in step with `value` however it changed (initial
249
+ // value, parent rebind, form reset): known from a search, else resolved.
250
+ $effect(() => {
251
+ const id = value;
252
+ const known = results.find((item) => item.id === id);
253
+ untrack(() => {
254
+ if (!id) {
255
+ selectedLabel = '';
256
+ labelledId = '';
257
+ return;
258
+ }
259
+ if (id === labelledId) return;
260
+ if (known) {
261
+ selectedLabel = known.label;
262
+ labelledId = id;
263
+ return;
264
+ }
265
+ // A different id than the one labelled: never keep the previous label.
266
+ selectedLabel = '';
267
+ labelledId = '';
268
+ if (!resolve || resolvingId === id) return;
269
+ resolvingId = id;
270
+ resolve(id)
271
+ .then((record) => {
272
+ if (value === id && record) {
273
+ selectedLabel = record.label;
274
+ labelledId = id;
275
+ }
276
+ })
277
+ .catch((err) => {
278
+ console.warn('[smrt-ui] RelationInput resolve failed', {
279
+ error: String(err),
280
+ });
281
+ })
282
+ .finally(() => {
283
+ if (resolvingId === id) resolvingId = '';
284
+ });
285
+ });
286
+ });
287
+
288
+ $effect(() => () => clearTimeout(timer));
289
+ </script>
290
+
291
+ <div class="relation-input" bind:this={rootEl}>
292
+ <div class="field">
293
+ <Combobox
294
+ {name}
295
+ {label}
296
+ {options}
297
+ {placeholder}
298
+ {disabled}
299
+ {required}
300
+ {interaction}
301
+ {status}
302
+ {describedby}
303
+ bind:value
304
+ valueLabel={selectedLabel}
305
+ filter={false}
306
+ busy={loading}
307
+ invalid={!!error}
308
+ onquery={handleQuery}
309
+ onvaluechange={handleValueChange}
310
+ >
311
+ {#snippet optionContent(option)}
312
+ <span class="option-label">{option.label}</span>
313
+ {#if detailById.get(String(option.value))}
314
+ <span class="option-detail">{detailById.get(String(option.value))}</span>
315
+ {/if}
316
+ {/snippet}
317
+ </Combobox>
318
+ {#if canClear}
319
+ <Button
320
+ variant="ghost"
321
+ size="sm"
322
+ class="clear"
323
+ aria-label={clearLabel ?? `Clear ${label}`}
324
+ onclick={clear}
325
+ >
326
+ &times;
327
+ </Button>
328
+ {/if}
329
+ </div>
330
+ {#if description}<p id={hintId} class="hint">{description}</p>{/if}
331
+ {#if onCreate}
332
+ <Button
333
+ variant="ghost"
334
+ size="sm"
335
+ class="create"
336
+ disabled={disabled || creating}
337
+ onclick={create}
338
+ >
339
+ + {newLabel}
340
+ </Button>
341
+ {/if}
342
+ {#if error}<p id={errorId} class="error" role="alert">{error}</p>{/if}
343
+ </div>
344
+
345
+ <style>
346
+ .relation-input {
347
+ display: grid;
348
+ gap: var(--smrt-spacing-1, 4px);
349
+ min-width: 0;
350
+ }
351
+ .field {
352
+ position: relative;
353
+ }
354
+ .field :global(.clear) {
355
+ position: absolute;
356
+ right: var(--smrt-spacing-1, 4px);
357
+ bottom: var(--smrt-spacing-1, 4px);
358
+ }
359
+ .option-label,
360
+ .option-detail {
361
+ display: block;
362
+ }
363
+ .option-detail {
364
+ font-size: var(--smrt-typography-body-small-size, 0.75rem);
365
+ color: var(--smrt-color-on-surface-variant);
366
+ }
367
+ .hint,
368
+ .error {
369
+ margin: 0;
370
+ font-size: var(--smrt-typography-body-small-size, 0.75rem);
371
+ }
372
+ .hint {
373
+ color: var(--smrt-color-on-surface-variant);
374
+ }
375
+ .error {
376
+ color: var(--smrt-color-error);
377
+ }
378
+ </style>
@@ -0,0 +1,75 @@
1
+ import type { ControlInteractionOptions } from './control-interaction.js';
2
+ import type { RelationOption } from './types.js';
3
+ /**
4
+ * RelationInput - searchable single-select for relation (foreign key) fields.
5
+ *
6
+ * Presentation only: the caller supplies `search` (and optionally `resolve`),
7
+ * so the same lookup an assistant uses to turn "Acme" into a record id backs
8
+ * the picker. Built on the smrt-ui `Combobox`, which supplies the ARIA 1.2
9
+ * combobox pattern (arrow keys, Enter, Escape, `aria-activedescendant`) and
10
+ * registers with the rich Form's control registry. The form posts the
11
+ * committed id under `name`, never the visible label.
12
+ *
13
+ * @example
14
+ * ```svelte
15
+ * <RelationInput
16
+ * name="customerId"
17
+ * label="Customer"
18
+ * bind:value={customerId}
19
+ * search={(q) => customers.search(q)}
20
+ * resolve={(id) => customers.byId(id)}
21
+ * />
22
+ * ```
23
+ */
24
+ export interface Props {
25
+ /** Field name; the selected id posts under it. */
26
+ name: string;
27
+ /** Visible label. */
28
+ label: string;
29
+ /** Selected record id (bindable). Empty string means no selection. */
30
+ value?: string;
31
+ /** Required field: hides the clear button and flags an empty value. */
32
+ required?: boolean;
33
+ /** Disabled state. */
34
+ disabled?: boolean;
35
+ /** Validation error message, shown below the field. */
36
+ error?: string;
37
+ /** Placeholder shown when nothing is selected. */
38
+ placeholder?: string;
39
+ /** Hint announced with the field. */
40
+ description?: string;
41
+ /** Look up records matching the text; called with '' when the list opens. */
42
+ search: (query: string) => Promise<RelationOption[]>;
43
+ /** Look up the current value's label when it has not been searched yet. */
44
+ resolve?: (id: string) => Promise<{
45
+ id: string;
46
+ label: string;
47
+ } | null>;
48
+ /**
49
+ * "New ..." action. Receives the text most recently searched for. May
50
+ * return the created record, which is then selected.
51
+ */
52
+ onCreate?: (query: string) => void | RelationOption | Promise<void | RelationOption>;
53
+ /** Milliseconds to wait after typing before searching. */
54
+ debounceMs?: number;
55
+ /** Combobox interaction options (stable id, subject) for agents. */
56
+ interaction?: ControlInteractionOptions | false;
57
+ /** Text for the "New ..." button. */
58
+ createLabel?: string;
59
+ /** Accessible name for the clear button. */
60
+ clearLabel?: string;
61
+ /** Status text while searching. */
62
+ loadingText?: string;
63
+ /** Status text when nothing matches. */
64
+ emptyText?: string;
65
+ /** Status text when the search fails. */
66
+ errorText?: string;
67
+ /** Status text announcing how many results are listed. */
68
+ resultsText?: (count: number) => string;
69
+ /** Called when the selected id changes ('' when cleared). */
70
+ onchange?: (value: string) => void;
71
+ }
72
+ declare const RelationInput: import("svelte").Component<Props, {}, "value">;
73
+ type RelationInput = ReturnType<typeof RelationInput>;
74
+ export default RelationInput;
75
+ //# sourceMappingURL=RelationInput.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RelationInput.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/RelationInput.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAK1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,KAAK;IACpB,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IACrD,2EAA2E;IAC3E,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACxE;;;OAGG;IACH,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,KACV,IAAI,GAAG,cAAc,GAAG,OAAO,CAAC,IAAI,GAAG,cAAc,CAAC,CAAC;IAC5D,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,WAAW,CAAC,EAAE,yBAAyB,GAAG,KAAK,CAAC;IAChD,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAyPD,QAAA,MAAM,aAAa,gDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -47,6 +47,7 @@ export { default as ProvinceSelect } from './ProvinceSelect.svelte';
47
47
  export { default as Radio } from './Radio.svelte';
48
48
  export { default as RadioGroup } from './RadioGroup.svelte';
49
49
  export { default as RangeSlider } from './RangeSlider.svelte';
50
+ export { default as RelationInput } from './RelationInput.svelte';
50
51
  export { playReadyBeep, primeReadyBeep, type ReadyBeepOptions, } from './ready-beep.js';
51
52
  export { default as SearchInput } from './SearchInput.svelte';
52
53
  export { default as SegmentedControl } from './SegmentedControl.svelte';
@@ -62,6 +63,6 @@ export { default as Textarea } from './Textarea.svelte';
62
63
  export { default as TimePicker } from './TimePicker.svelte';
63
64
  export { default as Toggle } from './Toggle.svelte';
64
65
  export { default as ToggleButton } from './ToggleButton.svelte';
65
- export type { CameraCaptureLabels, CameraCaptureState, CaptureResult, FormError, RangeSliderValue, SegmentedControlOption, SignaturePadLabels, SignaturePadState, } from './types.js';
66
+ export type { CameraCaptureLabels, CameraCaptureState, CaptureResult, FormError, RangeSliderValue, RelationOption, SegmentedControlOption, SignaturePadLabels, SignaturePadState, } from './types.js';
66
67
  export { type ControlRegistrationDescriptor, useControlRegistration, } from './use-control-registration.svelte.js';
67
68
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/forms/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/forms/index.ts"],"names":[],"mappings":"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"}
@@ -48,6 +48,7 @@ export { default as ProvinceSelect } from './ProvinceSelect.svelte';
48
48
  export { default as Radio } from './Radio.svelte';
49
49
  export { default as RadioGroup } from './RadioGroup.svelte';
50
50
  export { default as RangeSlider } from './RangeSlider.svelte';
51
+ export { default as RelationInput } from './RelationInput.svelte';
51
52
  export { playReadyBeep, primeReadyBeep, } from './ready-beep.js';
52
53
  export { default as SearchInput } from './SearchInput.svelte';
53
54
  export { default as SegmentedControl } from './SegmentedControl.svelte';
@@ -52,4 +52,13 @@ export interface SignaturePadLabels {
52
52
  useSignature: string;
53
53
  committed: string;
54
54
  }
55
+ /** A searchable relation target for the RelationInput component */
56
+ export interface RelationOption {
57
+ /** Record id; this is the value the form posts. */
58
+ id: string;
59
+ /** Human-readable name shown in the field and the list. */
60
+ label: string;
61
+ /** Secondary text shown under the label in the list. */
62
+ detail?: string;
63
+ }
55
64
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/forms/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,mEAAmE;AACnE,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,IAAI,EAAE,IAAI,CAAC;IACX,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,iFAAiF;AACjF,MAAM,MAAM,kBAAkB,GAC1B,KAAK,GACL,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,mBAAmB,GACnB,WAAW,GACX,aAAa,GACb,OAAO,GACP,UAAU,CAAC;AAEf,oFAAoF;AACpF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEjE,mFAAmF;AACnF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/forms/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,mEAAmE;AACnE,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,IAAI,EAAE,IAAI,CAAC;IACX,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,iFAAiF;AACjF,MAAM,MAAM,kBAAkB,GAC1B,KAAK,GACL,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,mBAAmB,GACnB,WAAW,GACX,aAAa,GACb,OAAO,GACP,UAAU,CAAC;AAEf,oFAAoF;AACpF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEjE,mFAAmF;AACnF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,mEAAmE;AACnE,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
@@ -13,6 +13,7 @@ import MultiSelect from '../../components/forms/MultiSelect.svelte';
13
13
  import Radio from '../../components/forms/Radio.svelte';
14
14
  import RadioGroup from '../../components/forms/RadioGroup.svelte';
15
15
  import RangeSlider from '../../components/forms/RangeSlider.svelte';
16
+ import RelationInput from '../../components/forms/RelationInput.svelte';
16
17
  import SegmentedControl from '../../components/forms/SegmentedControl.svelte';
17
18
  import Slider from '../../components/forms/Slider.svelte';
18
19
  import Switch from '../../components/forms/Switch.svelte';
@@ -27,6 +28,26 @@ const countries = [
27
28
  { value: 'us', label: 'United States' },
28
29
  { value: 'mx', label: 'Mexico' },
29
30
  ];
31
+ // RelationInput demo: a caller-supplied lookup standing in for a REST search.
32
+ const demoCustomers = [
33
+ { id: 'cus_001', label: 'Acme Corp', detail: 'acme@example.com' },
34
+ { id: 'cus_002', label: 'Globex', detail: 'billing@globex.example' },
35
+ { id: 'cus_003', label: 'Initech', detail: 'ap@initech.example' },
36
+ { id: 'cus_004', label: 'Umbrella Ltd' },
37
+ ];
38
+ async function searchCustomers(query: string) {
39
+ await new Promise((resolve) => setTimeout(resolve, 300));
40
+ const q = query.toLowerCase();
41
+ return demoCustomers.filter(
42
+ (c) =>
43
+ c.label.toLowerCase().includes(q) ||
44
+ (c.detail ?? '').toLowerCase().includes(q),
45
+ );
46
+ }
47
+ async function resolveCustomer(id: string) {
48
+ return demoCustomers.find((c) => c.id === id) ?? null;
49
+ }
50
+ let customerId = $state('');
30
51
  let accepted = $state(true);
31
52
  let notifications = $state(true);
32
53
  let pinned = $state(false);
@@ -83,6 +104,7 @@ const identity = (controlId: string) => ({
83
104
 
84
105
  <section><h5>Structured input</h5><div class="grid">
85
106
  <Combobox name="country" label="Country" options={countries} bind:value={country} />
107
+ <RelationInput name="customerId" label="Customer" description="Search by name or email" search={searchCustomers} resolve={resolveCustomer} bind:value={customerId} />
86
108
  <MultiSelect name="channels" label="Channels" options={[{ value: 'email', label: 'Email' }, { value: 'sms', label: 'SMS' }, { value: 'push', label: 'Push' }]} bind:values={channels} />
87
109
  <TagsInput name="topics" label="Topics" bind:values={tags} />
88
110
  <DatePicker name="date" label="Publish date" />
@@ -1 +1 @@
1
- {"version":3,"file":"InteractiveControlsPreview.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/playground/InteractiveControlsPreview.svelte.ts"],"names":[],"mappings":"AAwHA,QAAA,MAAM,0BAA0B,2DAAwC,CAAC;AACzE,KAAK,0BAA0B,GAAG,UAAU,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAChF,eAAe,0BAA0B,CAAC"}
1
+ {"version":3,"file":"InteractiveControlsPreview.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/playground/InteractiveControlsPreview.svelte.ts"],"names":[],"mappings":"AA+IA,QAAA,MAAM,0BAA0B,2DAAwC,CAAC;AACzE,KAAK,0BAA0B,GAAG,UAAU,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAChF,eAAe,0BAA0B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-ui",
3
- "version": "0.55.3",
3
+ "version": "0.55.5",
4
4
  "smrtJsdoc": "strict",
5
5
  "description": "Domain-agnostic Svelte 5 UI runtime for SMRT: primitives, i18n client, theme system, and module UI registry",
6
6
  "type": "module",
@@ -139,7 +139,7 @@
139
139
  "access": "public"
140
140
  },
141
141
  "dependencies": {
142
- "@happyvertical/smrt-types": "0.55.3",
142
+ "@happyvertical/smrt-types": "0.55.5",
143
143
  "esm-env": "^1.2.2"
144
144
  },
145
145
  "peerDependencies": {