@tristan2828/ui-foundation 3.19.0 → 3.20.1
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.
|
@@ -125,7 +125,9 @@ is a filled-in example while the Widgets demo is still here).
|
|
|
125
125
|
array of ids on the wire, a repeated any-of list filter, and names from
|
|
126
126
|
a **lookup by id** on the referenced entity
|
|
127
127
|
(`GET /widget-categories?ids=1&ids=3`, `getWidgetCategoriesByIds`,
|
|
128
|
-
`useWidgetCategoriesByIdsQuery`)
|
|
128
|
+
`useWidgetCategoriesByIdsQuery`), and `searching` from the search
|
|
129
|
+
query's `isPlaceholderData` so Enter waits for the results that match
|
|
130
|
+
what was typed. Add that `ids` parameter to the referenced
|
|
129
131
|
entity's list endpoint if it lacks one. Never name a picked id from
|
|
130
132
|
search results alone: a saved pick the current search doesn't return
|
|
131
133
|
would show without a name. The table looks up every id on its page in
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
export declare function MultiReference<Id extends string | number>({ options, value, onValueChange, getLabel, onSearchChange, id, placeholder, emptyText, 'aria-label': ariaLabel, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy, readOnly, className, }: {
|
|
2
|
+
export declare function MultiReference<Id extends string | number>({ options, value, onValueChange, getLabel, onSearchChange, id, placeholder, emptyText, 'aria-label': ariaLabel, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy, readOnly, searching, className, }: {
|
|
3
3
|
/** The records the current search returned, in display order. */
|
|
4
4
|
options: readonly {
|
|
5
5
|
id: Id;
|
|
@@ -27,5 +27,13 @@ export declare function MultiReference<Id extends string | number>({ options, va
|
|
|
27
27
|
'aria-describedby'?: string;
|
|
28
28
|
/** Shown, not changeable: editing in place, while the save is in flight. */
|
|
29
29
|
readOnly?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* The options don't answer what's typed yet: the app's search for it is
|
|
32
|
+
* in flight and the list still shows the last one's results (TanStack
|
|
33
|
+
* Query with `placeholderData: (previous) => previous`:
|
|
34
|
+
* `isPlaceholderData`). Enter does nothing until it answers; a click
|
|
35
|
+
* still picks. The list is `aria-busy` meanwhile.
|
|
36
|
+
*/
|
|
37
|
+
searching?: boolean;
|
|
30
38
|
className?: string;
|
|
31
39
|
}): React.JSX.Element;
|
|
@@ -13,15 +13,24 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
13
13
|
// - Matching happens on the server: `onSearchChange` reports what was typed
|
|
14
14
|
// and `options` is whatever the app's search query returned, so nothing
|
|
15
15
|
// is filtered here (filter={null}).
|
|
16
|
+
// - Until that search answers, the list still holds the last one's
|
|
17
|
+
// results, its first still highlighted. `searching` says so: Enter
|
|
18
|
+
// waits rather than picking from the old list (where it could even
|
|
19
|
+
// remove a pick: the highlighted item may be one already picked).
|
|
16
20
|
import * as React from 'react';
|
|
17
21
|
import { Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxContent, ComboboxEmpty, ComboboxItem, ComboboxList, ComboboxValue, useComboboxAnchor, } from '../ui/combobox.js';
|
|
18
22
|
/** Shown on a chip whose name hasn't loaded yet, instead of a bare id. */
|
|
19
23
|
const PENDING_LABEL = '…';
|
|
20
|
-
export function MultiReference({ options, value, onValueChange, getLabel, onSearchChange, id, placeholder, emptyText = 'No matches.', 'aria-label': ariaLabel, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy, readOnly = false, className, }) {
|
|
24
|
+
export function MultiReference({ options, value, onValueChange, getLabel, onSearchChange, id, placeholder, emptyText = 'No matches.', 'aria-label': ariaLabel, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy, readOnly = false, searching = false, className, }) {
|
|
21
25
|
const anchor = useComboboxAnchor();
|
|
22
26
|
const label = (option) => getLabel(option) ?? PENDING_LABEL;
|
|
23
27
|
return (_jsxs(Combobox, { multiple: true, autoHighlight: true,
|
|
24
28
|
// Ids, not {id,label} objects: Base UI compares items to the value
|
|
25
29
|
// with Object.is, so the items must have the value's own shape.
|
|
26
|
-
items: options.map((option) => option.id), value: value, onValueChange: (next) => onValueChange(next), onInputValueChange: onSearchChange, itemToStringLabel: label, filter: null, readOnly: readOnly, children: [_jsx(ComboboxChips, { ref: anchor, className: className, children: _jsx(ComboboxValue, { children: (selected) => (_jsxs(React.Fragment, { children: [selected.map((option) => (_jsx(ComboboxChip, { children: label(option) }, option))), _jsx(ComboboxChipsInput, { id: id,
|
|
30
|
+
items: options.map((option) => option.id), value: value, onValueChange: (next) => onValueChange(next), onInputValueChange: onSearchChange, itemToStringLabel: label, filter: null, readOnly: readOnly, children: [_jsx(ComboboxChips, { ref: anchor, className: className, children: _jsx(ComboboxValue, { children: (selected) => (_jsxs(React.Fragment, { children: [selected.map((option) => (_jsx(ComboboxChip, { children: label(option) }, option))), _jsx(ComboboxChipsInput, { id: id, onKeyDown: (event) => {
|
|
31
|
+
if (searching && event.key === 'Enter') {
|
|
32
|
+
event.preventDefault();
|
|
33
|
+
event.preventBaseUIHandler();
|
|
34
|
+
}
|
|
35
|
+
}, "aria-label": ariaLabel, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, placeholder: selected.length === 0 ? placeholder : undefined })] })) }) }), _jsxs(ComboboxContent, { anchor: anchor, children: [_jsx(ComboboxEmpty, { children: emptyText }), _jsx(ComboboxList, { "aria-busy": searching || undefined, children: (option) => (_jsx(ComboboxItem, { value: option, children: label(option) }, option)) })] })] }));
|
|
27
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tristan2828/ui-foundation",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.20.1",
|
|
4
4
|
"description": "Shared UI foundation for contract-first, database-backed React apps: shadcn primitives, DataTable/EntityForm composites, auth, the gateway error seam, design tokens, lint rules and the AI playbook.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|