@tensor_1/react 2.46.0 → 2.48.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.
- package/README.md +509 -1
- package/dist/{SchedulingCalendar-B6TakO2h.d.ts → SchedulingCalendar-WWyFL3Eo.d.ts} +51 -2
- package/dist/announce.d.ts +10 -0
- package/dist/announce.js +30 -0
- package/dist/chart-analytic.cjs +33 -25
- package/dist/chart-preset.cjs +23 -15
- package/dist/chart.cjs +23 -15
- package/dist/combobox-model.d.ts +10 -1
- package/dist/combobox-model.js +15 -0
- package/dist/command-palette-model.d.ts +44 -0
- package/dist/command-palette-model.js +36 -0
- package/dist/components/AccountMenu.d.ts +44 -0
- package/dist/components/AccountMenu.js +43 -0
- package/dist/components/AppShell.d.ts +45 -2
- package/dist/components/AppShell.js +116 -5
- package/dist/components/AuditLog.d.ts +128 -0
- package/dist/components/AuditLog.js +196 -0
- package/dist/components/BrandProfiles.js +4 -3
- package/dist/components/Breadcrumbs.d.ts +9 -2
- package/dist/components/Breadcrumbs.js +3 -2
- package/dist/components/BusyBar.d.ts +13 -0
- package/dist/components/BusyBar.js +10 -0
- package/dist/components/BusyRegion.d.ts +18 -0
- package/dist/components/BusyRegion.js +12 -0
- package/dist/components/Button.d.ts +33 -5
- package/dist/components/Button.js +12 -6
- package/dist/components/ButtonFeedback.d.ts +30 -0
- package/dist/components/ButtonFeedback.js +78 -0
- package/dist/components/Card.d.ts +18 -1
- package/dist/components/Card.js +5 -1
- package/dist/components/ChangeHighlight.d.ts +30 -0
- package/dist/components/ChangeHighlight.js +21 -0
- package/dist/components/ChartCore.js +2 -1
- package/dist/components/Checkbox.d.ts +27 -1
- package/dist/components/Checkbox.js +31 -2
- package/dist/components/CodeDisplay.d.ts +65 -0
- package/dist/components/CodeDisplay.js +124 -0
- package/dist/components/Combobox.d.ts +27 -6
- package/dist/components/Combobox.js +195 -58
- package/dist/components/CommandPalette.d.ts +36 -1
- package/dist/components/CommandPalette.js +254 -2
- package/dist/components/ContactCard.d.ts +66 -0
- package/dist/components/ContactCard.js +74 -0
- package/dist/components/Count.d.ts +24 -0
- package/dist/components/Count.js +32 -0
- package/dist/components/DataWorkspace.d.ts +167 -0
- package/dist/components/DataWorkspace.js +383 -0
- package/dist/components/DateFieldParts.d.ts +2 -1
- package/dist/components/DateFieldParts.js +7 -0
- package/dist/components/DatePicker.d.ts +9 -2
- package/dist/components/DatePicker.js +67 -28
- package/dist/components/DateRangePicker.d.ts +12 -2
- package/dist/components/DateRangePicker.js +13 -12
- package/dist/components/DelayedRender.d.ts +15 -0
- package/dist/components/DelayedRender.js +10 -0
- package/dist/components/DetailPage.d.ts +7 -1
- package/dist/components/DetailPage.js +2 -1
- package/dist/components/Dialogs.d.ts +71 -10
- package/dist/components/Dialogs.js +130 -30
- package/dist/components/DiffViewer.d.ts +23 -3
- package/dist/components/DiffViewer.js +26 -12
- package/dist/components/DiscardPrompt.d.ts +1 -0
- package/dist/components/DiscardPrompt.js +16 -0
- package/dist/components/DisplayPreferences.d.ts +83 -0
- package/dist/components/DisplayPreferences.js +185 -0
- package/dist/components/Drawer.d.ts +1 -0
- package/dist/components/EditorialHome.d.ts +23 -3
- package/dist/components/EditorialHome.js +16 -11
- package/dist/components/EntityPicker.d.ts +20 -3
- package/dist/components/EntityPicker.js +32 -13
- package/dist/components/FilterEditor.d.ts +18 -4
- package/dist/components/FilterEditor.js +141 -19
- package/dist/components/InfoTip.js +11 -12
- package/dist/components/InlineEdit.d.ts +87 -20
- package/dist/components/InlineEdit.js +197 -54
- package/dist/components/Input.d.ts +12 -2
- package/dist/components/Input.js +5 -3
- package/dist/components/Kbd.d.ts +6 -1
- package/dist/components/Kbd.js +6 -6
- package/dist/components/Lightbox.d.ts +41 -5
- package/dist/components/Lightbox.js +95 -9
- package/dist/components/MatchReview.d.ts +40 -1
- package/dist/components/MatchReview.js +130 -4
- package/dist/components/MetricInput.js +3 -2
- package/dist/components/Modal.d.ts +3 -1
- package/dist/components/Modal.js +13 -4
- package/dist/components/NavFavourites.d.ts +5 -0
- package/dist/components/NavFavourites.js +7 -7
- package/dist/components/OtpInput.d.ts +33 -0
- package/dist/components/OtpInput.js +123 -0
- package/dist/components/PageHeader.d.ts +7 -1
- package/dist/components/PageHeader.js +2 -2
- package/dist/components/Pagination.js +6 -5
- package/dist/components/PasswordInput.d.ts +34 -0
- package/dist/components/PasswordInput.js +101 -0
- package/dist/components/Peek.js +3 -3
- package/dist/components/Popover.js +2 -0
- package/dist/components/PrintLayout.d.ts +63 -0
- package/dist/components/PrintLayout.js +114 -0
- package/dist/components/QrCode.d.ts +47 -0
- package/dist/components/QrCode.js +55 -0
- package/dist/components/QrScanner.d.ts +95 -0
- package/dist/components/QrScanner.js +266 -0
- package/dist/components/RelationshipGraph.d.ts +32 -1
- package/dist/components/RelationshipGraph.js +18 -5
- package/dist/components/RelationshipGraphLayered.d.ts +1 -1
- package/dist/components/RelationshipGraphLayered.js +190 -115
- package/dist/components/RelationshipGraphList.js +33 -6
- package/dist/components/RuleBuilder.js +39 -7
- package/dist/components/SaveStatus.d.ts +7 -1
- package/dist/components/SaveStatus.js +13 -4
- package/dist/components/SchedulingAgenda.d.ts +3 -1
- package/dist/components/SchedulingCalendar.d.ts +3 -1
- package/dist/components/SchedulingCalendar.js +43 -4
- package/dist/components/SchedulingResourceDay.d.ts +57 -0
- package/dist/components/SchedulingResourceDay.js +287 -0
- package/dist/components/SchedulingTimeGrid.d.ts +3 -1
- package/dist/components/SchedulingTimeline.d.ts +3 -1
- package/dist/components/SearchBar.d.ts +1 -0
- package/dist/components/SearchSuggest.d.ts +23 -3
- package/dist/components/SearchSuggest.js +48 -17
- package/dist/components/Segmented.d.ts +39 -4
- package/dist/components/Segmented.js +31 -3
- package/dist/components/Select.d.ts +3 -2
- package/dist/components/Select.js +1 -1
- package/dist/components/ShortcutHint.d.ts +11 -10
- package/dist/components/ShortcutHint.js +44 -38
- package/dist/components/Sidebar.d.ts +36 -4
- package/dist/components/Sidebar.js +37 -12
- package/dist/components/SortableList.d.ts +45 -0
- package/dist/components/SortableList.js +134 -0
- package/dist/components/Spinner.d.ts +7 -1
- package/dist/components/Spinner.js +7 -1
- package/dist/components/Stat.d.ts +28 -3
- package/dist/components/Stat.js +13 -3
- package/dist/components/States.d.ts +7 -1
- package/dist/components/States.js +7 -1
- package/dist/components/StatusPill.d.ts +14 -3
- package/dist/components/StatusPill.js +11 -3
- package/dist/components/Steps.js +2 -1
- package/dist/components/Table.d.ts +16 -2
- package/dist/components/Table.js +8 -4
- package/dist/components/TableControls.js +18 -58
- package/dist/components/TableOverflowProbe.d.ts +12 -0
- package/dist/components/TableOverflowProbe.js +27 -0
- package/dist/components/Tabs.d.ts +2 -1
- package/dist/components/Tabs.js +1 -0
- package/dist/components/TagInput.d.ts +67 -0
- package/dist/components/TagInput.js +342 -0
- package/dist/components/Textarea.d.ts +5 -0
- package/dist/components/Textarea.js +2 -2
- package/dist/components/TimePicker.d.ts +80 -0
- package/dist/components/TimePicker.js +347 -0
- package/dist/components/Toast.js +3 -2
- package/dist/components/Tooltip.d.ts +8 -4
- package/dist/components/Tooltip.js +24 -21
- package/dist/components/Tree.d.ts +8 -0
- package/dist/components/Tree.js +11 -5
- package/dist/display-preferences-model.d.ts +53 -0
- package/dist/display-preferences-model.js +64 -0
- package/dist/emoji.cjs +28 -35
- package/dist/filter-model.d.ts +59 -3
- package/dist/filter-model.js +67 -2
- package/dist/hooks/useChangeHighlight.d.ts +33 -0
- package/dist/hooks/useChangeHighlight.js +44 -0
- package/dist/hooks/useDelayedPending.d.ts +22 -0
- package/dist/hooks/useDelayedPending.js +35 -0
- package/dist/hooks/useDialog.d.ts +5 -0
- package/dist/hooks/useDialog.js +3 -2
- package/dist/hooks/useOptimisticValue.d.ts +51 -0
- package/dist/hooks/useOptimisticValue.js +61 -0
- package/dist/hooks/useReducedMotion.d.ts +4 -1
- package/dist/hooks/useReducedMotion.js +7 -1
- package/dist/hooks/useResolvedFilterLabels.d.ts +15 -0
- package/dist/hooks/useResolvedFilterLabels.js +34 -0
- package/dist/hooks/useVirtualRows.d.ts +21 -2
- package/dist/hooks/useVirtualRows.js +30 -3
- package/dist/icons/Icon.d.ts +1 -1
- package/dist/index.cjs +18977 -13378
- package/dist/index.d.cts +3065 -664
- package/dist/index.d.ts +46 -16
- package/dist/index.js +149 -7
- package/dist/labels-context.d.ts +93 -3
- package/dist/motion-timing.d.ts +8 -0
- package/dist/motion-timing.js +8 -0
- package/dist/overlay-stack.d.ts +6 -0
- package/dist/overlay-stack.js +13 -0
- package/dist/qr-model.d.ts +65 -0
- package/dist/qr-model.js +323 -0
- package/dist/relationship-graph-model.d.ts +78 -3
- package/dist/relationship-graph-model.js +112 -5
- package/dist/reorder.d.ts +30 -0
- package/dist/reorder.js +74 -0
- package/dist/rich-text.cjs +4 -2
- package/dist/scheduling-resource-model.d.ts +76 -0
- package/dist/scheduling-resource-model.js +74 -0
- package/dist/search-suggest-model.js +3 -1
- package/dist/shell-context.d.ts +11 -0
- package/dist/shell-context.js +7 -0
- package/dist/time-field-model.d.ts +46 -0
- package/dist/time-field-model.js +152 -0
- package/dist/toast-store.d.ts +17 -1
- package/dist/toast-store.js +23 -7
- package/package.json +2 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useId, useMemo, useState } from "react";
|
|
4
|
+
import { useUixLabels } from "../labels-context.js";
|
|
4
5
|
import { buildThreeWayDiff, summarizeDiff } from "../diff-model.js";
|
|
5
6
|
import { cx } from "../cx.js";
|
|
6
7
|
import { StatusPill } from "./StatusPill.js";
|
|
@@ -34,7 +35,7 @@ const DEFAULT_DIFF_VIEWER_LABELS = {
|
|
|
34
35
|
progress: "{resolved} of {total} differences resolved."
|
|
35
36
|
};
|
|
36
37
|
const GROUPS = ["conflicted", "changed", "added", "removed"];
|
|
37
|
-
const
|
|
38
|
+
const jsonText = (value, notPresent) => value === void 0 ? notPresent : JSON.stringify(value, null, 2);
|
|
38
39
|
function DiffViewer({
|
|
39
40
|
base,
|
|
40
41
|
current,
|
|
@@ -44,9 +45,17 @@ function DiffViewer({
|
|
|
44
45
|
onResolutionChange,
|
|
45
46
|
labels: labelOverrides,
|
|
46
47
|
controlSize = "sm",
|
|
47
|
-
className
|
|
48
|
+
className,
|
|
49
|
+
labelFor,
|
|
50
|
+
renderValue,
|
|
51
|
+
provenance
|
|
48
52
|
}) {
|
|
49
|
-
const labels = { ...DEFAULT_DIFF_VIEWER_LABELS, ...labelOverrides };
|
|
53
|
+
const labels = { ...DEFAULT_DIFF_VIEWER_LABELS, ...useUixLabels().diffViewer, ...labelOverrides };
|
|
54
|
+
const nameOf = (path) => {
|
|
55
|
+
const node = labelFor?.(path);
|
|
56
|
+
if (node == null || node === false) return { node: /* @__PURE__ */ jsx("code", { children: path }), text: path };
|
|
57
|
+
return { node, text: typeof node === "string" || typeof node === "number" ? String(node) : path };
|
|
58
|
+
};
|
|
50
59
|
const resolutionLabel = { accept: labels.resolutionAccept, skip: labels.resolutionSkip, pending: labels.resolutionPending };
|
|
51
60
|
const buttonSize = controlSize === "md" ? void 0 : `uix-btn--${controlSize}`;
|
|
52
61
|
const id = useId();
|
|
@@ -91,19 +100,24 @@ function DiffViewer({
|
|
|
91
100
|
] }),
|
|
92
101
|
grouped.map((entry) => {
|
|
93
102
|
const resolution = resolutions[entry.path] ?? "pending";
|
|
103
|
+
const name = nameOf(entry.path);
|
|
94
104
|
return /* @__PURE__ */ jsxs("details", { className: "uix-diff-viewer__entry", "data-kind": entry.kind, open: entry.kind === "conflicted", children: [
|
|
95
105
|
/* @__PURE__ */ jsxs("summary", { children: [
|
|
96
|
-
/* @__PURE__ */ jsx("
|
|
106
|
+
/* @__PURE__ */ jsx("span", { className: "uix-diff-viewer__name", children: name.node }),
|
|
97
107
|
/* @__PURE__ */ jsx(StatusPill, { tone: resolution === "pending" ? "warning" : "success", children: resolutionLabel[resolution] })
|
|
98
108
|
] }),
|
|
99
|
-
/* @__PURE__ */ jsx("div", { className: "uix-diff-viewer__versions", children: ["base", "current", "incoming"].map((version) =>
|
|
100
|
-
|
|
101
|
-
/* @__PURE__ */
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
/* @__PURE__ */ jsx("div", { className: "uix-diff-viewer__versions", children: ["base", "current", "incoming"].map((version) => {
|
|
110
|
+
const origin = provenance?.(entry.path, version);
|
|
111
|
+
return /* @__PURE__ */ jsxs("div", { role: "region", "aria-label": fillLabel(labels.valueFor, { version: labels[version], path: name.text }), children: [
|
|
112
|
+
/* @__PURE__ */ jsx("strong", { children: labels[version] }),
|
|
113
|
+
renderValue ? /* @__PURE__ */ jsx("div", { className: "uix-diff-viewer__value", children: renderValue(entry[version], entry.path, version) ?? labels.notPresent }) : /* @__PURE__ */ jsx("pre", { children: jsonText(entry[version], labels.notPresent) }),
|
|
114
|
+
origin != null && origin !== false && /* @__PURE__ */ jsx("div", { className: "uix-diff-viewer__provenance", children: origin })
|
|
115
|
+
] }, version);
|
|
116
|
+
}) }),
|
|
117
|
+
/* @__PURE__ */ jsxs("div", { className: "uix-diff-viewer__actions", role: "group", "aria-label": fillLabel(labels.resolve, { path: name.text }), children: [
|
|
118
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: cx("uix-btn uix-btn--primary", buttonSize), "aria-label": fillLabel(labels.acceptIncomingFor, { path: name.text }), "aria-pressed": resolution === "accept", onClick: () => resolve(entry.path, "accept"), children: labels.acceptIncoming }),
|
|
119
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: cx("uix-btn uix-btn--secondary", buttonSize), "aria-label": fillLabel(labels.keepCurrentFor, { path: name.text }), "aria-pressed": resolution === "skip", onClick: () => resolve(entry.path, "skip"), children: labels.keepCurrent }),
|
|
120
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: cx("uix-btn uix-btn--ghost", buttonSize), "aria-label": fillLabel(labels.markPendingFor, { path: name.text }), "aria-pressed": resolution === "pending", onClick: () => resolve(entry.path, "pending"), children: labels.markPending })
|
|
107
121
|
] })
|
|
108
122
|
] }, entry.path);
|
|
109
123
|
})
|
|
@@ -3,6 +3,7 @@ import { SyntheticEvent, RefObject, KeyboardEvent, ReactNode } from 'react';
|
|
|
3
3
|
import { ButtonProps } from './Button.js';
|
|
4
4
|
import '../link.js';
|
|
5
5
|
import './Kbd.js';
|
|
6
|
+
import './ButtonFeedback.js';
|
|
6
7
|
|
|
7
8
|
/** The words of the "Discard changes?" step a `dirty` Drawer or Modal asks before it closes (HAR-2029). */
|
|
8
9
|
interface DiscardPromptLabels {
|
|
@@ -68,6 +68,7 @@ function useGuardedDialog({ open, onClose, dirty = false, pending = false, label
|
|
|
68
68
|
setPrompt({ anchor: anchor ?? closeRef.current, returnTo: active instanceof HTMLElement ? active : null });
|
|
69
69
|
}, [closeRef, setPrompt]);
|
|
70
70
|
const dialogRef = useRef(null);
|
|
71
|
+
const lightDismissed = useRef(false);
|
|
71
72
|
const backOut = useRef({ focus: null, forced: false });
|
|
72
73
|
const keepEditing = useCallback(() => {
|
|
73
74
|
const asked = latest.current.prompt;
|
|
@@ -79,6 +80,7 @@ function useGuardedDialog({ open, onClose, dirty = false, pending = false, label
|
|
|
79
80
|
back?.focus();
|
|
80
81
|
}, [closeRef, setPrompt]);
|
|
81
82
|
const ref = useDialog(open, onClose, () => {
|
|
83
|
+
if (lightDismissed.current) return true;
|
|
82
84
|
if (!guarded()) return false;
|
|
83
85
|
if (backOut.current.forced) {
|
|
84
86
|
backOut.current.forced = false;
|
|
@@ -104,7 +106,19 @@ function useGuardedDialog({ open, onClose, dirty = false, pending = false, label
|
|
|
104
106
|
dialogRef.current = el;
|
|
105
107
|
if (!el || !open) return;
|
|
106
108
|
const ours = (event) => event.target?.closest?.("dialog") === el;
|
|
109
|
+
const onBeforeToggle = (event) => {
|
|
110
|
+
const kind = event.target.getAttribute?.("popover");
|
|
111
|
+
if (event.newState !== "closed" || kind == null || kind === "manual") return;
|
|
112
|
+
lightDismissed.current = true;
|
|
113
|
+
window.setTimeout(() => {
|
|
114
|
+
lightDismissed.current = false;
|
|
115
|
+
}, 0);
|
|
116
|
+
};
|
|
107
117
|
const onCancel = (event) => {
|
|
118
|
+
if (lightDismissed.current) {
|
|
119
|
+
event.preventDefault();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
108
122
|
if (!guarded()) return;
|
|
109
123
|
event.preventDefault();
|
|
110
124
|
if (latest.current.prompt) {
|
|
@@ -133,6 +147,7 @@ function useGuardedDialog({ open, onClose, dirty = false, pending = false, label
|
|
|
133
147
|
setPrompt(null);
|
|
134
148
|
};
|
|
135
149
|
el.addEventListener("cancel", onCancel);
|
|
150
|
+
document.addEventListener("beforetoggle", onBeforeToggle, true);
|
|
136
151
|
el.addEventListener("pointerdown", onPointerDownCapture, true);
|
|
137
152
|
el.addEventListener("pointerup", onPointerUpCapture, true);
|
|
138
153
|
el.addEventListener("pointercancel", forget, true);
|
|
@@ -141,6 +156,7 @@ function useGuardedDialog({ open, onClose, dirty = false, pending = false, label
|
|
|
141
156
|
el.addEventListener("keydown", forget, true);
|
|
142
157
|
return () => {
|
|
143
158
|
el.removeEventListener("cancel", onCancel);
|
|
159
|
+
document.removeEventListener("beforetoggle", onBeforeToggle, true);
|
|
144
160
|
el.removeEventListener("pointerdown", onPointerDownCapture, true);
|
|
145
161
|
el.removeEventListener("pointerup", onPointerUpCapture, true);
|
|
146
162
|
el.removeEventListener("pointercancel", forget, true);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes } from 'react';
|
|
3
|
+
import { DisplayPreferenceValues, ThemePreference } from '../display-preferences-model.js';
|
|
4
|
+
|
|
5
|
+
interface UseDisplayPreferencesOptions {
|
|
6
|
+
/** `localStorage` key. Default `DISPLAY_PREFERENCES_STORAGE_KEY`; pass the same key to `displayPreferencesScript`. */
|
|
7
|
+
storageKey?: string;
|
|
8
|
+
/** Your defaults for anything not stored yet. Pass the same to `displayPreferencesScript`. */
|
|
9
|
+
defaults?: Partial<DisplayPreferenceValues>;
|
|
10
|
+
/** Write the attributes on `<html>`. Default `true`; `false` for a preview that must not restyle the page. */
|
|
11
|
+
apply?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface UseDisplayPreferencesResult {
|
|
14
|
+
/** The person's choices (`system` where they left it to the system). */
|
|
15
|
+
preferences: DisplayPreferenceValues;
|
|
16
|
+
/** The theme in force: `system` resolved against `prefers-color-scheme`. */
|
|
17
|
+
resolvedTheme: 'light' | 'dark';
|
|
18
|
+
setPreference: <K extends keyof DisplayPreferenceValues>(key: K, value: DisplayPreferenceValues[K]) => void;
|
|
19
|
+
setPreferences: (next: DisplayPreferenceValues) => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The display preferences (HAR-1372): read from `localStorage`, kept in step with the system
|
|
23
|
+
* (`prefers-color-scheme`, `prefers-contrast`), with other tabs and with other users of the hook in
|
|
24
|
+
* this tab, and written to `<html>` as the attributes the tokens read. Changing one applies at once,
|
|
25
|
+
* with every transition suspended for that frame so nothing fades between themes.
|
|
26
|
+
* The server renders `defaults`; the stored choice is read before the first paint on the client
|
|
27
|
+
* (pair it with `displayPreferencesScript` in the head, so the page itself never flashes).
|
|
28
|
+
*/
|
|
29
|
+
declare function useDisplayPreferences({ storageKey, defaults: defaultOverrides, apply }?: UseDisplayPreferencesOptions): UseDisplayPreferencesResult;
|
|
30
|
+
interface DisplayPreferencesLabels {
|
|
31
|
+
theme: string;
|
|
32
|
+
light: string;
|
|
33
|
+
dark: string;
|
|
34
|
+
system: string;
|
|
35
|
+
contrast: string;
|
|
36
|
+
contrastStandard: string;
|
|
37
|
+
contrastMore: string;
|
|
38
|
+
textScale: string;
|
|
39
|
+
textSmall: string;
|
|
40
|
+
textDefault: string;
|
|
41
|
+
textLarge: string;
|
|
42
|
+
textLarger: string;
|
|
43
|
+
motion: string;
|
|
44
|
+
motionSystem: string;
|
|
45
|
+
motionReduce: string;
|
|
46
|
+
density: string;
|
|
47
|
+
densityCompact: string;
|
|
48
|
+
densityStandard: string;
|
|
49
|
+
densityComfortable: string;
|
|
50
|
+
}
|
|
51
|
+
declare const DEFAULT_DISPLAY_PREFERENCES_LABELS: DisplayPreferencesLabels;
|
|
52
|
+
interface ThemeSwitcherProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
|
53
|
+
/** Controlled theme. Leave it out and the switcher keeps the preference itself (`useDisplayPreferences`). */
|
|
54
|
+
value?: ThemePreference;
|
|
55
|
+
onValueChange?: (value: ThemePreference) => void;
|
|
56
|
+
/** The storage key when uncontrolled. */
|
|
57
|
+
storageKey?: string;
|
|
58
|
+
/** `icons` shows the icons only (each keeps its word as its name); default shows icon and word. */
|
|
59
|
+
display?: 'labels' | 'icons';
|
|
60
|
+
labels?: Partial<DisplayPreferencesLabels>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Light / dark / system (HAR-1372), a radio group over `Segmented`. Uncontrolled, it reads and
|
|
64
|
+
* writes the display preferences itself; controlled, it only reports.
|
|
65
|
+
*/
|
|
66
|
+
declare function ThemeSwitcher({ value, onValueChange, storageKey, display, labels: labelOverrides, className, ...props }: ThemeSwitcherProps): react.JSX.Element;
|
|
67
|
+
interface DisplayPreferencesProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
|
68
|
+
/** Controlled values. Leave out to keep them in `localStorage` (`useDisplayPreferences`). */
|
|
69
|
+
value?: DisplayPreferenceValues;
|
|
70
|
+
onValueChange?: (value: DisplayPreferenceValues) => void;
|
|
71
|
+
storageKey?: string;
|
|
72
|
+
/** Show only some of the five settings, in this order. Default all. */
|
|
73
|
+
settings?: readonly (keyof DisplayPreferenceValues)[];
|
|
74
|
+
labels?: Partial<DisplayPreferencesLabels>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The five display settings (HAR-1372; MOTUS C-4, TENSOR A7): theme, contrast, text size, motion
|
|
78
|
+
* and density, each a labelled radio group. Put it in a preferences page or an account menu's
|
|
79
|
+
* dialog. Changes apply at once and are remembered.
|
|
80
|
+
*/
|
|
81
|
+
declare function DisplayPreferences({ value, onValueChange, storageKey, settings, labels: labelOverrides, className, ...props }: DisplayPreferencesProps): react.JSX.Element;
|
|
82
|
+
|
|
83
|
+
export { DEFAULT_DISPLAY_PREFERENCES_LABELS, DisplayPreferences, type DisplayPreferencesLabels, type DisplayPreferencesProps, ThemeSwitcher, type ThemeSwitcherProps, type UseDisplayPreferencesOptions, type UseDisplayPreferencesResult, useDisplayPreferences };
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
|
|
4
|
+
import { cx } from "../cx.js";
|
|
5
|
+
import { useUixLabels } from "../labels-context.js";
|
|
6
|
+
import { MonitorIcon, MoonIcon, SunIcon } from "../icons/components.js";
|
|
7
|
+
import { Segmented, SegmentedOption } from "./Segmented.js";
|
|
8
|
+
import {
|
|
9
|
+
DEFAULT_DISPLAY_PREFERENCES,
|
|
10
|
+
DISPLAY_PREFERENCES_STORAGE_KEY,
|
|
11
|
+
applyDisplayAttributes,
|
|
12
|
+
parseDisplayPreferences,
|
|
13
|
+
readSystemDisplayPreferences,
|
|
14
|
+
resolveDisplayAttributes
|
|
15
|
+
} from "../display-preferences-model.js";
|
|
16
|
+
const useIsomorphicLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;
|
|
17
|
+
const CHANGE_EVENT = "uixdisplaypreferencechange";
|
|
18
|
+
function useDisplayPreferences({ storageKey = DISPLAY_PREFERENCES_STORAGE_KEY, defaults: defaultOverrides, apply = true } = {}) {
|
|
19
|
+
const defaults = { ...DEFAULT_DISPLAY_PREFERENCES, ...defaultOverrides };
|
|
20
|
+
const defaultsKey = JSON.stringify(defaults);
|
|
21
|
+
const [preferences, setState] = useState(defaults);
|
|
22
|
+
const [system, setSystem] = useState({ dark: false, moreContrast: false });
|
|
23
|
+
const [loaded, setLoaded] = useState(false);
|
|
24
|
+
const read = useCallback(() => {
|
|
25
|
+
let raw = null;
|
|
26
|
+
try {
|
|
27
|
+
raw = window.localStorage.getItem(storageKey);
|
|
28
|
+
} catch {
|
|
29
|
+
}
|
|
30
|
+
return parseDisplayPreferences(raw, JSON.parse(defaultsKey));
|
|
31
|
+
}, [storageKey, defaultsKey]);
|
|
32
|
+
const latest = useRef(preferences);
|
|
33
|
+
latest.current = preferences;
|
|
34
|
+
useIsomorphicLayoutEffect(() => {
|
|
35
|
+
setState(read());
|
|
36
|
+
setSystem(readSystemDisplayPreferences());
|
|
37
|
+
setLoaded(true);
|
|
38
|
+
}, [read]);
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const queries = typeof matchMedia === "function" ? [matchMedia("(prefers-color-scheme: dark)"), matchMedia("(prefers-contrast: more)")] : [];
|
|
41
|
+
const onSystem = () => setSystem(readSystemDisplayPreferences());
|
|
42
|
+
const onStorage = (event) => {
|
|
43
|
+
if (event.key === storageKey) setState(read());
|
|
44
|
+
};
|
|
45
|
+
const onChange = (event) => {
|
|
46
|
+
const detail = event.detail;
|
|
47
|
+
if (detail?.storageKey === storageKey) setState(detail.value);
|
|
48
|
+
};
|
|
49
|
+
for (const q of queries) q.addEventListener("change", onSystem);
|
|
50
|
+
window.addEventListener("storage", onStorage);
|
|
51
|
+
window.addEventListener(CHANGE_EVENT, onChange);
|
|
52
|
+
return () => {
|
|
53
|
+
for (const q of queries) q.removeEventListener("change", onSystem);
|
|
54
|
+
window.removeEventListener("storage", onStorage);
|
|
55
|
+
window.removeEventListener(CHANGE_EVENT, onChange);
|
|
56
|
+
};
|
|
57
|
+
}, [storageKey, read]);
|
|
58
|
+
useIsomorphicLayoutEffect(() => {
|
|
59
|
+
if (!apply || !loaded) return;
|
|
60
|
+
const root = document.documentElement;
|
|
61
|
+
const next = resolveDisplayAttributes(preferences, system);
|
|
62
|
+
const names = ["data-theme", "data-contrast", "data-text-scale", "data-motion", "data-density"];
|
|
63
|
+
if (names.every((name) => (next[name] ?? null) === root.getAttribute(name))) return void 0;
|
|
64
|
+
root.setAttribute("data-uix-switching", "");
|
|
65
|
+
applyDisplayAttributes(root, next);
|
|
66
|
+
const frame = requestAnimationFrame(() => requestAnimationFrame(() => root.removeAttribute("data-uix-switching")));
|
|
67
|
+
return () => {
|
|
68
|
+
cancelAnimationFrame(frame);
|
|
69
|
+
root.removeAttribute("data-uix-switching");
|
|
70
|
+
};
|
|
71
|
+
}, [apply, loaded, preferences, system]);
|
|
72
|
+
const setPreferences = useCallback((next) => {
|
|
73
|
+
setState(next);
|
|
74
|
+
try {
|
|
75
|
+
window.localStorage.setItem(storageKey, JSON.stringify(next));
|
|
76
|
+
} catch {
|
|
77
|
+
}
|
|
78
|
+
window.dispatchEvent(new CustomEvent(CHANGE_EVENT, { detail: { storageKey, value: next } }));
|
|
79
|
+
}, [storageKey]);
|
|
80
|
+
const setPreference = useCallback((key, value) => {
|
|
81
|
+
setPreferences({ ...latest.current, [key]: value });
|
|
82
|
+
}, [setPreferences]);
|
|
83
|
+
return { preferences, resolvedTheme: resolveDisplayAttributes(preferences, system)["data-theme"], setPreference, setPreferences };
|
|
84
|
+
}
|
|
85
|
+
const DEFAULT_DISPLAY_PREFERENCES_LABELS = {
|
|
86
|
+
theme: "Theme",
|
|
87
|
+
light: "Light",
|
|
88
|
+
dark: "Dark",
|
|
89
|
+
system: "System",
|
|
90
|
+
contrast: "Contrast",
|
|
91
|
+
contrastStandard: "Standard",
|
|
92
|
+
contrastMore: "More",
|
|
93
|
+
textScale: "Text size",
|
|
94
|
+
textSmall: "Small",
|
|
95
|
+
textDefault: "Default",
|
|
96
|
+
textLarge: "Large",
|
|
97
|
+
textLarger: "Larger",
|
|
98
|
+
motion: "Motion",
|
|
99
|
+
motionSystem: "System",
|
|
100
|
+
motionReduce: "Reduce",
|
|
101
|
+
density: "Density",
|
|
102
|
+
densityCompact: "Compact",
|
|
103
|
+
densityStandard: "Standard",
|
|
104
|
+
densityComfortable: "Comfortable"
|
|
105
|
+
};
|
|
106
|
+
const useDisplayLabels = (overrides) => {
|
|
107
|
+
const uixLabels = useUixLabels();
|
|
108
|
+
return { ...DEFAULT_DISPLAY_PREFERENCES_LABELS, ...uixLabels.displayPreferences, ...overrides };
|
|
109
|
+
};
|
|
110
|
+
function ThemeOptions({ labels, iconsOnly }) {
|
|
111
|
+
const option = (value, icon, word) => /* @__PURE__ */ jsxs(SegmentedOption, { value, "aria-label": iconsOnly ? word : void 0, children: [
|
|
112
|
+
icon,
|
|
113
|
+
iconsOnly ? null : /* @__PURE__ */ jsx("span", { children: word })
|
|
114
|
+
] });
|
|
115
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
116
|
+
option("light", /* @__PURE__ */ jsx(SunIcon, { size: "sm" }), labels.light),
|
|
117
|
+
option("dark", /* @__PURE__ */ jsx(MoonIcon, { size: "sm" }), labels.dark),
|
|
118
|
+
option("system", /* @__PURE__ */ jsx(MonitorIcon, { size: "sm" }), labels.system)
|
|
119
|
+
] });
|
|
120
|
+
}
|
|
121
|
+
function ThemeSwitcher({ value, onValueChange, storageKey, display = "labels", labels: labelOverrides, className, ...props }) {
|
|
122
|
+
const labels = useDisplayLabels(labelOverrides);
|
|
123
|
+
const own = useDisplayPreferences({ storageKey, apply: value === void 0 });
|
|
124
|
+
const current = value ?? own.preferences.theme;
|
|
125
|
+
return /* @__PURE__ */ jsx(
|
|
126
|
+
Segmented,
|
|
127
|
+
{
|
|
128
|
+
selection: "radio",
|
|
129
|
+
"aria-label": labels.theme,
|
|
130
|
+
className: cx("uix-theme-switcher", className),
|
|
131
|
+
value: current,
|
|
132
|
+
onChange: (next) => {
|
|
133
|
+
if (value === void 0) own.setPreference("theme", next);
|
|
134
|
+
onValueChange?.(next);
|
|
135
|
+
},
|
|
136
|
+
...props,
|
|
137
|
+
children: /* @__PURE__ */ jsx(ThemeOptions, { labels, iconsOnly: display === "icons" })
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
function DisplayPreferences({ value, onValueChange, storageKey, settings = ["theme", "contrast", "textScale", "motion", "density"], labels: labelOverrides, className, ...props }) {
|
|
142
|
+
const labels = useDisplayLabels(labelOverrides);
|
|
143
|
+
const own = useDisplayPreferences({ storageKey, apply: value === void 0 });
|
|
144
|
+
const current = value ?? own.preferences;
|
|
145
|
+
const id = useId();
|
|
146
|
+
const change = (key, next) => {
|
|
147
|
+
const all = { ...current, [key]: next };
|
|
148
|
+
if (value === void 0) own.setPreference(key, next);
|
|
149
|
+
onValueChange?.(all);
|
|
150
|
+
};
|
|
151
|
+
const row = (key, title, options) => /* @__PURE__ */ jsxs("div", { className: "uix-display-prefs__row", children: [
|
|
152
|
+
/* @__PURE__ */ jsx("span", { className: "uix-display-prefs__label", id: `${id}-${key}`, children: title }),
|
|
153
|
+
/* @__PURE__ */ jsx(Segmented, { selection: "radio", "aria-labelledby": `${id}-${key}`, value: current[key], onChange: (next) => change(key, next), children: options })
|
|
154
|
+
] }, key);
|
|
155
|
+
const rows = {
|
|
156
|
+
theme: () => row("theme", labels.theme, /* @__PURE__ */ jsx(ThemeOptions, { labels, iconsOnly: false })),
|
|
157
|
+
contrast: () => row("contrast", labels.contrast, /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
158
|
+
/* @__PURE__ */ jsx(SegmentedOption, { value: "standard", children: labels.contrastStandard }),
|
|
159
|
+
/* @__PURE__ */ jsx(SegmentedOption, { value: "more", children: labels.contrastMore }),
|
|
160
|
+
/* @__PURE__ */ jsx(SegmentedOption, { value: "system", children: labels.system })
|
|
161
|
+
] })),
|
|
162
|
+
textScale: () => row("textScale", labels.textScale, /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
163
|
+
/* @__PURE__ */ jsx(SegmentedOption, { value: "sm", children: labels.textSmall }),
|
|
164
|
+
/* @__PURE__ */ jsx(SegmentedOption, { value: "md", children: labels.textDefault }),
|
|
165
|
+
/* @__PURE__ */ jsx(SegmentedOption, { value: "lg", children: labels.textLarge }),
|
|
166
|
+
/* @__PURE__ */ jsx(SegmentedOption, { value: "xl", children: labels.textLarger })
|
|
167
|
+
] })),
|
|
168
|
+
motion: () => row("motion", labels.motion, /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
169
|
+
/* @__PURE__ */ jsx(SegmentedOption, { value: "system", children: labels.motionSystem }),
|
|
170
|
+
/* @__PURE__ */ jsx(SegmentedOption, { value: "reduce", children: labels.motionReduce })
|
|
171
|
+
] })),
|
|
172
|
+
density: () => row("density", labels.density, /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
173
|
+
/* @__PURE__ */ jsx(SegmentedOption, { value: "compact", children: labels.densityCompact }),
|
|
174
|
+
/* @__PURE__ */ jsx(SegmentedOption, { value: "standard", children: labels.densityStandard }),
|
|
175
|
+
/* @__PURE__ */ jsx(SegmentedOption, { value: "comfortable", children: labels.densityComfortable })
|
|
176
|
+
] }))
|
|
177
|
+
};
|
|
178
|
+
return /* @__PURE__ */ jsx("div", { className: cx("uix-display-prefs", className), ...props, children: settings.map((key) => rows[key]()) });
|
|
179
|
+
}
|
|
180
|
+
export {
|
|
181
|
+
DEFAULT_DISPLAY_PREFERENCES_LABELS,
|
|
182
|
+
DisplayPreferences,
|
|
183
|
+
ThemeSwitcher,
|
|
184
|
+
useDisplayPreferences
|
|
185
|
+
};
|
|
@@ -4,6 +4,7 @@ import { DialogCloseEvent, DiscardPromptLabels } from './DiscardPrompt.js';
|
|
|
4
4
|
import './Button.js';
|
|
5
5
|
import '../link.js';
|
|
6
6
|
import './Kbd.js';
|
|
7
|
+
import './ButtonFeedback.js';
|
|
7
8
|
|
|
8
9
|
interface DrawerProps extends Omit<HTMLAttributes<HTMLDialogElement>, 'title'> {
|
|
9
10
|
/** TENSOR RX-125 (UIX-04): translatable; English default. */
|
|
@@ -34,9 +34,16 @@ interface SectionHeadProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'>
|
|
|
34
34
|
helpLabel?: string;
|
|
35
35
|
/** Right-aligned slot — a `.uix-section-link` anchor, a button, a pill. */
|
|
36
36
|
action?: ReactNode;
|
|
37
|
+
/**
|
|
38
|
+
* `compact` is the in-card section preset (HAR-1815): a 15 px / 1.3 / 600 title in place of
|
|
39
|
+
* the 24 px editorial h2. Default `default`.
|
|
40
|
+
*/
|
|
41
|
+
size?: 'default' | 'compact';
|
|
42
|
+
/** Heading level of the title. Default `h2`; an in-card section is often an `h3`. */
|
|
43
|
+
as?: 'h2' | 'h3';
|
|
37
44
|
}
|
|
38
45
|
/** Section head — title row (+ optional `help` ?) with an optional trailing action (`.uix-section-head`). */
|
|
39
|
-
declare function SectionHead({ title, titleId, help, helpLabel, action, className, ...props }: SectionHeadProps): react.JSX.Element;
|
|
46
|
+
declare function SectionHead({ title, titleId, help, helpLabel, action, size, as: Heading, className, ...props }: SectionHeadProps): react.JSX.Element;
|
|
40
47
|
interface NoticeQueueProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
|
|
41
48
|
/** Leading icon slot. */
|
|
42
49
|
icon?: ReactNode;
|
|
@@ -173,9 +180,22 @@ interface EventRowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
|
173
180
|
day: ReactNode;
|
|
174
181
|
title: ReactNode;
|
|
175
182
|
meta?: ReactNode;
|
|
183
|
+
/** Weekday above the month in the date tile, e.g. "Sri" (HAR-1370). */
|
|
184
|
+
weekday?: ReactNode;
|
|
185
|
+
/** Year under the day in the date tile, for a list that crosses a year (HAR-1370). */
|
|
186
|
+
year?: ReactNode;
|
|
187
|
+
/** Start time at the foot of the date tile, e.g. "18:00" (HAR-1370). */
|
|
188
|
+
time?: ReactNode;
|
|
189
|
+
/**
|
|
190
|
+
* The machine-readable date (and time) of the tile, e.g. `"2026-10-22T18:00+02:00"`: the tile
|
|
191
|
+
* becomes a `<time>` element, so the date is not only words.
|
|
192
|
+
*/
|
|
193
|
+
dateTime?: string;
|
|
194
|
+
/** A thumbnail at the end of the row (an `<img>`), drawn 4:3 and cropped to fill (HAR-1370). */
|
|
195
|
+
media?: ReactNode;
|
|
176
196
|
}
|
|
177
|
-
/** Event row — date block + title/meta (`.uix-event-row`). */
|
|
178
|
-
declare function EventRow({ month, day, title, meta, className, ...props }: EventRowProps): react.JSX.Element;
|
|
197
|
+
/** Event row — date block + title/meta (`.uix-event-row`), with an optional thumbnail. */
|
|
198
|
+
declare function EventRow({ month, day, title, meta, weekday, year, time, dateTime, media, className, ...props }: EventRowProps): react.JSX.Element;
|
|
179
199
|
interface StatusRowProps extends HTMLAttributes<HTMLDivElement> {
|
|
180
200
|
/** Leading indicator slot (e.g. a `.uix-dot`). */
|
|
181
201
|
indicator?: ReactNode;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from "../cx.js";
|
|
3
3
|
import { InfoTip } from "./InfoTip.js";
|
|
4
4
|
import { hasHelp, helpLabelFor } from "../info-tip-model.js";
|
|
@@ -28,9 +28,9 @@ function PageIntro({
|
|
|
28
28
|
children
|
|
29
29
|
] });
|
|
30
30
|
}
|
|
31
|
-
function SectionHead({ title, titleId, help, helpLabel, action, className, ...props }) {
|
|
32
|
-
const heading = /* @__PURE__ */ jsx(
|
|
33
|
-
return /* @__PURE__ */ jsxs("div", { className: cx("uix-section-head", className), ...props, children: [
|
|
31
|
+
function SectionHead({ title, titleId, help, helpLabel, action, size = "default", as: Heading = "h2", className, ...props }) {
|
|
32
|
+
const heading = /* @__PURE__ */ jsx(Heading, { className: "uix-section-title", id: titleId, children: title });
|
|
33
|
+
return /* @__PURE__ */ jsxs("div", { className: cx("uix-section-head", size === "compact" && "uix-section-head--compact", className), ...props, children: [
|
|
34
34
|
hasHelp(help) ? /* @__PURE__ */ jsxs("div", { className: "uix-section-head__title-row", children: [
|
|
35
35
|
heading,
|
|
36
36
|
/* @__PURE__ */ jsx(InfoTip, { content: help, label: helpLabel ?? helpLabelFor(title) })
|
|
@@ -153,16 +153,21 @@ function StatLine({ items, children, className, ...props }) {
|
|
|
153
153
|
children
|
|
154
154
|
] });
|
|
155
155
|
}
|
|
156
|
-
function EventRow({ month, day, title, meta, className, ...props }) {
|
|
157
|
-
|
|
158
|
-
/* @__PURE__ */
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
156
|
+
function EventRow({ month, day, title, meta, weekday, year, time, dateTime, media, className, ...props }) {
|
|
157
|
+
const tile = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
158
|
+
weekday != null && /* @__PURE__ */ jsx("span", { className: "uix-event-date__weekday", children: weekday }),
|
|
159
|
+
/* @__PURE__ */ jsx("span", { className: "uix-event-date__month", children: month }),
|
|
160
|
+
/* @__PURE__ */ jsx("span", { className: "uix-event-date__day", children: day }),
|
|
161
|
+
year != null && /* @__PURE__ */ jsx("span", { className: "uix-event-date__year", children: year }),
|
|
162
|
+
time != null && /* @__PURE__ */ jsx("span", { className: "uix-event-date__time", children: time })
|
|
163
|
+
] });
|
|
164
|
+
return /* @__PURE__ */ jsxs("div", { className: cx("uix-event-row", media != null && "uix-event-row--media", className), ...props, children: [
|
|
165
|
+
dateTime ? /* @__PURE__ */ jsx("time", { className: "uix-event-date", dateTime, children: tile }) : /* @__PURE__ */ jsx("div", { className: "uix-event-date", children: tile }),
|
|
162
166
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
163
167
|
/* @__PURE__ */ jsx("p", { className: "uix-event-row__title", children: title }),
|
|
164
168
|
meta != null && /* @__PURE__ */ jsx("p", { className: "uix-event-row__meta", children: meta })
|
|
165
|
-
] })
|
|
169
|
+
] }),
|
|
170
|
+
media != null && /* @__PURE__ */ jsx("div", { className: "uix-event-row__media", children: media })
|
|
166
171
|
] });
|
|
167
172
|
}
|
|
168
173
|
function StatusRow({ indicator, name, meta, className, ...props }) {
|
|
@@ -24,12 +24,23 @@ interface EntityPickerLabels {
|
|
|
24
24
|
more: string;
|
|
25
25
|
/** Polite count when there are more matches than shown. `{count}` is the number shown. */
|
|
26
26
|
resultsMore: string;
|
|
27
|
+
/**
|
|
28
|
+
* Shown, with no Retry, when the search failed for good (HAR-1834): `onSearch` rejected with
|
|
29
|
+
* `{ retryable: false }` (and no `message`), or `isRetryable` said no. E.g. no permission.
|
|
30
|
+
*/
|
|
31
|
+
forbidden: string;
|
|
27
32
|
/** Shown in the open list while the query is shorter than `minQueryLength`. `{count}`. */
|
|
28
33
|
tooShort: string;
|
|
29
34
|
/** The same when `minQueryLength` is 1. */
|
|
30
35
|
typeToSearch: string;
|
|
31
36
|
}
|
|
32
37
|
declare const DEFAULT_ENTITY_PICKER_LABELS: EntityPickerLabels;
|
|
38
|
+
/** What `onSearch` may reject with when trying again cannot help (HAR-1834). */
|
|
39
|
+
interface EntityPickerSearchError {
|
|
40
|
+
retryable: false;
|
|
41
|
+
/** Shown instead of the `forbidden` words. */
|
|
42
|
+
message?: string;
|
|
43
|
+
}
|
|
33
44
|
/** What `onSearch` may resolve to when it knows its list was cut off. */
|
|
34
45
|
interface EntityPickerSearchResult {
|
|
35
46
|
options: readonly SearchSuggestOption[];
|
|
@@ -43,9 +54,15 @@ interface EntityPickerProps {
|
|
|
43
54
|
/**
|
|
44
55
|
* Finds records for the query; a rejection shows the error state with a Retry row. Resolve
|
|
45
56
|
* with the options, or with `{ options, hasMore: true }` when the list was cut off (a page of
|
|
46
|
-
* 20 out of many): the list then says that more match.
|
|
57
|
+
* 20 out of many): the list then says that more match. Reject with
|
|
58
|
+
* `{ retryable: false, message? }` (an {@link EntityPickerSearchError}) when trying again
|
|
59
|
+
* cannot help, e.g. no permission: the error shows without Retry.
|
|
47
60
|
*/
|
|
48
61
|
onSearch: (query: string) => Promise<readonly SearchSuggestOption[] | EntityPickerSearchResult>;
|
|
62
|
+
/** Decide from the rejection whether Retry is offered. Default: not for `{ retryable: false }`. */
|
|
63
|
+
isRetryable?: (error: unknown) => boolean;
|
|
64
|
+
/** Ids of the visible label that names the search field (instead of `label`). */
|
|
65
|
+
'aria-labelledby'?: string;
|
|
49
66
|
/** The field's name (the visible label usually comes from a surrounding `Field`). */
|
|
50
67
|
label: string;
|
|
51
68
|
/** Id of the control, for `Field htmlFor`/`label for`. */
|
|
@@ -76,6 +93,6 @@ interface EntityPickerProps {
|
|
|
76
93
|
* clear ×; choosing again or typing opens a `SearchSuggest` list with loading, empty and error
|
|
77
94
|
* states, and Retry is a list row, so the keyboard reaches it. Stale results are ignored.
|
|
78
95
|
*/
|
|
79
|
-
declare function EntityPicker({ value, onValueChange, onSearch, label, id, name, placeholder, minQueryLength, delay, disabled, invalid, 'aria-describedby': describedBy, renderValue, labels: labelOverrides, className, }: EntityPickerProps): react.JSX.Element;
|
|
96
|
+
declare function EntityPicker({ value, onValueChange, onSearch, isRetryable, label, id, name, placeholder, minQueryLength, delay, disabled, invalid, 'aria-describedby': describedBy, 'aria-labelledby': labelledBy, renderValue, labels: labelOverrides, className, }: EntityPickerProps): react.JSX.Element;
|
|
80
97
|
|
|
81
|
-
export { DEFAULT_ENTITY_PICKER_LABELS, EntityPicker, type EntityPickerLabels, type EntityPickerProps, type EntityPickerSearchResult };
|
|
98
|
+
export { DEFAULT_ENTITY_PICKER_LABELS, EntityPicker, type EntityPickerLabels, type EntityPickerProps, type EntityPickerSearchError, type EntityPickerSearchResult };
|