@tensor_1/react 2.31.0 → 2.33.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 +367 -0
- package/dist/SchedulingCalendar-C4XVbqZr.d.ts +339 -0
- package/dist/attachment-labels.d.ts +16 -0
- package/dist/attachment-labels.js +9 -0
- package/dist/calendar-model.d.ts +141 -1
- package/dist/calendar-model.js +200 -11
- package/dist/chart-preset.cjs +127 -7
- package/dist/chart-preset.d.cts +2 -2
- package/dist/chart-preset.d.ts +1 -0
- package/dist/chart-preset.js +6 -1
- package/dist/chart-theme-Cl0IrkKQ.d.cts +112 -0
- package/dist/chart-theme.d.ts +51 -0
- package/dist/chart-theme.js +99 -0
- package/dist/chart.cjs +127 -7
- package/dist/chart.d.cts +2 -2
- package/dist/chart.d.ts +1 -0
- package/dist/chart.js +6 -2
- package/dist/components/Alert.d.ts +17 -1
- package/dist/components/Alert.js +9 -4
- package/dist/components/AlertDismiss.d.ts +14 -0
- package/dist/components/AlertDismiss.js +11 -0
- package/dist/components/Attachment.d.ts +15 -12
- package/dist/components/Attachment.js +17 -17
- package/dist/components/AttachmentParts.d.ts +24 -0
- package/dist/components/AttachmentParts.js +20 -0
- package/dist/components/Chart.d.ts +1 -0
- package/dist/components/Chart.js +6 -1
- package/dist/components/ChartCore.d.ts +6 -1
- package/dist/components/ChartCore.js +23 -5
- package/dist/components/Chip.d.ts +17 -2
- package/dist/components/Chip.js +35 -11
- package/dist/components/ChipRemove.d.ts +17 -0
- package/dist/components/ChipRemove.js +13 -0
- package/dist/components/CollapsibleParts.d.ts +29 -0
- package/dist/components/CollapsibleParts.js +20 -0
- package/dist/components/CollapsibleSection.d.ts +30 -3
- package/dist/components/CollapsibleSection.js +7 -11
- package/dist/components/CollapsibleSectionState.d.ts +1 -1
- package/dist/components/CollapsibleSectionState.js +54 -27
- package/dist/components/DashboardGrid.d.ts +60 -0
- package/dist/components/DashboardGrid.js +40 -0
- package/dist/components/DateRangePicker.d.ts +7 -1
- package/dist/components/DateRangePicker.js +5 -1
- package/dist/components/DescriptionList.d.ts +12 -1
- package/dist/components/DescriptionList.js +5 -5
- package/dist/components/Dialogs.d.ts +24 -3
- package/dist/components/Dialogs.js +74 -16
- package/dist/components/FileUpload.d.ts +10 -2
- package/dist/components/FileUpload.js +10 -4
- package/dist/components/FilterEditor.d.ts +4 -0
- package/dist/components/FilterEditor.js +13 -2
- package/dist/components/Heartbeat.d.ts +31 -0
- package/dist/components/Heartbeat.js +28 -0
- package/dist/components/Menu.d.ts +53 -5
- package/dist/components/Menu.js +104 -4
- package/dist/components/Meter.d.ts +7 -3
- package/dist/components/Meter.js +2 -1
- package/dist/components/Popover.d.ts +21 -2
- package/dist/components/Popover.js +94 -17
- package/dist/components/Prose.d.ts +6 -2
- package/dist/components/Prose.js +1 -1
- package/dist/components/RuleBuilder.d.ts +52 -4
- package/dist/components/RuleBuilder.js +109 -24
- package/dist/components/SchedulingCalendar.d.ts +4 -110
- package/dist/components/SchedulingCalendar.js +299 -63
- package/dist/components/SchedulingTimeGrid.d.ts +4 -0
- package/dist/components/SchedulingTimeGrid.js +521 -0
- package/dist/components/SchedulingTimeline.d.ts +2 -1
- package/dist/components/Segmented.d.ts +17 -3
- package/dist/components/Segmented.js +59 -5
- package/dist/components/Select.d.ts +66 -2
- package/dist/components/Select.js +800 -10
- package/dist/components/Spinner.d.ts +2 -1
- package/dist/components/Spinner.js +1 -1
- package/dist/components/StatusPill.d.ts +8 -2
- package/dist/components/StatusPill.js +2 -2
- package/dist/components/Steps.d.ts +28 -4
- package/dist/components/Steps.js +46 -16
- package/dist/components/TableControls.js +4 -3
- package/dist/components/Tabs.d.ts +7 -2
- package/dist/components/Tabs.js +14 -1
- package/dist/components/TextDiff.d.ts +49 -0
- package/dist/components/TextDiff.js +124 -0
- package/dist/{emoji-image-6mPZ15Zy.d.cts → emoji-image-DjCKpdwE.d.cts} +1 -1
- package/dist/emoji.cjs +169 -27
- package/dist/emoji.d.cts +3 -3
- package/dist/file-model.d.ts +22 -3
- package/dist/file-model.js +6 -4
- package/dist/filter-model.d.ts +26 -2
- package/dist/filter-model.js +13 -1
- package/dist/hooks/useAnchoredPosition.d.ts +24 -1
- package/dist/hooks/useAnchoredPosition.js +58 -8
- package/dist/hooks/useVirtualRows.d.ts +17 -2
- package/dist/hooks/useVirtualRows.js +6 -5
- package/dist/icons/Icon.d.ts +14 -14
- package/dist/index.cjs +4888 -1714
- package/dist/index.d.cts +1871 -658
- package/dist/index.d.ts +16 -9
- package/dist/index.js +67 -4
- package/dist/labels-context.d.ts +29 -1
- package/dist/{overlay-position-Cjv5E7mn.d.cts → overlay-position-2Afke51l.d.cts} +20 -0
- package/dist/overlay-position.d.ts +20 -0
- package/dist/overlay-position.js +17 -2
- package/dist/rich-text.cjs +169 -27
- package/dist/rich-text.d.cts +3 -3
- package/dist/rule-builder-model.d.ts +34 -2
- package/dist/rule-builder-model.js +48 -12
- package/dist/scheduling-calendar-model.d.ts +169 -0
- package/dist/scheduling-calendar-model.js +176 -0
- package/dist/select-model.d.ts +80 -0
- package/dist/select-model.js +109 -0
- package/dist/table-engine.d.ts +7 -0
- package/dist/table-engine.js +2 -1
- package/dist/text-diff-model.d.ts +71 -0
- package/dist/text-diff-model.js +261 -0
- package/package.json +1 -1
- package/dist/ChartCore-DzWe10bN.d.cts +0 -59
|
@@ -16,6 +16,7 @@ const DEFAULT_DATE_RANGE_PICKER_LABELS = {
|
|
|
16
16
|
rangeEnd: "end of selected range",
|
|
17
17
|
inRange: "in selected range"
|
|
18
18
|
};
|
|
19
|
+
const localDateKey = (now = /* @__PURE__ */ new Date()) => `${now.getFullYear().toString().padStart(4, "0")}-${(now.getMonth() + 1).toString().padStart(2, "0")}-${now.getDate().toString().padStart(2, "0")}`;
|
|
19
20
|
const monthLabel = (date, locale) => new Intl.DateTimeFormat(locale, { month: "long", year: "numeric", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
|
|
20
21
|
const dateLabel = (date, locale) => new Intl.DateTimeFormat(locale, { dateStyle: "full", timeZone: "UTC" }).format(/* @__PURE__ */ new Date(`${date}T00:00:00Z`));
|
|
21
22
|
const weekdayNames = (locale) => {
|
|
@@ -37,7 +38,8 @@ function DateRangePicker({
|
|
|
37
38
|
invalidMessage = "The selected date range is invalid.",
|
|
38
39
|
disabled,
|
|
39
40
|
className,
|
|
40
|
-
labels: labelOverrides
|
|
41
|
+
labels: labelOverrides,
|
|
42
|
+
today
|
|
41
43
|
}) {
|
|
42
44
|
const id = useId();
|
|
43
45
|
const uixLabels = useUixLabels();
|
|
@@ -53,6 +55,7 @@ function DateRangePicker({
|
|
|
53
55
|
const month = startOfMonth(addCalendarMonths(visibleMonth, index));
|
|
54
56
|
return { month, days: buildMonthGrid(month).filter((day) => day.inMonth) };
|
|
55
57
|
}), [visibleMonth, count]);
|
|
58
|
+
const todayDate = today === void 0 ? localDateKey() : today;
|
|
56
59
|
const rangeInvalid = invalid || !!value.start && !!value.end && value.start > value.end;
|
|
57
60
|
useEffect(() => {
|
|
58
61
|
const first = calendars[0]?.days[0]?.date;
|
|
@@ -123,6 +126,7 @@ function DateRangePicker({
|
|
|
123
126
|
"data-in-range": isDateInRange(day.date, value) || void 0,
|
|
124
127
|
"aria-pressed": edge != null || isDateInRange(day.date, value),
|
|
125
128
|
"aria-label": `${dateLabel(day.date, locale)}${edge ? `, ${edge === "start" ? labels.rangeStart : labels.rangeEnd}` : isDateInRange(day.date, value) ? `, ${labels.inRange}` : ""}`,
|
|
129
|
+
"aria-current": day.date === todayDate ? "date" : void 0,
|
|
126
130
|
tabIndex: activeDate === day.date ? 0 : -1,
|
|
127
131
|
disabled: unavailable,
|
|
128
132
|
onFocus: () => setActiveDate(day.date),
|
|
@@ -26,6 +26,10 @@ interface DescriptionListItem {
|
|
|
26
26
|
id?: string;
|
|
27
27
|
term: ReactNode;
|
|
28
28
|
description: ReactNode;
|
|
29
|
+
/** Attributes for this row's `<dt>` (an `id` to anchor to, `data-*`). */
|
|
30
|
+
termProps?: HTMLAttributes<HTMLElement>;
|
|
31
|
+
/** Attributes for this row's `<dd>`. */
|
|
32
|
+
descriptionProps?: HTMLAttributes<HTMLElement>;
|
|
29
33
|
}
|
|
30
34
|
interface DescriptionListProps extends HTMLAttributes<HTMLDListElement> {
|
|
31
35
|
items?: DescriptionListItem[];
|
|
@@ -35,12 +39,19 @@ declare function DescriptionList({ items, children, className, ...props }: Descr
|
|
|
35
39
|
interface DescriptionItemProps {
|
|
36
40
|
term: ReactNode;
|
|
37
41
|
children?: ReactNode;
|
|
42
|
+
/**
|
|
43
|
+
* Attributes for the `<dt>`: an `id` a deep link or `aria-describedby` points at, `data-*`,
|
|
44
|
+
* a `className` (HAR-1628). The pair has no wrapper element, so this is where they go.
|
|
45
|
+
*/
|
|
46
|
+
termProps?: HTMLAttributes<HTMLElement>;
|
|
47
|
+
/** Attributes for the `<dd>`. */
|
|
48
|
+
descriptionProps?: HTMLAttributes<HTMLElement>;
|
|
38
49
|
}
|
|
39
50
|
/**
|
|
40
51
|
* One hand-authored row. Renders a `<dt>`/`<dd>` fragment so the pair lands as
|
|
41
52
|
* direct grid children of the enclosing `<DescriptionList>` (never wrap it in
|
|
42
53
|
* an element — that breaks the two-column alignment).
|
|
43
54
|
*/
|
|
44
|
-
declare function DescriptionItem({ term, children }: DescriptionItemProps): react.JSX.Element;
|
|
55
|
+
declare function DescriptionItem({ term, children, termProps, descriptionProps }: DescriptionItemProps): react.JSX.Element;
|
|
45
56
|
|
|
46
57
|
export { DescriptionItem, type DescriptionItemProps, DescriptionList, type DescriptionListItem, type DescriptionListProps };
|
|
@@ -4,16 +4,16 @@ import { cx } from "../cx.js";
|
|
|
4
4
|
function DescriptionList({ items, children, className, ...props }) {
|
|
5
5
|
return /* @__PURE__ */ jsxs("dl", { className: cx("uix-dl", className), ...props, children: [
|
|
6
6
|
items?.map((item, i) => /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
7
|
-
/* @__PURE__ */ jsx("dt", { children: item.term }),
|
|
8
|
-
/* @__PURE__ */ jsx("dd", { children: item.description })
|
|
7
|
+
/* @__PURE__ */ jsx("dt", { ...item.termProps, children: item.term }),
|
|
8
|
+
/* @__PURE__ */ jsx("dd", { ...item.descriptionProps, children: item.description })
|
|
9
9
|
] }, item.id ?? i)),
|
|
10
10
|
children
|
|
11
11
|
] });
|
|
12
12
|
}
|
|
13
|
-
function DescriptionItem({ term, children }) {
|
|
13
|
+
function DescriptionItem({ term, children, termProps, descriptionProps }) {
|
|
14
14
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
15
|
-
/* @__PURE__ */ jsx("dt", { children: term }),
|
|
16
|
-
/* @__PURE__ */ jsx("dd", { children })
|
|
15
|
+
/* @__PURE__ */ jsx("dt", { ...termProps, children: term }),
|
|
16
|
+
/* @__PURE__ */ jsx("dd", { ...descriptionProps, children })
|
|
17
17
|
] });
|
|
18
18
|
}
|
|
19
19
|
export {
|
|
@@ -8,7 +8,8 @@ interface ConfirmDialogProps {
|
|
|
8
8
|
description?: ReactNode;
|
|
9
9
|
confirmLabel: ReactNode;
|
|
10
10
|
cancelLabel: ReactNode;
|
|
11
|
-
|
|
11
|
+
/** Accessible name of the close button. Default: `UixLabelsProvider` `modal.close`, then "Close dialog". */
|
|
12
|
+
closeLabel?: string;
|
|
12
13
|
onConfirm: () => void | Promise<void>;
|
|
13
14
|
onCancel: () => void;
|
|
14
15
|
pending?: boolean;
|
|
@@ -75,12 +76,32 @@ interface PromptDialogProps {
|
|
|
75
76
|
placeholder?: string;
|
|
76
77
|
submitLabel: ReactNode;
|
|
77
78
|
cancelLabel: ReactNode;
|
|
78
|
-
|
|
79
|
+
/** Accessible name of the close button. Default: `UixLabelsProvider` `modal.close`, then "Close dialog". */
|
|
80
|
+
closeLabel?: string;
|
|
79
81
|
onSubmit: (value: string) => void | Promise<void>;
|
|
80
82
|
onCancel: () => void;
|
|
81
83
|
validate?: (value: string) => ReactNode | undefined;
|
|
82
84
|
pending?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Ask for a longer answer in a textarea (a reason, a note); a number sets its rows, `true`
|
|
87
|
+
* is 4. Enter adds a line there, so the form is sent with the submit button or Ctrl/⌘+Enter
|
|
88
|
+
* (HAR-1614).
|
|
89
|
+
*/
|
|
90
|
+
multiline?: boolean | number;
|
|
91
|
+
/**
|
|
92
|
+
* The answer confirms something that cannot be taken back (a forced import): the submit
|
|
93
|
+
* button is the danger button and the dialog is an `alertdialog`.
|
|
94
|
+
*/
|
|
95
|
+
destructive?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Why the last submit failed, from the server. Shown under the field (`role="alert"`) and
|
|
98
|
+
* tied to it; the dialog stays open. A message from `validate` takes its place while the
|
|
99
|
+
* value is invalid.
|
|
100
|
+
*/
|
|
101
|
+
error?: ReactNode;
|
|
102
|
+
/** Default: `'alertdialog'` when `destructive`, else `'dialog'`. */
|
|
103
|
+
role?: 'dialog' | 'alertdialog';
|
|
83
104
|
}
|
|
84
|
-
declare function PromptDialog({ open, title, description, inputLabel, defaultValue, placeholder, submitLabel, cancelLabel, closeLabel, onSubmit, onCancel, validate, pending }: PromptDialogProps): react.JSX.Element;
|
|
105
|
+
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;
|
|
85
106
|
|
|
86
107
|
export { ConfirmDialog, type ConfirmDialogProps, Popconfirm, type PopconfirmProps, PromptDialog, type PromptDialogProps };
|
|
@@ -4,6 +4,7 @@ import { useEffect, useId, useRef, useState } from "react";
|
|
|
4
4
|
import { Button } from "./Button.js";
|
|
5
5
|
import { Input } from "./Input.js";
|
|
6
6
|
import { Modal } from "./Modal.js";
|
|
7
|
+
import { Textarea } from "./Textarea.js";
|
|
7
8
|
import { Popover } from "./Popover.js";
|
|
8
9
|
import { fillLabel } from "../fill-label.js";
|
|
9
10
|
import { resolveAnchor } from "../hooks/useAnchoredPosition.js";
|
|
@@ -181,33 +182,90 @@ function Popconfirm({
|
|
|
181
182
|
}
|
|
182
183
|
);
|
|
183
184
|
}
|
|
184
|
-
function PromptDialog({
|
|
185
|
+
function PromptDialog({
|
|
186
|
+
open,
|
|
187
|
+
title,
|
|
188
|
+
description,
|
|
189
|
+
inputLabel,
|
|
190
|
+
defaultValue = "",
|
|
191
|
+
placeholder,
|
|
192
|
+
submitLabel,
|
|
193
|
+
cancelLabel,
|
|
194
|
+
closeLabel,
|
|
195
|
+
onSubmit,
|
|
196
|
+
onCancel,
|
|
197
|
+
validate,
|
|
198
|
+
pending,
|
|
199
|
+
multiline = false,
|
|
200
|
+
destructive = false,
|
|
201
|
+
error: submitError,
|
|
202
|
+
role
|
|
203
|
+
}) {
|
|
185
204
|
const inputId = useId();
|
|
186
205
|
const errorId = `${inputId}-error`;
|
|
206
|
+
const descriptionId = `${inputId}-description`;
|
|
207
|
+
const formRef = useRef(null);
|
|
208
|
+
const fieldRef = useRef(null);
|
|
187
209
|
const [value, setValue] = useState(defaultValue);
|
|
188
|
-
const [
|
|
210
|
+
const [issue, setIssue] = useState();
|
|
211
|
+
const error = issue ?? submitError;
|
|
212
|
+
useEffect(() => {
|
|
213
|
+
if (open) fieldRef.current?.focus();
|
|
214
|
+
}, [open]);
|
|
189
215
|
useEffect(() => {
|
|
190
216
|
if (open) {
|
|
191
217
|
setValue(defaultValue);
|
|
192
|
-
|
|
218
|
+
setIssue(void 0);
|
|
193
219
|
}
|
|
194
220
|
}, [defaultValue, open]);
|
|
195
221
|
const handleSubmit = (event) => {
|
|
196
222
|
event.preventDefault();
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
if (
|
|
223
|
+
const found = validate?.(value);
|
|
224
|
+
setIssue(found);
|
|
225
|
+
if (found == null) void onSubmit(value);
|
|
200
226
|
};
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
error != null
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
227
|
+
const field = {
|
|
228
|
+
id: inputId,
|
|
229
|
+
value,
|
|
230
|
+
placeholder,
|
|
231
|
+
"aria-describedby": error != null ? errorId : void 0,
|
|
232
|
+
invalid: error != null,
|
|
233
|
+
ref: fieldRef
|
|
234
|
+
};
|
|
235
|
+
return /* @__PURE__ */ jsx(
|
|
236
|
+
Modal,
|
|
237
|
+
{
|
|
238
|
+
open,
|
|
239
|
+
onClose: onCancel,
|
|
240
|
+
title,
|
|
241
|
+
closeLabel,
|
|
242
|
+
role: role ?? (destructive ? "alertdialog" : "dialog"),
|
|
243
|
+
"aria-describedby": description != null ? descriptionId : void 0,
|
|
244
|
+
children: /* @__PURE__ */ jsxs("form", { ref: formRef, className: "uix-prompt", onSubmit: handleSubmit, children: [
|
|
245
|
+
description != null && /* @__PURE__ */ jsx("div", { id: descriptionId, children: description }),
|
|
246
|
+
/* @__PURE__ */ jsx("label", { className: "uix-field__label", htmlFor: inputId, children: inputLabel }),
|
|
247
|
+
multiline ? /* @__PURE__ */ jsx(
|
|
248
|
+
Textarea,
|
|
249
|
+
{
|
|
250
|
+
...field,
|
|
251
|
+
rows: typeof multiline === "number" ? multiline : 4,
|
|
252
|
+
onChange: (event) => setValue(event.currentTarget.value),
|
|
253
|
+
onKeyDown: (event) => {
|
|
254
|
+
if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) {
|
|
255
|
+
event.preventDefault();
|
|
256
|
+
formRef.current?.requestSubmit();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
) : /* @__PURE__ */ jsx(Input, { ...field, onChange: (event) => setValue(event.currentTarget.value) }),
|
|
261
|
+
error != null && /* @__PURE__ */ jsx("div", { id: errorId, className: "uix-field__error", role: "alert", children: error }),
|
|
262
|
+
/* @__PURE__ */ jsxs("div", { className: "uix-dialog__footer uix-prompt__actions", children: [
|
|
263
|
+
/* @__PURE__ */ jsx(Button, { type: "button", onClick: onCancel, disabled: pending, children: cancelLabel }),
|
|
264
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", variant: destructive ? "danger" : "primary", loading: pending, children: submitLabel })
|
|
265
|
+
] })
|
|
266
|
+
] })
|
|
267
|
+
}
|
|
268
|
+
);
|
|
211
269
|
}
|
|
212
270
|
export {
|
|
213
271
|
ConfirmDialog,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { FileRejection } from '../file-model.js';
|
|
3
|
+
import { FileRejection, FileSizeFormatter } from '../file-model.js';
|
|
4
4
|
|
|
5
5
|
type FileUploadStatus = 'queued' | 'uploading' | 'done' | 'error';
|
|
6
6
|
interface FileUploadItem {
|
|
@@ -74,6 +74,14 @@ interface FileUploadProps {
|
|
|
74
74
|
/** Second line of the drop zone, e.g. "PDF or images, up to 20 MB". */
|
|
75
75
|
hint?: ReactNode;
|
|
76
76
|
locale?: string;
|
|
77
|
+
/** Count sizes in 1024s (1 MB = 1,048,576 bytes) instead of 1000s. Default `1000`. */
|
|
78
|
+
sizeBase?: 1000 | 1024;
|
|
79
|
+
/** The product's own size formatter; it replaces `formatFileSize` (and `sizeBase`). */
|
|
80
|
+
formatSize?: FileSizeFormatter;
|
|
81
|
+
/**
|
|
82
|
+
* Words for this instance. Each one defaults to `UixLabelsProvider` `fileUpload`, then to
|
|
83
|
+
* English, so one provider translates every upload below it (HAR-1630).
|
|
84
|
+
*/
|
|
77
85
|
labels?: Partial<FileUploadLabels>;
|
|
78
86
|
className?: string;
|
|
79
87
|
}
|
|
@@ -82,6 +90,6 @@ interface FileUploadProps {
|
|
|
82
90
|
* product owns the upload: it receives validated `File`s and reports progress, success and
|
|
83
91
|
* failure through `items`. Keyboard users get a real button; drop is an extra, not the only way.
|
|
84
92
|
*/
|
|
85
|
-
declare function FileUpload({ items, onFilesAdded, onRemove, onRetry, onAltChange, accept, maxSize, maxFiles, multiple, capture, disabled, hint, locale, labels: labelOverrides, className, }: FileUploadProps): react.JSX.Element;
|
|
93
|
+
declare function FileUpload({ items, onFilesAdded, onRemove, onRetry, onAltChange, accept, maxSize, maxFiles, multiple, capture, disabled, hint, locale, sizeBase, formatSize, labels: labelOverrides, className, }: FileUploadProps): react.JSX.Element;
|
|
86
94
|
|
|
87
95
|
export { DEFAULT_FILE_UPLOAD_LABELS, FileUpload, type FileUploadItem, type FileUploadLabels, type FileUploadProps, type FileUploadStatus };
|
|
@@ -4,6 +4,7 @@ import { useId, useRef, useState } from "react";
|
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
5
|
import { fillLabel } from "../fill-label.js";
|
|
6
6
|
import { formatFileSize, partitionFiles } from "../file-model.js";
|
|
7
|
+
import { useUixLabels } from "../labels-context.js";
|
|
7
8
|
import { Button } from "./Button.js";
|
|
8
9
|
import { Input } from "./Input.js";
|
|
9
10
|
import { Progress } from "./Progress.js";
|
|
@@ -40,10 +41,13 @@ function FileUpload({
|
|
|
40
41
|
disabled,
|
|
41
42
|
hint,
|
|
42
43
|
locale,
|
|
44
|
+
sizeBase,
|
|
45
|
+
formatSize,
|
|
43
46
|
labels: labelOverrides,
|
|
44
47
|
className
|
|
45
48
|
}) {
|
|
46
|
-
const labels = { ...DEFAULT_FILE_UPLOAD_LABELS, ...labelOverrides };
|
|
49
|
+
const labels = { ...DEFAULT_FILE_UPLOAD_LABELS, ...useUixLabels().fileUpload, ...labelOverrides };
|
|
50
|
+
const sizeText = (bytes) => formatSize ? formatSize(bytes, locale) : formatFileSize(bytes, locale, { base: sizeBase });
|
|
47
51
|
const id = useId();
|
|
48
52
|
const inputRef = useRef(null);
|
|
49
53
|
const [over, setOver] = useState(false);
|
|
@@ -53,7 +57,7 @@ function FileUpload({
|
|
|
53
57
|
if (!list || disabled) return;
|
|
54
58
|
const files = Array.from(list);
|
|
55
59
|
const { accepted, rejected } = partitionFiles(files, { accept, maxSize, maxFiles, existing: items.length });
|
|
56
|
-
const max = maxSize !== void 0 ?
|
|
60
|
+
const max = maxSize !== void 0 ? sizeText(maxSize) : "";
|
|
57
61
|
setMessages(rejected.map(({ file, reason }) => fillLabel(
|
|
58
62
|
reason === "type" ? labels.wrongType : reason === "size" ? labels.tooLarge : labels.tooMany,
|
|
59
63
|
{ name: file.name, accept: accept ?? "", max: reason === "count" ? String(maxFiles) : max }
|
|
@@ -108,7 +112,9 @@ function FileUpload({
|
|
|
108
112
|
]
|
|
109
113
|
}
|
|
110
114
|
),
|
|
111
|
-
messages.length > 0 &&
|
|
115
|
+
messages.length > 0 && // The alert wraps the list: role="alert" on the <ul> itself replaced its list role and
|
|
116
|
+
// left the <li>s outside any list (axe `listitem`).
|
|
117
|
+
/* @__PURE__ */ jsx("div", { role: "alert", children: /* @__PURE__ */ jsx("ul", { className: "uix-file-upload__errors", children: messages.map((m, i) => /* @__PURE__ */ jsx("li", { children: m }, i)) }) }),
|
|
112
118
|
/* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", role: "status", "aria-live": "polite", children: announce }),
|
|
113
119
|
items.length > 0 && /* @__PURE__ */ jsx("ul", { className: "uix-filelist uix-file-upload__list", "aria-label": labels.list, children: items.map((item) => {
|
|
114
120
|
const statusText = item.status === "uploading" ? fillLabel(labels.uploading, { progress: item.progress ?? 0 }) : item.status === "done" ? labels.done : item.status === "error" ? labels.failed : labels.queued;
|
|
@@ -118,7 +124,7 @@ function FileUpload({
|
|
|
118
124
|
/* @__PURE__ */ jsxs("div", { className: "uix-file-upload__body", children: [
|
|
119
125
|
/* @__PURE__ */ jsx("span", { className: "uix-file-upload__name", children: item.name }),
|
|
120
126
|
/* @__PURE__ */ jsxs("span", { className: "uix-file-upload__meta", children: [
|
|
121
|
-
/* @__PURE__ */ jsx("span", { children:
|
|
127
|
+
/* @__PURE__ */ jsx("span", { children: sizeText(item.size) }),
|
|
122
128
|
item.status !== "uploading" && /* @__PURE__ */ jsx("span", { className: "uix-file-upload__status", children: statusText })
|
|
123
129
|
] }),
|
|
124
130
|
item.status === "uploading" && /* @__PURE__ */ jsx(Progress, { value: item.progress, indeterminate: item.progress === void 0, max: 100, label: statusText }),
|
|
@@ -24,6 +24,8 @@ interface FilterEditorLabels {
|
|
|
24
24
|
selected: string;
|
|
25
25
|
/** Remove one chosen record. `{label}`. */
|
|
26
26
|
remove: string;
|
|
27
|
+
/** Names the row of `field.presets` chips. Optional so existing label objects still type-check; default "Presets". */
|
|
28
|
+
presets?: string;
|
|
27
29
|
operators: Record<TextFilterOperator | NumberFilterOperator, string>;
|
|
28
30
|
}
|
|
29
31
|
declare const DEFAULT_FILTER_EDITOR_LABELS: FilterEditorLabels;
|
|
@@ -45,6 +47,8 @@ interface FilterEditorProps {
|
|
|
45
47
|
* multi-select, text or number with a condition, a date range, yes/no, or records found by
|
|
46
48
|
* an async search. The product owns the popover trigger and the query; `summarizeFilter`
|
|
47
49
|
* turns the applied value into the chip text. `FilterPopover` stays for the one-string case.
|
|
50
|
+
* An enum field's `presets` (HAR-1505) render as toggle chips above its options. Words come
|
|
51
|
+
* from `labels`, or from `UixLabelsProvider`'s `filterEditor` entry.
|
|
48
52
|
*/
|
|
49
53
|
declare function FilterEditor({ field, value: valueProp, onValueChange, onApply, onClear, labels: labelOverrides, searchThreshold, searchDelay }: FilterEditorProps): react.JSX.Element;
|
|
50
54
|
|
|
@@ -8,8 +8,9 @@ import { Radio, RadioGroup } from "./Radio.js";
|
|
|
8
8
|
import { Select } from "./Select.js";
|
|
9
9
|
import { Chip } from "./Chip.js";
|
|
10
10
|
import { fillLabel } from "../fill-label.js";
|
|
11
|
+
import { useUixLabels } from "../labels-context.js";
|
|
11
12
|
import { foldForSearch } from "../search-suggest-model.js";
|
|
12
|
-
import { NUMBER_FILTER_OPERATORS, TEXT_FILTER_OPERATORS, emptyFilterValue } from "../filter-model.js";
|
|
13
|
+
import { NUMBER_FILTER_OPERATORS, TEXT_FILTER_OPERATORS, emptyFilterValue, matchFilterPreset } from "../filter-model.js";
|
|
13
14
|
const DEFAULT_FILTER_EDITOR_LABELS = {
|
|
14
15
|
apply: "Apply",
|
|
15
16
|
clear: "Clear",
|
|
@@ -30,6 +31,7 @@ const DEFAULT_FILTER_EDITOR_LABELS = {
|
|
|
30
31
|
selectNone: "Clear selection",
|
|
31
32
|
selected: "{count} selected",
|
|
32
33
|
remove: "Remove {label}",
|
|
34
|
+
presets: "Presets",
|
|
33
35
|
operators: {
|
|
34
36
|
contains: "contains",
|
|
35
37
|
"not-contains": "does not contain",
|
|
@@ -46,7 +48,13 @@ const DEFAULT_FILTER_EDITOR_LABELS = {
|
|
|
46
48
|
}
|
|
47
49
|
};
|
|
48
50
|
function FilterEditor({ field, value: valueProp, onValueChange, onApply, onClear, labels: labelOverrides, searchThreshold = 8, searchDelay = 250 }) {
|
|
49
|
-
const
|
|
51
|
+
const provided = useUixLabels().filterEditor;
|
|
52
|
+
const labels = {
|
|
53
|
+
...DEFAULT_FILTER_EDITOR_LABELS,
|
|
54
|
+
...provided,
|
|
55
|
+
...labelOverrides,
|
|
56
|
+
operators: { ...DEFAULT_FILTER_EDITOR_LABELS.operators, ...provided?.operators, ...labelOverrides?.operators }
|
|
57
|
+
};
|
|
50
58
|
const id = useId();
|
|
51
59
|
const value = valueProp?.kind === field.kind ? valueProp : emptyFilterValue(field.kind);
|
|
52
60
|
let body = null;
|
|
@@ -121,7 +129,10 @@ function EnumBody({ field, value, onChange, labels, threshold }) {
|
|
|
121
129
|
}, [options, query]);
|
|
122
130
|
const selected = new Set(value.values);
|
|
123
131
|
const toggle = (v, on) => onChange({ kind: "enum", values: on ? [...value.values, v] : value.values.filter((x) => x !== v) });
|
|
132
|
+
const presets = field.presets ?? [];
|
|
133
|
+
const active = matchFilterPreset(field, value);
|
|
124
134
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
135
|
+
presets.length > 0 && /* @__PURE__ */ jsx("div", { className: "uix-chip-group uix-filter-editor__presets", role: "group", "aria-label": labels.presets ?? DEFAULT_FILTER_EDITOR_LABELS.presets, children: presets.map((p) => /* @__PURE__ */ jsx(Chip, { pressed: p === active, onPressedChange: () => onChange({ kind: "enum", values: [...p.values] }), children: p.label }, p.id)) }),
|
|
125
136
|
options.length > threshold && /* @__PURE__ */ jsx(Input, { type: "search", size: "sm", "aria-label": labels.search, placeholder: labels.search, value: query, onChange: (e) => setQuery(e.currentTarget.value), autoFocus: true }),
|
|
126
137
|
/* @__PURE__ */ jsxs("div", { className: "uix-filter-editor__bulk", children: [
|
|
127
138
|
/* @__PURE__ */ jsx("span", { className: "uix-filter-editor__count", "aria-live": "polite", children: fillLabel(labels.selected, { count: value.values.length }) }),
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type HeartbeatState = 'live' | 'idle' | 'warning' | 'danger';
|
|
5
|
+
interface HeartbeatProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'children'> {
|
|
6
|
+
/**
|
|
7
|
+
* `live` (default): a green dot with a pulsing ring. `warning` / `danger`: the same in that
|
|
8
|
+
* tone. `idle`: a grey dot, no ring.
|
|
9
|
+
*/
|
|
10
|
+
state?: HeartbeatState;
|
|
11
|
+
/**
|
|
12
|
+
* What the dot means, for assistive technology ("Live", "Connection lost"). Without it the
|
|
13
|
+
* dot is decorative (`aria-hidden`): say the state in text beside it, as `LiveIndicator` does.
|
|
14
|
+
*/
|
|
15
|
+
label?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The pulsing status dot over `.uix-heartbeat` (HAR-1606), so products stop hand-writing its
|
|
19
|
+
* three spans. Decorative unless it has a `label`. The ring stops under
|
|
20
|
+
* `prefers-reduced-motion` (the kit's global motion guard); the dot and its colour stay.
|
|
21
|
+
*/
|
|
22
|
+
declare function Heartbeat({ state, label, className, ...props }: HeartbeatProps): react.JSX.Element;
|
|
23
|
+
interface LiveIndicatorProps extends HTMLAttributes<HTMLSpanElement> {
|
|
24
|
+
state?: HeartbeatState;
|
|
25
|
+
/** The state in words: "Live", "Paused", "Reconnecting…". It is what a screen reader gets. */
|
|
26
|
+
children?: ReactNode;
|
|
27
|
+
}
|
|
28
|
+
/** A `Heartbeat` with its state in text beside it, over `.uix-live`. */
|
|
29
|
+
declare function LiveIndicator({ state, children, className, ...props }: LiveIndicatorProps): react.JSX.Element;
|
|
30
|
+
|
|
31
|
+
export { Heartbeat, type HeartbeatProps, type HeartbeatState, LiveIndicator, type LiveIndicatorProps };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from "../cx.js";
|
|
3
|
+
function Heartbeat({ state = "live", label, className, ...props }) {
|
|
4
|
+
return /* @__PURE__ */ jsxs(
|
|
5
|
+
"span",
|
|
6
|
+
{
|
|
7
|
+
className: cx("uix-heartbeat", state !== "live" && `uix-heartbeat--${state}`, className),
|
|
8
|
+
role: label ? "img" : void 0,
|
|
9
|
+
"aria-label": label,
|
|
10
|
+
"aria-hidden": label ? void 0 : true,
|
|
11
|
+
...props,
|
|
12
|
+
children: [
|
|
13
|
+
/* @__PURE__ */ jsx("span", { className: "uix-heartbeat__ping" }),
|
|
14
|
+
/* @__PURE__ */ jsx("span", { className: "uix-heartbeat__dot" })
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
function LiveIndicator({ state = "live", children, className, ...props }) {
|
|
20
|
+
return /* @__PURE__ */ jsxs("span", { className: cx("uix-live", className), ...props, children: [
|
|
21
|
+
/* @__PURE__ */ jsx(Heartbeat, { state }),
|
|
22
|
+
children
|
|
23
|
+
] });
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
Heartbeat,
|
|
27
|
+
LiveIndicator
|
|
28
|
+
};
|
|
@@ -3,13 +3,18 @@ import { ReactElement, ReactNode } from 'react';
|
|
|
3
3
|
import { UixRenderLink } from '../link.js';
|
|
4
4
|
import { Placement } from '../overlay-position.js';
|
|
5
5
|
|
|
6
|
+
/** `id` and `data-*` for the item's own element (HAR-1629): a stable hook for tests, journeys and analytics. */
|
|
7
|
+
interface MenuItemAttributes {
|
|
8
|
+
id?: string;
|
|
9
|
+
[dataAttribute: `data-${string}`]: string | number | boolean | undefined;
|
|
10
|
+
}
|
|
6
11
|
interface MenuProps {
|
|
7
12
|
/**
|
|
8
13
|
* The menu button, usually a `Button`. It receives `aria-haspopup="menu"`,
|
|
9
14
|
* `aria-expanded`, `aria-controls` and the open/keyboard handlers.
|
|
10
15
|
*/
|
|
11
16
|
trigger: ReactElement;
|
|
12
|
-
/** `MenuItem`, `MenuGroup` and `MenuSeparator`. */
|
|
17
|
+
/** `MenuItem`, `MenuItemRadio`, `MenuItemCheckbox`, `MenuRadioGroup`, `MenuGroup` and `MenuSeparator`. */
|
|
13
18
|
children?: ReactNode;
|
|
14
19
|
/** Controlled open state; pair with `onOpenChange`. */
|
|
15
20
|
open?: boolean;
|
|
@@ -23,10 +28,11 @@ interface MenuProps {
|
|
|
23
28
|
/**
|
|
24
29
|
* A menu button with an APG menu (HAR-1359; TENSOR C1, MOTUS B-A14): ↓/↑ move, Home/End,
|
|
25
30
|
* type-to-find, Enter/Space activate, Esc and Tab close, focus returns to the trigger.
|
|
26
|
-
* The surface is the kit `Popover`, so it escapes clipping containers
|
|
31
|
+
* The surface is the kit `Popover`, so it escapes clipping containers; a menu taller than
|
|
32
|
+
* the viewport is capped to it and scrolls (HAR-1613).
|
|
27
33
|
*/
|
|
28
34
|
declare function Menu({ trigger, children, open: controlledOpen, defaultOpen, onOpenChange, label, placement, className }: MenuProps): react.JSX.Element;
|
|
29
|
-
interface MenuItemProps {
|
|
35
|
+
interface MenuItemProps extends MenuItemAttributes {
|
|
30
36
|
children?: ReactNode;
|
|
31
37
|
/** Runs on click, Enter or Space; the menu then closes and focus returns to the trigger. */
|
|
32
38
|
onSelect?: () => void;
|
|
@@ -40,7 +46,49 @@ interface MenuItemProps {
|
|
|
40
46
|
disabled?: boolean;
|
|
41
47
|
className?: string;
|
|
42
48
|
}
|
|
43
|
-
declare function MenuItem({ children, onSelect, href, renderLink, icon, shortcut, tone, disabled, className }: MenuItemProps): react.JSX.Element;
|
|
49
|
+
declare function MenuItem({ children, onSelect, href, renderLink, icon, shortcut, tone, disabled, className, ...rest }: MenuItemProps): react.JSX.Element;
|
|
50
|
+
interface MenuRadioGroupProps {
|
|
51
|
+
/** The `value` of the checked `MenuItemRadio`. */
|
|
52
|
+
value?: string;
|
|
53
|
+
onValueChange?: (value: string) => void;
|
|
54
|
+
/** Visible group heading; it also names the group. */
|
|
55
|
+
label?: ReactNode;
|
|
56
|
+
/** Names the group when there is no visible `label`. */
|
|
57
|
+
'aria-label'?: string;
|
|
58
|
+
children?: ReactNode;
|
|
59
|
+
}
|
|
60
|
+
/** One choice out of several inside a `Menu`: wraps `MenuItemRadio` rows and holds their value (HAR-1629). */
|
|
61
|
+
declare function MenuRadioGroup({ value, onValueChange, label, 'aria-label': ariaLabel, children }: MenuRadioGroupProps): react.JSX.Element;
|
|
62
|
+
interface MenuItemRadioProps extends MenuItemAttributes {
|
|
63
|
+
/** Checked when it equals the surrounding `MenuRadioGroup`'s `value`. */
|
|
64
|
+
value: string;
|
|
65
|
+
/** For a radio item used without a `MenuRadioGroup`. */
|
|
66
|
+
checked?: boolean;
|
|
67
|
+
/** Runs when the item is chosen, after the group's `onValueChange`. */
|
|
68
|
+
onSelect?: () => void;
|
|
69
|
+
/** Close the menu after a click or Enter. Default `true`. Space never closes it. */
|
|
70
|
+
closeOnSelect?: boolean;
|
|
71
|
+
children?: ReactNode;
|
|
72
|
+
icon?: ReactNode;
|
|
73
|
+
shortcut?: ReactNode;
|
|
74
|
+
disabled?: boolean;
|
|
75
|
+
className?: string;
|
|
76
|
+
}
|
|
77
|
+
/** A `role="menuitemradio"` row with `aria-checked` and a check mark for the current choice. */
|
|
78
|
+
declare function MenuItemRadio({ value, checked, onSelect, closeOnSelect, ...rest }: MenuItemRadioProps): react.JSX.Element;
|
|
79
|
+
interface MenuItemCheckboxProps extends MenuItemAttributes {
|
|
80
|
+
checked: boolean;
|
|
81
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
82
|
+
/** Close the menu after a click or Enter. Default `false`: several boxes are usually ticked in a row. */
|
|
83
|
+
closeOnSelect?: boolean;
|
|
84
|
+
children?: ReactNode;
|
|
85
|
+
icon?: ReactNode;
|
|
86
|
+
shortcut?: ReactNode;
|
|
87
|
+
disabled?: boolean;
|
|
88
|
+
className?: string;
|
|
89
|
+
}
|
|
90
|
+
/** A `role="menuitemcheckbox"` row with `aria-checked`; the menu stays open while boxes are ticked. */
|
|
91
|
+
declare function MenuItemCheckbox({ checked, onCheckedChange, closeOnSelect, ...rest }: MenuItemCheckboxProps): react.JSX.Element;
|
|
44
92
|
interface MenuGroupProps {
|
|
45
93
|
label: ReactNode;
|
|
46
94
|
children?: ReactNode;
|
|
@@ -48,4 +96,4 @@ interface MenuGroupProps {
|
|
|
48
96
|
declare function MenuGroup({ label, children }: MenuGroupProps): react.JSX.Element;
|
|
49
97
|
declare function MenuSeparator(): react.JSX.Element;
|
|
50
98
|
|
|
51
|
-
export { Menu, MenuGroup, type MenuGroupProps, MenuItem, type MenuItemProps, type MenuProps, MenuSeparator };
|
|
99
|
+
export { Menu, MenuGroup, type MenuGroupProps, MenuItem, type MenuItemAttributes, MenuItemCheckbox, type MenuItemCheckboxProps, type MenuItemProps, MenuItemRadio, type MenuItemRadioProps, type MenuProps, MenuRadioGroup, type MenuRadioGroupProps, MenuSeparator };
|