@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
|
@@ -24,6 +24,13 @@ interface DateRangePickerLabels {
|
|
|
24
24
|
fieldStart: string;
|
|
25
25
|
/** Field mode: the trigger's text for a range; `{start}`, `{end}`. */
|
|
26
26
|
fieldRange: string;
|
|
27
|
+
/**
|
|
28
|
+
* The picker's name: the heading of the inline months, the field's hidden name and its popover's
|
|
29
|
+
* `aria-label` (HAR-2432). The `label` prop wins.
|
|
30
|
+
*/
|
|
31
|
+
label: string;
|
|
32
|
+
/** Shown when the range is invalid (an end before the start, or `invalid`). The `invalidMessage` prop wins. */
|
|
33
|
+
invalidMessage: string;
|
|
27
34
|
}
|
|
28
35
|
declare const DEFAULT_DATE_RANGE_PICKER_LABELS: DateRangePickerLabels;
|
|
29
36
|
interface DateRangePickerProps {
|
|
@@ -35,9 +42,12 @@ interface DateRangePickerProps {
|
|
|
35
42
|
min?: string;
|
|
36
43
|
max?: string;
|
|
37
44
|
isDateDisabled?: (date: string) => boolean;
|
|
45
|
+
/** Default: `UixLabelsProvider` `dateFormat.locale`, then the viewer's. */
|
|
38
46
|
locale?: string;
|
|
47
|
+
/** Default: `labels.label` ("Choose date range"). */
|
|
39
48
|
label?: string;
|
|
40
49
|
invalid?: boolean;
|
|
50
|
+
/** Default: `labels.invalidMessage`. */
|
|
41
51
|
invalidMessage?: string;
|
|
42
52
|
disabled?: boolean;
|
|
43
53
|
className?: string;
|
|
@@ -57,7 +67,7 @@ interface DateRangePickerProps {
|
|
|
57
67
|
* bars. It closes once the range has both ends; Escape closes it and returns focus.
|
|
58
68
|
*/
|
|
59
69
|
mode?: 'inline' | 'field';
|
|
60
|
-
/** Field mode: how the trigger writes a date. Default: the locale's numeric date. */
|
|
70
|
+
/** Field mode: how the trigger writes a date. Default: `UixLabelsProvider` `dateFormat.formatDate`, then the locale's numeric date. */
|
|
61
71
|
formatDate?: (date: string) => string;
|
|
62
72
|
/** Field mode: lands on the trigger, so a `<label htmlFor>` or `<Field>` names it. */
|
|
63
73
|
id?: string;
|
|
@@ -74,6 +84,6 @@ interface DateRangePickerProps {
|
|
|
74
84
|
* Controlled date-range picker using the UIx calendar language: two months inline, or with
|
|
75
85
|
* `mode="field"` a form control that opens them in a popover.
|
|
76
86
|
*/
|
|
77
|
-
declare function DateRangePicker({ labels: labelOverrides, ...
|
|
87
|
+
declare function DateRangePicker({ labels: labelOverrides, ...rest }: DateRangePickerProps): react.JSX.Element;
|
|
78
88
|
|
|
79
89
|
export { DEFAULT_DATE_RANGE_PICKER_LABELS, DateRangePicker, type DateRangePickerLabels, type DateRangePickerProps };
|
|
@@ -21,7 +21,9 @@ const DEFAULT_DATE_RANGE_PICKER_LABELS = {
|
|
|
21
21
|
inRange: "in selected range",
|
|
22
22
|
placeholder: "Choose dates",
|
|
23
23
|
fieldStart: "{start} \u2013",
|
|
24
|
-
fieldRange: "{start} \u2013 {end}"
|
|
24
|
+
fieldRange: "{start} \u2013 {end}",
|
|
25
|
+
label: "Choose date range",
|
|
26
|
+
invalidMessage: "The selected date range is invalid."
|
|
25
27
|
};
|
|
26
28
|
const monthLabel = (date, locale) => new Intl.DateTimeFormat(locale, { month: "long", year: "numeric", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
|
|
27
29
|
const dateLabel = (date, locale) => new Intl.DateTimeFormat(locale, { dateStyle: "full", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
|
|
@@ -35,9 +37,9 @@ function RangeCalendar({
|
|
|
35
37
|
max,
|
|
36
38
|
isDateDisabled,
|
|
37
39
|
locale,
|
|
38
|
-
label
|
|
40
|
+
label: labelProp,
|
|
39
41
|
invalid,
|
|
40
|
-
invalidMessage
|
|
42
|
+
invalidMessage: invalidMessageProp,
|
|
41
43
|
disabled,
|
|
42
44
|
className,
|
|
43
45
|
words: labels,
|
|
@@ -46,6 +48,8 @@ function RangeCalendar({
|
|
|
46
48
|
autoFocus = false
|
|
47
49
|
}) {
|
|
48
50
|
const id = useId();
|
|
51
|
+
const label = labelProp ?? labels.label;
|
|
52
|
+
const invalidMessage = invalidMessageProp ?? labels.invalidMessage;
|
|
49
53
|
const weekdays = useMemo(() => weekdayNames(locale, weekStartsOn), [locale, weekStartsOn]);
|
|
50
54
|
const [internalMonth, setInternalMonth] = useState(() => startOfMonth(controlledMonth ?? value.start ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)));
|
|
51
55
|
const visibleMonth = startOfMonth(controlledMonth ?? internalMonth);
|
|
@@ -145,9 +149,10 @@ function RangeCalendar({
|
|
|
145
149
|
rangeInvalid && /* @__PURE__ */ jsx("p", { className: "uix-field__error", role: "alert", children: invalidMessage })
|
|
146
150
|
] });
|
|
147
151
|
}
|
|
148
|
-
function DateRangePicker({ labels: labelOverrides, ...
|
|
152
|
+
function DateRangePicker({ labels: labelOverrides, ...rest }) {
|
|
149
153
|
const uixLabels = useUixLabels();
|
|
150
154
|
const words = { ...DEFAULT_DATE_RANGE_PICKER_LABELS, ...uixLabels.dateRangePicker, ...labelOverrides };
|
|
155
|
+
const props = { ...rest, locale: rest.locale ?? uixLabels.dateFormat?.locale, formatDate: rest.formatDate ?? uixLabels.dateFormat?.formatDate };
|
|
151
156
|
if (props.mode === "field") return /* @__PURE__ */ jsx(DateRangeField, { ...props, words });
|
|
152
157
|
return /* @__PURE__ */ jsx(RangeCalendar, { ...props, words });
|
|
153
158
|
}
|
|
@@ -156,7 +161,7 @@ function DateRangeField(props) {
|
|
|
156
161
|
value,
|
|
157
162
|
onChange,
|
|
158
163
|
locale,
|
|
159
|
-
label
|
|
164
|
+
label: labelProp,
|
|
160
165
|
invalid,
|
|
161
166
|
disabled,
|
|
162
167
|
className,
|
|
@@ -170,12 +175,13 @@ function DateRangeField(props) {
|
|
|
170
175
|
"aria-invalid": ariaInvalid
|
|
171
176
|
} = props;
|
|
172
177
|
const uid = useId();
|
|
178
|
+
const label = labelProp ?? words.label;
|
|
173
179
|
const id = idProp ?? `${uid}-trigger`;
|
|
174
180
|
const popoverId = `${uid}-months`;
|
|
175
181
|
const valueId = `${uid}-value`;
|
|
176
182
|
const anchorRef = useRef(null);
|
|
177
183
|
const triggerRef = useRef(null);
|
|
178
|
-
const { open, hide, triggerProps } = useFieldPopover(popoverId, () => triggerRef.current?.focus({ preventScroll: true }), onOpenChange);
|
|
184
|
+
const { open, hide, onFieldKeyDown, triggerProps } = useFieldPopover(popoverId, () => triggerRef.current?.focus({ preventScroll: true }), onOpenChange);
|
|
179
185
|
const [labelled, setLabelled] = useState(false);
|
|
180
186
|
useEffect(() => {
|
|
181
187
|
setLabelled((triggerRef.current?.labels?.length ?? 0) > 0);
|
|
@@ -189,12 +195,7 @@ function DateRangeField(props) {
|
|
|
189
195
|
{
|
|
190
196
|
ref: anchorRef,
|
|
191
197
|
className: cx("uix-date-range-field", className),
|
|
192
|
-
onKeyDown:
|
|
193
|
-
if (event.key !== "Escape" || !open) return;
|
|
194
|
-
event.stopPropagation();
|
|
195
|
-
event.preventDefault();
|
|
196
|
-
hide();
|
|
197
|
-
},
|
|
198
|
+
onKeyDown: onFieldKeyDown,
|
|
198
199
|
children: [
|
|
199
200
|
/* @__PURE__ */ jsxs(
|
|
200
201
|
"button",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Renders `children` only once they have been mounted for `delay` ms (HAR-2580). The client
|
|
6
|
+
* half of `Spinner delay` and `LoadingState delay`, so those stay server-renderable: a
|
|
7
|
+
* loader whose work ends inside the delay never appears. Inserting the loader after the
|
|
8
|
+
* delay is what announces it, as it always has.
|
|
9
|
+
*/
|
|
10
|
+
declare function DelayedRender({ delay, children }: {
|
|
11
|
+
delay: number;
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
}): react.JSX.Element | null;
|
|
14
|
+
|
|
15
|
+
export { DelayedRender };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useDelayedPending } from "../hooks/useDelayedPending.js";
|
|
4
|
+
function DelayedRender({ delay, children }) {
|
|
5
|
+
const shown = useDelayedPending(true, { delay, minVisible: 0 });
|
|
6
|
+
return shown ? /* @__PURE__ */ jsx(Fragment, { children }) : null;
|
|
7
|
+
}
|
|
8
|
+
export {
|
|
9
|
+
DelayedRender
|
|
10
|
+
};
|
|
@@ -45,8 +45,14 @@ interface DetailPageProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
|
|
|
45
45
|
* modified click, no JS).
|
|
46
46
|
*/
|
|
47
47
|
onTabSelect?: (id: string) => void;
|
|
48
|
+
/**
|
|
49
|
+
* `staff` is the staff type preset (HAR-1815): the title at h2 with its 1.15 leading, the back
|
|
50
|
+
* link an inline hushed link without an underline, and each metric's label an eyebrow over an
|
|
51
|
+
* h3-size, regular-weight, tabular value. `default` is the plain record page.
|
|
52
|
+
*/
|
|
53
|
+
variant?: 'default' | 'staff';
|
|
48
54
|
}
|
|
49
55
|
/** Framework-neutral record-detail scaffold; routing and data policy stay with the application. */
|
|
50
|
-
declare function DetailPage({ back, eyebrow, title, subtitle, actions, tabs, metrics, side, children, tabsLabel, renderLink, onTabSelect, className, ...props }: DetailPageProps): react.JSX.Element;
|
|
56
|
+
declare function DetailPage({ back, eyebrow, title, subtitle, actions, tabs, metrics, side, children, tabsLabel, renderLink, onTabSelect, variant, className, ...props }: DetailPageProps): react.JSX.Element;
|
|
51
57
|
|
|
52
58
|
export { DetailPage, type DetailPageLinkProps, type DetailPageMetric, type DetailPageProps, type DetailPageTab };
|
|
@@ -18,10 +18,11 @@ function DetailPage({
|
|
|
18
18
|
tabsLabel,
|
|
19
19
|
renderLink = plainLink,
|
|
20
20
|
onTabSelect,
|
|
21
|
+
variant = "default",
|
|
21
22
|
className,
|
|
22
23
|
...props
|
|
23
24
|
}) {
|
|
24
|
-
return /* @__PURE__ */ jsxs("section", { className: cx("uix-detail-page", className), ...props, children: [
|
|
25
|
+
return /* @__PURE__ */ jsxs("section", { className: cx("uix-detail-page", variant === "staff" && "uix-detail-page--staff", className), ...props, children: [
|
|
25
26
|
back && renderLink({ href: back.href, className: "uix-link uix-link--quiet uix-detail-page__back", children: back.label }),
|
|
26
27
|
/* @__PURE__ */ jsx(PageHeader, { eyebrow, title, subtitle, actions }),
|
|
27
28
|
metrics && metrics.length > 0 && /* @__PURE__ */ jsx("dl", { className: "uix-detail-page__metrics", children: metrics.map((metric) => /* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode, RefObject } from 'react';
|
|
2
|
+
import { ButtonHTMLAttributes, ReactNode, RefObject } from 'react';
|
|
3
3
|
import { Placement } from '../overlay-position.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Attributes for a dialog's confirm button (HAR-1831): `data-*` (TENSOR puts `data-action-id`
|
|
7
|
+
* there), `id`, `className`, an `aria-describedby`. Its `onClick` runs first; `preventDefault()`
|
|
8
|
+
* there stops the confirm. `disabled` adds to the dialog's own reasons to disable it.
|
|
9
|
+
*/
|
|
10
|
+
type ConfirmButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'type'> & {
|
|
11
|
+
[dataAttribute: `data-${string}`]: string | number | boolean | undefined;
|
|
12
|
+
};
|
|
5
13
|
interface ConfirmDialogProps {
|
|
6
14
|
open: boolean;
|
|
7
15
|
title: ReactNode;
|
|
@@ -12,6 +20,11 @@ interface ConfirmDialogProps {
|
|
|
12
20
|
closeLabel?: string;
|
|
13
21
|
onConfirm: () => void | Promise<void>;
|
|
14
22
|
onCancel: () => void;
|
|
23
|
+
/**
|
|
24
|
+
* The confirmed action is running: Confirm shows its loading state, Cancel is disabled, and
|
|
25
|
+
* Escape, the × and a press on Cancel do nothing until it settles (HAR-1831), so the person
|
|
26
|
+
* cannot walk away from an action that is still being carried out.
|
|
27
|
+
*/
|
|
15
28
|
pending?: boolean;
|
|
16
29
|
destructive?: boolean;
|
|
17
30
|
/**
|
|
@@ -34,21 +47,47 @@ interface ConfirmDialogProps {
|
|
|
34
47
|
* this text, e.g. the record's name.
|
|
35
48
|
*/
|
|
36
49
|
typeToConfirm?: string;
|
|
37
|
-
/**
|
|
38
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Label of the type-to-confirm field. In a string, `{value}` is replaced by `typeToConfirm`;
|
|
52
|
+
* pass a node for markup, e.g. the value in `<code>` (HAR-1831).
|
|
53
|
+
*/
|
|
54
|
+
typeToConfirmLabel?: ReactNode;
|
|
55
|
+
/** Placeholder of the type-to-confirm field, e.g. the value itself (HAR-1831). */
|
|
56
|
+
typeToConfirmPlaceholder?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Shown under the field (and its description) once the typed text stops matching; `{value}` is
|
|
59
|
+
* replaced by `typeToConfirm` (HAR-1831). Default: `UixLabelsProvider` `confirmDialog`, then
|
|
60
|
+
* "That does not match {value}."
|
|
61
|
+
*/
|
|
62
|
+
typeToConfirmMismatch?: string;
|
|
39
63
|
/** How the action is undone or compensated, shown as its own labelled block (TENSOR C2). */
|
|
40
64
|
compensation?: ReactNode;
|
|
41
65
|
/** Heading of the compensation block. Default "How to undo this". */
|
|
42
66
|
compensationLabel?: string;
|
|
67
|
+
/** Attributes for the confirm button, e.g. `{ 'data-action-id': 'ci.delete' }` (HAR-1831). */
|
|
68
|
+
confirmProps?: ConfirmButtonProps;
|
|
43
69
|
}
|
|
44
|
-
/**
|
|
45
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Generic confirmation shell. Audit, authorization and compensation policy remain application-owned.
|
|
72
|
+
* The ref reaches the `<dialog>` (HAR-1831).
|
|
73
|
+
*/
|
|
74
|
+
declare const ConfirmDialog: react.ForwardRefExoticComponent<ConfirmDialogProps & react.RefAttributes<HTMLDialogElement>>;
|
|
46
75
|
interface PopconfirmProps {
|
|
47
76
|
open: boolean;
|
|
48
77
|
/** The control that asked for confirmation; the step is placed against it and focus returns to it. */
|
|
49
78
|
anchor: RefObject<HTMLElement | null> | HTMLElement | null;
|
|
50
|
-
|
|
79
|
+
/**
|
|
80
|
+
* The question. Optional (HAR-1831): without it the step is named by its confirm button
|
|
81
|
+
* ("Delete"), which then has to say what it does.
|
|
82
|
+
*/
|
|
83
|
+
title?: ReactNode;
|
|
51
84
|
description?: ReactNode;
|
|
85
|
+
/**
|
|
86
|
+
* Content between the text and the buttons, e.g. a required reason field (HAR-1831). When it
|
|
87
|
+
* holds a field, that field has focus when the step opens, and the step takes its full width.
|
|
88
|
+
* Pair a required field with `confirmDisabled` until it is filled.
|
|
89
|
+
*/
|
|
90
|
+
children?: ReactNode;
|
|
52
91
|
confirmLabel: ReactNode;
|
|
53
92
|
cancelLabel: ReactNode;
|
|
54
93
|
onConfirm: () => void | Promise<void>;
|
|
@@ -56,17 +95,39 @@ interface PopconfirmProps {
|
|
|
56
95
|
onCancel: () => void;
|
|
57
96
|
pending?: boolean;
|
|
58
97
|
destructive?: boolean;
|
|
98
|
+
/** Confirm cannot be pressed yet, e.g. while a required reason is empty (HAR-1831). */
|
|
99
|
+
confirmDisabled?: boolean;
|
|
100
|
+
/** Attributes for the confirm button, as on `ConfirmDialog` (HAR-1831). */
|
|
101
|
+
confirmProps?: ConfirmButtonProps;
|
|
59
102
|
/** How the action is undone, under its own heading (TENSOR C2 "single_click" tier). */
|
|
60
103
|
compensation?: ReactNode;
|
|
61
104
|
compensationLabel?: string;
|
|
62
105
|
placement?: Placement;
|
|
106
|
+
/** Default: `'alertdialog'` when `destructive`, else `'dialog'` (HAR-1831). */
|
|
107
|
+
role?: 'dialog' | 'alertdialog';
|
|
108
|
+
/**
|
|
109
|
+
* What names the step: its `title` (default when there is one) or its confirm button
|
|
110
|
+
* (default without a title) (HAR-1831).
|
|
111
|
+
*/
|
|
112
|
+
labelledBy?: 'title' | 'confirm';
|
|
113
|
+
/**
|
|
114
|
+
* Called with the Escape keydown before the step cancels; `event.preventDefault()` keeps it open
|
|
115
|
+
* (HAR-1831). The step only takes an Escape that is its own: not while a popup inside it is open
|
|
116
|
+
* (a Select's list), not one meant for a dialog opened above it, and only the newest open step.
|
|
117
|
+
*/
|
|
118
|
+
onEscapeKeyDown?: (event: KeyboardEvent) => void;
|
|
119
|
+
/**
|
|
120
|
+
* Cancel when the anchor has scrolled out of view (outside the viewport or a clipping ancestor),
|
|
121
|
+
* rather than leave the step floating over unrelated content (HAR-1831). Default `false`.
|
|
122
|
+
*/
|
|
123
|
+
closeWhenAnchorHidden?: boolean;
|
|
63
124
|
}
|
|
64
125
|
/**
|
|
65
126
|
* An anchored, single-step confirmation for low-risk actions (HAR-1356; TENSOR C2): a small
|
|
66
|
-
*
|
|
67
|
-
* and focus returns to the anchor. Use `ConfirmDialog` when the action is hard to undo.
|
|
127
|
+
* dialog on the kit `Popover`, focus on Cancel (or on a field in its content), Escape or a press
|
|
128
|
+
* outside cancels, and focus returns to the anchor. Use `ConfirmDialog` when the action is hard to undo.
|
|
68
129
|
*/
|
|
69
|
-
declare function Popconfirm({ open, anchor, title, description, confirmLabel, cancelLabel, onConfirm, onCancel, pending, destructive, compensation, compensationLabel: compensationLabelProp, placement, }: PopconfirmProps): react.JSX.Element;
|
|
130
|
+
declare function Popconfirm({ open, anchor, title, description, children, confirmLabel, cancelLabel, onConfirm, onCancel, pending, destructive, confirmDisabled, confirmProps, compensation, compensationLabel: compensationLabelProp, placement, role, labelledBy, onEscapeKeyDown, closeWhenAnchorHidden, }: PopconfirmProps): react.JSX.Element;
|
|
70
131
|
interface PromptDialogProps {
|
|
71
132
|
open: boolean;
|
|
72
133
|
title: ReactNode;
|
|
@@ -104,4 +165,4 @@ interface PromptDialogProps {
|
|
|
104
165
|
}
|
|
105
166
|
declare function PromptDialog({ open, title, description, inputLabel, defaultValue, placeholder, submitLabel, cancelLabel, closeLabel, onSubmit, onCancel, validate, pending, multiline, destructive, error: submitError, role, }: PromptDialogProps): react.JSX.Element;
|
|
106
167
|
|
|
107
|
-
export { ConfirmDialog, type ConfirmDialogProps, Popconfirm, type PopconfirmProps, PromptDialog, type PromptDialogProps };
|
|
168
|
+
export { type ConfirmButtonProps, ConfirmDialog, type ConfirmDialogProps, Popconfirm, type PopconfirmProps, PromptDialog, type PromptDialogProps };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useId, useRef, useState } from "react";
|
|
3
|
+
import { forwardRef, useEffect, useId, useRef, useState } from "react";
|
|
4
|
+
import { cx } from "../cx.js";
|
|
4
5
|
import { Button } from "./Button.js";
|
|
5
6
|
import { Input } from "./Input.js";
|
|
6
7
|
import { Modal } from "./Modal.js";
|
|
@@ -8,8 +9,21 @@ import { Textarea } from "./Textarea.js";
|
|
|
8
9
|
import { Popover } from "./Popover.js";
|
|
9
10
|
import { fillLabel } from "../fill-label.js";
|
|
10
11
|
import { resolveAnchor } from "../hooks/useAnchoredPosition.js";
|
|
12
|
+
import { isTopLayer, pushLayer } from "../overlay-stack.js";
|
|
11
13
|
import { useUixLabels } from "../labels-context.js";
|
|
12
|
-
|
|
14
|
+
const confirmButtonProps = (extra, own) => {
|
|
15
|
+
const { onClick, disabled, className, ...rest } = extra ?? {};
|
|
16
|
+
return {
|
|
17
|
+
...rest,
|
|
18
|
+
className,
|
|
19
|
+
disabled: own.disabled || !!disabled,
|
|
20
|
+
onClick: (event) => {
|
|
21
|
+
onClick?.(event);
|
|
22
|
+
if (!event.defaultPrevented) own.onConfirm();
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
const ConfirmDialog = forwardRef(function ConfirmDialog2({
|
|
13
27
|
open,
|
|
14
28
|
title,
|
|
15
29
|
description,
|
|
@@ -18,7 +32,7 @@ function ConfirmDialog({
|
|
|
18
32
|
closeLabel,
|
|
19
33
|
onConfirm,
|
|
20
34
|
onCancel,
|
|
21
|
-
pending,
|
|
35
|
+
pending = false,
|
|
22
36
|
destructive,
|
|
23
37
|
children,
|
|
24
38
|
error,
|
|
@@ -27,20 +41,26 @@ function ConfirmDialog({
|
|
|
27
41
|
onTertiary,
|
|
28
42
|
typeToConfirm,
|
|
29
43
|
typeToConfirmLabel: typeToConfirmLabelProp,
|
|
44
|
+
typeToConfirmPlaceholder,
|
|
45
|
+
typeToConfirmMismatch: mismatchProp,
|
|
30
46
|
compensation,
|
|
31
|
-
compensationLabel: compensationLabelProp
|
|
32
|
-
|
|
47
|
+
compensationLabel: compensationLabelProp,
|
|
48
|
+
confirmProps
|
|
49
|
+
}, ref) {
|
|
33
50
|
const uixLabels = useUixLabels();
|
|
34
51
|
const typeToConfirmLabel = typeToConfirmLabelProp ?? uixLabels.confirmDialog?.typeToConfirm ?? "Type {value} to confirm";
|
|
35
52
|
const compensationLabel = compensationLabelProp ?? uixLabels.confirmDialog?.compensation ?? "How to undo this";
|
|
53
|
+
const mismatch = mismatchProp ?? uixLabels.confirmDialog?.typeToConfirmMismatch ?? "That does not match {value}.";
|
|
36
54
|
const id = useId();
|
|
37
55
|
const bodyId = `${id}-body`;
|
|
38
56
|
const typedId = `${id}-typed`;
|
|
57
|
+
const typedMsgId = `${id}-typed-msg`;
|
|
39
58
|
const cancelRef = useRef(null);
|
|
40
59
|
const confirmRef = useRef(null);
|
|
41
60
|
const typedRef = useRef(null);
|
|
42
61
|
const [typed, setTyped] = useState("");
|
|
43
62
|
const typedMatches = typeToConfirm == null || typed === typeToConfirm;
|
|
63
|
+
const typedWrong = typeToConfirm != null && typed !== "" && !typeToConfirm.startsWith(typed);
|
|
44
64
|
const hasBody = description != null || children != null || compensation != null;
|
|
45
65
|
useEffect(() => {
|
|
46
66
|
if (open) setTyped("");
|
|
@@ -51,11 +71,19 @@ function ConfirmDialog({
|
|
|
51
71
|
const target = typeToConfirm != null ? typedRef.current : focus === "cancel" ? cancelRef.current : focus === "confirm" ? confirmRef.current : null;
|
|
52
72
|
target?.focus();
|
|
53
73
|
}, [open, initialFocus, destructive, typeToConfirm]);
|
|
74
|
+
const confirm = confirmButtonProps(confirmProps, {
|
|
75
|
+
disabled: !typedMatches,
|
|
76
|
+
onConfirm: () => {
|
|
77
|
+
if (typedMatches && !pending) void onConfirm();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
54
80
|
return /* @__PURE__ */ jsx(
|
|
55
81
|
Modal,
|
|
56
82
|
{
|
|
83
|
+
ref,
|
|
57
84
|
open,
|
|
58
85
|
onClose: onCancel,
|
|
86
|
+
pending,
|
|
59
87
|
title,
|
|
60
88
|
closeLabel,
|
|
61
89
|
role: "alertdialog",
|
|
@@ -63,9 +91,7 @@ function ConfirmDialog({
|
|
|
63
91
|
footer: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
64
92
|
tertiaryLabel != null && onTertiary && /* @__PURE__ */ jsx(Button, { type: "button", className: "uix-confirm__tertiary", onClick: () => void onTertiary(), disabled: pending, children: tertiaryLabel }),
|
|
65
93
|
/* @__PURE__ */ jsx(Button, { ref: cancelRef, type: "button", onClick: onCancel, disabled: pending, children: cancelLabel }),
|
|
66
|
-
/* @__PURE__ */ jsx(Button, { ref: confirmRef, type: "button", variant: destructive ? "danger" : "primary",
|
|
67
|
-
if (typedMatches) void onConfirm();
|
|
68
|
-
}, loading: pending, disabled: !typedMatches, children: confirmLabel })
|
|
94
|
+
/* @__PURE__ */ jsx(Button, { ref: confirmRef, type: "button", variant: destructive ? "danger" : "primary", loading: pending, ...confirm, children: confirmLabel })
|
|
69
95
|
] }),
|
|
70
96
|
children: /* @__PURE__ */ jsxs("div", { className: "uix-confirm", children: [
|
|
71
97
|
hasBody && /* @__PURE__ */ jsxs("div", { id: bodyId, className: "uix-confirm__body", children: [
|
|
@@ -77,40 +103,80 @@ function ConfirmDialog({
|
|
|
77
103
|
] })
|
|
78
104
|
] }),
|
|
79
105
|
typeToConfirm != null && /* @__PURE__ */ jsxs("div", { className: "uix-field", children: [
|
|
80
|
-
/* @__PURE__ */ jsx("label", { className: "uix-field__label", htmlFor: typedId, children: fillLabel(typeToConfirmLabel, { value: typeToConfirm }) }),
|
|
81
|
-
/* @__PURE__ */ jsx(
|
|
106
|
+
/* @__PURE__ */ jsx("label", { className: "uix-field__label", htmlFor: typedId, children: typeof typeToConfirmLabel === "string" ? fillLabel(typeToConfirmLabel, { value: typeToConfirm }) : typeToConfirmLabel }),
|
|
107
|
+
/* @__PURE__ */ jsx(
|
|
108
|
+
Input,
|
|
109
|
+
{
|
|
110
|
+
ref: typedRef,
|
|
111
|
+
id: typedId,
|
|
112
|
+
value: typed,
|
|
113
|
+
autoComplete: "off",
|
|
114
|
+
spellCheck: false,
|
|
115
|
+
autoCapitalize: "none",
|
|
116
|
+
autoCorrect: "off",
|
|
117
|
+
placeholder: typeToConfirmPlaceholder,
|
|
118
|
+
invalid: typedWrong,
|
|
119
|
+
disabled: pending,
|
|
120
|
+
"aria-describedby": typedWrong ? typedMsgId : void 0,
|
|
121
|
+
onChange: (event) => setTyped(event.currentTarget.value)
|
|
122
|
+
}
|
|
123
|
+
),
|
|
124
|
+
/* @__PURE__ */ jsxs("div", { className: "uix-field__msg uix-confirm__typed-msg", children: [
|
|
125
|
+
/* @__PURE__ */ jsx("span", { className: "uix-field__error", "aria-hidden": "true", children: fillLabel(mismatch, { value: typeToConfirm }) }),
|
|
126
|
+
typedWrong && /* @__PURE__ */ jsx("span", { id: typedMsgId, className: "uix-field__error", role: "alert", children: fillLabel(mismatch, { value: typeToConfirm }) })
|
|
127
|
+
] })
|
|
82
128
|
] }),
|
|
83
129
|
error != null && /* @__PURE__ */ jsx("div", { className: "uix-confirm__error", role: "alert", children: error })
|
|
84
130
|
] })
|
|
85
131
|
}
|
|
86
132
|
);
|
|
87
|
-
}
|
|
133
|
+
});
|
|
134
|
+
ConfirmDialog.displayName = "ConfirmDialog";
|
|
135
|
+
const FIELD = 'input:not([type="hidden"]):not(:disabled), textarea:not(:disabled), select:not(:disabled), [contenteditable="true"]';
|
|
136
|
+
const isOwnEscape = (event, token) => event.key === "Escape" && !event.defaultPrevented && !event.isComposing && event.keyCode !== 229 && isTopLayer(token);
|
|
88
137
|
function Popconfirm({
|
|
89
138
|
open,
|
|
90
139
|
anchor,
|
|
91
140
|
title,
|
|
92
141
|
description,
|
|
142
|
+
children,
|
|
93
143
|
confirmLabel,
|
|
94
144
|
cancelLabel,
|
|
95
145
|
onConfirm,
|
|
96
146
|
onCancel,
|
|
97
147
|
pending,
|
|
98
148
|
destructive,
|
|
149
|
+
confirmDisabled = false,
|
|
150
|
+
confirmProps,
|
|
99
151
|
compensation,
|
|
100
152
|
compensationLabel: compensationLabelProp,
|
|
101
|
-
placement = "bottom-start"
|
|
153
|
+
placement = "bottom-start",
|
|
154
|
+
role,
|
|
155
|
+
labelledBy,
|
|
156
|
+
onEscapeKeyDown,
|
|
157
|
+
closeWhenAnchorHidden = false
|
|
102
158
|
}) {
|
|
103
159
|
const uixLabels = useUixLabels();
|
|
104
160
|
const compensationLabel = compensationLabelProp ?? uixLabels.confirmDialog?.compensation ?? "How to undo this";
|
|
105
161
|
const id = useId();
|
|
106
162
|
const panelId = `${id}-popconfirm`;
|
|
163
|
+
const confirmId = confirmProps?.id ?? `${id}-confirm`;
|
|
107
164
|
const cancelRef = useRef(null);
|
|
108
|
-
const
|
|
109
|
-
|
|
165
|
+
const contentRef = useRef(null);
|
|
166
|
+
const latest = useRef({ onCancel, onEscapeKeyDown });
|
|
167
|
+
latest.current = { onCancel, onEscapeKeyDown };
|
|
168
|
+
const layer = useRef(null);
|
|
169
|
+
const [holdsInput, setHoldsInput] = useState(false);
|
|
170
|
+
const readContent = () => {
|
|
171
|
+
const fields = Array.from(contentRef.current?.querySelectorAll('input:not([type="hidden"]), textarea') ?? []);
|
|
172
|
+
setHoldsInput(fields.some((field) => field.value.trim() !== ""));
|
|
173
|
+
};
|
|
110
174
|
const hasBody = description != null || compensation != null;
|
|
175
|
+
const hasTitle = title != null;
|
|
176
|
+
const nameBy = labelledBy ?? (hasTitle ? "title" : "confirm");
|
|
111
177
|
useEffect(() => {
|
|
112
178
|
const panel = document.getElementById(panelId);
|
|
113
|
-
if (!panel) return;
|
|
179
|
+
if (!panel) return void 0;
|
|
114
180
|
const shown = () => {
|
|
115
181
|
try {
|
|
116
182
|
return panel.matches(":popover-open");
|
|
@@ -127,7 +193,7 @@ function Popconfirm({
|
|
|
127
193
|
}
|
|
128
194
|
if (focusInside) resolveAnchor(anchor)?.focus();
|
|
129
195
|
}
|
|
130
|
-
return;
|
|
196
|
+
return void 0;
|
|
131
197
|
}
|
|
132
198
|
if (!shown()) {
|
|
133
199
|
try {
|
|
@@ -135,25 +201,45 @@ function Popconfirm({
|
|
|
135
201
|
} catch {
|
|
136
202
|
}
|
|
137
203
|
}
|
|
138
|
-
cancelRef.current?.focus();
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
204
|
+
(contentRef.current?.querySelector(FIELD) ?? cancelRef.current)?.focus();
|
|
205
|
+
const token = {};
|
|
206
|
+
layer.current = token;
|
|
207
|
+
const pop = pushLayer(token);
|
|
208
|
+
const onDocumentKey = (event) => {
|
|
209
|
+
if (panel.contains(event.target) || !isOwnEscape(event, token)) return;
|
|
210
|
+
if (panel.querySelector('[aria-expanded="true"][aria-haspopup]')) return;
|
|
211
|
+
const dialog = event.target?.closest?.("dialog");
|
|
212
|
+
if (dialog && !dialog.contains(panel)) return;
|
|
213
|
+
cancelOnEscape(event);
|
|
144
214
|
};
|
|
145
215
|
const onPointer = (event) => {
|
|
146
216
|
const target = event.target;
|
|
147
217
|
if (panel.contains(target) || resolveAnchor(anchor)?.contains(target)) return;
|
|
148
|
-
|
|
218
|
+
if (!isTopLayer(token)) return;
|
|
219
|
+
latest.current.onCancel();
|
|
149
220
|
};
|
|
150
|
-
document.addEventListener("keydown",
|
|
221
|
+
document.addEventListener("keydown", onDocumentKey, true);
|
|
151
222
|
document.addEventListener("pointerdown", onPointer, true);
|
|
152
223
|
return () => {
|
|
153
|
-
|
|
224
|
+
pop();
|
|
225
|
+
layer.current = null;
|
|
226
|
+
document.removeEventListener("keydown", onDocumentKey, true);
|
|
154
227
|
document.removeEventListener("pointerdown", onPointer, true);
|
|
155
228
|
};
|
|
156
229
|
}, [open, anchor, panelId]);
|
|
230
|
+
const cancelOnEscape = (event) => {
|
|
231
|
+
latest.current.onEscapeKeyDown?.(event);
|
|
232
|
+
event.stopPropagation();
|
|
233
|
+
if (event.defaultPrevented) return;
|
|
234
|
+
event.preventDefault();
|
|
235
|
+
latest.current.onCancel();
|
|
236
|
+
};
|
|
237
|
+
const confirm = confirmButtonProps(confirmProps, {
|
|
238
|
+
disabled: confirmDisabled,
|
|
239
|
+
onConfirm: () => {
|
|
240
|
+
if (!pending) void onConfirm();
|
|
241
|
+
}
|
|
242
|
+
});
|
|
157
243
|
return /* @__PURE__ */ jsxs(
|
|
158
244
|
Popover,
|
|
159
245
|
{
|
|
@@ -161,12 +247,25 @@ function Popconfirm({
|
|
|
161
247
|
popover: "manual",
|
|
162
248
|
anchor,
|
|
163
249
|
placement,
|
|
164
|
-
role: "dialog",
|
|
165
|
-
"aria-labelledby": `${id}-title
|
|
250
|
+
role: role ?? (destructive ? "alertdialog" : "dialog"),
|
|
251
|
+
"aria-labelledby": nameBy === "title" && hasTitle ? `${id}-title` : confirmId,
|
|
166
252
|
"aria-describedby": hasBody ? `${id}-body` : void 0,
|
|
167
|
-
className: "uix-popconfirm",
|
|
253
|
+
className: cx("uix-popconfirm", children != null && "uix-popconfirm--content"),
|
|
254
|
+
closeWhenAnchorHidden: closeWhenAnchorHidden && !holdsInput,
|
|
255
|
+
onAnchorHidden: closeWhenAnchorHidden ? () => {
|
|
256
|
+
const panel = document.getElementById(panelId);
|
|
257
|
+
if (panel?.contains(document.activeElement)) resolveAnchor(anchor)?.focus({ preventScroll: true });
|
|
258
|
+
latest.current.onCancel();
|
|
259
|
+
} : void 0,
|
|
260
|
+
onKeyDown: (event) => {
|
|
261
|
+
const token = layer.current;
|
|
262
|
+
if (!token || !isOwnEscape(event.nativeEvent, token)) return;
|
|
263
|
+
cancelOnEscape(event.nativeEvent);
|
|
264
|
+
event.stopPropagation();
|
|
265
|
+
},
|
|
266
|
+
onInput: readContent,
|
|
168
267
|
children: [
|
|
169
|
-
/* @__PURE__ */ jsx("div", { id: `${id}-title`, className: "uix-popconfirm__title", children: title }),
|
|
268
|
+
hasTitle && /* @__PURE__ */ jsx("div", { id: `${id}-title`, className: "uix-popconfirm__title", children: title }),
|
|
170
269
|
hasBody && /* @__PURE__ */ jsxs("div", { id: `${id}-body`, className: "uix-popconfirm__body", children: [
|
|
171
270
|
description != null && /* @__PURE__ */ jsx("div", { children: description }),
|
|
172
271
|
compensation != null && /* @__PURE__ */ jsxs("div", { className: "uix-confirm__compensation", children: [
|
|
@@ -174,9 +273,10 @@ function Popconfirm({
|
|
|
174
273
|
/* @__PURE__ */ jsx("div", { children: compensation })
|
|
175
274
|
] })
|
|
176
275
|
] }),
|
|
276
|
+
children != null && /* @__PURE__ */ jsx("div", { ref: contentRef, className: "uix-popconfirm__content", children }),
|
|
177
277
|
/* @__PURE__ */ jsxs("div", { className: "uix-popconfirm__actions", children: [
|
|
178
278
|
/* @__PURE__ */ jsx(Button, { ref: cancelRef, type: "button", size: "sm", onClick: onCancel, disabled: pending, children: cancelLabel }),
|
|
179
|
-
/* @__PURE__ */ jsx(Button, { type: "button", size: "sm", variant: destructive ? "danger" : "primary",
|
|
279
|
+
/* @__PURE__ */ jsx(Button, { type: "button", size: "sm", variant: destructive ? "danger" : "primary", loading: pending, ...confirm, id: confirmId, children: confirmLabel })
|
|
180
280
|
] })
|
|
181
281
|
]
|
|
182
282
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
2
3
|
import { JsonValue } from '../json-value.js';
|
|
3
4
|
import { DiffEntry, DiffResolution } from '../diff-model.js';
|
|
4
5
|
|
|
@@ -56,8 +57,27 @@ interface DiffViewerProps {
|
|
|
56
57
|
*/
|
|
57
58
|
controlSize?: 'sm' | 'md' | 'lg';
|
|
58
59
|
className?: string;
|
|
60
|
+
/**
|
|
61
|
+
* The name of an entry for people (HAR-1369): "First author" for `$.authors[0].name`. Shown
|
|
62
|
+
* instead of the path; a string also names the entry's regions and buttons (anything else
|
|
63
|
+
* leaves the path there). Default: the path.
|
|
64
|
+
*/
|
|
65
|
+
labelFor?: (path: string) => ReactNode;
|
|
66
|
+
/**
|
|
67
|
+
* How a value shows: a name, a date, a chip, instead of `JSON.stringify`. `undefined` is a
|
|
68
|
+
* value that is not there in that version. Default: the JSON, or `labels.notPresent`.
|
|
69
|
+
*/
|
|
70
|
+
renderValue?: (value: JsonValue | undefined, path: string, side: DiffSide) => ReactNode;
|
|
71
|
+
/** Where a version's value came from, shown under it ("Edited by Ana, 12 Oct"). Nothing for none. */
|
|
72
|
+
provenance?: (path: string, side: DiffSide) => ReactNode;
|
|
59
73
|
}
|
|
60
|
-
/**
|
|
61
|
-
|
|
74
|
+
/** One of the three versions a `DiffViewer` entry compares. */
|
|
75
|
+
type DiffSide = 'base' | 'current' | 'incoming';
|
|
76
|
+
/**
|
|
77
|
+
* Three-way configuration diff with per-entry keyboard-operable resolution. For people who do
|
|
78
|
+
* not read JSON paths (HAR-1369, MOTUS B-A9): `labelFor` names an entry, `renderValue` shows a
|
|
79
|
+
* value as a name, a date or a chip, and `provenance` says where each version came from.
|
|
80
|
+
*/
|
|
81
|
+
declare function DiffViewer({ base, current, incoming, entries: providedEntries, resolutions: controlledResolutions, onResolutionChange, labels: labelOverrides, controlSize, className, labelFor, renderValue, provenance, }: DiffViewerProps): react.JSX.Element;
|
|
62
82
|
|
|
63
|
-
export { DEFAULT_DIFF_VIEWER_LABELS, DiffViewer, type DiffViewerLabels, type DiffViewerProps };
|
|
83
|
+
export { DEFAULT_DIFF_VIEWER_LABELS, type DiffSide, DiffViewer, type DiffViewerLabels, type DiffViewerProps };
|