rift-ds 1.18.0 → 1.19.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/behaviors/useAnchoredPosition.d.ts +3 -1
- package/behaviors/useAnchoredPosition.js +1 -0
- package/behaviors/useTopLayer.d.ts +35 -0
- package/behaviors/useTopLayer.js +22 -0
- package/chat/ChatLauncher.js +23 -0
- package/chat/ChatWidget.css +8 -3
- package/components/AiButton/AiButton.css +11 -0
- package/components/AiButton/AiButton.d.ts +2 -0
- package/components/AiButton/AiButton.js +3 -1
- package/components/AppSidebar/AppSidebar.css +59 -9
- package/components/AppSidebar/AppSidebar.d.ts +2 -2
- package/components/AppSidebar/AppSidebar.js +15 -9
- package/components/Chart/BarChart.js +1 -0
- package/components/Chart/Chart.css +6 -0
- package/components/Chart/ComboChart.js +1 -0
- package/components/Chart/StackedBarChart.js +1 -0
- package/components/CommandPaletteTrigger/CommandPaletteTrigger.css +13 -1
- package/components/Composer/Composer.css +72 -0
- package/components/Composer/Composer.d.ts +5 -1
- package/components/Composer/Composer.js +8 -1
- package/components/DataTable/DataTable.css +7 -3
- package/components/Popover/Popover.d.ts +2 -0
- package/components/Popover/Popover.js +2 -1
- package/components/SegmentedControl/SegmentedControl.css +3 -1
- package/components/SegmentedControl/SegmentedControl.js +2 -0
- package/components/SidebarPanel/SidebarPanel.css +10 -2
- package/components/SidebarPanel/SidebarPanel.d.ts +1 -1
- package/components/SidebarPanel/SidebarPanel.js +5 -2
- package/components/SiteHeader/SiteHeader.css +63 -5
- package/components/SiteHeader/SiteHeader.d.ts +11 -3
- package/components/SiteHeader/SiteHeader.js +51 -16
- package/components/SourceChip/SourceChip.css +5 -1
- package/components/SourceChip/SourceChip.js +3 -0
- package/components/TableToolbar/TableToolbar.css +130 -1
- package/components/TableToolbar/TableToolbar.d.ts +52 -0
- package/components/TableToolbar/TableToolbar.js +232 -32
- package/components/ThemeToggle/ThemeToggle.css +26 -0
- package/components/ThemeToggle/ThemeToggle.d.ts +11 -1
- package/components/ThemeToggle/ThemeToggle.js +36 -5
- package/components/Toast/Toast.css +4 -0
- package/components/Toast/Toast.d.ts +9 -0
- package/components/Toast/Toast.js +9 -2
- package/components/Tooltip/Tooltip.css +9 -4
- package/components/Tooltip/Tooltip.d.ts +4 -2
- package/components/Tooltip/Tooltip.js +16 -4
- package/index.js +2 -1
- package/package.json +1 -1
- package/tokens/motion.d.ts +2 -0
- package/tokens/motion.js +2 -0
- package/tokens/tokens-light.css +4 -3
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
3
|
-
import React, { useState, useEffect, useRef, useLayoutEffect, useId, useSyncExternalStore } from "react";
|
|
3
|
+
import React, { useState, useEffect, useRef, useLayoutEffect, useId, useSyncExternalStore, useCallback } from "react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
|
+
import { AiButton } from "../AiButton/AiButton.js";
|
|
5
6
|
import { Button } from "../Button/Button.js";
|
|
6
7
|
import { Chip } from "../Chip/Chip.js";
|
|
8
|
+
import { Composer } from "../Composer/Composer.js";
|
|
7
9
|
import { Dropdown } from "../Dropdown/Dropdown.js";
|
|
8
10
|
import { CircularButton } from "../CircularButton/CircularButton.js";
|
|
9
11
|
import { DropdownMenu } from "../DropdownMenu/DropdownMenu.js";
|
|
@@ -13,14 +15,17 @@ import { Popover } from "../Popover/Popover.js";
|
|
|
13
15
|
import { SegmentedControl } from "../SegmentedControl/SegmentedControl.js";
|
|
14
16
|
import { SplitButton } from "../SplitButton/SplitButton.js";
|
|
15
17
|
import { Tabs } from "../Tabs/Tabs.js";
|
|
18
|
+
import { useOptionalToast, ToastProvider, useToast } from "../Toast/Toast.js";
|
|
16
19
|
import { ToggleSwitch } from "../ToggleSwitch/ToggleSwitch.js";
|
|
20
|
+
import { Tooltip } from "../Tooltip/Tooltip.js";
|
|
17
21
|
import { FilterEditor } from "../DataTable/FilterEditor.js";
|
|
18
22
|
import { startDrag } from "../DataTable/dragGesture.js";
|
|
19
23
|
import { parseDate, absoluteDate } from "../DataTable/columnTypes.js";
|
|
20
24
|
import "../../behaviors/visually-hidden.css";
|
|
21
25
|
import { DEFAULT_FILTER_LABELS, isComplete, VALUELESS, OPERATORS, valueText, newFilterId } from "../DataTable/filterModel.js";
|
|
22
26
|
import { useTableContext } from "../DataTable/tableContext.js";
|
|
23
|
-
import {
|
|
27
|
+
import { MOTION_ACTION_DISMISS_MS } from "../../tokens/motion.js";
|
|
28
|
+
import { BREAKPOINT_PHONE_PX, BREAKPOINT_SHEET_PX, mediaBelow } from "../../tokens/breakpoints.js";
|
|
24
29
|
import "./TableToolbar.css";
|
|
25
30
|
import { mirrorClass } from "../../behaviors/direction.js";
|
|
26
31
|
const DEFAULT_LABELS = {
|
|
@@ -67,6 +72,14 @@ const DEFAULT_LABELS = {
|
|
|
67
72
|
movedSort: (column, rank, count) => `${column}, sort ${rank} of ${count}`,
|
|
68
73
|
selected: (count) => `${count} selected`,
|
|
69
74
|
allSelected: (count) => `All ${count} selected`,
|
|
75
|
+
command: "Describe",
|
|
76
|
+
commandClose: "Close",
|
|
77
|
+
commandPlaceholder: "Tell the table what to show",
|
|
78
|
+
commandSend: "Apply",
|
|
79
|
+
commandApplied: "The table has been updated.",
|
|
80
|
+
commandFailed: "That did not work. The table has not changed.",
|
|
81
|
+
commandUndo: "Undo",
|
|
82
|
+
commandDismiss: "Dismiss",
|
|
70
83
|
moreBulkActions: "More actions",
|
|
71
84
|
selectAll: (count) => `Select all ${count}`,
|
|
72
85
|
clearSelection: "Clear selection",
|
|
@@ -127,6 +140,134 @@ function ToolbarPanel({
|
|
|
127
140
|
)
|
|
128
141
|
] });
|
|
129
142
|
}
|
|
143
|
+
function CommandRow({ command, table, labels, size, open, onClose, state, onStateChange }) {
|
|
144
|
+
const { toast, dismiss } = useToast();
|
|
145
|
+
const [text, setText] = useState("");
|
|
146
|
+
const row = useRef(null);
|
|
147
|
+
const lastToast = useRef(null);
|
|
148
|
+
const controller = useRef(null);
|
|
149
|
+
const latestTable = useRef(table);
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
latestTable.current = table;
|
|
152
|
+
}, [table]);
|
|
153
|
+
useEffect(() => {
|
|
154
|
+
if (open) row.current?.querySelector("textarea")?.focus();
|
|
155
|
+
}, [open]);
|
|
156
|
+
useEffect(() => () => controller.current?.abort(), []);
|
|
157
|
+
const report = useCallback(
|
|
158
|
+
(data) => {
|
|
159
|
+
lastToast.current = toast(data);
|
|
160
|
+
},
|
|
161
|
+
[toast]
|
|
162
|
+
);
|
|
163
|
+
const run = useCallback(
|
|
164
|
+
async (typed) => {
|
|
165
|
+
const trimmed = typed.trim();
|
|
166
|
+
if (!trimmed || state === "working") return;
|
|
167
|
+
const before = JSON.parse(JSON.stringify(table.view));
|
|
168
|
+
onClose();
|
|
169
|
+
setText("");
|
|
170
|
+
onStateChange("working");
|
|
171
|
+
if (lastToast.current) dismiss(lastToast.current);
|
|
172
|
+
lastToast.current = null;
|
|
173
|
+
const running = new AbortController();
|
|
174
|
+
controller.current = running;
|
|
175
|
+
try {
|
|
176
|
+
const result = await command.onSubmit({
|
|
177
|
+
text: trimmed,
|
|
178
|
+
view: before,
|
|
179
|
+
columns: table.columns,
|
|
180
|
+
signal: running.signal
|
|
181
|
+
});
|
|
182
|
+
if (running.signal.aborted) return;
|
|
183
|
+
const changed = Boolean(result.view) && JSON.stringify(result.view) !== JSON.stringify(before);
|
|
184
|
+
if (changed) {
|
|
185
|
+
latestTable.current.applyView(result.view);
|
|
186
|
+
const id = `table-command-${Date.now()}`;
|
|
187
|
+
report({
|
|
188
|
+
id,
|
|
189
|
+
variant: "positive",
|
|
190
|
+
description: result.message || labels.commandApplied,
|
|
191
|
+
duration: MOTION_ACTION_DISMISS_MS,
|
|
192
|
+
action: /* @__PURE__ */ jsx(
|
|
193
|
+
Button,
|
|
194
|
+
{
|
|
195
|
+
variant: "secondary",
|
|
196
|
+
tone: "neutral",
|
|
197
|
+
size: "compact",
|
|
198
|
+
iconLeft: "refresh",
|
|
199
|
+
label: labels.commandUndo,
|
|
200
|
+
onClick: () => {
|
|
201
|
+
latestTable.current.applyView(before);
|
|
202
|
+
dismiss(id);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
});
|
|
207
|
+
} else if (result.message) {
|
|
208
|
+
report({ variant: "info", description: result.message });
|
|
209
|
+
}
|
|
210
|
+
onStateChange("done");
|
|
211
|
+
} catch (error) {
|
|
212
|
+
if (running.signal.aborted) return;
|
|
213
|
+
setText(trimmed);
|
|
214
|
+
report({
|
|
215
|
+
variant: "error",
|
|
216
|
+
description: error instanceof Error && error.message ? error.message : labels.commandFailed,
|
|
217
|
+
duration: 0
|
|
218
|
+
});
|
|
219
|
+
onStateChange("error");
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
[command, table, state, onClose, onStateChange, labels.commandApplied, labels.commandFailed, labels.commandUndo, report, dismiss]
|
|
223
|
+
);
|
|
224
|
+
return /* @__PURE__ */ jsxs(
|
|
225
|
+
"div",
|
|
226
|
+
{
|
|
227
|
+
className: "ds-table-toolbar__command-row",
|
|
228
|
+
"data-open": open || void 0,
|
|
229
|
+
inert: !open || void 0,
|
|
230
|
+
ref: row,
|
|
231
|
+
onKeyDown: (event) => {
|
|
232
|
+
if (event.key !== "Escape") return;
|
|
233
|
+
event.preventDefault();
|
|
234
|
+
onClose();
|
|
235
|
+
},
|
|
236
|
+
children: [
|
|
237
|
+
/* @__PURE__ */ jsx(
|
|
238
|
+
CircularButton,
|
|
239
|
+
{
|
|
240
|
+
className: "ds-table-toolbar__command-close",
|
|
241
|
+
icon: "close",
|
|
242
|
+
variant: "tertiary",
|
|
243
|
+
size,
|
|
244
|
+
ariaLabel: labels.commandClose,
|
|
245
|
+
tooltip: false,
|
|
246
|
+
onClick: onClose
|
|
247
|
+
}
|
|
248
|
+
),
|
|
249
|
+
/* @__PURE__ */ jsx(
|
|
250
|
+
Composer,
|
|
251
|
+
{
|
|
252
|
+
inline: true,
|
|
253
|
+
size,
|
|
254
|
+
value: text,
|
|
255
|
+
onValueChange: setText,
|
|
256
|
+
onSubmit: run,
|
|
257
|
+
placeholder: labels.commandPlaceholder,
|
|
258
|
+
sendLabel: labels.commandSend,
|
|
259
|
+
attachButton: false
|
|
260
|
+
}
|
|
261
|
+
)
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
function ToolbarCommand(props) {
|
|
267
|
+
const host = useOptionalToast();
|
|
268
|
+
if (host) return /* @__PURE__ */ jsx(CommandRow, { ...props });
|
|
269
|
+
return /* @__PURE__ */ jsx(ToastProvider, { position: "bottom-right", dismissLabel: props.labels.commandDismiss, children: /* @__PURE__ */ jsx(CommandRow, { ...props }) });
|
|
270
|
+
}
|
|
130
271
|
function useBound(value, defaultValue, onChange, table) {
|
|
131
272
|
const [own, setOwn] = useState(defaultValue);
|
|
132
273
|
const current = value !== void 0 ? value : table ? table.value : own;
|
|
@@ -181,6 +322,7 @@ const TableToolbar = React.forwardRef(
|
|
|
181
322
|
primaryAction,
|
|
182
323
|
secondaryActions = [],
|
|
183
324
|
actions,
|
|
325
|
+
command,
|
|
184
326
|
labels: labelsProp,
|
|
185
327
|
filterLabels: filterLabelsProp,
|
|
186
328
|
className = "",
|
|
@@ -366,10 +508,14 @@ const TableToolbar = React.forwardRef(
|
|
|
366
508
|
const activeFilters = filters.filter(isComplete).length;
|
|
367
509
|
const rootRef = useRef(null);
|
|
368
510
|
const [narrow, setNarrow] = useState(false);
|
|
511
|
+
const [crowded, setCrowded] = useState(false);
|
|
369
512
|
useEffect(() => {
|
|
370
513
|
const node = rootRef.current;
|
|
371
514
|
if (!node) return;
|
|
372
|
-
const observer = new ResizeObserver(([entry]) =>
|
|
515
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
516
|
+
setNarrow(entry.contentRect.width < BREAKPOINT_PHONE_PX);
|
|
517
|
+
setCrowded(entry.contentRect.width < BREAKPOINT_SHEET_PX);
|
|
518
|
+
});
|
|
373
519
|
observer.observe(node);
|
|
374
520
|
return () => observer.disconnect();
|
|
375
521
|
}, []);
|
|
@@ -459,6 +605,29 @@ const TableToolbar = React.forwardRef(
|
|
|
459
605
|
const bulkMode = selectedCount > 0;
|
|
460
606
|
const phoneScreen = useSyncExternalStore(subscribePhone, isPhone, () => false);
|
|
461
607
|
const floating = phoneScreen && bulkMode;
|
|
608
|
+
const [commandOpen, setCommandOpen] = useState(false);
|
|
609
|
+
const [commandState, setCommandState] = useState("idle");
|
|
610
|
+
const commandButton = useRef(null);
|
|
611
|
+
const commandShown = commandOpen && Boolean(command && table) && !(bulkMode && !floating);
|
|
612
|
+
const closeCommand = () => {
|
|
613
|
+
setCommandOpen(false);
|
|
614
|
+
requestAnimationFrame(() => commandButton.current?.focus());
|
|
615
|
+
};
|
|
616
|
+
const covered = commandShown || bulkMode && !floating;
|
|
617
|
+
const commandButtonDrawn = /* @__PURE__ */ jsx(
|
|
618
|
+
AiButton,
|
|
619
|
+
{
|
|
620
|
+
ref: commandButton,
|
|
621
|
+
className: "ds-table-toolbar__command",
|
|
622
|
+
iconOnly: narrow,
|
|
623
|
+
size,
|
|
624
|
+
label: labels.command,
|
|
625
|
+
state: commandState,
|
|
626
|
+
"aria-expanded": commandShown,
|
|
627
|
+
onClick: () => setCommandOpen(true)
|
|
628
|
+
}
|
|
629
|
+
);
|
|
630
|
+
const commandTrigger = narrow ? /* @__PURE__ */ jsx(Tooltip, { content: labels.command, children: commandButtonDrawn }) : commandButtonDrawn;
|
|
462
631
|
const matching = table?.matchingIds ?? [];
|
|
463
632
|
const matchingCount = table?.matchingCount ?? matching.length;
|
|
464
633
|
const allMatching = table?.allMatching ?? (matching.length > 0 && matching.every((id) => table.selectedIds.includes(id)));
|
|
@@ -1070,7 +1239,30 @@ const TableToolbar = React.forwardRef(
|
|
|
1070
1239
|
}
|
|
1071
1240
|
)
|
|
1072
1241
|
] });
|
|
1073
|
-
const
|
|
1242
|
+
const groupOffered = groupable && groupableColumns.length > 0;
|
|
1243
|
+
const groupPanel = /* @__PURE__ */ jsxs("div", { className: "ds-table-toolbar__menu", children: [
|
|
1244
|
+
/* @__PURE__ */ jsx("div", { className: "ds-table-toolbar__menu-heading", "aria-hidden": "true", children: labels.groupBy }),
|
|
1245
|
+
/* @__PURE__ */ jsx("div", { className: "ds-table-toolbar__menu-row", children: /* @__PURE__ */ jsx(
|
|
1246
|
+
Dropdown,
|
|
1247
|
+
{
|
|
1248
|
+
className: "ds-table-toolbar__menu-field",
|
|
1249
|
+
size,
|
|
1250
|
+
"aria-label": labels.groupBy,
|
|
1251
|
+
options: [
|
|
1252
|
+
{ value: "", label: labels.noGrouping },
|
|
1253
|
+
...groupableColumns.map((col) => ({ value: col.key, label: col.label }))
|
|
1254
|
+
],
|
|
1255
|
+
value: groupedBy ?? "",
|
|
1256
|
+
onValueChange: (next) => setGrouping(next ? [String(next)] : [])
|
|
1257
|
+
}
|
|
1258
|
+
) }),
|
|
1259
|
+
table && groupedBy && menuSeparator,
|
|
1260
|
+
table && groupedBy && /* @__PURE__ */ jsxs("div", { className: "ds-table-toolbar__menu-actions", children: [
|
|
1261
|
+
menuItem(labels.collapseAll, "unfold_less", () => table.setAllGroupsOpen(false)),
|
|
1262
|
+
menuItem(labels.expandAll, "unfold_more", () => table.setAllGroupsOpen(true))
|
|
1263
|
+
] })
|
|
1264
|
+
] });
|
|
1265
|
+
const groupControl = groupOffered && /* @__PURE__ */ jsxs("span", { className: "ds-table-toolbar__badge-host", children: [
|
|
1074
1266
|
/* @__PURE__ */ jsx(
|
|
1075
1267
|
ToolbarPanel,
|
|
1076
1268
|
{
|
|
@@ -1079,28 +1271,7 @@ const TableToolbar = React.forwardRef(
|
|
|
1079
1271
|
size,
|
|
1080
1272
|
ariaLabel: labels.group(Boolean(groupedBy)),
|
|
1081
1273
|
position: "bottom",
|
|
1082
|
-
content:
|
|
1083
|
-
/* @__PURE__ */ jsx("div", { className: "ds-table-toolbar__menu-heading", "aria-hidden": "true", children: labels.groupBy }),
|
|
1084
|
-
/* @__PURE__ */ jsx("div", { className: "ds-table-toolbar__menu-row", children: /* @__PURE__ */ jsx(
|
|
1085
|
-
Dropdown,
|
|
1086
|
-
{
|
|
1087
|
-
className: "ds-table-toolbar__menu-field",
|
|
1088
|
-
size,
|
|
1089
|
-
"aria-label": labels.groupBy,
|
|
1090
|
-
options: [
|
|
1091
|
-
{ value: "", label: labels.noGrouping },
|
|
1092
|
-
...groupableColumns.map((col) => ({ value: col.key, label: col.label }))
|
|
1093
|
-
],
|
|
1094
|
-
value: groupedBy ?? "",
|
|
1095
|
-
onValueChange: (next) => setGrouping(next ? [String(next)] : [])
|
|
1096
|
-
}
|
|
1097
|
-
) }),
|
|
1098
|
-
table && groupedBy && menuSeparator,
|
|
1099
|
-
table && groupedBy && /* @__PURE__ */ jsxs("div", { className: "ds-table-toolbar__menu-actions", children: [
|
|
1100
|
-
menuItem(labels.collapseAll, "unfold_less", () => table.setAllGroupsOpen(false)),
|
|
1101
|
-
menuItem(labels.expandAll, "unfold_more", () => table.setAllGroupsOpen(true))
|
|
1102
|
-
] })
|
|
1103
|
-
] }),
|
|
1274
|
+
content: groupPanel,
|
|
1104
1275
|
children: /* @__PURE__ */ jsx(
|
|
1105
1276
|
CircularButton,
|
|
1106
1277
|
{
|
|
@@ -1144,7 +1315,9 @@ const TableToolbar = React.forwardRef(
|
|
|
1144
1315
|
);
|
|
1145
1316
|
const foldedActions = narrow ? [...secondaryActions, ...primaryAction?.menu ?? []] : [];
|
|
1146
1317
|
const foldedDensity = narrow ? density : false;
|
|
1147
|
-
const
|
|
1318
|
+
const foldedGroup = crowded && groupOffered;
|
|
1319
|
+
const foldedColumns = crowded ? columnsList : false;
|
|
1320
|
+
const morePanel = /* @__PURE__ */ jsx(
|
|
1148
1321
|
ToolbarPanel,
|
|
1149
1322
|
{
|
|
1150
1323
|
sheet: phoneScreen,
|
|
@@ -1157,10 +1330,19 @@ const TableToolbar = React.forwardRef(
|
|
|
1157
1330
|
/* @__PURE__ */ jsx("div", { className: "ds-table-toolbar__menu-heading", "aria-hidden": "true", children: labels.actionsSection }),
|
|
1158
1331
|
/* @__PURE__ */ jsx("div", { className: "ds-table-toolbar__menu-actions", children: foldedActions.map((action) => /* @__PURE__ */ jsx("span", { children: menuItem(action.label, action.icon, action.onSelect, action.disabled) }, action.id)) })
|
|
1159
1332
|
] }),
|
|
1160
|
-
foldedActions.length > 0 && foldedDensity && menuSeparator,
|
|
1333
|
+
foldedActions.length > 0 && (foldedGroup || foldedDensity) && menuSeparator,
|
|
1334
|
+
foldedGroup && groupPanel,
|
|
1335
|
+
foldedGroup && foldedDensity && menuSeparator,
|
|
1161
1336
|
foldedDensity && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1162
1337
|
/* @__PURE__ */ jsx("div", { className: "ds-table-toolbar__menu-heading", "aria-hidden": "true", children: labels.density }),
|
|
1163
1338
|
/* @__PURE__ */ jsx("div", { className: "ds-table-toolbar__menu-row", children: foldedDensity })
|
|
1339
|
+
] }),
|
|
1340
|
+
foldedColumns && (foldedActions.length > 0 || foldedGroup || foldedDensity) && menuSeparator,
|
|
1341
|
+
foldedColumns && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1342
|
+
/* @__PURE__ */ jsx("div", { className: "ds-table-toolbar__menu-heading", "aria-hidden": "true", children: labels.columns }),
|
|
1343
|
+
foldedColumns,
|
|
1344
|
+
menuSeparator,
|
|
1345
|
+
/* @__PURE__ */ jsx("div", { className: "ds-table-toolbar__menu-actions", children: showAllColumns })
|
|
1164
1346
|
] })
|
|
1165
1347
|
] }),
|
|
1166
1348
|
children: /* @__PURE__ */ jsx(
|
|
@@ -1176,6 +1358,10 @@ const TableToolbar = React.forwardRef(
|
|
|
1176
1358
|
)
|
|
1177
1359
|
}
|
|
1178
1360
|
);
|
|
1361
|
+
const moreControl = (foldedDensity || foldedActions.length > 0 || foldedGroup || foldedColumns) && /* @__PURE__ */ jsxs("span", { className: "ds-table-toolbar__badge-host", children: [
|
|
1362
|
+
morePanel,
|
|
1363
|
+
foldedGroup && groupedBy && /* @__PURE__ */ jsx("span", { className: "ds-table-toolbar__badge", "aria-hidden": "true", children: "1" })
|
|
1364
|
+
] });
|
|
1179
1365
|
const showFilters = filterable && filterableColumns.length > 0;
|
|
1180
1366
|
return /* @__PURE__ */ jsxs(
|
|
1181
1367
|
"div",
|
|
@@ -1203,14 +1389,28 @@ const TableToolbar = React.forwardRef(
|
|
|
1203
1389
|
}
|
|
1204
1390
|
)
|
|
1205
1391
|
] }),
|
|
1392
|
+
command && table && /* @__PURE__ */ jsx(
|
|
1393
|
+
ToolbarCommand,
|
|
1394
|
+
{
|
|
1395
|
+
command,
|
|
1396
|
+
table,
|
|
1397
|
+
labels,
|
|
1398
|
+
size,
|
|
1399
|
+
open: commandShown,
|
|
1400
|
+
onClose: closeCommand,
|
|
1401
|
+
state: commandState,
|
|
1402
|
+
onStateChange: setCommandState
|
|
1403
|
+
}
|
|
1404
|
+
),
|
|
1206
1405
|
/* @__PURE__ */ jsxs(
|
|
1207
1406
|
"div",
|
|
1208
1407
|
{
|
|
1209
1408
|
className: "ds-table-toolbar__main",
|
|
1210
|
-
inert:
|
|
1211
|
-
"data-covered":
|
|
1409
|
+
inert: covered || void 0,
|
|
1410
|
+
"data-covered": commandShown ? "command" : covered ? "bulk" : void 0,
|
|
1212
1411
|
children: [
|
|
1213
1412
|
/* @__PURE__ */ jsxs("div", { className: "ds-table-toolbar__start", ref: startRef, children: [
|
|
1413
|
+
command && table && commandTrigger,
|
|
1214
1414
|
searchable && !narrow && searchField,
|
|
1215
1415
|
showFilters && !narrow && /* @__PURE__ */ jsx("span", { className: "ds-table-toolbar__filter-button", children: addFilterMenu(filterIcon) }),
|
|
1216
1416
|
showFilters && !narrow && chips,
|
|
@@ -1268,8 +1468,8 @@ const TableToolbar = React.forwardRef(
|
|
|
1268
1468
|
),
|
|
1269
1469
|
showFilters && narrow && filterPanel,
|
|
1270
1470
|
sortControl,
|
|
1271
|
-
groupControl,
|
|
1272
|
-
columnsControl,
|
|
1471
|
+
!crowded && groupControl,
|
|
1472
|
+
!crowded && columnsControl,
|
|
1273
1473
|
!narrow && densityIcons,
|
|
1274
1474
|
moreControl,
|
|
1275
1475
|
(primaryAction || !narrow && secondaryActions.length > 0) && /* @__PURE__ */ jsxs("span", { className: "ds-table-toolbar__actions", children: [
|
|
@@ -10,3 +10,29 @@
|
|
|
10
10
|
.ds-theme-toggle {
|
|
11
11
|
flex-shrink: 0;
|
|
12
12
|
}
|
|
13
|
+
|
|
14
|
+
/* The cycle form: one circle in the track recipe the strip, the
|
|
15
|
+
CommandPaletteTrigger and the ThemePicker's trigger wear (container fill,
|
|
16
|
+
hairline border, the track's curve), so it reads as one of that family in
|
|
17
|
+
a row; it hovers like the picker's trigger, the fill stepping up. */
|
|
18
|
+
.ds-theme-toggle--cycle {
|
|
19
|
+
display: inline-flex;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.ds-theme-toggle--cycle .ds-circular-button {
|
|
23
|
+
background-color: var(--color-bg-container-primary);
|
|
24
|
+
border: var(--border-025) solid var(--color-bg-container-border);
|
|
25
|
+
border-radius: calc(var(--radius-pill) + var(--padding-100));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.ds-theme-toggle--cycle .ds-circular-button:not(.ds-circular-button--disabled):hover {
|
|
29
|
+
background-color: var(--color-bg-container-secondary);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* The glyph holds the icon colour in every state, as the family's glyphs
|
|
33
|
+
do: only the fill steps on hover. */
|
|
34
|
+
.ds-theme-toggle--cycle .ds-circular-button .ds-circular-button__icon,
|
|
35
|
+
.ds-theme-toggle--cycle .ds-circular-button:not(.ds-circular-button--disabled):hover .ds-circular-button__icon,
|
|
36
|
+
.ds-theme-toggle--cycle .ds-circular-button:not(.ds-circular-button--disabled):active .ds-circular-button__icon {
|
|
37
|
+
color: var(--color-action-icon-default);
|
|
38
|
+
}
|
|
@@ -17,6 +17,15 @@ type ThemeToggleOwnProps = {
|
|
|
17
17
|
size?: 'default' | 'compact';
|
|
18
18
|
/** Show each segment's label beside its icon. Set `false` for an icon-only strip: each label becomes the segment's tooltip on hover and keyboard focus, and stays its accessible name. */
|
|
19
19
|
showLabels?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Draw one circle that shows the current setting and steps to the next on
|
|
22
|
+
* each press (System, Light, Dark), in place of the strip, for a row short
|
|
23
|
+
* of room, such as a condensed SiteHeader's `condensedActions`. Each label
|
|
24
|
+
* becomes the circle's tooltip and its accessible name comes from `cycleLabel`.
|
|
25
|
+
*/
|
|
26
|
+
cycle?: boolean;
|
|
27
|
+
/** The cycle circle's accessible name, from the current setting's label and the next one's. Default: the control's `aria-label`, then both, as in "Colour mode: Light. Switch to Dark". */
|
|
28
|
+
cycleLabel?: (current: string, next: string) => string;
|
|
20
29
|
/** The localStorage key the choice is kept under. Match the key given to `colorModeScript`. Default `theme`. */
|
|
21
30
|
storageKey?: string;
|
|
22
31
|
/** Label of the setting that follows the operating system. */
|
|
@@ -36,7 +45,8 @@ export interface ThemeToggleProps extends ThemeToggleOwnProps, Omit<React.Compon
|
|
|
36
45
|
* choice survives a reload. Pair it with `colorModeScript` in the document
|
|
37
46
|
* head, which applies the stored choice before first paint.
|
|
38
47
|
*
|
|
39
|
-
* Forwards a ref to the strip
|
|
48
|
+
* Forwards a ref to the strip (in the `cycle` form, the circle's wrapper)
|
|
49
|
+
* and spreads unrecognised props onto it.
|
|
40
50
|
*/
|
|
41
51
|
export declare const ThemeToggle: React.ForwardRefExoticComponent<ThemeToggleProps & React.RefAttributes<HTMLDivElement>>;
|
|
42
52
|
export {};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import React, { useEffect } from "react";
|
|
4
4
|
import { SegmentedControl } from "../SegmentedControl/SegmentedControl.js";
|
|
5
|
+
import { CircularButton } from "../CircularButton/CircularButton.js";
|
|
5
6
|
import { DEFAULT_COLOR_MODE_STORAGE_KEY } from "./colorMode.js";
|
|
6
7
|
import { useColorMode } from "./useColorMode.js";
|
|
7
8
|
import "./ThemeToggle.css";
|
|
@@ -12,12 +13,14 @@ const ThemeToggle = React.forwardRef(
|
|
|
12
13
|
onValueChange,
|
|
13
14
|
size = "default",
|
|
14
15
|
showLabels = true,
|
|
16
|
+
cycle = false,
|
|
15
17
|
storageKey,
|
|
16
18
|
systemLabel = "System",
|
|
17
19
|
lightLabel = "Light",
|
|
18
20
|
darkLabel = "Dark",
|
|
19
21
|
className = "",
|
|
20
22
|
"aria-label": ariaLabel = "Colour mode",
|
|
23
|
+
cycleLabel = (current, next) => `${ariaLabel}: ${current}. Switch to ${next}`,
|
|
21
24
|
...rest
|
|
22
25
|
}, ref) => {
|
|
23
26
|
const [storedSetting, setSetting] = useColorMode({ storageKey });
|
|
@@ -36,6 +39,38 @@ const ThemeToggle = React.forwardRef(
|
|
|
36
39
|
{ value: "light", label: lightLabel, icon: "light_mode" },
|
|
37
40
|
{ value: "dark", label: darkLabel, icon: "dark_mode" }
|
|
38
41
|
];
|
|
42
|
+
const pick = (next) => {
|
|
43
|
+
if (value === void 0) setSetting(next);
|
|
44
|
+
onValueChange?.(next);
|
|
45
|
+
};
|
|
46
|
+
if (cycle) {
|
|
47
|
+
const index = Math.max(
|
|
48
|
+
segments.findIndex((segment) => segment.value === setting),
|
|
49
|
+
0
|
|
50
|
+
);
|
|
51
|
+
const current = segments[index];
|
|
52
|
+
const next = segments[(index + 1) % segments.length];
|
|
53
|
+
return /* @__PURE__ */ jsx(
|
|
54
|
+
"div",
|
|
55
|
+
{
|
|
56
|
+
...rest,
|
|
57
|
+
ref,
|
|
58
|
+
className: ["ds-theme-toggle", "ds-theme-toggle--cycle", className].filter(Boolean).join(" "),
|
|
59
|
+
children: /* @__PURE__ */ jsx(
|
|
60
|
+
CircularButton,
|
|
61
|
+
{
|
|
62
|
+
icon: current.icon,
|
|
63
|
+
size,
|
|
64
|
+
variant: "secondary",
|
|
65
|
+
tone: "neutral",
|
|
66
|
+
ariaLabel: cycleLabel(current.label, next.label),
|
|
67
|
+
tooltip: current.label,
|
|
68
|
+
onClick: () => pick(next.value)
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
}
|
|
39
74
|
return /* @__PURE__ */ jsx(
|
|
40
75
|
SegmentedControl,
|
|
41
76
|
{
|
|
@@ -45,11 +80,7 @@ const ThemeToggle = React.forwardRef(
|
|
|
45
80
|
className: ["ds-theme-toggle", !showLabels && "ds-theme-toggle--icons", className].filter(Boolean).join(" "),
|
|
46
81
|
segments,
|
|
47
82
|
value: setting,
|
|
48
|
-
onValueChange: (picked) =>
|
|
49
|
-
const next = picked;
|
|
50
|
-
if (value === void 0) setSetting(next);
|
|
51
|
-
onValueChange?.(next);
|
|
52
|
-
},
|
|
83
|
+
onValueChange: (picked) => pick(picked),
|
|
53
84
|
size,
|
|
54
85
|
showLabels
|
|
55
86
|
}
|
|
@@ -14,6 +14,8 @@ export interface ToastData {
|
|
|
14
14
|
dismissible?: boolean;
|
|
15
15
|
/** Custom icon override — Material Symbol name (string) or custom element (ReactNode) */
|
|
16
16
|
icon?: string | React.ReactNode;
|
|
17
|
+
/** A control under the text, such as an Undo. One whose control must not leave on a timer sets `duration` to 0. */
|
|
18
|
+
action?: React.ReactNode;
|
|
17
19
|
}
|
|
18
20
|
export interface ToastProviderProps {
|
|
19
21
|
/** Children that can access the toast context */
|
|
@@ -40,6 +42,11 @@ export interface ToastContextValue {
|
|
|
40
42
|
* Must be used within a ToastProvider.
|
|
41
43
|
*/
|
|
42
44
|
export declare const useToast: () => ToastContextValue;
|
|
45
|
+
/**
|
|
46
|
+
* The toast API when a ToastProvider surrounds the caller, else null: for a
|
|
47
|
+
* component that joins the host's stack if there is one and brings its own if not.
|
|
48
|
+
*/
|
|
49
|
+
export declare const useOptionalToast: () => ToastContextValue | null;
|
|
43
50
|
/**
|
|
44
51
|
* ToastProvider wraps your application and provides the
|
|
45
52
|
* toast context. Renders a fixed container that stacks
|
|
@@ -61,6 +68,8 @@ type ToastOwnProps = {
|
|
|
61
68
|
dismissible?: boolean;
|
|
62
69
|
/** Custom icon override — Material Symbol name (string) or custom element (ReactNode) */
|
|
63
70
|
icon?: string | React.ReactNode;
|
|
71
|
+
/** A control under the text, such as an Undo. */
|
|
72
|
+
action?: React.ReactNode;
|
|
64
73
|
/** Fires when the dismiss button is pressed. The button renders only when this is given (and `dismissible` is not false); the host owns removing the toast */
|
|
65
74
|
onDismiss?: () => void;
|
|
66
75
|
/** Accessible name of the dismiss button */
|
|
@@ -16,6 +16,7 @@ const useToast = () => {
|
|
|
16
16
|
}
|
|
17
17
|
return context;
|
|
18
18
|
};
|
|
19
|
+
const useOptionalToast = () => useContext(ToastContext);
|
|
19
20
|
const defaultIcons = {
|
|
20
21
|
info: "info",
|
|
21
22
|
positive: "check_circle",
|
|
@@ -31,6 +32,7 @@ const ToastItem = ({
|
|
|
31
32
|
variant,
|
|
32
33
|
dismissible,
|
|
33
34
|
icon,
|
|
35
|
+
action,
|
|
34
36
|
duration,
|
|
35
37
|
dismissLabel,
|
|
36
38
|
onDismiss
|
|
@@ -81,7 +83,8 @@ const ToastItem = ({
|
|
|
81
83
|
),
|
|
82
84
|
/* @__PURE__ */ jsxs("div", { className: `${baseClass}__content`, children: [
|
|
83
85
|
title && /* @__PURE__ */ jsx("p", { className: `${baseClass}__title`, children: title }),
|
|
84
|
-
description && /* @__PURE__ */ jsx("p", { className: `${baseClass}__description`, children: description })
|
|
86
|
+
description && /* @__PURE__ */ jsx("p", { className: `${baseClass}__description`, children: description }),
|
|
87
|
+
action && /* @__PURE__ */ jsx("div", { className: `${baseClass}__action`, children: action })
|
|
85
88
|
] }),
|
|
86
89
|
dismissible && /* @__PURE__ */ jsx(
|
|
87
90
|
"button",
|
|
@@ -164,6 +167,7 @@ const ToastProvider = ({
|
|
|
164
167
|
variant: t.variant || "info",
|
|
165
168
|
dismissible: t.dismissible ?? true,
|
|
166
169
|
icon: t.icon,
|
|
170
|
+
action: t.action,
|
|
167
171
|
duration: t.duration ?? MOTION_AUTO_DISMISS_MS,
|
|
168
172
|
dismissLabel,
|
|
169
173
|
onDismiss: dismiss
|
|
@@ -183,6 +187,7 @@ const Toast = React.forwardRef(
|
|
|
183
187
|
variant = "info",
|
|
184
188
|
dismissible = true,
|
|
185
189
|
icon,
|
|
190
|
+
action,
|
|
186
191
|
onDismiss,
|
|
187
192
|
dismissLabel = "Dismiss notification",
|
|
188
193
|
className = "",
|
|
@@ -202,7 +207,8 @@ const Toast = React.forwardRef(
|
|
|
202
207
|
),
|
|
203
208
|
/* @__PURE__ */ jsxs("div", { className: `${baseClass}__content`, children: [
|
|
204
209
|
title && /* @__PURE__ */ jsx("p", { className: `${baseClass}__title`, children: title }),
|
|
205
|
-
description && /* @__PURE__ */ jsx("p", { className: `${baseClass}__description`, children: description })
|
|
210
|
+
description && /* @__PURE__ */ jsx("p", { className: `${baseClass}__description`, children: description }),
|
|
211
|
+
action && /* @__PURE__ */ jsx("div", { className: `${baseClass}__action`, children: action })
|
|
206
212
|
] }),
|
|
207
213
|
dismissible && onDismiss && /* @__PURE__ */ jsx(
|
|
208
214
|
"button",
|
|
@@ -221,5 +227,6 @@ Toast.displayName = "Toast";
|
|
|
221
227
|
export {
|
|
222
228
|
Toast,
|
|
223
229
|
ToastProvider,
|
|
230
|
+
useOptionalToast,
|
|
224
231
|
useToast
|
|
225
232
|
};
|
|
@@ -16,11 +16,15 @@
|
|
|
16
16
|
/* Placed by the shared anchored-position behaviour
|
|
17
17
|
(src/behaviors/useAnchoredPosition.ts), which makes the panel fixed and
|
|
18
18
|
sets where it sits, so a scroll container around the trigger (a bordered
|
|
19
|
-
table's wrapper, a rail) cannot clip it.
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
table's wrapper, a rail) cannot clip it. While visible the panel is a
|
|
20
|
+
manual popover in the top layer (src/behaviors/useTopLayer), above every
|
|
21
|
+
z-index on the page; the floating layer only ranks it in a browser
|
|
22
|
+
without the Popover API. The side it lands on, after any flip, is its
|
|
23
|
+
data-side. */
|
|
22
24
|
.ds-tooltip__panel {
|
|
23
25
|
z-index: var(--layer-floating);
|
|
26
|
+
/* Over the popover default, so the arrow outside the box still draws. */
|
|
27
|
+
overflow: visible;
|
|
24
28
|
padding: var(--padding-150) var(--padding-200); /* 6px 8px */
|
|
25
29
|
border-radius: var(--radius-100); /* 4px */
|
|
26
30
|
/* The floating surface, same recipe as Popover: page background (the
|
|
@@ -43,7 +47,8 @@
|
|
|
43
47
|
browsers without those skip the fades but show and hide correctly. */
|
|
44
48
|
display: none;
|
|
45
49
|
opacity: 0;
|
|
46
|
-
|
|
50
|
+
/* overlay holds the panel in the top layer through the exit fade. */
|
|
51
|
+
transition-property: opacity, transform, display, overlay;
|
|
47
52
|
transition-duration: var(--motion-duration-fast);
|
|
48
53
|
transition-timing-function: var(--motion-ease-standard);
|
|
49
54
|
transition-behavior: allow-discrete;
|
|
@@ -24,8 +24,10 @@ export interface TooltipProps extends TooltipOwnProps, Omit<React.ComponentProps
|
|
|
24
24
|
* Rides the system's floating surface — page background, hairline border,
|
|
25
25
|
* floating shadow — so the bubble stays light in light theme and dark in dark.
|
|
26
26
|
*
|
|
27
|
-
* The panel stays in the trigger's DOM position (so it keeps
|
|
28
|
-
*
|
|
27
|
+
* The panel stays in the trigger's DOM position (so it keeps any theme
|
|
28
|
+
* scoped above it) but paints in the browser's top layer while visible
|
|
29
|
+
* (src/behaviors/useTopLayer), above every z-index on the page, and is
|
|
30
|
+
* `position: fixed`,
|
|
29
31
|
* placed from the trigger's rectangle each time it shows and again on scroll
|
|
30
32
|
* and resize while visible. That is what lets a tooltip inside a scroll
|
|
31
33
|
* container — a bordered table's wrapper, a rail — show whole instead of
|