@tensor_1/react 2.47.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 +470 -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/Button.d.ts +12 -5
- package/dist/components/Button.js +7 -3
- 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/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.js +16 -0
- package/dist/components/DisplayPreferences.d.ts +83 -0
- package/dist/components/DisplayPreferences.js +185 -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 +2 -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/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/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/Stat.d.ts +28 -3
- package/dist/components/Stat.js +13 -3
- 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 +9 -2
- package/dist/components/Table.js +4 -2
- 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/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 +18644 -13210
- package/dist/index.d.cts +3003 -699
- package/dist/index.d.ts +42 -16
- package/dist/index.js +139 -7
- package/dist/labels-context.d.ts +90 -3
- 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,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useId, useRef, useState } from "react";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useId, useLayoutEffect, useRef, useState } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
5
|
import { fillLabel } from "../fill-label.js";
|
|
6
6
|
import { useUixLabels } from "../labels-context.js";
|
|
7
|
+
import { useDelayedPending } from "../hooks/useDelayedPending.js";
|
|
7
8
|
import { PencilIcon } from "../icons/components.js";
|
|
8
9
|
import { Button } from "./Button.js";
|
|
9
10
|
import { Input } from "./Input.js";
|
|
@@ -15,8 +16,10 @@ const DEFAULT_INLINE_EDIT_LABELS = {
|
|
|
15
16
|
saving: "Saving\u2026",
|
|
16
17
|
saved: "Saved",
|
|
17
18
|
failed: "Could not save.",
|
|
18
|
-
empty: "Not set"
|
|
19
|
+
empty: "Not set",
|
|
20
|
+
readOnly: "Read-only:"
|
|
19
21
|
};
|
|
22
|
+
const defaultEmpty = (value) => value == null || value === "";
|
|
20
23
|
function InlineEdit({
|
|
21
24
|
value,
|
|
22
25
|
onSave,
|
|
@@ -25,22 +28,45 @@ function InlineEdit({
|
|
|
25
28
|
renderEditor,
|
|
26
29
|
multiline = false,
|
|
27
30
|
validate,
|
|
31
|
+
equals = Object.is,
|
|
32
|
+
isEmpty = defaultEmpty,
|
|
33
|
+
commit = "explicit",
|
|
28
34
|
placeholder,
|
|
29
35
|
disabled = false,
|
|
36
|
+
readOnlyReason,
|
|
37
|
+
trigger = "value",
|
|
38
|
+
editing: editingProp,
|
|
39
|
+
onEditingChange,
|
|
40
|
+
draft: draftProp,
|
|
41
|
+
onDraftChange,
|
|
42
|
+
renderError,
|
|
43
|
+
onStateChange,
|
|
30
44
|
labels: labelOverrides,
|
|
31
45
|
className
|
|
32
46
|
}) {
|
|
33
47
|
const labels = { ...DEFAULT_INLINE_EDIT_LABELS, ...useUixLabels().inlineEdit, ...labelOverrides };
|
|
34
48
|
const id = useId();
|
|
35
49
|
const errorId = `${id}-error`;
|
|
36
|
-
const
|
|
37
|
-
const [
|
|
50
|
+
const reasonId = `${id}-reason`;
|
|
51
|
+
const [editingState, setEditingState] = useState(false);
|
|
52
|
+
const isEditing = editingProp ?? editingState;
|
|
53
|
+
const [draftState, setDraftState] = useState(value);
|
|
54
|
+
const draft = draftProp !== void 0 ? draftProp : draftState;
|
|
38
55
|
const [pending, setPending] = useState(false);
|
|
56
|
+
const pendingVisible = useDelayedPending(pending) && pending;
|
|
39
57
|
const [error, setError] = useState();
|
|
40
58
|
const [status, setStatus] = useState("");
|
|
59
|
+
const [saved, setSaved] = useState(false);
|
|
60
|
+
const [accepted, setAccepted] = useState(null);
|
|
41
61
|
const viewRef = useRef(null);
|
|
42
62
|
const editorRef = useRef(null);
|
|
43
|
-
const
|
|
63
|
+
const focusView = useRef(false);
|
|
64
|
+
const editingRef = useRef(isEditing);
|
|
65
|
+
editingRef.current = isEditing;
|
|
66
|
+
const inFlight = useRef(false);
|
|
67
|
+
const lastSent = useRef(null);
|
|
68
|
+
const session = useRef(0);
|
|
69
|
+
const pressInside = useRef(false);
|
|
44
70
|
const mounted = useRef(true);
|
|
45
71
|
useEffect(() => {
|
|
46
72
|
mounted.current = true;
|
|
@@ -48,83 +74,143 @@ function InlineEdit({
|
|
|
48
74
|
mounted.current = false;
|
|
49
75
|
};
|
|
50
76
|
}, []);
|
|
77
|
+
const lastValue = useRef(value);
|
|
78
|
+
useLayoutEffect(() => {
|
|
79
|
+
if (equals(lastValue.current, value)) return;
|
|
80
|
+
lastValue.current = value;
|
|
81
|
+
setAccepted(null);
|
|
82
|
+
});
|
|
83
|
+
const shown = accepted ? accepted.value : value;
|
|
51
84
|
useEffect(() => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
85
|
+
if (pendingVisible && pending) setStatus(labels.saving);
|
|
86
|
+
}, [pendingVisible, pending]);
|
|
87
|
+
const state = pending ? "saving" : error?.failure ? "error" : isEditing ? "editing" : saved ? "saved" : "idle";
|
|
88
|
+
const onStateChangeRef = useRef(onStateChange);
|
|
89
|
+
onStateChangeRef.current = onStateChange;
|
|
90
|
+
const reported = useRef("idle");
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (reported.current === state) return;
|
|
93
|
+
reported.current = state;
|
|
94
|
+
onStateChangeRef.current?.(state);
|
|
95
|
+
}, [state]);
|
|
96
|
+
const setEditing = (next) => {
|
|
97
|
+
editingRef.current = next;
|
|
98
|
+
if (editingProp === void 0) setEditingState(next);
|
|
99
|
+
onEditingChange?.(next);
|
|
100
|
+
};
|
|
101
|
+
const setDraft = (next) => {
|
|
102
|
+
if (draftProp === void 0) setDraftState(next);
|
|
103
|
+
onDraftChange?.(next);
|
|
104
|
+
};
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
if (isEditing) {
|
|
107
|
+
const control = editorRef.current?.querySelector("input, textarea, select, button, [tabindex]");
|
|
108
|
+
control?.focus();
|
|
109
|
+
if (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement) control.select();
|
|
57
110
|
return;
|
|
58
111
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
112
|
+
if (focusView.current) {
|
|
113
|
+
focusView.current = false;
|
|
114
|
+
viewRef.current?.focus();
|
|
115
|
+
}
|
|
116
|
+
}, [isEditing]);
|
|
117
|
+
const wasEditing = useRef(isEditing);
|
|
118
|
+
useLayoutEffect(() => {
|
|
119
|
+
if (isEditing && !wasEditing.current && editingProp !== void 0) resetForOpen();
|
|
120
|
+
wasEditing.current = isEditing;
|
|
121
|
+
});
|
|
122
|
+
function resetForOpen() {
|
|
123
|
+
setDraft(shown);
|
|
65
124
|
setError(void 0);
|
|
66
125
|
setStatus("");
|
|
67
|
-
|
|
126
|
+
setSaved(false);
|
|
127
|
+
lastSent.current = null;
|
|
128
|
+
}
|
|
129
|
+
const start = () => {
|
|
130
|
+
session.current += 1;
|
|
131
|
+
resetForOpen();
|
|
68
132
|
setEditing(true);
|
|
69
133
|
};
|
|
70
|
-
const close = () => {
|
|
71
|
-
|
|
134
|
+
const close = (returnFocus) => {
|
|
135
|
+
focusView.current = returnFocus;
|
|
136
|
+
inFlight.current = false;
|
|
72
137
|
setEditing(false);
|
|
73
138
|
};
|
|
74
139
|
const cancel = () => {
|
|
75
|
-
if (
|
|
140
|
+
if (inFlight.current) return;
|
|
76
141
|
setError(void 0);
|
|
77
|
-
|
|
142
|
+
setDraft(shown);
|
|
143
|
+
close(true);
|
|
78
144
|
};
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
145
|
+
const failureMessage = (reason) => typeof reason === "string" && reason ? reason : reason instanceof Error && reason.message ? reason.message : labels.failed;
|
|
146
|
+
const send = (next, explicit) => {
|
|
147
|
+
if (!editingRef.current || inFlight.current) return;
|
|
148
|
+
if (!explicit && error?.failure && lastSent.current && equals(lastSent.current.value, next)) return;
|
|
149
|
+
const issue = validate?.(next);
|
|
82
150
|
if (issue != null) {
|
|
83
|
-
setError(issue);
|
|
151
|
+
setError({ message: issue });
|
|
84
152
|
return;
|
|
85
153
|
}
|
|
86
|
-
if (
|
|
87
|
-
|
|
154
|
+
if (equals(next, shown)) {
|
|
155
|
+
setError(void 0);
|
|
156
|
+
close(explicit);
|
|
88
157
|
return;
|
|
89
158
|
}
|
|
90
159
|
setError(void 0);
|
|
160
|
+
lastSent.current = { value: next };
|
|
161
|
+
const mine = session.current;
|
|
91
162
|
let result;
|
|
92
163
|
try {
|
|
93
|
-
result = onSave(
|
|
164
|
+
result = onSave(next);
|
|
94
165
|
} catch (thrown) {
|
|
95
|
-
setError(
|
|
166
|
+
setError({ message: failureMessage(thrown), failure: { reason: thrown } });
|
|
96
167
|
return;
|
|
97
168
|
}
|
|
98
|
-
|
|
169
|
+
const done = () => {
|
|
170
|
+
setAccepted({ value: next });
|
|
99
171
|
setStatus(labels.saved);
|
|
100
|
-
|
|
172
|
+
setSaved(true);
|
|
173
|
+
if (editingRef.current && session.current === mine) close(explicit);
|
|
174
|
+
};
|
|
175
|
+
if (!result || typeof result.then !== "function") {
|
|
176
|
+
done();
|
|
101
177
|
return;
|
|
102
178
|
}
|
|
179
|
+
inFlight.current = true;
|
|
103
180
|
setPending(true);
|
|
104
|
-
setStatus(labels.saving);
|
|
105
181
|
result.then(
|
|
106
182
|
() => {
|
|
107
183
|
if (!mounted.current) return;
|
|
184
|
+
inFlight.current = false;
|
|
108
185
|
setPending(false);
|
|
109
|
-
|
|
110
|
-
close();
|
|
186
|
+
done();
|
|
111
187
|
},
|
|
112
188
|
(reason) => {
|
|
113
189
|
if (!mounted.current) return;
|
|
190
|
+
inFlight.current = false;
|
|
114
191
|
setPending(false);
|
|
192
|
+
if (session.current !== mine) return;
|
|
115
193
|
setStatus("");
|
|
116
|
-
setError(
|
|
117
|
-
focusNext.current = null;
|
|
194
|
+
setError({ message: failureMessage(reason), failure: { reason } });
|
|
118
195
|
editorRef.current?.querySelector("input, textarea, select")?.focus();
|
|
119
196
|
}
|
|
120
197
|
);
|
|
121
198
|
};
|
|
199
|
+
const save = (next) => send(next === void 0 ? draft : next, true);
|
|
200
|
+
const retry = () => send(lastSent.current && equals(lastSent.current.value, draft) ? lastSent.current.value : draft, true);
|
|
201
|
+
const change = (next) => {
|
|
202
|
+
if (inFlight.current) return;
|
|
203
|
+
setDraft(next);
|
|
204
|
+
if (error && !error.failure) setError(void 0);
|
|
205
|
+
if (commit === "change") send(next, true);
|
|
206
|
+
};
|
|
122
207
|
const onEditorKeyDown = (event) => {
|
|
123
208
|
if (event.key === "Escape") {
|
|
209
|
+
if (event.defaultPrevented || event.target.closest?.('[aria-expanded="true"]')) return;
|
|
124
210
|
event.preventDefault();
|
|
125
211
|
event.stopPropagation();
|
|
126
212
|
cancel();
|
|
127
|
-
} else if (event.key === "Enter" && !event.defaultPrevented) {
|
|
213
|
+
} else if (event.key === "Enter" && !event.defaultPrevented && commit !== "change") {
|
|
128
214
|
const inField = event.target.matches("input, textarea");
|
|
129
215
|
if (!inField) return;
|
|
130
216
|
const isTextarea = event.target.tagName === "TEXTAREA";
|
|
@@ -133,15 +219,56 @@ function InlineEdit({
|
|
|
133
219
|
save();
|
|
134
220
|
}
|
|
135
221
|
};
|
|
222
|
+
const onEditorBlur = (event) => {
|
|
223
|
+
if (commit === "explicit") return;
|
|
224
|
+
const next = event.relatedTarget;
|
|
225
|
+
if (next && event.currentTarget.contains(next)) return;
|
|
226
|
+
if (pressInside.current) return;
|
|
227
|
+
if (next) {
|
|
228
|
+
send(draft, false);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const draftNow = draft;
|
|
232
|
+
setTimeout(() => {
|
|
233
|
+
if (!editingRef.current || pressInside.current) return;
|
|
234
|
+
if (editorRef.current?.contains(document.activeElement)) return;
|
|
235
|
+
send(draftNow, false);
|
|
236
|
+
}, 0);
|
|
237
|
+
};
|
|
238
|
+
const onEditorPointerDown = () => {
|
|
239
|
+
pressInside.current = true;
|
|
240
|
+
const release = () => {
|
|
241
|
+
setTimeout(() => {
|
|
242
|
+
pressInside.current = false;
|
|
243
|
+
}, 0);
|
|
244
|
+
window.removeEventListener("pointerup", release, true);
|
|
245
|
+
window.removeEventListener("pointercancel", release, true);
|
|
246
|
+
};
|
|
247
|
+
window.addEventListener("pointerup", release, true);
|
|
248
|
+
window.addEventListener("pointercancel", release, true);
|
|
249
|
+
};
|
|
136
250
|
const liveRegion = /* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", role: "status", "aria-live": "polite", children: status });
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
251
|
+
const empty = isEmpty(shown);
|
|
252
|
+
const display = empty ? /* @__PURE__ */ jsx("span", { className: "uix-inline-edit__placeholder", children: placeholder ?? labels.empty }) : renderView?.(shown) ?? String(shown);
|
|
253
|
+
if (disabled || readOnlyReason != null) {
|
|
254
|
+
return /* @__PURE__ */ jsxs("span", { className: cx("uix-inline-edit", "uix-inline-edit--readonly", readOnlyReason != null && "uix-inline-edit--reason", className), children: [
|
|
255
|
+
/* @__PURE__ */ jsx("span", { className: "uix-inline-edit__value", "aria-describedby": readOnlyReason != null ? reasonId : void 0, children: display }),
|
|
256
|
+
readOnlyReason != null && /* @__PURE__ */ jsxs("span", { id: reasonId, className: "uix-inline-edit__reason", children: [
|
|
257
|
+
/* @__PURE__ */ jsxs("span", { className: "uix-visually-hidden", children: [
|
|
258
|
+
labels.readOnly,
|
|
259
|
+
" "
|
|
260
|
+
] }),
|
|
261
|
+
readOnlyReason
|
|
262
|
+
] })
|
|
263
|
+
] });
|
|
140
264
|
}
|
|
141
|
-
if (!
|
|
142
|
-
return /* @__PURE__ */ jsxs("span", { className: cx("uix-inline-edit", className), children: [
|
|
143
|
-
/* @__PURE__ */ jsxs(
|
|
144
|
-
/* @__PURE__ */ jsx("span", { className: "uix-inline-edit__value", children:
|
|
265
|
+
if (!isEditing) {
|
|
266
|
+
return /* @__PURE__ */ jsxs("span", { className: cx("uix-inline-edit", trigger === "icon" && "uix-inline-edit--icon-trigger", className), children: [
|
|
267
|
+
trigger === "icon" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
268
|
+
/* @__PURE__ */ jsx("span", { className: "uix-inline-edit__value", children: display }),
|
|
269
|
+
/* @__PURE__ */ jsx("button", { ref: viewRef, type: "button", className: "uix-inline-edit__trigger", "aria-label": fillLabel(labels.edit, { label }), onClick: start, children: /* @__PURE__ */ jsx(PencilIcon, { size: "sm" }) })
|
|
270
|
+
] }) : /* @__PURE__ */ jsxs("button", { ref: viewRef, type: "button", className: "uix-inline-edit__view", onClick: start, children: [
|
|
271
|
+
/* @__PURE__ */ jsx("span", { className: "uix-inline-edit__value", children: display }),
|
|
145
272
|
/* @__PURE__ */ jsxs("span", { className: "uix-visually-hidden", children: [
|
|
146
273
|
", ",
|
|
147
274
|
fillLabel(labels.edit, { label })
|
|
@@ -156,17 +283,33 @@ function InlineEdit({
|
|
|
156
283
|
"aria-label": label,
|
|
157
284
|
"aria-describedby": error != null ? errorId : void 0,
|
|
158
285
|
"aria-invalid": error != null ? true : void 0,
|
|
159
|
-
disabled:
|
|
286
|
+
disabled: pendingVisible,
|
|
287
|
+
readOnly: pendingVisible
|
|
160
288
|
};
|
|
289
|
+
const textDraft = typeof draft === "string" ? draft : String(draft ?? "");
|
|
290
|
+
const errorBody = error && (error.failure && renderError ? renderError(error.failure.reason, { message: error.message, draft, retry, cancel }) : error.message);
|
|
161
291
|
return /* @__PURE__ */ jsxs("span", { className: cx("uix-inline-edit", "uix-inline-edit--editing", className), children: [
|
|
162
|
-
/* @__PURE__ */ jsxs(
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
292
|
+
/* @__PURE__ */ jsxs(
|
|
293
|
+
"div",
|
|
294
|
+
{
|
|
295
|
+
ref: editorRef,
|
|
296
|
+
className: "uix-inline-edit__editor",
|
|
297
|
+
role: "group",
|
|
298
|
+
"aria-label": fillLabel(labels.edit, { label }),
|
|
299
|
+
"aria-busy": pending || void 0,
|
|
300
|
+
onKeyDown: onEditorKeyDown,
|
|
301
|
+
onBlur: onEditorBlur,
|
|
302
|
+
onPointerDownCapture: onEditorPointerDown,
|
|
303
|
+
children: [
|
|
304
|
+
/* @__PURE__ */ jsx("div", { className: "uix-inline-edit__control", children: renderEditor ? renderEditor({ value: draft, onChange: change, save, cancel, field }) : multiline ? /* @__PURE__ */ jsx(Textarea, { ...field, disabled: false, rows: 3, value: textDraft, invalid: error != null, onChange: (event) => change(event.currentTarget.value) }) : /* @__PURE__ */ jsx(Input, { ...field, disabled: false, size: "sm", value: textDraft, invalid: error != null, onChange: (event) => change(event.currentTarget.value) }) }),
|
|
305
|
+
/* @__PURE__ */ jsxs("span", { className: "uix-inline-edit__actions", children: [
|
|
306
|
+
commit !== "change" && /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", variant: "primary", loading: pending, onMouseDown: (event) => event.preventDefault(), onClick: () => save(), children: labels.save }),
|
|
307
|
+
/* @__PURE__ */ jsx(Button, { type: "button", size: "sm", disabled: pendingVisible, onMouseDown: (event) => event.preventDefault(), onClick: cancel, children: labels.cancel })
|
|
308
|
+
] }),
|
|
309
|
+
errorBody != null && /* @__PURE__ */ jsx("div", { id: errorId, className: "uix-field__error uix-inline-edit__error", role: "alert", children: errorBody })
|
|
310
|
+
]
|
|
311
|
+
}
|
|
312
|
+
),
|
|
170
313
|
liveRegion
|
|
171
314
|
] });
|
|
172
315
|
}
|
|
@@ -2,7 +2,11 @@ import * as react from 'react';
|
|
|
2
2
|
import { InputHTMLAttributes, ReactNode, AriaAttributes } from 'react';
|
|
3
3
|
|
|
4
4
|
interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* `sm` 28 px, `md` 36 px (default), `lg` 44 px: the touch-target floor of a public page, the
|
|
7
|
+
* same height as `Button size="lg"` (HAR-1812).
|
|
8
|
+
*/
|
|
9
|
+
size?: 'sm' | 'md' | 'lg';
|
|
6
10
|
invalid?: boolean;
|
|
7
11
|
valid?: boolean;
|
|
8
12
|
/**
|
|
@@ -16,6 +20,12 @@ interface InputGroupProps {
|
|
|
16
20
|
children: ReactNode;
|
|
17
21
|
leadingIcon?: ReactNode;
|
|
18
22
|
trailingIcon?: ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* A control at the end of the field, inside its border: a reveal toggle, a clear or a copy
|
|
25
|
+
* button (HAR-1376). Give it the class `uix-input-group__action` (an icon button sized to the
|
|
26
|
+
* field) and an accessible name. It takes the place of `trailingIcon`.
|
|
27
|
+
*/
|
|
28
|
+
trailingAction?: ReactNode;
|
|
19
29
|
className?: string;
|
|
20
30
|
/** Injected by `<Field>` when it wraps the group; forwarded onto the child control. */
|
|
21
31
|
id?: string;
|
|
@@ -29,6 +39,6 @@ interface InputGroupProps {
|
|
|
29
39
|
* single child element (the child's own props win) so label/message association survives
|
|
30
40
|
* the extra div (UIX-A11Y-3).
|
|
31
41
|
*/
|
|
32
|
-
declare function InputGroup({ children, leadingIcon, trailingIcon, className, id, 'aria-describedby': describedBy, 'aria-invalid': invalid, 'aria-required': required, }: InputGroupProps): react.JSX.Element;
|
|
42
|
+
declare function InputGroup({ children, leadingIcon, trailingIcon, trailingAction, className, id, 'aria-describedby': describedBy, 'aria-invalid': invalid, 'aria-required': required, }: InputGroupProps): react.JSX.Element;
|
|
33
43
|
|
|
34
44
|
export { Input, InputGroup, type InputGroupProps, type InputProps };
|
package/dist/components/Input.js
CHANGED
|
@@ -6,7 +6,7 @@ const Input = forwardRef(
|
|
|
6
6
|
"input",
|
|
7
7
|
{
|
|
8
8
|
ref,
|
|
9
|
-
className: cx("uix-input", size
|
|
9
|
+
className: cx("uix-input", size !== "md" && `uix-input--${size}`, className),
|
|
10
10
|
"aria-invalid": invalid || void 0,
|
|
11
11
|
"data-valid": valid || void 0,
|
|
12
12
|
"data-warning": warning || void 0,
|
|
@@ -19,6 +19,7 @@ function InputGroup({
|
|
|
19
19
|
children,
|
|
20
20
|
leadingIcon,
|
|
21
21
|
trailingIcon,
|
|
22
|
+
trailingAction,
|
|
22
23
|
className,
|
|
23
24
|
id,
|
|
24
25
|
"aria-describedby": describedBy,
|
|
@@ -37,13 +38,14 @@ function InputGroup({
|
|
|
37
38
|
className: cx(
|
|
38
39
|
"uix-input-group",
|
|
39
40
|
leadingIcon && "uix-input-group--has-leading",
|
|
40
|
-
trailingIcon && "uix-input-group--has-trailing",
|
|
41
|
+
(trailingIcon || trailingAction) && "uix-input-group--has-trailing",
|
|
42
|
+
trailingAction && "uix-input-group--has-action",
|
|
41
43
|
className
|
|
42
44
|
),
|
|
43
45
|
children: [
|
|
44
46
|
leadingIcon && /* @__PURE__ */ jsx("span", { className: "uix-input-group__icon", "aria-hidden": "true", children: leadingIcon }),
|
|
45
47
|
control,
|
|
46
|
-
trailingIcon && /* @__PURE__ */ jsx("span", { className: "uix-input-group__icon uix-input-group__icon--trailing", "aria-hidden": "true", children: trailingIcon })
|
|
48
|
+
trailingAction ?? (trailingIcon && /* @__PURE__ */ jsx("span", { className: "uix-input-group__icon uix-input-group__icon--trailing", "aria-hidden": "true", children: trailingIcon }))
|
|
47
49
|
]
|
|
48
50
|
}
|
|
49
51
|
);
|
package/dist/components/Kbd.d.ts
CHANGED
|
@@ -26,6 +26,11 @@ type KeyShortcut = readonly string[];
|
|
|
26
26
|
declare function ariaKeyShortcuts(keys: KeyShortcut, platform: KbdPlatform): string;
|
|
27
27
|
/** A `shortcut` prop that is key names rather than a node (HAR-2031). */
|
|
28
28
|
declare const isKeyShortcut: (value: unknown) => value is KeyShortcut;
|
|
29
|
+
/**
|
|
30
|
+
* What a screen reader should hear for a shortcut: the key names joined with "+", e.g.
|
|
31
|
+
* "Control+K" or "Command+K" (HAR-2416). The same text `KbdCombo` speaks. Internal.
|
|
32
|
+
*/
|
|
33
|
+
declare function spokenKeys(keys: KeyShortcut, platform: KbdPlatform, keyLabels?: Partial<Record<string, string>>): string;
|
|
29
34
|
interface KbdComboProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'children'> {
|
|
30
35
|
/**
|
|
31
36
|
* Keys in order, e.g. `['Mod', 'K']`. `Mod` is ⌘ on Apple and Ctrl elsewhere; `Shift`,
|
|
@@ -43,4 +48,4 @@ interface KbdComboProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'children'
|
|
|
43
48
|
*/
|
|
44
49
|
declare function KbdCombo({ keys, platform, keyLabels, className, ...props }: KbdComboProps): react.JSX.Element;
|
|
45
50
|
|
|
46
|
-
export { Kbd, KbdCombo, type KbdComboProps, type KbdPlatform, type KbdProps, type KeyShortcut, ariaKeyShortcuts, isKeyShortcut, useKbdPlatform };
|
|
51
|
+
export { Kbd, KbdCombo, type KbdComboProps, type KbdPlatform, type KbdProps, type KeyShortcut, ariaKeyShortcuts, isKeyShortcut, spokenKeys, useKbdPlatform };
|
package/dist/components/Kbd.js
CHANGED
|
@@ -48,15 +48,14 @@ function ariaKeyShortcuts(keys, platform) {
|
|
|
48
48
|
return keys.map((key) => ARIA_KEYS[key]?.[platform] ?? (key.length === 1 ? key.toUpperCase() : key)).join("+");
|
|
49
49
|
}
|
|
50
50
|
const isKeyShortcut = (value) => Array.isArray(value) && value.length > 0 && value.every((key) => typeof key === "string");
|
|
51
|
+
function spokenKeys(keys, platform, keyLabels) {
|
|
52
|
+
return keys.map((key) => keyLabels?.[key] ?? KEYS[key]?.[platform]?.[1] ?? key).join("+");
|
|
53
|
+
}
|
|
51
54
|
function KbdCombo({ keys, platform, keyLabels, className, ...props }) {
|
|
52
55
|
const active = useKbdPlatform(platform);
|
|
53
|
-
const parts = keys.map((key) => {
|
|
54
|
-
const known = KEYS[key]?.[active];
|
|
55
|
-
return { glyph: known?.[0] ?? key, spoken: keyLabels?.[key] ?? known?.[1] ?? key };
|
|
56
|
-
});
|
|
57
56
|
return /* @__PURE__ */ jsxs("span", { className: cx("uix-kbd-combo", className), ...props, children: [
|
|
58
|
-
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children:
|
|
59
|
-
/* @__PURE__ */ jsx("span", { className: "uix-kbd-combo__keys", "aria-hidden": "true", children:
|
|
57
|
+
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: spokenKeys(keys, active, keyLabels) }),
|
|
58
|
+
/* @__PURE__ */ jsx("span", { className: "uix-kbd-combo__keys", "aria-hidden": "true", children: keys.map((key, i) => /* @__PURE__ */ jsx("kbd", { className: "uix-kbd", children: KEYS[key]?.[active]?.[0] ?? key }, `${key}-${i}`)) })
|
|
60
59
|
] });
|
|
61
60
|
}
|
|
62
61
|
export {
|
|
@@ -64,5 +63,6 @@ export {
|
|
|
64
63
|
KbdCombo,
|
|
65
64
|
ariaKeyShortcuts,
|
|
66
65
|
isKeyShortcut,
|
|
66
|
+
spokenKeys,
|
|
67
67
|
useKbdPlatform
|
|
68
68
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
2
|
+
import { ReactNode, KeyboardEvent } from 'react';
|
|
3
3
|
|
|
4
4
|
interface LightboxItem {
|
|
5
5
|
src: string;
|
|
@@ -22,6 +22,21 @@ interface LightboxLabels {
|
|
|
22
22
|
zoomOut: string;
|
|
23
23
|
}
|
|
24
24
|
declare const DEFAULT_LIGHTBOX_LABELS: LightboxLabels;
|
|
25
|
+
/** What `renderOverlay` receives (HAR-1826). */
|
|
26
|
+
interface LightboxOverlayContext {
|
|
27
|
+
/** The item on show (0-based) and the item itself. */
|
|
28
|
+
index: number;
|
|
29
|
+
item: LightboxItem;
|
|
30
|
+
/** The rendered `<img>` or `<video>`; `naturalWidth` / `videoWidth` give the bitmap size. */
|
|
31
|
+
media: HTMLImageElement | HTMLVideoElement | null;
|
|
32
|
+
/**
|
|
33
|
+
* The overlay's size in CSS pixels. The overlay covers exactly the picture (not the letterbox
|
|
34
|
+
* around a fitted one), so a point at `(x, y)` in it is bitmap pixel
|
|
35
|
+
* `(x / width * naturalWidth, y / height * naturalHeight)`. 0 until the media has loaded.
|
|
36
|
+
*/
|
|
37
|
+
width: number;
|
|
38
|
+
height: number;
|
|
39
|
+
}
|
|
25
40
|
interface LightboxProps {
|
|
26
41
|
items: readonly LightboxItem[];
|
|
27
42
|
/** The item on show (0-based). */
|
|
@@ -33,13 +48,34 @@ interface LightboxProps {
|
|
|
33
48
|
loop?: boolean;
|
|
34
49
|
labels?: Partial<LightboxLabels>;
|
|
35
50
|
className?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Buttons for the toolbar beside Close (HAR-1826), e.g. "Hide area" and "Undo" on a viewer
|
|
53
|
+
* that redacts a screenshot. Tab order: these, then Close. They sit inside the dialog.
|
|
54
|
+
*/
|
|
55
|
+
actions?: ReactNode;
|
|
56
|
+
/**
|
|
57
|
+
* Content laid over the picture, in the picture's own box: it moves and scales with the
|
|
58
|
+
* fit / actual-size toggle. Pointer events on it never swipe or page, so it can draw. Each
|
|
59
|
+
* child positions itself (`position: absolute` in `%`, or px of `width` / `height`).
|
|
60
|
+
*/
|
|
61
|
+
renderOverlay?: (context: LightboxOverlayContext) => ReactNode;
|
|
62
|
+
/**
|
|
63
|
+
* Runs first for every key in the viewer. Call `event.preventDefault()` to claim the key:
|
|
64
|
+
* the viewer then neither pages (arrows, Home, End) nor closes (Escape). For a draft that
|
|
65
|
+
* arrow keys move and Escape cancels.
|
|
66
|
+
*/
|
|
67
|
+
onKeyDown?: (event: KeyboardEvent<HTMLDialogElement>) => void;
|
|
68
|
+
/** Escape only: `event.preventDefault()` keeps the viewer open. */
|
|
69
|
+
onEscapeKeyDown?: (event: KeyboardEvent<HTMLDialogElement>) => void;
|
|
36
70
|
}
|
|
37
71
|
/**
|
|
38
72
|
* A full-screen media viewer over `.uix-lightbox` (HAR-1367; TENSOR C12 `capture-viewer.tsx`,
|
|
39
73
|
* MOTUS C-8 programme gallery): previous/next buttons, ←/→ and Home/End, swipe on touch, an
|
|
40
|
-
* announced "n of total" counter, a caption, and a fit/actual-size
|
|
41
|
-
* so focus stays inside and Escape closes; focus returns to the
|
|
74
|
+
* announced "n of total" counter (not for a single item), a caption, and a fit/actual-size
|
|
75
|
+
* toggle. Native `<dialog>`, so focus stays inside and Escape closes; focus returns to the
|
|
76
|
+
* control that opened it. An annotating viewer (HAR-1826) adds toolbar `actions`, draws in
|
|
77
|
+
* `renderOverlay`, and claims Escape and the arrows with `onKeyDown` while a draft is open.
|
|
42
78
|
*/
|
|
43
|
-
declare function Lightbox({ items, index, onIndexChange, open, onClose, loop, labels: labelOverrides, className }: LightboxProps): react.JSX.Element;
|
|
79
|
+
declare function Lightbox({ items, index, onIndexChange, open, onClose, loop, labels: labelOverrides, className, actions, renderOverlay, onKeyDown: onKeyDownProp, onEscapeKeyDown, }: LightboxProps): react.JSX.Element;
|
|
44
80
|
|
|
45
|
-
export { DEFAULT_LIGHTBOX_LABELS, Lightbox, type LightboxItem, type LightboxLabels, type LightboxProps };
|
|
81
|
+
export { DEFAULT_LIGHTBOX_LABELS, Lightbox, type LightboxItem, type LightboxLabels, type LightboxOverlayContext, type LightboxProps };
|