@tensor_1/react 2.47.0 → 2.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +470 -1
- package/dist/{SchedulingCalendar-B6TakO2h.d.ts → SchedulingCalendar-WWyFL3Eo.d.ts} +51 -2
- package/dist/announce.d.ts +10 -0
- package/dist/announce.js +30 -0
- package/dist/chart-analytic.cjs +33 -25
- package/dist/chart-preset.cjs +23 -15
- package/dist/chart.cjs +23 -15
- package/dist/combobox-model.d.ts +10 -1
- package/dist/combobox-model.js +15 -0
- package/dist/command-palette-model.d.ts +44 -0
- package/dist/command-palette-model.js +36 -0
- package/dist/components/AccountMenu.d.ts +44 -0
- package/dist/components/AccountMenu.js +43 -0
- package/dist/components/AppShell.d.ts +45 -2
- package/dist/components/AppShell.js +116 -5
- package/dist/components/AuditLog.d.ts +128 -0
- package/dist/components/AuditLog.js +196 -0
- package/dist/components/BrandProfiles.js +4 -3
- package/dist/components/Breadcrumbs.d.ts +9 -2
- package/dist/components/Breadcrumbs.js +3 -2
- package/dist/components/Button.d.ts +12 -5
- package/dist/components/Button.js +7 -3
- package/dist/components/Card.d.ts +18 -1
- package/dist/components/Card.js +5 -1
- package/dist/components/ChangeHighlight.d.ts +30 -0
- package/dist/components/ChangeHighlight.js +21 -0
- package/dist/components/ChartCore.js +2 -1
- package/dist/components/Checkbox.d.ts +27 -1
- package/dist/components/Checkbox.js +31 -2
- package/dist/components/CodeDisplay.d.ts +65 -0
- package/dist/components/CodeDisplay.js +124 -0
- package/dist/components/Combobox.d.ts +27 -6
- package/dist/components/Combobox.js +195 -58
- package/dist/components/CommandPalette.d.ts +36 -1
- package/dist/components/CommandPalette.js +254 -2
- package/dist/components/ContactCard.d.ts +66 -0
- package/dist/components/ContactCard.js +74 -0
- package/dist/components/Count.d.ts +24 -0
- package/dist/components/Count.js +32 -0
- package/dist/components/DataWorkspace.d.ts +167 -0
- package/dist/components/DataWorkspace.js +383 -0
- package/dist/components/DateFieldParts.d.ts +2 -1
- package/dist/components/DateFieldParts.js +7 -0
- package/dist/components/DatePicker.d.ts +9 -2
- package/dist/components/DatePicker.js +67 -28
- package/dist/components/DateRangePicker.d.ts +12 -2
- package/dist/components/DateRangePicker.js +13 -12
- package/dist/components/DetailPage.d.ts +7 -1
- package/dist/components/DetailPage.js +2 -1
- package/dist/components/Dialogs.d.ts +71 -10
- package/dist/components/Dialogs.js +130 -30
- package/dist/components/DiffViewer.d.ts +23 -3
- package/dist/components/DiffViewer.js +26 -12
- package/dist/components/DiscardPrompt.js +16 -0
- package/dist/components/DisplayPreferences.d.ts +83 -0
- package/dist/components/DisplayPreferences.js +185 -0
- package/dist/components/EditorialHome.d.ts +23 -3
- package/dist/components/EditorialHome.js +16 -11
- package/dist/components/EntityPicker.d.ts +20 -3
- package/dist/components/EntityPicker.js +32 -13
- package/dist/components/FilterEditor.d.ts +18 -4
- package/dist/components/FilterEditor.js +141 -19
- package/dist/components/InfoTip.js +11 -12
- package/dist/components/InlineEdit.d.ts +87 -20
- package/dist/components/InlineEdit.js +197 -54
- package/dist/components/Input.d.ts +12 -2
- package/dist/components/Input.js +5 -3
- package/dist/components/Kbd.d.ts +6 -1
- package/dist/components/Kbd.js +6 -6
- package/dist/components/Lightbox.d.ts +41 -5
- package/dist/components/Lightbox.js +95 -9
- package/dist/components/MatchReview.d.ts +40 -1
- package/dist/components/MatchReview.js +130 -4
- package/dist/components/MetricInput.js +3 -2
- package/dist/components/Modal.d.ts +2 -1
- package/dist/components/Modal.js +13 -4
- package/dist/components/NavFavourites.d.ts +5 -0
- package/dist/components/NavFavourites.js +7 -7
- package/dist/components/OtpInput.d.ts +33 -0
- package/dist/components/OtpInput.js +123 -0
- package/dist/components/PageHeader.d.ts +7 -1
- package/dist/components/PageHeader.js +2 -2
- package/dist/components/Pagination.js +6 -5
- package/dist/components/PasswordInput.d.ts +34 -0
- package/dist/components/PasswordInput.js +101 -0
- package/dist/components/Peek.js +3 -3
- package/dist/components/Popover.js +2 -0
- package/dist/components/PrintLayout.d.ts +63 -0
- package/dist/components/PrintLayout.js +114 -0
- package/dist/components/QrCode.d.ts +47 -0
- package/dist/components/QrCode.js +55 -0
- package/dist/components/QrScanner.d.ts +95 -0
- package/dist/components/QrScanner.js +266 -0
- package/dist/components/RelationshipGraph.d.ts +32 -1
- package/dist/components/RelationshipGraph.js +18 -5
- package/dist/components/RelationshipGraphLayered.d.ts +1 -1
- package/dist/components/RelationshipGraphLayered.js +190 -115
- package/dist/components/RelationshipGraphList.js +33 -6
- package/dist/components/RuleBuilder.js +39 -7
- package/dist/components/SchedulingAgenda.d.ts +3 -1
- package/dist/components/SchedulingCalendar.d.ts +3 -1
- package/dist/components/SchedulingCalendar.js +43 -4
- package/dist/components/SchedulingResourceDay.d.ts +57 -0
- package/dist/components/SchedulingResourceDay.js +287 -0
- package/dist/components/SchedulingTimeGrid.d.ts +3 -1
- package/dist/components/SchedulingTimeline.d.ts +3 -1
- package/dist/components/SearchSuggest.d.ts +23 -3
- package/dist/components/SearchSuggest.js +48 -17
- package/dist/components/Segmented.d.ts +39 -4
- package/dist/components/Segmented.js +31 -3
- package/dist/components/Select.d.ts +3 -2
- package/dist/components/Select.js +1 -1
- package/dist/components/ShortcutHint.d.ts +11 -10
- package/dist/components/ShortcutHint.js +44 -38
- package/dist/components/Sidebar.d.ts +36 -4
- package/dist/components/Sidebar.js +37 -12
- package/dist/components/SortableList.d.ts +45 -0
- package/dist/components/SortableList.js +134 -0
- package/dist/components/Stat.d.ts +28 -3
- package/dist/components/Stat.js +13 -3
- package/dist/components/StatusPill.d.ts +14 -3
- package/dist/components/StatusPill.js +11 -3
- package/dist/components/Steps.js +2 -1
- package/dist/components/Table.d.ts +9 -2
- package/dist/components/Table.js +4 -2
- package/dist/components/TableControls.js +18 -58
- package/dist/components/TableOverflowProbe.d.ts +12 -0
- package/dist/components/TableOverflowProbe.js +27 -0
- package/dist/components/Tabs.d.ts +2 -1
- package/dist/components/Tabs.js +1 -0
- package/dist/components/TagInput.d.ts +67 -0
- package/dist/components/TagInput.js +342 -0
- package/dist/components/Textarea.d.ts +5 -0
- package/dist/components/Textarea.js +2 -2
- package/dist/components/TimePicker.d.ts +80 -0
- package/dist/components/TimePicker.js +347 -0
- package/dist/components/Toast.js +3 -2
- package/dist/components/Tooltip.d.ts +8 -4
- package/dist/components/Tooltip.js +24 -21
- package/dist/components/Tree.d.ts +8 -0
- package/dist/components/Tree.js +11 -5
- package/dist/display-preferences-model.d.ts +53 -0
- package/dist/display-preferences-model.js +64 -0
- package/dist/emoji.cjs +28 -35
- package/dist/filter-model.d.ts +59 -3
- package/dist/filter-model.js +67 -2
- package/dist/hooks/useChangeHighlight.d.ts +33 -0
- package/dist/hooks/useChangeHighlight.js +44 -0
- package/dist/hooks/useDialog.d.ts +5 -0
- package/dist/hooks/useDialog.js +3 -2
- package/dist/hooks/useOptimisticValue.d.ts +51 -0
- package/dist/hooks/useOptimisticValue.js +61 -0
- package/dist/hooks/useReducedMotion.d.ts +4 -1
- package/dist/hooks/useReducedMotion.js +7 -1
- package/dist/hooks/useResolvedFilterLabels.d.ts +15 -0
- package/dist/hooks/useResolvedFilterLabels.js +34 -0
- package/dist/hooks/useVirtualRows.d.ts +21 -2
- package/dist/hooks/useVirtualRows.js +30 -3
- package/dist/icons/Icon.d.ts +1 -1
- package/dist/index.cjs +18644 -13210
- package/dist/index.d.cts +3003 -699
- package/dist/index.d.ts +42 -16
- package/dist/index.js +139 -7
- package/dist/labels-context.d.ts +90 -3
- package/dist/overlay-stack.d.ts +6 -0
- package/dist/overlay-stack.js +13 -0
- package/dist/qr-model.d.ts +65 -0
- package/dist/qr-model.js +323 -0
- package/dist/relationship-graph-model.d.ts +78 -3
- package/dist/relationship-graph-model.js +112 -5
- package/dist/reorder.d.ts +30 -0
- package/dist/reorder.js +74 -0
- package/dist/rich-text.cjs +4 -2
- package/dist/scheduling-resource-model.d.ts +76 -0
- package/dist/scheduling-resource-model.js +74 -0
- package/dist/search-suggest-model.js +3 -1
- package/dist/shell-context.d.ts +11 -0
- package/dist/shell-context.js +7 -0
- package/dist/time-field-model.d.ts +46 -0
- package/dist/time-field-model.js +152 -0
- package/dist/toast-store.d.ts +17 -1
- package/dist/toast-store.js +23 -7
- package/package.json +2 -1
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useId, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { cx } from "../cx.js";
|
|
5
|
+
import { fillLabel } from "../fill-label.js";
|
|
6
|
+
import { useUixLabels } from "../labels-context.js";
|
|
7
|
+
import { renderUixLink } from "../link.js";
|
|
8
|
+
import { isFilterEmpty, summarizeFilter } from "../filter-model.js";
|
|
9
|
+
import { useResolvedFilterLabels } from "../hooks/useResolvedFilterLabels.js";
|
|
10
|
+
import { BusyRegion } from "./BusyRegion.js";
|
|
11
|
+
import { Button } from "./Button.js";
|
|
12
|
+
import { Checkbox } from "./Checkbox.js";
|
|
13
|
+
import { Chip } from "./Chip.js";
|
|
14
|
+
import { FilterEditor } from "./FilterEditor.js";
|
|
15
|
+
import { Menu, MenuItem } from "./Menu.js";
|
|
16
|
+
import { Pagination } from "./Pagination.js";
|
|
17
|
+
import { Popover } from "./Popover.js";
|
|
18
|
+
import { SearchBar } from "./SearchBar.js";
|
|
19
|
+
import { Select } from "./Select.js";
|
|
20
|
+
import { EmptyState, ErrorState, FilteredEmptyState, LoadingState } from "./States.js";
|
|
21
|
+
import { BulkBar, RowActions, Table, TableWrap, Td, Th, Tr } from "./Table.js";
|
|
22
|
+
import { SavedViewMenu, ViewMenu } from "./TableControls.js";
|
|
23
|
+
const DEFAULT_DATA_WORKSPACE_LABELS = {
|
|
24
|
+
search: "Search",
|
|
25
|
+
searchPlaceholder: "Search\u2026",
|
|
26
|
+
addFilter: "Filter",
|
|
27
|
+
clearFilters: "Clear filters",
|
|
28
|
+
savedView: "View: {name}",
|
|
29
|
+
view: "View",
|
|
30
|
+
saveView: "Save changes",
|
|
31
|
+
saveViewAs: "Save as\u2026",
|
|
32
|
+
maximize: "Maximize",
|
|
33
|
+
restore: "Restore size",
|
|
34
|
+
selectAll: "Select all rows on this page",
|
|
35
|
+
selectRow: "Select {label}",
|
|
36
|
+
selected: "{count} selected",
|
|
37
|
+
bulkActions: "Bulk actions",
|
|
38
|
+
open: "Open {label}",
|
|
39
|
+
rowActions: "Actions",
|
|
40
|
+
range: "{from}\u2013{to} of {total}",
|
|
41
|
+
rowsPerPage: "Rows per page",
|
|
42
|
+
loading: "Loading",
|
|
43
|
+
empty: "Nothing here yet",
|
|
44
|
+
emptyDetail: "Records show here as soon as they exist.",
|
|
45
|
+
filteredEmpty: "Nothing matches",
|
|
46
|
+
filteredEmptyDetail: "Change the search or clear a filter.",
|
|
47
|
+
error: "The list could not be loaded",
|
|
48
|
+
retry: "Try again"
|
|
49
|
+
};
|
|
50
|
+
const ArrowUpRight = () => /* @__PURE__ */ jsx("svg", { className: "uix-id-cell__arrow", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: "M7 17 17 7M8 7h9v9" }) });
|
|
51
|
+
const PlusIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: "M12 5v14M5 12h14" }) });
|
|
52
|
+
const MaximizeIcon = ({ restore }) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: restore ? "M4 14h6v6M20 10h-6V4M14 10l7-7M3 21l7-7" : "M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7" }) });
|
|
53
|
+
function DataWorkspace({
|
|
54
|
+
label,
|
|
55
|
+
rows,
|
|
56
|
+
getRowId,
|
|
57
|
+
columns,
|
|
58
|
+
idColumn,
|
|
59
|
+
sort,
|
|
60
|
+
onSortChange,
|
|
61
|
+
selection,
|
|
62
|
+
onSelectionChange,
|
|
63
|
+
bulkActions,
|
|
64
|
+
rowActions,
|
|
65
|
+
getRowProps,
|
|
66
|
+
density,
|
|
67
|
+
search,
|
|
68
|
+
filters,
|
|
69
|
+
savedViews,
|
|
70
|
+
viewMenu,
|
|
71
|
+
viewChanged = false,
|
|
72
|
+
onSaveView,
|
|
73
|
+
onSaveViewAs,
|
|
74
|
+
maximized = false,
|
|
75
|
+
onMaximizedChange,
|
|
76
|
+
toolbarEnd,
|
|
77
|
+
pagination,
|
|
78
|
+
status = "ready",
|
|
79
|
+
refreshing = false,
|
|
80
|
+
onRetry,
|
|
81
|
+
labels: labelOverrides,
|
|
82
|
+
className,
|
|
83
|
+
...props
|
|
84
|
+
}) {
|
|
85
|
+
const labels = { ...DEFAULT_DATA_WORKSPACE_LABELS, ...useUixLabels().dataWorkspace, ...labelOverrides };
|
|
86
|
+
const id = useId();
|
|
87
|
+
const searchRef = useRef(null);
|
|
88
|
+
const rootRef = useRef(null);
|
|
89
|
+
const anchor = useRef(null);
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!search) return;
|
|
92
|
+
const onKey = (event) => {
|
|
93
|
+
if (event.key !== "/" || !(event.ctrlKey || event.metaKey) || event.altKey || event.defaultPrevented) return;
|
|
94
|
+
const root = rootRef.current;
|
|
95
|
+
const target = event.target;
|
|
96
|
+
const inside = root != null && target != null && root.contains(target);
|
|
97
|
+
const unfocused = target === document.body || target === document.documentElement || target === document;
|
|
98
|
+
if (inside || unfocused && root != null && document.querySelector(".uix-data-workspace") === root) {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
searchRef.current?.focus();
|
|
101
|
+
searchRef.current?.select();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
window.addEventListener("keydown", onKey);
|
|
105
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
106
|
+
}, [search != null]);
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (!maximized || !onMaximizedChange) return;
|
|
109
|
+
const onKey = (event) => {
|
|
110
|
+
if (event.key !== "Escape" || event.defaultPrevented) return;
|
|
111
|
+
const root = rootRef.current;
|
|
112
|
+
const target = event.target;
|
|
113
|
+
const owner = target?.closest?.('[popover], [role="dialog"], [role="menu"], [role="listbox"], [aria-expanded="true"]');
|
|
114
|
+
if (owner && root?.contains(owner)) return;
|
|
115
|
+
if (root && openInside(root)) return;
|
|
116
|
+
onMaximizedChange(false);
|
|
117
|
+
};
|
|
118
|
+
window.addEventListener("keydown", onKey);
|
|
119
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
120
|
+
}, [maximized, onMaximizedChange]);
|
|
121
|
+
const filtered = (filters ? filters.fields.some((field) => !isFilterEmpty(filters.values[field.id])) : false) || (search?.value.trim() ?? "") !== "";
|
|
122
|
+
const [settled, setSettled] = useState({ rows, filtered });
|
|
123
|
+
if (!refreshing && rows !== settled.rows) setSettled({ rows, filtered });
|
|
124
|
+
const view = rows.length > 0 ? { rows, filtered } : refreshing || rows === settled.rows ? settled : { rows, filtered };
|
|
125
|
+
const shownRows = view.rows;
|
|
126
|
+
const ids = useMemo(() => shownRows.map(getRowId), [shownRows, getRowId]);
|
|
127
|
+
const selected = selection ?? /* @__PURE__ */ new Set();
|
|
128
|
+
const selectable = onSelectionChange != null;
|
|
129
|
+
const onPage = ids.filter((rowId) => selected.has(rowId)).length;
|
|
130
|
+
const toggleRow = (rowId, on, event) => {
|
|
131
|
+
if (!onSelectionChange) return;
|
|
132
|
+
const next = new Set(selected);
|
|
133
|
+
const from = anchor.current ? ids.indexOf(anchor.current) : -1;
|
|
134
|
+
const to = ids.indexOf(rowId);
|
|
135
|
+
if (event?.shiftKey && from >= 0 && to >= 0) {
|
|
136
|
+
for (let i = Math.min(from, to); i <= Math.max(from, to); i += 1) {
|
|
137
|
+
if (on) next.add(ids[i]);
|
|
138
|
+
else next.delete(ids[i]);
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
if (on) next.add(rowId);
|
|
142
|
+
else next.delete(rowId);
|
|
143
|
+
anchor.current = rowId;
|
|
144
|
+
}
|
|
145
|
+
onSelectionChange(next);
|
|
146
|
+
};
|
|
147
|
+
const toggleAll = (on) => {
|
|
148
|
+
if (!onSelectionChange) return;
|
|
149
|
+
const next = new Set(selected);
|
|
150
|
+
for (const rowId of ids) {
|
|
151
|
+
if (on) next.add(rowId);
|
|
152
|
+
else next.delete(rowId);
|
|
153
|
+
}
|
|
154
|
+
onSelectionChange(next);
|
|
155
|
+
};
|
|
156
|
+
const actionsByRow = useMemo(() => rowActions ? shownRows.map((row) => rowActions(row)) : [], [shownRows, rowActions]);
|
|
157
|
+
const hasActions = actionsByRow.some((node) => node != null && node !== false);
|
|
158
|
+
const activeFilters = filters ? filters.fields.filter((field) => !isFilterEmpty(filters.values[field.id])) : [];
|
|
159
|
+
const searching = (search?.value.trim() ?? "") !== "";
|
|
160
|
+
const clearAll = () => {
|
|
161
|
+
for (const field of activeFilters) filters?.onChange(field.id, void 0);
|
|
162
|
+
if (searching) search?.onChange("");
|
|
163
|
+
};
|
|
164
|
+
const sortFor = (column) => sort?.columnId === column.id ? sort.direction : "none";
|
|
165
|
+
const cycleSort = (column) => {
|
|
166
|
+
const current = sortFor(column);
|
|
167
|
+
onSortChange?.(current === "none" ? { columnId: column.id, direction: "ascending" } : current === "ascending" ? { columnId: column.id, direction: "descending" } : null);
|
|
168
|
+
};
|
|
169
|
+
const toolbar = /* @__PURE__ */ jsxs("div", { className: "uix-toolbar uix-data-workspace__toolbar", role: "toolbar", "aria-label": label, children: [
|
|
170
|
+
savedViews && /* @__PURE__ */ jsx(PopoverButton, { id: `${id}-views`, label: savedViews.current, title: typeof savedViews.current === "string" ? fillLabel(labels.savedView, { name: savedViews.current }) : void 0, variant: "secondary", children: (close) => /* @__PURE__ */ jsx(SavedViewMenu, { ...savedViews.menu, onSelect: (viewId) => {
|
|
171
|
+
savedViews.menu.onSelect(viewId);
|
|
172
|
+
close();
|
|
173
|
+
} }) }),
|
|
174
|
+
viewChanged && onSaveView && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: onSaveView, children: labels.saveView }),
|
|
175
|
+
viewChanged && onSaveViewAs && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: onSaveViewAs, children: labels.saveViewAs }),
|
|
176
|
+
filters && /* @__PURE__ */ jsx(FilterChips, { filters, labels, baseId: id }),
|
|
177
|
+
/* @__PURE__ */ jsx("span", { className: "uix-toolbar__spacer" }),
|
|
178
|
+
search && /* @__PURE__ */ jsx(
|
|
179
|
+
SearchBar,
|
|
180
|
+
{
|
|
181
|
+
className: "uix-data-workspace__search",
|
|
182
|
+
landmark: false,
|
|
183
|
+
label: search.label ?? labels.search,
|
|
184
|
+
placeholder: search.placeholder ?? labels.searchPlaceholder,
|
|
185
|
+
value: search.value,
|
|
186
|
+
onValueChange: search.onChange,
|
|
187
|
+
inputRef: searchRef
|
|
188
|
+
}
|
|
189
|
+
),
|
|
190
|
+
viewMenu && /* @__PURE__ */ jsx(PopoverButton, { id: `${id}-view`, label: labels.view, variant: "ghost", children: () => /* @__PURE__ */ jsx(ViewMenu, { ...viewMenu }) }),
|
|
191
|
+
onMaximizedChange && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", icon: true, "aria-label": maximized ? labels.restore : labels.maximize, "aria-pressed": maximized, onClick: () => onMaximizedChange(!maximized), children: /* @__PURE__ */ jsx(MaximizeIcon, { restore: maximized }) }),
|
|
192
|
+
toolbarEnd
|
|
193
|
+
] });
|
|
194
|
+
let body;
|
|
195
|
+
if (status === "loading") body = /* @__PURE__ */ jsx(LoadingState, { rows: 6, label: labels.loading, className: "uix-data-workspace__loading" });
|
|
196
|
+
else if (status === "error") body = /* @__PURE__ */ jsx(ErrorState, { title: labels.error, action: onRetry && /* @__PURE__ */ jsx(Button, { size: "sm", onClick: onRetry, children: labels.retry }) });
|
|
197
|
+
else if (shownRows.length === 0) {
|
|
198
|
+
body = /* @__PURE__ */ jsx(BusyRegion, { busy: refreshing, className: "uix-data-workspace__state", children: view.filtered ? /* @__PURE__ */ jsx(FilteredEmptyState, { title: labels.filteredEmpty, action: /* @__PURE__ */ jsx(Button, { size: "sm", onClick: clearAll, children: labels.clearFilters }), children: labels.filteredEmptyDetail }) : /* @__PURE__ */ jsx(EmptyState, { title: labels.empty, children: labels.emptyDetail }) });
|
|
199
|
+
} else {
|
|
200
|
+
body = /* @__PURE__ */ jsx(TableWrap, { refreshing, className: "uix-data-workspace__table", children: /* @__PURE__ */ jsxs(Table, { density, pinnedCol: idColumn != null, pinnedActions: hasActions, className: cx(selectable && idColumn && "uix-data-workspace__grid--pin-id"), "aria-label": label, children: [
|
|
201
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
202
|
+
selectable && /* @__PURE__ */ jsx("th", { className: "uix-table__selcol", scope: "col", children: /* @__PURE__ */ jsx(Checkbox, { "aria-label": labels.selectAll, checked: onPage > 0 && onPage === ids.length, indeterminate: onPage > 0 && onPage < ids.length, onChange: (event) => toggleAll(event.currentTarget.checked) }) }),
|
|
203
|
+
idColumn && /* @__PURE__ */ jsx(Th, { scope: "col", children: idColumn.header }),
|
|
204
|
+
columns.map((column) => /* @__PURE__ */ jsx(
|
|
205
|
+
Th,
|
|
206
|
+
{
|
|
207
|
+
scope: "col",
|
|
208
|
+
className: column.align === "end" ? "uix-col--num" : void 0,
|
|
209
|
+
style: column.width ? { width: column.width } : void 0,
|
|
210
|
+
sortable: column.sortable && onSortChange != null,
|
|
211
|
+
sortDirection: sortFor(column),
|
|
212
|
+
onSort: () => cycleSort(column),
|
|
213
|
+
children: column.header
|
|
214
|
+
},
|
|
215
|
+
column.id
|
|
216
|
+
)),
|
|
217
|
+
hasActions && /* @__PURE__ */ jsx("th", { scope: "col", className: "uix-table__actions", children: /* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: labels.rowActions }) })
|
|
218
|
+
] }) }),
|
|
219
|
+
/* @__PURE__ */ jsx("tbody", { children: shownRows.map((row, index) => {
|
|
220
|
+
const rowId = ids[index];
|
|
221
|
+
const text = idColumn?.text(row) ?? rowId;
|
|
222
|
+
const isSelected = selected.has(rowId);
|
|
223
|
+
return /* @__PURE__ */ jsxs(Tr, { selected: isSelected, ...getRowProps?.(row), children: [
|
|
224
|
+
selectable && /* @__PURE__ */ jsx("td", { className: "uix-table__selcell", children: /* @__PURE__ */ jsx(
|
|
225
|
+
Checkbox,
|
|
226
|
+
{
|
|
227
|
+
"aria-label": fillLabel(labels.selectRow, { label: text }),
|
|
228
|
+
checked: isSelected,
|
|
229
|
+
onClick: (event) => toggleRow(rowId, event.currentTarget.checked, event),
|
|
230
|
+
onChange: () => {
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
) }),
|
|
234
|
+
idColumn && /* @__PURE__ */ jsx(Td, { className: "uix-id-cell", children: /* @__PURE__ */ jsxs("span", { className: "uix-data-workspace__id", children: [
|
|
235
|
+
idColumn.onPeek ? /* @__PURE__ */ jsx("button", { type: "button", className: "uix-id-cell__btn", "aria-haspopup": "dialog", onClick: () => idColumn.onPeek?.(row), children: text }) : /* @__PURE__ */ jsx("span", { className: "uix-cell-strong", children: text }),
|
|
236
|
+
idColumn.href && renderUixLink(idColumn.renderLink, {
|
|
237
|
+
href: idColumn.href(row),
|
|
238
|
+
className: "uix-data-workspace__open",
|
|
239
|
+
"aria-label": fillLabel(labels.open, { label: text }),
|
|
240
|
+
children: /* @__PURE__ */ jsx(ArrowUpRight, {})
|
|
241
|
+
})
|
|
242
|
+
] }) }),
|
|
243
|
+
columns.map((column) => /* @__PURE__ */ jsx(Td, { className: column.align === "end" ? "uix-col--num" : void 0, children: column.cell(row) }, column.id)),
|
|
244
|
+
hasActions && /* @__PURE__ */ jsx(RowActions, { children: actionsByRow[index] })
|
|
245
|
+
] }, rowId);
|
|
246
|
+
}) })
|
|
247
|
+
] }) });
|
|
248
|
+
}
|
|
249
|
+
const selectedIds = [...selected];
|
|
250
|
+
return /* @__PURE__ */ jsxs(
|
|
251
|
+
"section",
|
|
252
|
+
{
|
|
253
|
+
ref: rootRef,
|
|
254
|
+
className: cx("uix-data-workspace", maximized && "uix-data-workspace--maximized", className),
|
|
255
|
+
"aria-label": label,
|
|
256
|
+
...props,
|
|
257
|
+
children: [
|
|
258
|
+
toolbar,
|
|
259
|
+
bulkActions && selectedIds.length > 0 && /* @__PURE__ */ jsx(BulkBar, { count: selectedIds.length, label: labels.bulkActions, selectedLabel: (n) => fillLabel(labels.selected, { count: n }), children: bulkActions(selectedIds) }),
|
|
260
|
+
body,
|
|
261
|
+
pagination && status === "ready" && shownRows.length > 0 && /* @__PURE__ */ jsx(Footer, { pagination, labels, rowCount: shownRows.length })
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
function Footer({ pagination, labels, rowCount }) {
|
|
267
|
+
const sizes = pagination.pageSizes;
|
|
268
|
+
const size = pagination.mode === "offset" ? pagination.pageSize : pagination.pageSize;
|
|
269
|
+
const rangeText = pagination.mode === "offset" ? fillLabel(labels.range, {
|
|
270
|
+
from: pagination.total === 0 ? 0 : (pagination.page - 1) * pagination.pageSize + 1,
|
|
271
|
+
to: Math.min(pagination.total, (pagination.page - 1) * pagination.pageSize + rowCount),
|
|
272
|
+
total: pagination.total
|
|
273
|
+
}) : null;
|
|
274
|
+
return /* @__PURE__ */ jsxs("div", { className: "uix-data-workspace__footer", children: [
|
|
275
|
+
rangeText != null && /* @__PURE__ */ jsx("span", { className: "uix-data-workspace__range", children: rangeText }),
|
|
276
|
+
sizes && sizes.length > 1 && size != null && pagination.onPageSizeChange && /* @__PURE__ */ jsxs("label", { className: "uix-data-workspace__size", children: [
|
|
277
|
+
/* @__PURE__ */ jsx("span", { children: labels.rowsPerPage }),
|
|
278
|
+
/* @__PURE__ */ jsx(Select, { size: "sm", value: String(size), onValueChange: (v) => pagination.onPageSizeChange?.(Number(v)), options: sizes.map((s) => ({ value: String(s), label: String(s) })) })
|
|
279
|
+
] }),
|
|
280
|
+
/* @__PURE__ */ jsx("span", { className: "uix-toolbar__spacer" }),
|
|
281
|
+
pagination.mode === "offset" ? /* @__PURE__ */ jsx(Pagination, { mode: "offset", page: pagination.page, pageCount: Math.max(1, Math.ceil(pagination.total / pagination.pageSize)), onChange: pagination.onPageChange }) : /* @__PURE__ */ jsx(Pagination, { mode: "cursor", hasPrevious: pagination.hasPrevious, hasNext: pagination.hasNext, onFirst: pagination.onFirst, onPrevious: pagination.onPrevious, onNext: pagination.onNext, summary: pagination.summary })
|
|
282
|
+
] });
|
|
283
|
+
}
|
|
284
|
+
function PopoverButton({ id, label, title, variant, children }) {
|
|
285
|
+
const anchor = useRef(null);
|
|
286
|
+
const [open, setOpen] = useState(false);
|
|
287
|
+
const popover = () => document.getElementById(id);
|
|
288
|
+
const close = () => {
|
|
289
|
+
popover()?.hidePopover?.();
|
|
290
|
+
anchor.current?.querySelector("button")?.focus();
|
|
291
|
+
};
|
|
292
|
+
useEffect(() => {
|
|
293
|
+
const el = popover();
|
|
294
|
+
if (!el) return;
|
|
295
|
+
const onToggle = (event) => setOpen(event.newState === "open");
|
|
296
|
+
el.addEventListener("toggle", onToggle);
|
|
297
|
+
return () => el.removeEventListener("toggle", onToggle);
|
|
298
|
+
});
|
|
299
|
+
return /* @__PURE__ */ jsxs("span", { ref: anchor, className: "uix-data-workspace__menu", children: [
|
|
300
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", variant, title, "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": id, onClick: () => popover()?.togglePopover?.(), children: label }),
|
|
301
|
+
/* @__PURE__ */ jsx(Popover, { id, anchor, role: "dialog", "aria-label": title ?? (typeof label === "string" ? label : void 0), children: children(close) })
|
|
302
|
+
] });
|
|
303
|
+
}
|
|
304
|
+
function ChipSummary({ field, value, options }) {
|
|
305
|
+
const resolved = useResolvedFilterLabels(field, value);
|
|
306
|
+
return /* @__PURE__ */ jsx(Fragment, { children: summarizeFilter(resolved.field, resolved.value, options) });
|
|
307
|
+
}
|
|
308
|
+
function openInside(root) {
|
|
309
|
+
try {
|
|
310
|
+
if (root.querySelector(":popover-open")) return true;
|
|
311
|
+
} catch {
|
|
312
|
+
}
|
|
313
|
+
return root.querySelector('dialog[open], [aria-expanded="true"]') != null;
|
|
314
|
+
}
|
|
315
|
+
function FilterChips({ filters, labels, baseId }) {
|
|
316
|
+
const [editing, setEditing] = useState(null);
|
|
317
|
+
const [draft, setDraft] = useState();
|
|
318
|
+
const [focusChip, setFocusChip] = useState(null);
|
|
319
|
+
const popId = `${baseId}-filter`;
|
|
320
|
+
const anchors = useRef(/* @__PURE__ */ new Map());
|
|
321
|
+
const addRef = useRef(null);
|
|
322
|
+
const set = filters.fields.filter((f) => !isFilterEmpty(filters.values[f.id]));
|
|
323
|
+
const unset = filters.fields.filter((f) => isFilterEmpty(filters.values[f.id]));
|
|
324
|
+
const edit = (field, anchor) => {
|
|
325
|
+
if (!anchor) return;
|
|
326
|
+
setDraft(filters.values[field.id]);
|
|
327
|
+
setEditing({ field, anchor });
|
|
328
|
+
};
|
|
329
|
+
useEffect(() => {
|
|
330
|
+
const el = document.getElementById(popId);
|
|
331
|
+
if (!el || !editing) return;
|
|
332
|
+
if (!el.matches(":popover-open")) el.showPopover?.();
|
|
333
|
+
const onToggle = (event) => {
|
|
334
|
+
if (event.newState === "closed") setEditing(null);
|
|
335
|
+
};
|
|
336
|
+
el.addEventListener("toggle", onToggle);
|
|
337
|
+
return () => el.removeEventListener("toggle", onToggle);
|
|
338
|
+
}, [editing, popId]);
|
|
339
|
+
const finish = (value) => {
|
|
340
|
+
if (!editing) return;
|
|
341
|
+
const applied = value && !isFilterEmpty(value) ? value : void 0;
|
|
342
|
+
filters.onChange(editing.field.id, applied);
|
|
343
|
+
const back = anchors.current.get(editing.field.id) ?? addRef.current?.querySelector("button");
|
|
344
|
+
document.getElementById(popId)?.hidePopover?.();
|
|
345
|
+
(back?.querySelector?.("button") ?? back)?.focus?.();
|
|
346
|
+
if (applied && !anchors.current.get(editing.field.id)) setFocusChip(editing.field.id);
|
|
347
|
+
};
|
|
348
|
+
useEffect(() => {
|
|
349
|
+
if (!focusChip) return;
|
|
350
|
+
const chip = anchors.current.get(focusChip)?.querySelector("button");
|
|
351
|
+
if (chip) {
|
|
352
|
+
chip.focus();
|
|
353
|
+
setFocusChip(null);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
357
|
+
set.map((field) => /* @__PURE__ */ jsx("span", { ref: (el) => {
|
|
358
|
+
if (el) anchors.current.set(field.id, el);
|
|
359
|
+
else anchors.current.delete(field.id);
|
|
360
|
+
}, className: "uix-data-workspace__chip", children: /* @__PURE__ */ jsx(
|
|
361
|
+
Chip,
|
|
362
|
+
{
|
|
363
|
+
"aria-haspopup": "dialog",
|
|
364
|
+
count: filters.counts?.[field.id],
|
|
365
|
+
onClick: () => edit(field, anchors.current.get(field.id) ?? null),
|
|
366
|
+
onRemove: () => filters.onChange(field.id, void 0),
|
|
367
|
+
children: /* @__PURE__ */ jsx(ChipSummary, { field, value: filters.values[field.id], options: filters.summary })
|
|
368
|
+
}
|
|
369
|
+
) }, field.id)),
|
|
370
|
+
unset.length > 0 && /* @__PURE__ */ jsx("span", { ref: addRef, children: /* @__PURE__ */ jsx(Menu, { trigger: /* @__PURE__ */ jsxs("button", { type: "button", className: "uix-chip uix-chip--add", children: [
|
|
371
|
+
/* @__PURE__ */ jsx("span", { className: "uix-chip__icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(PlusIcon, {}) }),
|
|
372
|
+
/* @__PURE__ */ jsx("span", { className: "uix-chip__label", children: labels.addFilter })
|
|
373
|
+
] }), children: unset.map((field) => /* @__PURE__ */ jsx(MenuItem, { onSelect: () => edit(field, addRef.current), children: field.label }, field.id)) }) }),
|
|
374
|
+
set.length > 0 && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "link", onClick: () => {
|
|
375
|
+
for (const f of set) filters.onChange(f.id, void 0);
|
|
376
|
+
}, children: labels.clearFilters }),
|
|
377
|
+
/* @__PURE__ */ jsx(Popover, { id: popId, anchor: editing?.anchor ?? null, role: "dialog", "aria-label": editing?.field.label, children: editing && /* @__PURE__ */ jsx(FilterEditor, { field: editing.field, value: draft, onValueChange: setDraft, onApply: (v) => finish(v), onClear: () => finish(void 0) }) })
|
|
378
|
+
] });
|
|
379
|
+
}
|
|
380
|
+
export {
|
|
381
|
+
DEFAULT_DATA_WORKSPACE_LABELS,
|
|
382
|
+
DataWorkspace
|
|
383
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { MouseEvent } from 'react';
|
|
2
|
+
import { KeyboardEvent, MouseEvent } from 'react';
|
|
3
3
|
import { CalendarWeekday } from '../calendar-model.js';
|
|
4
4
|
|
|
5
5
|
/** The viewer's local calendar date as `YYYY-MM-DD` (not UTC: "today" is a local idea). */
|
|
@@ -20,6 +20,7 @@ declare function useFieldPopover(popoverId: string, restoreFocus: () => void, on
|
|
|
20
20
|
open: boolean;
|
|
21
21
|
show: () => void;
|
|
22
22
|
hide: (restore?: boolean) => void;
|
|
23
|
+
onFieldKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
|
|
23
24
|
/** Spread on the button that opens the calendar. */
|
|
24
25
|
triggerProps: {
|
|
25
26
|
'aria-haspopup': "dialog";
|
|
@@ -62,10 +62,17 @@ function useFieldPopover(popoverId, restoreFocus, onOpenChange) {
|
|
|
62
62
|
skipRestore.current = false;
|
|
63
63
|
}
|
|
64
64
|
};
|
|
65
|
+
const onFieldKeyDown = (event) => {
|
|
66
|
+
if (event.key !== "Escape" || !isOpen(popoverEl())) return;
|
|
67
|
+
event.stopPropagation();
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
hide();
|
|
70
|
+
};
|
|
65
71
|
return {
|
|
66
72
|
open,
|
|
67
73
|
show,
|
|
68
74
|
hide,
|
|
75
|
+
onFieldKeyDown,
|
|
69
76
|
/** Spread on the button that opens the calendar. */
|
|
70
77
|
triggerProps: {
|
|
71
78
|
"aria-haspopup": "dialog",
|
|
@@ -2,6 +2,7 @@ import * as react from 'react';
|
|
|
2
2
|
import { AriaAttributes, CSSProperties } from 'react';
|
|
3
3
|
import { CalendarWeekday } from '../calendar-model.js';
|
|
4
4
|
import { Placement } from '../overlay-position.js';
|
|
5
|
+
import { TimePickerLabels } from './TimePicker.js';
|
|
5
6
|
|
|
6
7
|
/** Every word `DatePicker` renders or announces. */
|
|
7
8
|
interface DatePickerLabels {
|
|
@@ -38,6 +39,8 @@ interface DatePickerProps {
|
|
|
38
39
|
/**
|
|
39
40
|
* How the field writes a date. Default: the locale's numeric date (22.11.2026, 11/22/2026).
|
|
40
41
|
* Pass `parseDate` with it when your format is not three numbers, so typing still works.
|
|
42
|
+
* `formatDate`, `parseDate`, `locale` and `placeholder` default to `UixLabelsProvider`
|
|
43
|
+
* `dateFormat`, so one provider sets the format of every date field (HAR-2432).
|
|
41
44
|
*/
|
|
42
45
|
formatDate?: (date: string) => string;
|
|
43
46
|
/** Read typed text into `YYYY-MM-DD`, or `null` when it is not a date. Default: ISO or the locale's order. */
|
|
@@ -112,14 +115,18 @@ interface DateTimePickerProps extends Omit<DatePickerProps, 'value' | 'onValueCh
|
|
|
112
115
|
timeZone?: string;
|
|
113
116
|
/** Text shown instead of the zone's short name ("Vienna time", "UTC+2"); `null` shows none. */
|
|
114
117
|
timeZoneLabel?: string | null;
|
|
115
|
-
/** Minutes
|
|
118
|
+
/** Minutes the time's arrow keys step by, and between the times in its list. Default 15. */
|
|
116
119
|
minuteStep?: number;
|
|
120
|
+
/** Words of the time field (its list button, list and messages); `UixLabelsProvider` `timePicker` too. */
|
|
121
|
+
timeLabels?: Partial<TimePickerLabels>;
|
|
122
|
+
/** `true` writes the time as 2:30 PM, `false` as 14:30, whatever the locale. */
|
|
123
|
+
hour12?: boolean;
|
|
117
124
|
/** The time a date gets when it is chosen before any time. Default `'00:00'`. */
|
|
118
125
|
defaultTime?: string;
|
|
119
126
|
labels?: Partial<DateTimePickerLabels>;
|
|
120
127
|
}
|
|
121
128
|
/**
|
|
122
|
-
* A date and a time as one field (HAR-1383): the {@link DatePicker} and a
|
|
129
|
+
* A date and a time as one field (HAR-1383): the {@link DatePicker} and a {@link TimePicker} (HAR-1835), with the
|
|
123
130
|
* time zone the pair is in shown beside them. The value is `YYYY-MM-DDTHH:mm`, the shape
|
|
124
131
|
* `datetime-local` uses, so it replaces that input without changing what a form submits.
|
|
125
132
|
* Clearing the date clears the value; a time alone is not a value.
|
|
@@ -9,6 +9,7 @@ import { CalendarIcon } from "../icons/components.js";
|
|
|
9
9
|
import { useUixLabels } from "../labels-context.js";
|
|
10
10
|
import { fullDateLabel, localDateKey, MonthCalendar, useFieldPopover } from "./DateFieldParts.js";
|
|
11
11
|
import { Popover } from "./Popover.js";
|
|
12
|
+
import { TimePicker } from "./TimePicker.js";
|
|
12
13
|
const DEFAULT_DATE_PICKER_LABELS = {
|
|
13
14
|
open: "Choose date",
|
|
14
15
|
openSelected: "Change date, {date}",
|
|
@@ -25,12 +26,12 @@ const DatePicker = forwardRef(function DatePicker2({
|
|
|
25
26
|
min,
|
|
26
27
|
max,
|
|
27
28
|
isUnavailable,
|
|
28
|
-
formatDate,
|
|
29
|
-
parseDate,
|
|
30
|
-
locale,
|
|
29
|
+
formatDate: formatDateProp,
|
|
30
|
+
parseDate: parseDateProp,
|
|
31
|
+
locale: localeProp,
|
|
31
32
|
weekStartsOn = 1,
|
|
32
33
|
today,
|
|
33
|
-
placeholder,
|
|
34
|
+
placeholder: placeholderProp,
|
|
34
35
|
size = "md",
|
|
35
36
|
disabled,
|
|
36
37
|
readOnly,
|
|
@@ -54,7 +55,12 @@ const DatePicker = forwardRef(function DatePicker2({
|
|
|
54
55
|
const id = idProp ?? `${uid}-input`;
|
|
55
56
|
const popoverId = `${uid}-calendar`;
|
|
56
57
|
const errorId = `${uid}-error`;
|
|
57
|
-
const
|
|
58
|
+
const uixLabels = useUixLabels();
|
|
59
|
+
const labels = { ...DEFAULT_DATE_PICKER_LABELS, ...uixLabels.datePicker, ...labelOverrides };
|
|
60
|
+
const locale = localeProp ?? uixLabels.dateFormat?.locale;
|
|
61
|
+
const formatDate = formatDateProp ?? uixLabels.dateFormat?.formatDate;
|
|
62
|
+
const parseDate = parseDateProp ?? uixLabels.dateFormat?.parseDate;
|
|
63
|
+
const placeholder = placeholderProp ?? uixLabels.dateFormat?.placeholder;
|
|
58
64
|
const anchorRef = useRef(null);
|
|
59
65
|
const inputRef = useRef(null);
|
|
60
66
|
useImperativeHandle(forwardedRef, () => inputRef.current);
|
|
@@ -73,7 +79,7 @@ const DatePicker = forwardRef(function DatePicker2({
|
|
|
73
79
|
setText(value ? format(value) : "");
|
|
74
80
|
setProblem(null);
|
|
75
81
|
}, [value, locale]);
|
|
76
|
-
const { open, show, hide, triggerProps } = useFieldPopover(popoverId, () => inputRef.current?.focus({ preventScroll: true }), onOpenChange);
|
|
82
|
+
const { open, show, hide, onFieldKeyDown, triggerProps } = useFieldPopover(popoverId, () => inputRef.current?.focus({ preventScroll: true }), onOpenChange);
|
|
77
83
|
const locked = !!disabled || !!readOnly;
|
|
78
84
|
const commit = () => {
|
|
79
85
|
const typed = text.trim();
|
|
@@ -129,12 +135,7 @@ const DatePicker = forwardRef(function DatePicker2({
|
|
|
129
135
|
ref: anchorRef,
|
|
130
136
|
className: cx("uix-date-picker", size === "sm" && "uix-date-picker--sm", className),
|
|
131
137
|
style,
|
|
132
|
-
onKeyDown:
|
|
133
|
-
if (event.key !== "Escape" || !open) return;
|
|
134
|
-
event.stopPropagation();
|
|
135
|
-
event.preventDefault();
|
|
136
|
-
hide();
|
|
137
|
-
},
|
|
138
|
+
onKeyDown: onFieldKeyDown,
|
|
138
139
|
children: [
|
|
139
140
|
/* @__PURE__ */ jsxs("div", { className: "uix-date-picker__control", children: [
|
|
140
141
|
/* @__PURE__ */ jsx(
|
|
@@ -212,6 +213,22 @@ const DatePicker = forwardRef(function DatePicker2({
|
|
|
212
213
|
}
|
|
213
214
|
);
|
|
214
215
|
});
|
|
216
|
+
function labelText(label, control) {
|
|
217
|
+
let text = "";
|
|
218
|
+
const walk = (node) => {
|
|
219
|
+
if (node === control) return;
|
|
220
|
+
if (node.nodeType === 3) {
|
|
221
|
+
text += node.nodeValue ?? "";
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (node.nodeType !== 1) return;
|
|
225
|
+
const el = node;
|
|
226
|
+
if (el.getAttribute("aria-hidden") === "true" || el.hidden) return;
|
|
227
|
+
node.childNodes.forEach(walk);
|
|
228
|
+
};
|
|
229
|
+
label.childNodes.forEach(walk);
|
|
230
|
+
return text.replace(/\s+/g, " ").trim();
|
|
231
|
+
}
|
|
215
232
|
const DEFAULT_DATE_TIME_PICKER_LABELS = {
|
|
216
233
|
...DEFAULT_DATE_PICKER_LABELS,
|
|
217
234
|
date: "Date",
|
|
@@ -224,7 +241,9 @@ const DateTimePicker = forwardRef(function DateTimePicker2({
|
|
|
224
241
|
onValueChange,
|
|
225
242
|
timeZone,
|
|
226
243
|
timeZoneLabel,
|
|
227
|
-
minuteStep =
|
|
244
|
+
minuteStep = 15,
|
|
245
|
+
timeLabels,
|
|
246
|
+
hour12,
|
|
228
247
|
defaultTime = "00:00",
|
|
229
248
|
min,
|
|
230
249
|
max,
|
|
@@ -234,7 +253,7 @@ const DateTimePicker = forwardRef(function DateTimePicker2({
|
|
|
234
253
|
"aria-labelledby": ariaLabelledBy,
|
|
235
254
|
"aria-describedby": ariaDescribedBy,
|
|
236
255
|
labels: labelOverrides,
|
|
237
|
-
locale,
|
|
256
|
+
locale: localeProp,
|
|
238
257
|
size = "md",
|
|
239
258
|
disabled,
|
|
240
259
|
readOnly,
|
|
@@ -246,31 +265,48 @@ const DateTimePicker = forwardRef(function DateTimePicker2({
|
|
|
246
265
|
const uid = useId();
|
|
247
266
|
const id = idProp ?? `${uid}-date`;
|
|
248
267
|
const zoneId = `${uid}-zone`;
|
|
249
|
-
const
|
|
268
|
+
const uixLabels = useUixLabels();
|
|
269
|
+
const labels = { ...DEFAULT_DATE_TIME_PICKER_LABELS, ...uixLabels.dateTimePicker, ...labelOverrides };
|
|
270
|
+
const locale = localeProp ?? uixLabels.dateFormat?.locale;
|
|
250
271
|
const { date, time } = splitDateTime(value);
|
|
251
272
|
const dateRef = useRef(null);
|
|
273
|
+
const rootRef = useRef(null);
|
|
252
274
|
useImperativeHandle(forwardedRef, () => dateRef.current);
|
|
253
275
|
const [pendingTime, setPendingTime] = useState("");
|
|
254
276
|
const shownTime = date ? time : pendingTime;
|
|
255
277
|
const [fieldLabel, setFieldLabel] = useState("");
|
|
256
278
|
const [viewerZone, setViewerZone] = useState(null);
|
|
257
279
|
const grouped = !!ariaLabel || !!ariaLabelledBy;
|
|
280
|
+
const [labelNode, setLabelNode] = useState(null);
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
if (grouped) return;
|
|
283
|
+
const input = dateRef.current;
|
|
284
|
+
const label = input?.labels?.[0] ?? null;
|
|
285
|
+
setLabelNode(label);
|
|
286
|
+
setFieldLabel(label ? labelText(label, rootRef.current) : "");
|
|
287
|
+
});
|
|
288
|
+
useEffect(() => {
|
|
289
|
+
if (grouped || !labelNode || typeof MutationObserver === "undefined") return void 0;
|
|
290
|
+
const observer = new MutationObserver(() => setFieldLabel(labelText(labelNode, rootRef.current)));
|
|
291
|
+
observer.observe(labelNode, { childList: true, characterData: true, subtree: true, attributes: true, attributeFilter: ["aria-hidden", "hidden"] });
|
|
292
|
+
return () => observer.disconnect();
|
|
293
|
+
}, [grouped, labelNode]);
|
|
258
294
|
useEffect(() => {
|
|
259
|
-
if (!grouped) setFieldLabel(dateRef.current?.labels?.[0]?.textContent?.trim() ?? "");
|
|
260
295
|
if (!timeZone) {
|
|
261
296
|
try {
|
|
262
297
|
setViewerZone(new Intl.DateTimeFormat().resolvedOptions().timeZone);
|
|
263
298
|
} catch {
|
|
264
299
|
}
|
|
265
300
|
}
|
|
266
|
-
}, [
|
|
301
|
+
}, [timeZone]);
|
|
267
302
|
const zone = timeZone ?? viewerZone;
|
|
268
303
|
const zoneText = timeZoneLabel === null ? null : timeZoneLabel ?? (zone ? timeZoneName(zone, date, locale) : null);
|
|
269
304
|
const timeName = grouped ? labels.time : fieldLabel ? fillLabel(labels.timeOf, { label: fieldLabel }) : labels.time;
|
|
270
|
-
const step = Math.max(1, Math.round(minuteStep))
|
|
305
|
+
const step = Math.max(1, Math.round(minuteStep));
|
|
271
306
|
return /* @__PURE__ */ jsxs(
|
|
272
307
|
"div",
|
|
273
308
|
{
|
|
309
|
+
ref: rootRef,
|
|
274
310
|
className: cx("uix-date-time-picker", size === "sm" && "uix-date-time-picker--sm", className),
|
|
275
311
|
style,
|
|
276
312
|
role: "group",
|
|
@@ -305,21 +341,24 @@ const DateTimePicker = forwardRef(function DateTimePicker2({
|
|
|
305
341
|
}
|
|
306
342
|
),
|
|
307
343
|
/* @__PURE__ */ jsx(
|
|
308
|
-
|
|
344
|
+
TimePicker,
|
|
309
345
|
{
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
346
|
+
className: "uix-date-time-picker__time",
|
|
347
|
+
value: shownTime || null,
|
|
348
|
+
minuteStep: step,
|
|
349
|
+
locale,
|
|
350
|
+
hour12,
|
|
351
|
+
size,
|
|
314
352
|
disabled,
|
|
315
353
|
readOnly,
|
|
316
354
|
required: required && !!date,
|
|
355
|
+
invalid: !!dateProps.invalid || dateProps["aria-invalid"] === true || dateProps["aria-invalid"] === "true",
|
|
317
356
|
"aria-label": timeName,
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (date) onValueChange(joinDateTime(date, next, defaultTime));
|
|
322
|
-
else setPendingTime(next);
|
|
357
|
+
labels: timeLabels,
|
|
358
|
+
"aria-describedby": [ariaDescribedBy, zoneText ? zoneId : void 0].filter(Boolean).join(" ") || void 0,
|
|
359
|
+
onValueChange: (next) => {
|
|
360
|
+
if (date) onValueChange(joinDateTime(date, next ?? "", defaultTime));
|
|
361
|
+
else setPendingTime(next ?? "");
|
|
323
362
|
}
|
|
324
363
|
}
|
|
325
364
|
),
|