@jirawatpyk/aura-react 5.5.0 → 5.7.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 +28 -2
- package/dist/aura.bundle.js +256 -69
- package/dist/cjs/index.cjs +256 -69
- package/dist/esm/AppShell.js +1 -1
- package/dist/esm/BottomNav.js +4 -1
- package/dist/esm/Breadcrumb.js +4 -1
- package/dist/esm/DataTable.js +34 -12
- package/dist/esm/Dialog.js +24 -2
- package/dist/esm/DropdownMenu.js +1 -0
- package/dist/esm/Menu.js +43 -27
- package/dist/esm/Toaster.js +148 -28
- package/dist/index.d.cts +48 -10
- package/dist/index.d.ts +48 -10
- package/dist/styles.css +37 -4
- package/dist/styles.layer.css +37 -4
- package/package.json +1 -1
package/dist/esm/DataTable.js
CHANGED
|
@@ -52,7 +52,27 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
|
|
|
52
52
|
);
|
|
53
53
|
const sort = sortState[0], setSort = sortState[1];
|
|
54
54
|
const selState = useMaybeControlled(props.selected, props.defaultSelected || [], props.onSelectionChange);
|
|
55
|
-
const
|
|
55
|
+
const canSelect = function(r) {
|
|
56
|
+
return !!r && (!props.isRowSelectable || !!props.isRowSelectable(r));
|
|
57
|
+
};
|
|
58
|
+
const rejected = {};
|
|
59
|
+
if (props.isRowSelectable)
|
|
60
|
+
rows.forEach(function(r) {
|
|
61
|
+
if (!canSelect(r)) rejected[String(r[rowKey])] = true;
|
|
62
|
+
});
|
|
63
|
+
const selected = selState[0].filter(function(k) {
|
|
64
|
+
return !rejected[String(k)];
|
|
65
|
+
}), setSelected = selState[1];
|
|
66
|
+
const droppedSig = selState[0].length === selected.length ? "" : "#" + selState[0].filter(function(k) {
|
|
67
|
+
return rejected[String(k)];
|
|
68
|
+
}).join("\0") + "#" + selected.join("\0");
|
|
69
|
+
React.useEffect(
|
|
70
|
+
function() {
|
|
71
|
+
if (droppedSig !== "") setSelected(selected);
|
|
72
|
+
},
|
|
73
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
74
|
+
[droppedSig]
|
|
75
|
+
);
|
|
56
76
|
const pageState = useMaybeControlled(
|
|
57
77
|
props.page,
|
|
58
78
|
props.defaultPage || 1,
|
|
@@ -463,7 +483,7 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
|
|
|
463
483
|
el.focus();
|
|
464
484
|
}
|
|
465
485
|
}
|
|
466
|
-
const pageKeys = pageRows.map(function(r) {
|
|
486
|
+
const pageKeys = pageRows.filter(canSelect).map(function(r) {
|
|
467
487
|
return r[rowKey];
|
|
468
488
|
});
|
|
469
489
|
const selSet = {};
|
|
@@ -727,10 +747,12 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
|
|
|
727
747
|
if (inner) inner.focus();
|
|
728
748
|
else handled = false;
|
|
729
749
|
} else if (k === " " || k === "Enter") {
|
|
730
|
-
if (r === 0 && !col && selectable && nRows)
|
|
731
|
-
|
|
732
|
-
else if (r
|
|
733
|
-
else if (r > 0 && k === "
|
|
750
|
+
if (r === 0 && !col && selectable && nRows) {
|
|
751
|
+
if (pageKeys.length) toggleAll();
|
|
752
|
+
} else if (r === 0 && col && col.sortable && canSortAny) sortBy(col.key);
|
|
753
|
+
else if (r > 0 && k === " " && selectable) {
|
|
754
|
+
if (canSelect(row)) toggle(row[rowKey], !selSet[row[rowKey]]);
|
|
755
|
+
} else if (r > 0 && k === "Enter" && rowHref && !target.querySelector("button, a[href]:not(.aura-table__row-link), input, select, textarea"))
|
|
734
756
|
followRow(target.closest('[role="row"]'), e);
|
|
735
757
|
else if (r > 0 && k === "Enter" && target.querySelector("button, a[href], input, select, textarea"))
|
|
736
758
|
target.querySelector("button, a[href], input, select, textarea").focus();
|
|
@@ -759,7 +781,7 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
|
|
|
759
781
|
activeState[1]({ r: 0, c: 0 });
|
|
760
782
|
}
|
|
761
783
|
},
|
|
762
|
-
|
|
784
|
+
pageKeys.length ? /* @__PURE__ */ React.createElement(
|
|
763
785
|
Checkbox,
|
|
764
786
|
{
|
|
765
787
|
hideLabel: true,
|
|
@@ -770,7 +792,7 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
|
|
|
770
792
|
onChange: toggleAll
|
|
771
793
|
}
|
|
772
794
|
) : null,
|
|
773
|
-
|
|
795
|
+
pageKeys.length ? /* @__PURE__ */ React.createElement("span", { className: "aura-table__sel-text" }, nSel ? t.selectedCount(nSel) : t.selectAll) : /* @__PURE__ */ React.createElement("span", { className: "aura-sr-only" }, t.selectRows)
|
|
774
796
|
)
|
|
775
797
|
);
|
|
776
798
|
vis.forEach(function(c, i) {
|
|
@@ -908,12 +930,12 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
|
|
|
908
930
|
className: cx("aura-table__sel", nPinned && "is-pinned"),
|
|
909
931
|
tabIndex: tabFor(ri, 0),
|
|
910
932
|
"data-rc": ri + ":0",
|
|
911
|
-
"aria-label": t.selectRow(k),
|
|
933
|
+
"aria-label": canSelect(r) ? t.selectRow(k) : props.rowSelectDisabledLabel ? props.rowSelectDisabledLabel(r) : void 0,
|
|
912
934
|
onFocus: function(e) {
|
|
913
935
|
if (e.target === e.currentTarget) activeState[1]({ r: ri, c: 0 });
|
|
914
936
|
}
|
|
915
937
|
},
|
|
916
|
-
/* @__PURE__ */ React.createElement(
|
|
938
|
+
canSelect(r) ? /* @__PURE__ */ React.createElement(
|
|
917
939
|
Checkbox,
|
|
918
940
|
{
|
|
919
941
|
hideLabel: true,
|
|
@@ -924,7 +946,7 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
|
|
|
924
946
|
toggle(k, on);
|
|
925
947
|
}
|
|
926
948
|
}
|
|
927
|
-
)
|
|
949
|
+
) : null
|
|
928
950
|
)
|
|
929
951
|
);
|
|
930
952
|
vis.forEach(function(c, j) {
|
|
@@ -1027,7 +1049,7 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
|
|
|
1027
1049
|
key: k,
|
|
1028
1050
|
role: "row",
|
|
1029
1051
|
"aria-rowindex": first + i + 2,
|
|
1030
|
-
"aria-selected": selectable ? isSel : void 0,
|
|
1052
|
+
"aria-selected": selectable && canSelect(r) ? isSel : void 0,
|
|
1031
1053
|
className: cx(
|
|
1032
1054
|
"aura-table__row",
|
|
1033
1055
|
isSel && "is-selected",
|
package/dist/esm/Dialog.js
CHANGED
|
@@ -12,6 +12,7 @@ const Dialog = React.forwardRef(function Dialog2(props, ref) {
|
|
|
12
12
|
function close() {
|
|
13
13
|
if (props.dismissible !== false && props.onClose) props.onClose();
|
|
14
14
|
}
|
|
15
|
+
const scrimCloses = props.dismissible !== false && (props.dismissOnScrim !== void 0 ? props.dismissOnScrim : props.role !== "alertdialog");
|
|
15
16
|
const modal = useModal(props.open, own, {
|
|
16
17
|
autoFocus: props.autoFocus,
|
|
17
18
|
onEscape: close,
|
|
@@ -20,7 +21,17 @@ const Dialog = React.forwardRef(function Dialog2(props, ref) {
|
|
|
20
21
|
});
|
|
21
22
|
if (!modal.ready) return null;
|
|
22
23
|
return createPortal(
|
|
23
|
-
/* @__PURE__ */ React.createElement("div", { className: "aura-dialog-layer", "data-density": density, onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React.createElement(
|
|
24
|
+
/* @__PURE__ */ React.createElement("div", { className: "aura-dialog-layer", "data-density": density, onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React.createElement(
|
|
25
|
+
"div",
|
|
26
|
+
{
|
|
27
|
+
className: "aura-scrim",
|
|
28
|
+
onClick: scrimCloses ? close : void 0,
|
|
29
|
+
onMouseDown: scrimCloses ? void 0 : function(e) {
|
|
30
|
+
e.preventDefault();
|
|
31
|
+
},
|
|
32
|
+
"aria-hidden": true
|
|
33
|
+
}
|
|
34
|
+
), /* @__PURE__ */ React.createElement(
|
|
24
35
|
"div",
|
|
25
36
|
{
|
|
26
37
|
ref: merged,
|
|
@@ -54,8 +65,19 @@ const Drawer = React.forwardRef(function Drawer2(props, ref) {
|
|
|
54
65
|
});
|
|
55
66
|
if (!modal.ready) return null;
|
|
56
67
|
const side = props.side === "left" ? "left" : "right";
|
|
68
|
+
const drawerScrimCloses = props.dismissible !== false && props.dismissOnScrim !== false;
|
|
57
69
|
return createPortal(
|
|
58
|
-
/* @__PURE__ */ React.createElement("div", { className: "aura-dialog-layer aura-drawer-layer", "data-density": density, onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React.createElement(
|
|
70
|
+
/* @__PURE__ */ React.createElement("div", { className: "aura-dialog-layer aura-drawer-layer", "data-density": density, onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React.createElement(
|
|
71
|
+
"div",
|
|
72
|
+
{
|
|
73
|
+
className: "aura-scrim",
|
|
74
|
+
onClick: drawerScrimCloses ? close : void 0,
|
|
75
|
+
onMouseDown: !drawerScrimCloses ? function(e) {
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
} : void 0,
|
|
78
|
+
"aria-hidden": true
|
|
79
|
+
}
|
|
80
|
+
), /* @__PURE__ */ React.createElement(
|
|
59
81
|
"div",
|
|
60
82
|
{
|
|
61
83
|
ref: merged,
|
package/dist/esm/DropdownMenu.js
CHANGED
|
@@ -36,6 +36,7 @@ const DropdownMenu = React.forwardRef(function DropdownMenu2(props, ref) {
|
|
|
36
36
|
label: props.label,
|
|
37
37
|
items: props.items,
|
|
38
38
|
linkComponent: props.linkComponent,
|
|
39
|
+
header: props.header,
|
|
39
40
|
onClose: function(restore) {
|
|
40
41
|
setAnchor(null);
|
|
41
42
|
if (restore && typeof anchor.focus === "function") anchor.focus();
|
package/dist/esm/Menu.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
|
-
import { cx, useIsoLayoutEffect, useMounted, useMergedRef } from "./internal.js";
|
|
4
|
+
import { cx, uid, useIsoLayoutEffect, useMounted, useMergedRef } from "./internal.js";
|
|
5
5
|
import { Icon } from "./Icon.js";
|
|
6
6
|
import { useLinkComponent } from "./locale.js";
|
|
7
7
|
const Menu = React.forwardRef(function Menu2(props, ref) {
|
|
@@ -10,6 +10,8 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
|
|
|
10
10
|
const pos = posState[0], setPos = posState[1];
|
|
11
11
|
const items = props.items || [];
|
|
12
12
|
const mounted = useMounted();
|
|
13
|
+
const headerId = uid();
|
|
14
|
+
const hasHeader = props.header != null && props.header !== false && props.header !== "";
|
|
13
15
|
const Link = useLinkComponent(props.linkComponent);
|
|
14
16
|
useIsoLayoutEffect(
|
|
15
17
|
function() {
|
|
@@ -27,7 +29,20 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
|
|
|
27
29
|
const left = Math.max(8, Math.min(r.right - mw, window.innerWidth - mw - 8));
|
|
28
30
|
setPos({ top, left, maxHeight });
|
|
29
31
|
},
|
|
30
|
-
[props.anchor, mounted]
|
|
32
|
+
[props.anchor, mounted, hasHeader]
|
|
33
|
+
);
|
|
34
|
+
const hadHeader = React.useRef(hasHeader);
|
|
35
|
+
React.useEffect(
|
|
36
|
+
function() {
|
|
37
|
+
if (hadHeader.current === hasHeader) return;
|
|
38
|
+
hadHeader.current = hasHeader;
|
|
39
|
+
const a = document.activeElement;
|
|
40
|
+
if (own.current && (!a || a === document.body)) {
|
|
41
|
+
const first = own.current.querySelector('[role^="menuitem"]:not([disabled])');
|
|
42
|
+
if (first) first.focus();
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
[hasHeader]
|
|
31
46
|
);
|
|
32
47
|
React.useEffect(
|
|
33
48
|
function() {
|
|
@@ -53,22 +68,22 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
|
|
|
53
68
|
[mounted]
|
|
54
69
|
);
|
|
55
70
|
function onKeyDown(e) {
|
|
56
|
-
const
|
|
71
|
+
const list2 = Array.prototype.slice.call(
|
|
57
72
|
own.current.querySelectorAll('[role^="menuitem"]:not([disabled])')
|
|
58
73
|
);
|
|
59
|
-
const i =
|
|
74
|
+
const i = list2.indexOf(document.activeElement);
|
|
60
75
|
if (e.key === "ArrowDown") {
|
|
61
76
|
e.preventDefault();
|
|
62
|
-
|
|
77
|
+
list2[(i + 1) % list2.length].focus();
|
|
63
78
|
} else if (e.key === "ArrowUp") {
|
|
64
79
|
e.preventDefault();
|
|
65
|
-
|
|
80
|
+
list2[(i - 1 + list2.length) % list2.length].focus();
|
|
66
81
|
} else if (e.key === "Home") {
|
|
67
82
|
e.preventDefault();
|
|
68
|
-
|
|
83
|
+
list2[0].focus();
|
|
69
84
|
} else if (e.key === "End") {
|
|
70
85
|
e.preventDefault();
|
|
71
|
-
|
|
86
|
+
list2[list2.length - 1].focus();
|
|
72
87
|
} else if (e.key === " " && document.activeElement && document.activeElement.tagName === "A") {
|
|
73
88
|
e.preventDefault();
|
|
74
89
|
document.activeElement.click();
|
|
@@ -80,9 +95,9 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
|
|
|
80
95
|
}
|
|
81
96
|
e.stopPropagation();
|
|
82
97
|
}
|
|
83
|
-
function groupItems(
|
|
98
|
+
function groupItems(list2) {
|
|
84
99
|
const out = [];
|
|
85
|
-
|
|
100
|
+
list2.forEach(function(it, i) {
|
|
86
101
|
const g = it.type === "radio" && it.group ? it.group : null;
|
|
87
102
|
const last = out[out.length - 1];
|
|
88
103
|
if (last && last.group === g && g !== null) last.items.push({ it, i });
|
|
@@ -137,27 +152,28 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
|
|
|
137
152
|
body
|
|
138
153
|
);
|
|
139
154
|
}
|
|
140
|
-
const
|
|
155
|
+
const style = {
|
|
156
|
+
top: pos ? pos.top : -9999,
|
|
157
|
+
left: pos ? pos.left : -9999,
|
|
158
|
+
maxHeight: pos && pos.maxHeight ? pos.maxHeight : void 0
|
|
159
|
+
};
|
|
160
|
+
const list = groupItems(items).map(function(block, bi) {
|
|
161
|
+
const rendered = block.items.map(function(x) {
|
|
162
|
+
return renderItem(x.it, x.i);
|
|
163
|
+
});
|
|
164
|
+
return block.group ? /* @__PURE__ */ React.createElement("div", { key: "g" + bi, role: "group", "aria-label": block.group, className: "aura-menu__group" }, rendered) : /* @__PURE__ */ React.createElement(React.Fragment, { key: "f" + bi }, rendered);
|
|
165
|
+
});
|
|
166
|
+
const el = hasHeader ? /* @__PURE__ */ React.createElement("div", { ref: merged, className: "aura-menu has-header", onKeyDown, style }, /* @__PURE__ */ React.createElement(
|
|
141
167
|
"div",
|
|
142
168
|
{
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
onKeyDown,
|
|
148
|
-
style: {
|
|
149
|
-
top: pos ? pos.top : -9999,
|
|
150
|
-
left: pos ? pos.left : -9999,
|
|
151
|
-
maxHeight: pos && pos.maxHeight ? pos.maxHeight : void 0
|
|
169
|
+
className: "aura-menu__header",
|
|
170
|
+
id: headerId,
|
|
171
|
+
onMouseDown: function(e) {
|
|
172
|
+
e.preventDefault();
|
|
152
173
|
}
|
|
153
174
|
},
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return renderItem(x.it, x.i);
|
|
157
|
-
});
|
|
158
|
-
return block.group ? /* @__PURE__ */ React.createElement("div", { key: "g" + bi, role: "group", "aria-label": block.group, className: "aura-menu__group" }, rendered) : /* @__PURE__ */ React.createElement(React.Fragment, { key: "f" + bi }, rendered);
|
|
159
|
-
})
|
|
160
|
-
);
|
|
175
|
+
props.header
|
|
176
|
+
), /* @__PURE__ */ React.createElement("div", { role: "menu", "aria-label": props.label, "aria-describedby": headerId, className: "aura-menu__list" }, list)) : /* @__PURE__ */ React.createElement("div", { ref: merged, role: "menu", "aria-label": props.label, className: "aura-menu", onKeyDown, style }, list);
|
|
161
177
|
return mounted ? createPortal(el, document.body) : null;
|
|
162
178
|
});
|
|
163
179
|
export {
|
package/dist/esm/Toaster.js
CHANGED
|
@@ -3,8 +3,8 @@ import * as React from "react";
|
|
|
3
3
|
import { ALERT_ICON } from "./Alert.js";
|
|
4
4
|
import { Icon } from "./Icon.js";
|
|
5
5
|
import { IconButton } from "./IconButton.js";
|
|
6
|
-
import { cx, useMounted } from "./internal.js";
|
|
7
|
-
import { useStrings } from "./locale.js";
|
|
6
|
+
import { cx, plainClick, useIsoLayoutEffect, useMounted } from "./internal.js";
|
|
7
|
+
import { useLinkComponent, useStrings } from "./locale.js";
|
|
8
8
|
import { createPortal } from "react-dom";
|
|
9
9
|
const MAX_VISIBLE = 3;
|
|
10
10
|
const toastState = {
|
|
@@ -73,10 +73,35 @@ toast.dismiss = function(id) {
|
|
|
73
73
|
promote();
|
|
74
74
|
emitToasts();
|
|
75
75
|
};
|
|
76
|
+
const hotkeyOrigin = /* @__PURE__ */ new WeakMap();
|
|
77
|
+
function outsideToaster(el) {
|
|
78
|
+
return el && el !== document.body && !el.closest(".aura-toaster") ? el : null;
|
|
79
|
+
}
|
|
76
80
|
function ToastItem(props) {
|
|
77
81
|
const str = useStrings();
|
|
82
|
+
const Link = useLinkComponent();
|
|
83
|
+
const node = React.useRef(null);
|
|
84
|
+
const origin = React.useRef(null);
|
|
85
|
+
useIsoLayoutEffect(function() {
|
|
86
|
+
origin.current = outsideToaster(document.activeElement);
|
|
87
|
+
const el = node.current;
|
|
88
|
+
return function() {
|
|
89
|
+
if (!el || !el.contains(document.activeElement)) return;
|
|
90
|
+
const back = [hotkeyOrigin.get(el), origin.current].filter(function(b) {
|
|
91
|
+
return !!b && b.isConnected;
|
|
92
|
+
})[0];
|
|
93
|
+
hotkeyOrigin.delete(el);
|
|
94
|
+
setTimeout(function() {
|
|
95
|
+
if (back && back.isConnected && (!document.activeElement || document.activeElement === document.body))
|
|
96
|
+
back.focus();
|
|
97
|
+
}, 0);
|
|
98
|
+
};
|
|
99
|
+
}, []);
|
|
78
100
|
const t = props.toast, timer = React.useRef(null), left = React.useRef(t.duration || 5e3), since = React.useRef(0);
|
|
79
|
-
const hover = React.useRef(false);
|
|
101
|
+
const hover = React.useRef(false), focused = React.useRef(false);
|
|
102
|
+
function syncFocus() {
|
|
103
|
+
if (focused.current && !(node.current && node.current.contains(document.activeElement))) focused.current = false;
|
|
104
|
+
}
|
|
80
105
|
function start() {
|
|
81
106
|
if (left.current === Infinity || timer.current) return;
|
|
82
107
|
since.current = Date.now();
|
|
@@ -85,11 +110,10 @@ function ToastItem(props) {
|
|
|
85
110
|
}, left.current);
|
|
86
111
|
}
|
|
87
112
|
function resume() {
|
|
88
|
-
|
|
89
|
-
start();
|
|
113
|
+
syncFocus();
|
|
114
|
+
if (!hover.current && !focused.current) start();
|
|
90
115
|
}
|
|
91
116
|
function pause() {
|
|
92
|
-
hover.current = true;
|
|
93
117
|
if (timer.current) {
|
|
94
118
|
clearTimeout(timer.current);
|
|
95
119
|
timer.current = null;
|
|
@@ -101,23 +125,56 @@ function ToastItem(props) {
|
|
|
101
125
|
if (timer.current) clearTimeout(timer.current);
|
|
102
126
|
timer.current = null;
|
|
103
127
|
left.current = t.duration || 5e3;
|
|
104
|
-
|
|
128
|
+
syncFocus();
|
|
129
|
+
if (!hover.current && !focused.current) start();
|
|
105
130
|
return function() {
|
|
106
131
|
if (timer.current) clearTimeout(timer.current);
|
|
107
132
|
};
|
|
108
133
|
},
|
|
109
134
|
[t.rev]
|
|
110
135
|
);
|
|
136
|
+
const rich = t.description != null && typeof t.description !== "boolean" && t.description !== "";
|
|
137
|
+
const actions = (t.actions && t.actions.length ? t.actions : t.action ? [t.action] : []).slice(0, 2);
|
|
138
|
+
function run(a) {
|
|
139
|
+
if (a.onClick) a.onClick();
|
|
140
|
+
if (a.dismiss !== false) toast.dismiss(t.id);
|
|
141
|
+
}
|
|
111
142
|
return /* @__PURE__ */ React.createElement(
|
|
112
143
|
"div",
|
|
113
144
|
{
|
|
114
|
-
|
|
145
|
+
ref: node,
|
|
146
|
+
onKeyDown: function(e) {
|
|
147
|
+
if (e.key === "Escape") {
|
|
148
|
+
e.stopPropagation();
|
|
149
|
+
toast.dismiss(t.id);
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
className: cx(
|
|
153
|
+
"aura-toast",
|
|
154
|
+
"aura-toast--" + t.tone,
|
|
155
|
+
t.loading && "is-loading",
|
|
156
|
+
/* Two actions, or one beside a description, go on their own row under the text so it keeps its width. */
|
|
157
|
+
(actions.length > 1 || actions.length && rich) && "has-action-row"
|
|
158
|
+
),
|
|
115
159
|
role: t.tone === "danger" ? "alert" : "status",
|
|
116
160
|
"aria-busy": t.loading || void 0,
|
|
117
|
-
onMouseEnter:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
161
|
+
onMouseEnter: function() {
|
|
162
|
+
hover.current = true;
|
|
163
|
+
pause();
|
|
164
|
+
},
|
|
165
|
+
onMouseLeave: function() {
|
|
166
|
+
hover.current = false;
|
|
167
|
+
resume();
|
|
168
|
+
},
|
|
169
|
+
onFocus: function() {
|
|
170
|
+
focused.current = true;
|
|
171
|
+
pause();
|
|
172
|
+
},
|
|
173
|
+
onBlur: function(e) {
|
|
174
|
+
if (node.current && e.relatedTarget && node.current.contains(e.relatedTarget)) return;
|
|
175
|
+
focused.current = false;
|
|
176
|
+
resume();
|
|
177
|
+
}
|
|
121
178
|
},
|
|
122
179
|
/* @__PURE__ */ React.createElement(
|
|
123
180
|
Icon,
|
|
@@ -126,19 +183,33 @@ function ToastItem(props) {
|
|
|
126
183
|
className: cx("aura-toast__icon", t.loading && "aura-spin")
|
|
127
184
|
}
|
|
128
185
|
),
|
|
129
|
-
/* @__PURE__ */ React.createElement("div", { className: "aura-toast__body" }, /* @__PURE__ */ React.createElement("p", { className: "aura-toast__title" }, t.title),
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
186
|
+
/* @__PURE__ */ React.createElement("div", { className: "aura-toast__body" }, /* @__PURE__ */ React.createElement("p", { className: "aura-toast__title" }, t.title), rich ? /* @__PURE__ */ React.createElement("div", { className: "aura-toast__text" }, t.description) : null),
|
|
187
|
+
actions.length ? /* @__PURE__ */ React.createElement("div", { className: "aura-toast__actions" }, actions.map(function(a, i) {
|
|
188
|
+
return a.href ? /* @__PURE__ */ React.createElement(
|
|
189
|
+
Link,
|
|
190
|
+
{
|
|
191
|
+
key: i,
|
|
192
|
+
href: a.href,
|
|
193
|
+
className: "aura-toast__action",
|
|
194
|
+
onClick: function(e) {
|
|
195
|
+
if (plainClick(e)) run(a);
|
|
196
|
+
else if (a.onClick) a.onClick();
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
a.label
|
|
200
|
+
) : /* @__PURE__ */ React.createElement(
|
|
201
|
+
"button",
|
|
202
|
+
{
|
|
203
|
+
key: i,
|
|
204
|
+
type: "button",
|
|
205
|
+
className: "aura-toast__action",
|
|
206
|
+
onClick: function() {
|
|
207
|
+
run(a);
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
a.label
|
|
211
|
+
);
|
|
212
|
+
})) : null,
|
|
142
213
|
/* @__PURE__ */ React.createElement(
|
|
143
214
|
IconButton,
|
|
144
215
|
{
|
|
@@ -152,7 +223,22 @@ function ToastItem(props) {
|
|
|
152
223
|
)
|
|
153
224
|
);
|
|
154
225
|
}
|
|
155
|
-
function
|
|
226
|
+
function hotkeyMatcher(spec) {
|
|
227
|
+
const parts = spec.split("+").map(function(p) {
|
|
228
|
+
return p.trim().toLowerCase();
|
|
229
|
+
});
|
|
230
|
+
const key = parts[parts.length - 1] || "";
|
|
231
|
+
const code = /^[a-z]$/.test(key) ? "Key" + key.toUpperCase() : /^[0-9]$/.test(key) ? "Digit" + key : null;
|
|
232
|
+
const want = function(m) {
|
|
233
|
+
return parts.indexOf(m) >= 0 && parts.indexOf(m) < parts.length - 1;
|
|
234
|
+
};
|
|
235
|
+
return function(e) {
|
|
236
|
+
if (e.altKey !== want("alt") || e.ctrlKey !== want("ctrl") || e.shiftKey !== want("shift") || e.metaKey !== (want("meta") || want("cmd")))
|
|
237
|
+
return false;
|
|
238
|
+
return code ? e.code === code : e.key.toLowerCase() === key;
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function Toaster(props = {}) {
|
|
156
242
|
const t = useStrings();
|
|
157
243
|
const mounted = useMounted();
|
|
158
244
|
const s = React.useState(toastState.list);
|
|
@@ -165,15 +251,49 @@ function Toaster(props) {
|
|
|
165
251
|
});
|
|
166
252
|
};
|
|
167
253
|
}, []);
|
|
254
|
+
const region = React.useRef(null);
|
|
255
|
+
const hotkey = props.hotkey === void 0 ? "Alt+T" : props.hotkey;
|
|
256
|
+
React.useEffect(
|
|
257
|
+
function() {
|
|
258
|
+
if (!hotkey) return;
|
|
259
|
+
const match = hotkeyMatcher(hotkey);
|
|
260
|
+
function onKey(e) {
|
|
261
|
+
if (!e.key || e.repeat || e.isComposing || !match(e) || !region.current) return;
|
|
262
|
+
const el = e.target;
|
|
263
|
+
const editable = !!el && (el.isContentEditable || /^(INPUT|TEXTAREA)$/.test(el.tagName || ""));
|
|
264
|
+
const mac = /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent || "");
|
|
265
|
+
if (editable && mac && e.altKey && !e.ctrlKey && !e.metaKey && e.key.length === 1) return;
|
|
266
|
+
const items = region.current.querySelectorAll(".aura-toast");
|
|
267
|
+
const last = items[items.length - 1];
|
|
268
|
+
if (!last) return;
|
|
269
|
+
const target = last.querySelector(".aura-toast__action") || last.querySelector("button");
|
|
270
|
+
if (!target) return;
|
|
271
|
+
e.preventDefault();
|
|
272
|
+
const from = outsideToaster(document.activeElement);
|
|
273
|
+
if (from) hotkeyOrigin.set(last, from);
|
|
274
|
+
target.focus();
|
|
275
|
+
}
|
|
276
|
+
document.addEventListener("keydown", onKey);
|
|
277
|
+
return function() {
|
|
278
|
+
document.removeEventListener("keydown", onKey);
|
|
279
|
+
};
|
|
280
|
+
},
|
|
281
|
+
[hotkey]
|
|
282
|
+
);
|
|
168
283
|
if (!mounted) return null;
|
|
284
|
+
const pos = props.position || "bottom";
|
|
285
|
+
const offset = props.offset;
|
|
169
286
|
return createPortal(
|
|
170
287
|
/* @__PURE__ */ React.createElement(
|
|
171
288
|
"div",
|
|
172
289
|
{
|
|
173
|
-
|
|
290
|
+
ref: region,
|
|
291
|
+
className: cx("aura-toaster", pos.indexOf("top") === 0 && "is-top", /center$/.test(pos) && "is-center"),
|
|
292
|
+
style: offset != null ? { "--aura-toaster-offset": typeof offset === "number" ? offset + "px" : offset } : void 0,
|
|
174
293
|
role: "region",
|
|
175
294
|
"aria-live": "polite",
|
|
176
|
-
"aria-label": t.notifications
|
|
295
|
+
"aria-label": hotkey ? t.notifications + " (" + hotkey + ")" : t.notifications,
|
|
296
|
+
"aria-keyshortcuts": hotkey || void 0
|
|
177
297
|
},
|
|
178
298
|
s[0].map(function(t2) {
|
|
179
299
|
return /* @__PURE__ */ React.createElement(ToastItem, { key: t2.id, toast: t2 });
|
package/dist/index.d.cts
CHANGED
|
@@ -124,6 +124,11 @@ export interface DataTableProps<Row extends Record<string, any> = Record<string,
|
|
|
124
124
|
onSortChange?: ((sort: DataTableSort | null) => void) | undefined;
|
|
125
125
|
/** Adds a checkbox column. */
|
|
126
126
|
selectable?: boolean | undefined;
|
|
127
|
+
/** Which rows can be selected (5.6). A row it rejects shows no checkbox (the cell stays, so columns line up), is
|
|
128
|
+
* skipped by select-all, Space and the count, and never appears in `onSelectionChange` — even if passed in. */
|
|
129
|
+
isRowSelectable?: RowCallback<boolean, Row> | undefined;
|
|
130
|
+
/** Accessible name for a rejected row's empty cell, e.g. `(r) => 'Not awaiting review'` (5.6). */
|
|
131
|
+
rowSelectDisabledLabel?: RowCallback<string, Row> | undefined;
|
|
127
132
|
/** Controlled selection (row keys); pair with onSelectionChange. */
|
|
128
133
|
selected?: Array<string | number> | undefined;
|
|
129
134
|
/** Initial selection when uncontrolled. */
|
|
@@ -305,6 +310,9 @@ export interface MenuProps {
|
|
|
305
310
|
autoFocus?: boolean | undefined;
|
|
306
311
|
/** Router link for items with `href`; defaults to AuraProvider's `linkComponent`, then `<a>`. (4.19) */
|
|
307
312
|
linkComponent?: React$1.ElementType | undefined;
|
|
313
|
+
/** Content above the items (5.7), e.g. who is signed in. Not an item: arrow keys skip it; it is the menu's
|
|
314
|
+
* accessible description, read once when the menu opens. */
|
|
315
|
+
header?: React$1.ReactNode | undefined;
|
|
308
316
|
}
|
|
309
317
|
export interface FieldProps {
|
|
310
318
|
/** Visible label; also the accessible name. */
|
|
@@ -487,14 +495,23 @@ export interface AlertProps {
|
|
|
487
495
|
onDismiss?: (() => void) | undefined;
|
|
488
496
|
className?: string | undefined;
|
|
489
497
|
}
|
|
498
|
+
/** A button (or, with `href`, a link through AuraProvider's `linkComponent`) on a toast (5.6: `href`, `dismiss`). */
|
|
499
|
+
export interface ToastAction {
|
|
500
|
+
label: string;
|
|
501
|
+
onClick?: (() => void) | undefined;
|
|
502
|
+
/** Makes the action a link (routes through `linkComponent`, so `next/link` navigates client-side). */
|
|
503
|
+
href?: string | undefined;
|
|
504
|
+
/** Default true: the toast closes when the action runs. `false` keeps it (a list of items to work through). */
|
|
505
|
+
dismiss?: boolean | undefined;
|
|
506
|
+
}
|
|
490
507
|
export interface ToastOptions {
|
|
491
508
|
title: string;
|
|
492
|
-
|
|
509
|
+
/** Text, or (5.6) rich content — lines with their own links, read once inside the toast's live region. */
|
|
510
|
+
description?: React$1.ReactNode | undefined;
|
|
493
511
|
tone?: FeedbackTone | undefined;
|
|
494
|
-
action?:
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
} | undefined;
|
|
512
|
+
action?: ToastAction | undefined;
|
|
513
|
+
/** Up to two actions in a row (5.6); used instead of `action` when given. */
|
|
514
|
+
actions?: ToastAction[] | undefined;
|
|
498
515
|
/** ms before it closes itself. Default 5000; Infinity keeps it until dismissed. */
|
|
499
516
|
duration?: number | undefined;
|
|
500
517
|
/** Reuse an id to replace a toast in place (same position, timer restarts) — never a second toast. */
|
|
@@ -533,6 +550,9 @@ export interface DialogProps {
|
|
|
533
550
|
size?: "sm" | "md" | "lg" | undefined;
|
|
534
551
|
/** Default true. False hides the close button and ignores Escape and scrim clicks. */
|
|
535
552
|
dismissible?: boolean | undefined;
|
|
553
|
+
/** Whether a click on the scrim closes it (5.7). Default true, false for `role="alertdialog"`: a stray click
|
|
554
|
+
* outside shouldn't throw away a typed reason. Escape and the close button still close it. */
|
|
555
|
+
dismissOnScrim?: boolean | undefined;
|
|
536
556
|
/** Use `alertdialog` for destructive confirmations. */
|
|
537
557
|
role?: "dialog" | "alertdialog" | undefined;
|
|
538
558
|
/** Default true. Turn off only for static demos. */
|
|
@@ -624,6 +644,7 @@ export interface SideNavProps {
|
|
|
624
644
|
}
|
|
625
645
|
export interface BreadcrumbProps {
|
|
626
646
|
/** Root first; the last item is the current page. */
|
|
647
|
+
/** An item with neither `href` nor `onClick` is plain text (5.7), for a segment with no page of its own. */
|
|
627
648
|
items: Array<{
|
|
628
649
|
label: string;
|
|
629
650
|
href?: string | undefined;
|
|
@@ -651,6 +672,9 @@ export interface DropdownMenuProps {
|
|
|
651
672
|
label?: string | undefined;
|
|
652
673
|
/** Router link for items with `href`. (4.19) */
|
|
653
674
|
linkComponent?: React$1.ElementType | undefined;
|
|
675
|
+
/** Content above the items (5.7), e.g. name, email and role in an account menu. Not an item: arrow keys skip
|
|
676
|
+
* it; it is the menu's accessible description (`aria-describedby`). */
|
|
677
|
+
header?: React$1.ReactNode | undefined;
|
|
654
678
|
}
|
|
655
679
|
export interface ComboboxOption {
|
|
656
680
|
value: string;
|
|
@@ -851,6 +875,8 @@ export interface DrawerProps {
|
|
|
851
875
|
footer?: React$1.ReactNode | undefined;
|
|
852
876
|
/** Scrim click and Escape close it. Default true. */
|
|
853
877
|
dismissible?: boolean | undefined;
|
|
878
|
+
/** Whether a click on the scrim closes it (5.7). Default true; Escape and the close button still close it. */
|
|
879
|
+
dismissOnScrim?: boolean | undefined;
|
|
854
880
|
/** Default true: focus moves to the element with `data-autofocus`, else the first control in the body. False only for static demos. */
|
|
855
881
|
autoFocus?: boolean | undefined;
|
|
856
882
|
/** Needed when there is no title. */
|
|
@@ -891,7 +917,8 @@ export interface AppShellProps {
|
|
|
891
917
|
children?: React$1.ReactNode | undefined;
|
|
892
918
|
navLabel?: string | undefined;
|
|
893
919
|
menuLabel?: string | undefined;
|
|
894
|
-
/** id of `<main>`, for a skip link. Default `main`.
|
|
920
|
+
/** id of `<main>`, for a skip link. Default `main`. `<main>` has `tabIndex={-1}` (5.7), so a skip link or
|
|
921
|
+
* `document.getElementById(mainId).focus()` moves focus there (no ring is drawn on it). */
|
|
895
922
|
mainId?: string | undefined;
|
|
896
923
|
/** A BottomNav for phones (4.19). Shown below its `hideFrom` breakpoint; the content keeps room for it, and a
|
|
897
924
|
* viewport ActionBar sits on top of it. With a `bottomNav` and no `nav`, there is no menu button. */
|
|
@@ -965,6 +992,9 @@ export interface BottomNavItem {
|
|
|
965
992
|
badge?: boolean | undefined;
|
|
966
993
|
/** Read after the label when `badge` is set, e.g. "new". */
|
|
967
994
|
badgeLabel?: string | undefined;
|
|
995
|
+
/** The full name for screen readers when `label` is shortened to fit (5.7), e.g. label "Konto", ariaLabel
|
|
996
|
+
* "Mitt konto". Include the visible label in it (WCAG 2.5.3). The count or badge is still appended. */
|
|
997
|
+
ariaLabel?: string | undefined;
|
|
968
998
|
}
|
|
969
999
|
export interface BottomNavProps {
|
|
970
1000
|
/** 2–5 items. */
|
|
@@ -1285,10 +1315,18 @@ export declare namespace toast {
|
|
|
1285
1315
|
var dismiss: (id: string) => void;
|
|
1286
1316
|
}
|
|
1287
1317
|
export interface ToasterProps {
|
|
1288
|
-
/** Default `bottom
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1318
|
+
/** Default `bottom` (bottom right). `top` is top right; `top-center` / `bottom-center` centre the stack (5.6).
|
|
1319
|
+
* Below 640px toasts span the width either way. */
|
|
1320
|
+
position?: "bottom" | "bottom-right" | "bottom-center" | "top" | "top-right" | "top-center" | undefined;
|
|
1321
|
+
/** Distance from the top or bottom edge (5.6), e.g. `64` to clear a 56px top bar; px or any CSS length. Sets
|
|
1322
|
+
* `--aura-toaster-offset`; the safe-area inset is added on top. Default 24px (16px below 640px). */
|
|
1323
|
+
offset?: number | string | undefined;
|
|
1324
|
+
/** Keyboard shortcut to the newest toast (5.6): focuses its action, else its close button, and pauses its timer;
|
|
1325
|
+
* Escape closes it and returns focus. Default `"Alt+T"` (matched on the physical key, so macOS Option+T works);
|
|
1326
|
+
* `false` turns it off. */
|
|
1327
|
+
hotkey?: string | false | undefined;
|
|
1328
|
+
}
|
|
1329
|
+
export declare function Toaster(props?: ToasterProps): React$1.ReactElement | null;
|
|
1292
1330
|
export declare const PasswordField: React$1.ForwardRefExoticComponent<PasswordFieldProps & React$1.RefAttributes<HTMLInputElement>>;
|
|
1293
1331
|
export declare const FormErrorSummary: React$1.ForwardRefExoticComponent<FormErrorSummaryProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
1294
1332
|
export declare const FilterBar: React$1.ForwardRefExoticComponent<FilterBarProps & React$1.RefAttributes<HTMLDivElement>>;
|