@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
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { AriaAttributes, CSSProperties } from 'react';
|
|
3
|
+
import { Placement } from '../overlay-position.js';
|
|
4
|
+
|
|
5
|
+
/** Every word `TimePicker` renders or announces. */
|
|
6
|
+
interface TimePickerLabels {
|
|
7
|
+
/** The list button's name while no time is set. */
|
|
8
|
+
open: string;
|
|
9
|
+
/** The list button's name with a time set; `{time}` is the time as the field shows it. */
|
|
10
|
+
openSelected: string;
|
|
11
|
+
/** The list of times (the popover's and the listbox's name). */
|
|
12
|
+
list: string;
|
|
13
|
+
/** Shown when the typed text is not a time; `{example}` is a time in the field's own shape. */
|
|
14
|
+
invalidTime: string;
|
|
15
|
+
/** Shown when the typed time is before `min`; `{min}`. */
|
|
16
|
+
tooEarly: string;
|
|
17
|
+
/** Shown when the typed time is after `max`; `{max}`. */
|
|
18
|
+
tooLate: string;
|
|
19
|
+
/** Shown when the typed time is outside an overnight range (`min` after `max`); `{min}`, `{max}`. */
|
|
20
|
+
outside: string;
|
|
21
|
+
}
|
|
22
|
+
declare const DEFAULT_TIME_PICKER_LABELS: TimePickerLabels;
|
|
23
|
+
/** Why typed text was refused: not a time, or a time before `min` / after `max`. */
|
|
24
|
+
type TimeInputProblem = 'format' | 'early' | 'late' | 'outside';
|
|
25
|
+
interface TimePickerProps {
|
|
26
|
+
/** The time as `HH:mm` (24-hour), or `null` for none. */
|
|
27
|
+
value: string | null;
|
|
28
|
+
onValueChange: (value: string | null) => void;
|
|
29
|
+
/** Minutes the arrow keys step the minutes by, and between the times in the list. Default 15. */
|
|
30
|
+
minuteStep?: number;
|
|
31
|
+
/**
|
|
32
|
+
* First and last time that can be chosen (`HH:mm`, inclusive). A `min` after `max` (22:00 to
|
|
33
|
+
* 06:00) is an overnight range: it wraps round midnight, in the keys, the list and the check.
|
|
34
|
+
*/
|
|
35
|
+
min?: string;
|
|
36
|
+
max?: string;
|
|
37
|
+
/**
|
|
38
|
+
* The locale that writes the time: 14:30 in `de`, 2:30 PM in `en-US`. Default: `UixLabelsProvider`
|
|
39
|
+
* `dateFormat.locale`, then the viewer's.
|
|
40
|
+
*/
|
|
41
|
+
locale?: string;
|
|
42
|
+
/** `true` writes 2:30 PM, `false` 14:30, whatever the locale. */
|
|
43
|
+
hour12?: boolean;
|
|
44
|
+
placeholder?: string;
|
|
45
|
+
size?: 'sm' | 'md';
|
|
46
|
+
disabled?: boolean;
|
|
47
|
+
readOnly?: boolean;
|
|
48
|
+
required?: boolean;
|
|
49
|
+
invalid?: boolean;
|
|
50
|
+
/** Submits the `HH:mm` value under this name (a hidden input), whatever the field shows. */
|
|
51
|
+
name?: string;
|
|
52
|
+
/** Lands on the text input, so a `<label htmlFor>` or `<Field>` names it. */
|
|
53
|
+
id?: string;
|
|
54
|
+
'aria-label'?: string;
|
|
55
|
+
'aria-labelledby'?: string;
|
|
56
|
+
'aria-describedby'?: string;
|
|
57
|
+
'aria-invalid'?: AriaAttributes['aria-invalid'];
|
|
58
|
+
'aria-required'?: AriaAttributes['aria-required'];
|
|
59
|
+
/** Where the list opens. Default `'bottom-start'`; it flips and shifts to stay on screen. */
|
|
60
|
+
placement?: Placement;
|
|
61
|
+
/** Told when typed text is refused, and `null` once the field is fine again. */
|
|
62
|
+
onInputProblem?: (problem: TimeInputProblem | null) => void;
|
|
63
|
+
onOpenChange?: (open: boolean) => void;
|
|
64
|
+
/** Translatable words; English defaults in {@link DEFAULT_TIME_PICKER_LABELS}. */
|
|
65
|
+
labels?: Partial<TimePickerLabels>;
|
|
66
|
+
className?: string;
|
|
67
|
+
style?: CSSProperties;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* A time of day as a form field (HAR-1835), beside `DatePicker`: a text input that shows the time
|
|
71
|
+
* in the locale's shape (14:30 in German, 2:30 PM in en-US) and reads a typed one (`1430`, `9`,
|
|
72
|
+
* `2:30 pm`) on Enter and when focus leaves it; a clock button opens the times at `minuteStep` in a
|
|
73
|
+
* list. ArrowUp / ArrowDown step the part the caret is in (the hour, or the minutes by
|
|
74
|
+
* `minuteStep`), PageUp / PageDown step the hour, Alt+ArrowDown opens the list. Text that is not a
|
|
75
|
+
* time, or a time outside `min` / `max`, marks the field invalid, says why and leaves `value` alone.
|
|
76
|
+
* The value is `HH:mm`, what `<input type="time">` holds.
|
|
77
|
+
*/
|
|
78
|
+
declare const TimePicker: react.ForwardRefExoticComponent<TimePickerProps & react.RefAttributes<HTMLInputElement>>;
|
|
79
|
+
|
|
80
|
+
export { DEFAULT_TIME_PICKER_LABELS, type TimeInputProblem, TimePicker, type TimePickerLabels, type TimePickerProps };
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { cx } from "../cx.js";
|
|
5
|
+
import { fillLabel } from "../fill-label.js";
|
|
6
|
+
import { ClockIcon } from "../icons/components.js";
|
|
7
|
+
import { useUixLabels } from "../labels-context.js";
|
|
8
|
+
import {
|
|
9
|
+
formatTimeKey,
|
|
10
|
+
isOvernightRange,
|
|
11
|
+
isTimeOutOfRange,
|
|
12
|
+
isValidTimeKey,
|
|
13
|
+
parseTimeInput,
|
|
14
|
+
stepTime,
|
|
15
|
+
timePartEnds,
|
|
16
|
+
timeSlots,
|
|
17
|
+
timeToMinutes
|
|
18
|
+
} from "../time-field-model.js";
|
|
19
|
+
import { useFieldPopover } from "./DateFieldParts.js";
|
|
20
|
+
import { Popover } from "./Popover.js";
|
|
21
|
+
const useIsomorphicLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;
|
|
22
|
+
const DEFAULT_TIME_PICKER_LABELS = {
|
|
23
|
+
open: "Choose time",
|
|
24
|
+
openSelected: "Change time, {time}",
|
|
25
|
+
list: "Times",
|
|
26
|
+
invalidTime: "Enter a time like {example}.",
|
|
27
|
+
tooEarly: "Choose {min} or later.",
|
|
28
|
+
tooLate: "Choose {max} or earlier.",
|
|
29
|
+
outside: "Choose a time from {min} to {max}."
|
|
30
|
+
};
|
|
31
|
+
const TimePicker = forwardRef(function TimePicker2({
|
|
32
|
+
value,
|
|
33
|
+
onValueChange,
|
|
34
|
+
minuteStep = 15,
|
|
35
|
+
min,
|
|
36
|
+
max,
|
|
37
|
+
locale: localeProp,
|
|
38
|
+
hour12,
|
|
39
|
+
placeholder,
|
|
40
|
+
size = "md",
|
|
41
|
+
disabled,
|
|
42
|
+
readOnly,
|
|
43
|
+
required,
|
|
44
|
+
invalid,
|
|
45
|
+
name,
|
|
46
|
+
id: idProp,
|
|
47
|
+
"aria-label": ariaLabel,
|
|
48
|
+
"aria-labelledby": ariaLabelledBy,
|
|
49
|
+
"aria-describedby": ariaDescribedBy,
|
|
50
|
+
"aria-invalid": ariaInvalid,
|
|
51
|
+
"aria-required": ariaRequired,
|
|
52
|
+
placement = "bottom-start",
|
|
53
|
+
onInputProblem,
|
|
54
|
+
onOpenChange,
|
|
55
|
+
labels: labelOverrides,
|
|
56
|
+
className,
|
|
57
|
+
style
|
|
58
|
+
}, forwardedRef) {
|
|
59
|
+
const uid = useId();
|
|
60
|
+
const id = idProp ?? `${uid}-input`;
|
|
61
|
+
const popoverId = `${uid}-times`;
|
|
62
|
+
const listId = `${uid}-list`;
|
|
63
|
+
const errorId = `${uid}-error`;
|
|
64
|
+
const uixLabels = useUixLabels();
|
|
65
|
+
const labels = { ...DEFAULT_TIME_PICKER_LABELS, ...uixLabels.timePicker, ...labelOverrides };
|
|
66
|
+
const locale = localeProp ?? uixLabels.dateFormat?.locale;
|
|
67
|
+
const anchorRef = useRef(null);
|
|
68
|
+
const inputRef = useRef(null);
|
|
69
|
+
const listRef = useRef(null);
|
|
70
|
+
useImperativeHandle(forwardedRef, () => inputRef.current);
|
|
71
|
+
const step = Math.max(1, Math.round(minuteStep));
|
|
72
|
+
const current = isValidTimeKey(value) ? value : null;
|
|
73
|
+
const [mounted, setMounted] = useState(false);
|
|
74
|
+
useEffect(() => setMounted(true), []);
|
|
75
|
+
const shownLocale = locale ?? (mounted ? void 0 : "en-GB");
|
|
76
|
+
const shownHour12 = hour12 ?? (locale || mounted ? void 0 : false);
|
|
77
|
+
const format = (time) => formatTimeKey(time, shownLocale, shownHour12);
|
|
78
|
+
const [text, setText] = useState(() => current ? format(current) : "");
|
|
79
|
+
const [problem, setProblemState] = useState(null);
|
|
80
|
+
const reported = useRef(null);
|
|
81
|
+
const setProblem = (next) => {
|
|
82
|
+
if (next !== reported.current) {
|
|
83
|
+
reported.current = next;
|
|
84
|
+
onInputProblem?.(next);
|
|
85
|
+
}
|
|
86
|
+
setProblemState(next);
|
|
87
|
+
};
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
setText(current ? format(current) : "");
|
|
90
|
+
setProblem(null);
|
|
91
|
+
}, [current, locale, hour12, mounted]);
|
|
92
|
+
const { open, show, hide, triggerProps } = useFieldPopover(popoverId, () => inputRef.current?.focus({ preventScroll: true }), onOpenChange);
|
|
93
|
+
const locked = !!disabled || !!readOnly;
|
|
94
|
+
const commit = () => {
|
|
95
|
+
const typed = text.trim();
|
|
96
|
+
if (!typed) {
|
|
97
|
+
setProblem(null);
|
|
98
|
+
if (text) setText("");
|
|
99
|
+
if (value !== null) onValueChange(null);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
if (current && typed === format(current)) {
|
|
103
|
+
setProblem(null);
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
const parsed = parseTimeInput(typed, locale);
|
|
107
|
+
if (!parsed) {
|
|
108
|
+
setProblem("format");
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
if (isTimeOutOfRange(parsed, min, max)) {
|
|
112
|
+
setProblem(isOvernightRange(min, max) ? "outside" : min && isValidTimeKey(min) && parsed < min ? "early" : "late");
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
setProblem(null);
|
|
116
|
+
setText(format(parsed));
|
|
117
|
+
if (parsed !== value) onValueChange(parsed);
|
|
118
|
+
return true;
|
|
119
|
+
};
|
|
120
|
+
const choose = (time) => {
|
|
121
|
+
setProblem(null);
|
|
122
|
+
setText(format(time));
|
|
123
|
+
if (time !== value) onValueChange(time);
|
|
124
|
+
};
|
|
125
|
+
const onInputKeyDown = (event) => {
|
|
126
|
+
if (locked || event.nativeEvent.isComposing || event.keyCode === 229) return;
|
|
127
|
+
const { key, altKey } = event;
|
|
128
|
+
if (key === "ArrowDown" && altKey) {
|
|
129
|
+
event.preventDefault();
|
|
130
|
+
show();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (key === "ArrowUp" || key === "ArrowDown" || key === "PageUp" || key === "PageDown") {
|
|
134
|
+
event.preventDefault();
|
|
135
|
+
const base = parseTimeInput(text, locale) ?? current;
|
|
136
|
+
const input = event.currentTarget;
|
|
137
|
+
const caret = input.selectionStart ?? 0;
|
|
138
|
+
const ends = base && input.value === format(base) ? timePartEnds(base, shownLocale, shownHour12) : null;
|
|
139
|
+
const hourEnd = ends?.hour ?? /^\s*\d{1,2}/.exec(input.value)?.[0].length ?? 0;
|
|
140
|
+
const onHour = key === "PageUp" || key === "PageDown" || !!base && caret <= hourEnd;
|
|
141
|
+
const sign = key === "ArrowUp" || key === "PageUp" ? 1 : -1;
|
|
142
|
+
let next;
|
|
143
|
+
if (!base) next = stepTime(null, 0, min, max);
|
|
144
|
+
else if (onHour) next = stepTime(base, sign * 60, min, max);
|
|
145
|
+
else {
|
|
146
|
+
const minutes = timeToMinutes(base);
|
|
147
|
+
const snapped = sign > 0 ? Math.floor(minutes / step) * step + step : Math.ceil(minutes / step) * step - step;
|
|
148
|
+
next = stepTime(base, snapped - minutes, min, max);
|
|
149
|
+
}
|
|
150
|
+
choose(next);
|
|
151
|
+
requestAnimationFrame(() => {
|
|
152
|
+
const el = inputRef.current;
|
|
153
|
+
if (!el || el.ownerDocument.activeElement !== el) return;
|
|
154
|
+
const moved = timePartEnds(next, shownLocale, shownHour12);
|
|
155
|
+
const at = moved ? onHour ? moved.hour : moved.minute : el.value.length;
|
|
156
|
+
try {
|
|
157
|
+
el.setSelectionRange(at, at);
|
|
158
|
+
} catch {
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (key === "Enter" && text.trim() !== (current ? format(current) : "")) {
|
|
164
|
+
event.preventDefault();
|
|
165
|
+
commit();
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
const pick = (time) => {
|
|
169
|
+
choose(time);
|
|
170
|
+
hide(false);
|
|
171
|
+
inputRef.current?.focus({ preventScroll: true });
|
|
172
|
+
};
|
|
173
|
+
const describedBy = [ariaDescribedBy, problem ? errorId : void 0].filter(Boolean).join(" ") || void 0;
|
|
174
|
+
const isInvalid = !!invalid || !!problem || ariaInvalid === true || ariaInvalid === "true";
|
|
175
|
+
const example = format(min && isValidTimeKey(min) && min > "14:30" ? min : "14:30");
|
|
176
|
+
return /* @__PURE__ */ jsxs(
|
|
177
|
+
"div",
|
|
178
|
+
{
|
|
179
|
+
ref: anchorRef,
|
|
180
|
+
className: cx("uix-date-picker", "uix-time-picker", size === "sm" && "uix-date-picker--sm", className),
|
|
181
|
+
style,
|
|
182
|
+
onKeyDown: (event) => {
|
|
183
|
+
if (event.key !== "Escape" || !open) return;
|
|
184
|
+
event.stopPropagation();
|
|
185
|
+
event.preventDefault();
|
|
186
|
+
hide(false);
|
|
187
|
+
inputRef.current?.focus({ preventScroll: true });
|
|
188
|
+
},
|
|
189
|
+
children: [
|
|
190
|
+
/* @__PURE__ */ jsxs("div", { className: "uix-date-picker__control", children: [
|
|
191
|
+
/* @__PURE__ */ jsx(
|
|
192
|
+
"input",
|
|
193
|
+
{
|
|
194
|
+
ref: inputRef,
|
|
195
|
+
id,
|
|
196
|
+
type: "text",
|
|
197
|
+
autoComplete: "off",
|
|
198
|
+
spellCheck: false,
|
|
199
|
+
className: cx("uix-input", size === "sm" && "uix-input--sm", "uix-date-picker__input", "uix-time-picker__input"),
|
|
200
|
+
value: text,
|
|
201
|
+
placeholder: placeholder ?? example,
|
|
202
|
+
disabled,
|
|
203
|
+
readOnly,
|
|
204
|
+
required,
|
|
205
|
+
"aria-label": ariaLabel,
|
|
206
|
+
"aria-labelledby": ariaLabelledBy,
|
|
207
|
+
"aria-describedby": describedBy,
|
|
208
|
+
"aria-invalid": isInvalid || void 0,
|
|
209
|
+
"aria-required": ariaRequired,
|
|
210
|
+
onChange: (event) => {
|
|
211
|
+
setText(event.target.value);
|
|
212
|
+
if (problem) setProblem(null);
|
|
213
|
+
},
|
|
214
|
+
onBlur: () => {
|
|
215
|
+
if (!locked) commit();
|
|
216
|
+
},
|
|
217
|
+
onKeyDown: onInputKeyDown
|
|
218
|
+
}
|
|
219
|
+
),
|
|
220
|
+
/* @__PURE__ */ jsx(
|
|
221
|
+
"button",
|
|
222
|
+
{
|
|
223
|
+
type: "button",
|
|
224
|
+
className: "uix-date-picker__toggle",
|
|
225
|
+
disabled: locked,
|
|
226
|
+
"aria-label": current ? fillLabel(labels.openSelected, { time: format(current) }) : labels.open,
|
|
227
|
+
...triggerProps,
|
|
228
|
+
children: /* @__PURE__ */ jsx(ClockIcon, { size: "sm" })
|
|
229
|
+
}
|
|
230
|
+
)
|
|
231
|
+
] }),
|
|
232
|
+
name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: current ?? "", disabled }),
|
|
233
|
+
problem && /* @__PURE__ */ jsx("span", { id: errorId, className: "uix-field__error uix-date-picker__error", role: "alert", children: problem === "format" ? fillLabel(labels.invalidTime, { example }) : problem === "outside" ? fillLabel(labels.outside, { min: format(min ?? ""), max: format(max ?? "") }) : problem === "early" ? fillLabel(labels.tooEarly, { min: format(min ?? "") }) : fillLabel(labels.tooLate, { max: format(max ?? "") }) }),
|
|
234
|
+
/* @__PURE__ */ jsx(
|
|
235
|
+
Popover,
|
|
236
|
+
{
|
|
237
|
+
id: popoverId,
|
|
238
|
+
anchor: anchorRef,
|
|
239
|
+
placement,
|
|
240
|
+
className: "uix-time-picker__popover",
|
|
241
|
+
role: "dialog",
|
|
242
|
+
"aria-label": labels.list,
|
|
243
|
+
capHeight: true,
|
|
244
|
+
closeWhenAnchorHidden: true,
|
|
245
|
+
children: open && /* @__PURE__ */ jsx(
|
|
246
|
+
TimeList,
|
|
247
|
+
{
|
|
248
|
+
ref: listRef,
|
|
249
|
+
id: listId,
|
|
250
|
+
value: current,
|
|
251
|
+
step,
|
|
252
|
+
min,
|
|
253
|
+
max,
|
|
254
|
+
label: labels.list,
|
|
255
|
+
format,
|
|
256
|
+
onPick: pick
|
|
257
|
+
}
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
TimePicker.displayName = "TimePicker";
|
|
266
|
+
const TimeList = forwardRef(function TimeList2({ id, value, step, min, max, label, format, onPick }, ref) {
|
|
267
|
+
const slots = useMemo(() => timeSlots(step, min, max), [step, min, max]);
|
|
268
|
+
const nearest = () => {
|
|
269
|
+
if (!slots.length) return -1;
|
|
270
|
+
if (!value) return 0;
|
|
271
|
+
const exact = slots.indexOf(value);
|
|
272
|
+
if (exact >= 0) return exact;
|
|
273
|
+
const at = slots.findIndex((slot) => slot > value);
|
|
274
|
+
return at < 0 ? slots.length - 1 : Math.max(0, at - 1);
|
|
275
|
+
};
|
|
276
|
+
const [active, setActive] = useState(nearest);
|
|
277
|
+
const listRef = useRef(null);
|
|
278
|
+
useImperativeHandle(ref, () => listRef.current);
|
|
279
|
+
const optionId = (index) => `${id}-${index}`;
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
listRef.current?.focus({ preventScroll: true });
|
|
282
|
+
}, []);
|
|
283
|
+
const placed = useRef(false);
|
|
284
|
+
useIsomorphicLayoutEffect(() => {
|
|
285
|
+
const list = listRef.current;
|
|
286
|
+
const el = list?.ownerDocument.getElementById(optionId(active));
|
|
287
|
+
if (!list || !el) return;
|
|
288
|
+
const top = el.offsetTop;
|
|
289
|
+
const bottom = top + el.offsetHeight;
|
|
290
|
+
if (!placed.current) {
|
|
291
|
+
placed.current = true;
|
|
292
|
+
list.scrollTop = top - (list.clientHeight - el.offsetHeight) / 2;
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (top < list.scrollTop) list.scrollTop = top;
|
|
296
|
+
else if (bottom > list.scrollTop + list.clientHeight) list.scrollTop = bottom - list.clientHeight;
|
|
297
|
+
}, [active]);
|
|
298
|
+
const perHour = Math.max(1, Math.round(60 / step));
|
|
299
|
+
const onKeyDown = (event) => {
|
|
300
|
+
const last = slots.length - 1;
|
|
301
|
+
const moves = { ArrowDown: 1, ArrowUp: -1, PageDown: perHour, PageUp: -perHour };
|
|
302
|
+
if (event.key in moves) {
|
|
303
|
+
event.preventDefault();
|
|
304
|
+
setActive((index) => Math.max(0, Math.min(last, index + moves[event.key])));
|
|
305
|
+
} else if (event.key === "Home" || event.key === "End") {
|
|
306
|
+
event.preventDefault();
|
|
307
|
+
setActive(event.key === "Home" ? 0 : last);
|
|
308
|
+
} else if (event.key === "Enter" || event.key === " ") {
|
|
309
|
+
event.preventDefault();
|
|
310
|
+
const slot = slots[active];
|
|
311
|
+
if (slot) onPick(slot);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
return /* @__PURE__ */ jsx(
|
|
315
|
+
"div",
|
|
316
|
+
{
|
|
317
|
+
ref: listRef,
|
|
318
|
+
id,
|
|
319
|
+
role: "listbox",
|
|
320
|
+
tabIndex: 0,
|
|
321
|
+
"aria-label": label,
|
|
322
|
+
className: "uix-listbox uix-time-picker__list",
|
|
323
|
+
"aria-activedescendant": active >= 0 ? optionId(active) : void 0,
|
|
324
|
+
onKeyDown,
|
|
325
|
+
children: slots.map((slot, index) => /* @__PURE__ */ jsx(
|
|
326
|
+
"div",
|
|
327
|
+
{
|
|
328
|
+
id: optionId(index),
|
|
329
|
+
role: "option",
|
|
330
|
+
className: "uix-listbox__option",
|
|
331
|
+
"aria-selected": slot === value,
|
|
332
|
+
"data-active": index === active || void 0,
|
|
333
|
+
onPointerMove: () => {
|
|
334
|
+
if (index !== active) setActive(index);
|
|
335
|
+
},
|
|
336
|
+
onClick: () => onPick(slot),
|
|
337
|
+
children: /* @__PURE__ */ jsx("span", { className: "uix-listbox__label", children: format(slot) })
|
|
338
|
+
},
|
|
339
|
+
slot
|
|
340
|
+
))
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
});
|
|
344
|
+
export {
|
|
345
|
+
DEFAULT_TIME_PICKER_LABELS,
|
|
346
|
+
TimePicker
|
|
347
|
+
};
|
package/dist/components/Toast.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
5
|
import { useUixLabels } from "../labels-context.js";
|
|
6
|
+
import { XIcon } from "../icons/components.js";
|
|
6
7
|
import { toast as defaultToast } from "../toast-store.js";
|
|
7
8
|
const ToasterContext = createContext(null);
|
|
8
9
|
function Toast({ title, message, tone, icon, action, onClose, leaving, dismissLabel: dismissLabelProp, className, ...props }) {
|
|
@@ -44,7 +45,7 @@ function Toast({ title, message, tone, icon, action, onClose, leaving, dismissLa
|
|
|
44
45
|
message && /* @__PURE__ */ jsx("div", { className: "uix-toast__msg", children: message })
|
|
45
46
|
] }),
|
|
46
47
|
action != null && /* @__PURE__ */ jsx("div", { className: "uix-toast__action-slot", children: action }),
|
|
47
|
-
onClose && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-toast__close", onClick: handleClose, "aria-label": dismissLabel, children:
|
|
48
|
+
onClose && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-toast__close", onClick: handleClose, "aria-label": dismissLabel, children: /* @__PURE__ */ jsx(XIcon, {}) })
|
|
48
49
|
]
|
|
49
50
|
}
|
|
50
51
|
);
|
|
@@ -124,7 +125,7 @@ function useToastTimers(store, visible, paused) {
|
|
|
124
125
|
timer = { version: record.version, remaining: record.duration, start: now };
|
|
125
126
|
map.set(record.id, timer);
|
|
126
127
|
}
|
|
127
|
-
if (paused) {
|
|
128
|
+
if (paused && record.pauseOnHover !== false) {
|
|
128
129
|
if (timer.handle != null) {
|
|
129
130
|
clearTimeout(timer.handle);
|
|
130
131
|
timer.handle = void 0;
|
|
@@ -16,10 +16,11 @@ interface TooltipProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'content'>
|
|
|
16
16
|
placement?: Placement;
|
|
17
17
|
/**
|
|
18
18
|
* The trigger's keyboard shortcut, in `KbdCombo` key names, e.g. `['Mod', 'K']` (HAR-2031):
|
|
19
|
-
* shown with `Kbd` after the text
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
19
|
+
* shown with `Kbd` after the text. Screen readers hear it once, in the trigger's description
|
|
20
|
+
* after the text ("Keyboard shortcuts, Shift+?"), the same rule as a `Button` with `shortcut`
|
|
21
|
+
* (HAR-2416). Default: the `shortcut` of a `Button` child, so a Tooltip around a Button with a
|
|
22
|
+
* shortcut shows one bubble with the label and the keys. The kit only shows it: the product
|
|
23
|
+
* binds the key.
|
|
23
24
|
*/
|
|
24
25
|
shortcut?: KeyShortcut;
|
|
25
26
|
children?: ReactNode;
|
|
@@ -31,6 +32,9 @@ interface TooltipProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'content'>
|
|
|
31
32
|
* (flip/shift), shown on hover and keyboard focus, dismissed on blur or Escape
|
|
32
33
|
* (document-level, per WCAG 1.4.13), and hoverable: the bubble stays open while
|
|
33
34
|
* the pointer is over it (UIX-A11Y-1).
|
|
35
|
+
*
|
|
36
|
+
* While the bubble is open it takes the first Escape (HAR-2415), also on a combobox-style trigger
|
|
37
|
+
* whose list is open (a Select, a date field): one Escape closes the bubble, the next one the list.
|
|
34
38
|
*/
|
|
35
39
|
declare function Tooltip({ label, content, placement, shortcut, className, children, ...props }: TooltipProps): react.JSX.Element;
|
|
36
40
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { cloneElement, isValidElement, useEffect, useId, useRef, useState } from "react";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cloneElement, isValidElement, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
5
|
import { useAnchoredPosition } from "../hooks/useAnchoredPosition.js";
|
|
6
|
-
import { KbdCombo,
|
|
6
|
+
import { KbdCombo, isKeyShortcut, spokenKeys, useKbdPlatform } from "./Kbd.js";
|
|
7
|
+
const useIsomorphicLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;
|
|
7
8
|
function Tooltip({ label, content, placement = "top", shortcut, className, children, ...props }) {
|
|
8
9
|
const triggerRef = useRef(null);
|
|
9
10
|
const bubbleRef = useRef(null);
|
|
@@ -14,7 +15,6 @@ function Tooltip({ label, content, placement = "top", shortcut, className, child
|
|
|
14
15
|
const childShortcut = isValidElement(children) ? children.props.shortcut : void 0;
|
|
15
16
|
const own = shortcut ?? (isKeyShortcut(childShortcut) ? childShortcut : void 0);
|
|
16
17
|
const keys = own && own.length > 0 ? own : null;
|
|
17
|
-
const keyshortcuts = keys ? ariaKeyShortcuts(keys, platform) : void 0;
|
|
18
18
|
useAnchoredPosition(triggerRef, bubbleRef, { open, placement, offset: 6 });
|
|
19
19
|
const cancelClose = () => {
|
|
20
20
|
if (closeTimer.current != null) {
|
|
@@ -48,13 +48,16 @@ function Tooltip({ label, content, placement = "top", shortcut, className, child
|
|
|
48
48
|
cancelClose();
|
|
49
49
|
closeTimer.current = window.setTimeout(hide, 100);
|
|
50
50
|
};
|
|
51
|
-
|
|
52
|
-
if (!open) return;
|
|
51
|
+
useIsomorphicLayoutEffect(() => {
|
|
52
|
+
if (!open) return void 0;
|
|
53
53
|
const onKey = (e) => {
|
|
54
|
-
if (e.key
|
|
54
|
+
if (e.key !== "Escape") return;
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
e.stopPropagation();
|
|
57
|
+
hide();
|
|
55
58
|
};
|
|
56
|
-
document.addEventListener("keydown", onKey);
|
|
57
|
-
return () => document.removeEventListener("keydown", onKey);
|
|
59
|
+
document.addEventListener("keydown", onKey, true);
|
|
60
|
+
return () => document.removeEventListener("keydown", onKey, true);
|
|
58
61
|
}, [open]);
|
|
59
62
|
useEffect(() => () => {
|
|
60
63
|
if (closeTimer.current != null) clearTimeout(closeTimer.current);
|
|
@@ -67,30 +70,27 @@ function Tooltip({ label, content, placement = "top", shortcut, className, child
|
|
|
67
70
|
}
|
|
68
71
|
}, []);
|
|
69
72
|
const bubbleAttr = { popover: "manual" };
|
|
73
|
+
const text = content ?? label;
|
|
74
|
+
const description = keys ? [text != null ? `${id}-text` : null, `${id}-keys`].filter(Boolean).join(" ") : id;
|
|
70
75
|
const single = isValidElement(children);
|
|
71
76
|
const child = single ? cloneElement(children, {
|
|
72
|
-
"aria-describedby": [children.props["aria-describedby"],
|
|
73
|
-
...keyshortcuts ? { "aria-keyshortcuts": keyshortcuts } : {}
|
|
77
|
+
"aria-describedby": [children.props["aria-describedby"], description].filter(Boolean).join(" ")
|
|
74
78
|
}) : children;
|
|
75
79
|
return /* @__PURE__ */ jsxs(
|
|
76
80
|
"span",
|
|
77
81
|
{
|
|
78
82
|
ref: triggerRef,
|
|
79
83
|
className: cx("uix-tooltip", className),
|
|
80
|
-
"aria-describedby": single ? void 0 :
|
|
81
|
-
"aria-keyshortcuts": single ? void 0 : keyshortcuts,
|
|
84
|
+
"aria-describedby": single ? void 0 : description,
|
|
82
85
|
tabIndex: single ? void 0 : 0,
|
|
83
86
|
onMouseEnter: show,
|
|
84
87
|
onMouseLeave: scheduleHide,
|
|
85
88
|
onFocus: show,
|
|
86
89
|
onBlur: hide,
|
|
87
|
-
onKeyDown: (e) => {
|
|
88
|
-
if (e.key === "Escape") hide();
|
|
89
|
-
},
|
|
90
90
|
...props,
|
|
91
91
|
children: [
|
|
92
92
|
child,
|
|
93
|
-
/* @__PURE__ */
|
|
93
|
+
/* @__PURE__ */ jsx(
|
|
94
94
|
"span",
|
|
95
95
|
{
|
|
96
96
|
ref: bubbleRef,
|
|
@@ -100,10 +100,13 @@ function Tooltip({ label, content, placement = "top", shortcut, className, child
|
|
|
100
100
|
onMouseEnter: cancelClose,
|
|
101
101
|
onMouseLeave: scheduleHide,
|
|
102
102
|
...bubbleAttr,
|
|
103
|
-
children: [
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
children: keys ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
104
|
+
text != null && /* @__PURE__ */ jsx("span", { id: `${id}-text`, children: text }),
|
|
105
|
+
/* @__PURE__ */ jsxs("span", { className: "uix-tooltip__shortcut", children: [
|
|
106
|
+
/* @__PURE__ */ jsx("span", { id: `${id}-keys`, className: "uix-visually-hidden", children: spokenKeys(keys, platform) }),
|
|
107
|
+
/* @__PURE__ */ jsx(KbdCombo, { keys: [...keys], platform, "aria-hidden": "true" })
|
|
108
|
+
] })
|
|
109
|
+
] }) : text
|
|
107
110
|
}
|
|
108
111
|
)
|
|
109
112
|
]
|
|
@@ -6,6 +6,12 @@ interface TreeNodeData {
|
|
|
6
6
|
label: ReactNode;
|
|
7
7
|
/** Plain-text label for keyboard typeahead when `label` is not a string (UIX-A11Y-2). */
|
|
8
8
|
typeaheadLabel?: string;
|
|
9
|
+
/**
|
|
10
|
+
* The row's accessible name when `label` is rich (an avatar, a name and a meta line) and only
|
|
11
|
+
* part of it should be spoken, e.g. the person's name (HAR-1828). By default a row is named by
|
|
12
|
+
* its own `label`, never by its children.
|
|
13
|
+
*/
|
|
14
|
+
ariaLabel?: string;
|
|
9
15
|
icon?: ReactNode;
|
|
10
16
|
children?: TreeNodeData[];
|
|
11
17
|
/**
|
|
@@ -39,6 +45,8 @@ interface TreeNodeProps {
|
|
|
39
45
|
rowToggles?: boolean;
|
|
40
46
|
/** Ids of nodes whose children are loading (`aria-busy`). */
|
|
41
47
|
busy?: ReadonlySet<string>;
|
|
48
|
+
/** Scopes the ids that name each row by its own label (HAR-1828). Set by `Tree`. */
|
|
49
|
+
treeId?: string;
|
|
42
50
|
}
|
|
43
51
|
interface TreeProps extends Omit<HTMLAttributes<HTMLUListElement>, 'onSelect' | 'onChange'> {
|
|
44
52
|
nodes: TreeNodeData[];
|
package/dist/components/Tree.js
CHANGED
|
@@ -15,13 +15,16 @@ const LOADING = "::uix-loading";
|
|
|
15
15
|
const FAILED = "::uix-error";
|
|
16
16
|
const syntheticOf = (id) => id.endsWith(LOADING) ? { parent: id.slice(0, -LOADING.length), kind: "loading" } : id.endsWith(FAILED) ? { parent: id.slice(0, -FAILED.length), kind: "error" } : null;
|
|
17
17
|
const Ring = () => /* @__PURE__ */ jsx("span", { className: "uix-spinner uix-spinner--sm", "aria-hidden": "true" });
|
|
18
|
-
|
|
18
|
+
const labelId = (treeId, nodeId) => `${treeId}-label-${encodeURIComponent(nodeId)}`;
|
|
19
|
+
const rowName = (treeId, node) => node.ariaLabel ? { "aria-label": node.ariaLabel } : { "aria-labelledby": labelId(treeId, node.id) };
|
|
20
|
+
function TreeNode({ node, expanded, selected, selectable, tabbableId, onToggle, onSelect, onFocusNode, level = 1, rowToggles = true, busy, treeId = "uix-tree" }) {
|
|
19
21
|
const hasChildren = Array.isArray(node.children) && node.children.length > 0;
|
|
20
22
|
const isExpanded = expanded.has(node.id);
|
|
21
23
|
return /* @__PURE__ */ jsxs(
|
|
22
24
|
"li",
|
|
23
25
|
{
|
|
24
26
|
role: "treeitem",
|
|
27
|
+
...rowName(treeId, node),
|
|
25
28
|
"aria-level": level,
|
|
26
29
|
"aria-expanded": hasChildren ? isExpanded : void 0,
|
|
27
30
|
"aria-selected": selectable && !syntheticOf(node.id) ? selected === node.id : void 0,
|
|
@@ -61,7 +64,7 @@ function TreeNode({ node, expanded, selected, selectable, tabbableId, onToggle,
|
|
|
61
64
|
)
|
|
62
65
|
) : /* @__PURE__ */ jsx("span", { className: "uix-tree__toggle" }),
|
|
63
66
|
node.icon && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: node.icon }),
|
|
64
|
-
/* @__PURE__ */ jsx("span", { className: "uix-tree__label", children: node.label })
|
|
67
|
+
/* @__PURE__ */ jsx("span", { id: labelId(treeId, node.id), className: "uix-tree__label", children: node.label })
|
|
65
68
|
] }),
|
|
66
69
|
hasChildren && isExpanded && /* @__PURE__ */ jsx("ul", { role: "group", className: "uix-tree__group", children: node.children.map((child) => /* @__PURE__ */ jsx(
|
|
67
70
|
TreeNode,
|
|
@@ -76,7 +79,8 @@ function TreeNode({ node, expanded, selected, selectable, tabbableId, onToggle,
|
|
|
76
79
|
onFocusNode,
|
|
77
80
|
level: level + 1,
|
|
78
81
|
rowToggles,
|
|
79
|
-
busy
|
|
82
|
+
busy,
|
|
83
|
+
treeId
|
|
80
84
|
},
|
|
81
85
|
child.id
|
|
82
86
|
)) })
|
|
@@ -317,7 +321,8 @@ function Tree({
|
|
|
317
321
|
onFocusNode: setFocusedId,
|
|
318
322
|
level: 1,
|
|
319
323
|
rowToggles,
|
|
320
|
-
busy: loading
|
|
324
|
+
busy: loading,
|
|
325
|
+
treeId
|
|
321
326
|
},
|
|
322
327
|
node.id
|
|
323
328
|
)) });
|
|
@@ -418,6 +423,7 @@ function VirtualTreeView({
|
|
|
418
423
|
"li",
|
|
419
424
|
{
|
|
420
425
|
role: "treeitem",
|
|
426
|
+
...rowName(treeId, f.node),
|
|
421
427
|
"aria-level": f.level,
|
|
422
428
|
"aria-setsize": f.setsize,
|
|
423
429
|
"aria-posinset": f.posinset,
|
|
@@ -458,7 +464,7 @@ function VirtualTreeView({
|
|
|
458
464
|
)
|
|
459
465
|
) : /* @__PURE__ */ jsx("span", { className: "uix-tree__toggle" }),
|
|
460
466
|
f.node.icon && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: f.node.icon }),
|
|
461
|
-
/* @__PURE__ */ jsx("span", { className: "uix-tree__label", children: f.node.label })
|
|
467
|
+
/* @__PURE__ */ jsx("span", { id: labelId(treeId, f.node.id), className: "uix-tree__label", children: f.node.label })
|
|
462
468
|
] })
|
|
463
469
|
},
|
|
464
470
|
f.node.id
|