@jirawatpyk/aura-react 4.12.0 → 4.14.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.
@@ -0,0 +1,215 @@
1
+ 'use client';
2
+ import * as React from "react";
3
+ import { createPortal } from "react-dom";
4
+ import { Icon } from "./Icon.js";
5
+ import { defaultFilter } from "./Combobox.js";
6
+ import { cx, uid } from "./internal.js";
7
+ import { useDensity, useStrings } from "./locale.js";
8
+ import { useModal } from "./useModal.js";
9
+ function isMac() {
10
+ return typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);
11
+ }
12
+ function Command(props) {
13
+ const t = useStrings();
14
+ const density = useDensity();
15
+ const id = uid(), listId = id + "-list";
16
+ const box = React.useRef(null);
17
+ const input = React.useRef(null);
18
+ const q = React.useState(""), act = React.useState(0);
19
+ const openRef = React.useRef(props.open);
20
+ openRef.current = props.open;
21
+ const close = function() {
22
+ props.onOpenChange(false);
23
+ };
24
+ const modal = useModal(props.open, box, { onEscape: close });
25
+ React.useEffect(
26
+ function() {
27
+ if (props.hotkey === false) return;
28
+ function onKey2(e) {
29
+ if ((e.metaKey || e.ctrlKey) && !e.altKey && !e.shiftKey && (e.key === "k" || e.key === "K")) {
30
+ e.preventDefault();
31
+ props.onOpenChange(!openRef.current);
32
+ }
33
+ }
34
+ document.addEventListener("keydown", onKey2, true);
35
+ return function() {
36
+ document.removeEventListener("keydown", onKey2, true);
37
+ };
38
+ },
39
+ [props.hotkey, props.onOpenChange]
40
+ );
41
+ React.useEffect(
42
+ function() {
43
+ if (!props.open) {
44
+ q[1]("");
45
+ act[1](0);
46
+ }
47
+ },
48
+ [props.open]
49
+ );
50
+ const filter = props.filter || defaultFilter;
51
+ const shown = (props.items || []).filter(function(it) {
52
+ return filter(it, q[0]);
53
+ });
54
+ const groups = [];
55
+ shown.forEach(function(it) {
56
+ const name = it.group || "";
57
+ let g = groups.filter(function(x) {
58
+ return x.name === name;
59
+ })[0];
60
+ if (!g) groups.push(g = { name, items: [] });
61
+ g.items.push(it);
62
+ });
63
+ const flat = [];
64
+ groups.forEach(function(g) {
65
+ g.items.forEach(function(it) {
66
+ flat.push(it);
67
+ });
68
+ });
69
+ const enabled = flat.map(function(it, i2) {
70
+ return it.disabled ? -1 : i2;
71
+ }).filter(function(i2) {
72
+ return i2 >= 0;
73
+ });
74
+ const active = enabled.indexOf(act[0]) >= 0 ? act[0] : enabled.length ? enabled[0] : -1;
75
+ const optId = function(i2) {
76
+ return id + "-o" + i2;
77
+ };
78
+ React.useEffect(
79
+ function() {
80
+ if (!box.current || active < 0) return;
81
+ const el = box.current.querySelector("#" + CSS.escape(optId(active)));
82
+ if (el && el.scrollIntoView) el.scrollIntoView({ block: "nearest" });
83
+ },
84
+ [active]
85
+ );
86
+ function run(it) {
87
+ if (it.disabled) return;
88
+ close();
89
+ if (it.onSelect) it.onSelect();
90
+ if (props.onSelect) props.onSelect(it);
91
+ }
92
+ function move(dir) {
93
+ if (!enabled.length) return;
94
+ const at = enabled.indexOf(active);
95
+ let n;
96
+ if (dir === "first") n = 0;
97
+ else if (dir === "last") n = enabled.length - 1;
98
+ else n = (at + dir + enabled.length) % enabled.length;
99
+ act[1](enabled[n]);
100
+ }
101
+ function onKey(e) {
102
+ if (e.key === "ArrowDown") {
103
+ e.preventDefault();
104
+ move(1);
105
+ } else if (e.key === "ArrowUp") {
106
+ e.preventDefault();
107
+ move(-1);
108
+ } else if (e.key === "Home" && e.ctrlKey) {
109
+ e.preventDefault();
110
+ move("first");
111
+ } else if (e.key === "End" && e.ctrlKey) {
112
+ e.preventDefault();
113
+ move("last");
114
+ } else if (e.key === "Enter") {
115
+ e.preventDefault();
116
+ if (active >= 0) run(flat[active]);
117
+ }
118
+ }
119
+ if (!modal.ready) return null;
120
+ let i = -1;
121
+ return createPortal(
122
+ /* @__PURE__ */ React.createElement(
123
+ "div",
124
+ {
125
+ className: "aura-dialog-layer aura-command-layer",
126
+ "data-density": density,
127
+ onKeyDown: function(e) {
128
+ modal.onKeyDown(e);
129
+ e.stopPropagation();
130
+ }
131
+ },
132
+ /* @__PURE__ */ React.createElement("div", { className: "aura-scrim", onClick: close, "aria-hidden": true }),
133
+ /* @__PURE__ */ React.createElement(
134
+ "div",
135
+ {
136
+ ref: box,
137
+ role: "dialog",
138
+ "aria-modal": true,
139
+ "aria-label": props.label || t.commandMenu,
140
+ tabIndex: -1,
141
+ className: cx("aura-command", props.className)
142
+ },
143
+ /* @__PURE__ */ React.createElement("div", { className: "aura-command__search" }, /* @__PURE__ */ React.createElement(Icon, { name: "search", className: "aura-command__search-icon" }), /* @__PURE__ */ React.createElement(
144
+ "input",
145
+ {
146
+ ref: input,
147
+ "data-autofocus": "",
148
+ type: "text",
149
+ role: "combobox",
150
+ "aria-expanded": true,
151
+ "aria-controls": listId,
152
+ "aria-autocomplete": "list",
153
+ "aria-activedescendant": active >= 0 ? optId(active) : void 0,
154
+ "aria-label": props.label || t.commandMenu,
155
+ placeholder: props.placeholder || t.commandPlaceholder,
156
+ className: "aura-command__input",
157
+ value: q[0],
158
+ autoComplete: "off",
159
+ spellCheck: false,
160
+ onChange: function(e) {
161
+ q[1](e.target.value);
162
+ act[1](0);
163
+ },
164
+ onKeyDown: onKey
165
+ }
166
+ ), /* @__PURE__ */ React.createElement("kbd", { className: "aura-command__kbd" }, "Esc")),
167
+ /* @__PURE__ */ React.createElement("div", { className: "aura-command__list", id: listId, role: "listbox", "aria-label": props.label || t.commandMenu }, !flat.length ? /* @__PURE__ */ React.createElement("p", { className: "aura-command__empty", role: "presentation" }, props.emptyText || t.noMatches) : groups.map(function(g, gi) {
168
+ const gid = id + "-g" + gi;
169
+ return /* @__PURE__ */ React.createElement(
170
+ "div",
171
+ {
172
+ key: g.name || gi,
173
+ role: "group",
174
+ "aria-labelledby": g.name ? gid : void 0,
175
+ className: "aura-command__group"
176
+ },
177
+ g.name ? /* @__PURE__ */ React.createElement("div", { className: "aura-command__heading", id: gid, role: "presentation" }, g.name) : null,
178
+ g.items.map(function(it) {
179
+ i++;
180
+ const n = i;
181
+ return /* @__PURE__ */ React.createElement(
182
+ "div",
183
+ {
184
+ key: it.id,
185
+ id: optId(n),
186
+ role: "option",
187
+ "aria-selected": n === active,
188
+ "aria-disabled": it.disabled || void 0,
189
+ className: cx("aura-command__item", n === active && "is-active", it.disabled && "is-disabled"),
190
+ onPointerDown: function(e) {
191
+ e.preventDefault();
192
+ },
193
+ onPointerMove: function() {
194
+ if (!it.disabled && act[0] !== n) act[1](n);
195
+ },
196
+ onClick: function() {
197
+ run(it);
198
+ }
199
+ },
200
+ it.icon ? /* @__PURE__ */ React.createElement(Icon, { name: it.icon }) : null,
201
+ /* @__PURE__ */ React.createElement("span", { className: "aura-command__text" }, /* @__PURE__ */ React.createElement("span", { className: "aura-command__label" }, it.label), it.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-command__desc" }, it.description) : null),
202
+ it.shortcut ? /* @__PURE__ */ React.createElement("kbd", { className: "aura-command__kbd" }, it.shortcut) : null
203
+ );
204
+ })
205
+ );
206
+ })),
207
+ /* @__PURE__ */ React.createElement("div", { className: "aura-command__foot", "aria-hidden": true }, t.commandHint, /* @__PURE__ */ React.createElement("span", { className: "aura-command__mod" }, isMac() ? "\u2318K" : "Ctrl K"))
208
+ )
209
+ ),
210
+ document.body
211
+ );
212
+ }
213
+ export {
214
+ Command
215
+ };
@@ -15,7 +15,7 @@ const DEFAULT_COLUMNS = [
15
15
  { key: "owner", label: "OWNER" }
16
16
  ];
17
17
  const SKELETON_WIDTHS = ["72%", "56%", "84%", "44%", "64%"];
18
- const ROW_H = 48, OVERSCAN = 8, FLEX_MIN = 160;
18
+ const ROW_H_DEFAULT = 48, OVERSCAN = 8, FLEX_MIN = 160;
19
19
  const DataTable = React.forwardRef(function DataTable2(props, ref) {
20
20
  const t = useStrings();
21
21
  const columns = props.columns || DEFAULT_COLUMNS;
@@ -99,6 +99,14 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
99
99
  return px != null && boxWidth[0] < px;
100
100
  }
101
101
  const scrollRef = React.useRef(null);
102
+ const rowH = React.useState(ROW_H_DEFAULT);
103
+ const ROW_H = rowH[0];
104
+ useIsoLayoutEffect(function() {
105
+ const el = wrapRef.current;
106
+ if (!el || typeof getComputedStyle === "undefined") return;
107
+ const v = parseFloat(getComputedStyle(el).getPropertyValue("--aura-table-row-height"));
108
+ if (v > 0 && v !== rowH[0]) rowH[1](v);
109
+ });
102
110
  const pending = React.useRef(null);
103
111
  const resizing = React.useRef(false);
104
112
  useIsoLayoutEffect(function() {
@@ -961,6 +969,7 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
961
969
  "div",
962
970
  {
963
971
  ref: wrapMerged,
972
+ "data-density": props.density,
964
973
  className: cx("aura-table aura-table--stacked", refreshing && "is-refreshing", props.className),
965
974
  role: "region",
966
975
  "aria-label": props.label,
@@ -989,6 +998,7 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
989
998
  "div",
990
999
  {
991
1000
  ref: wrapMerged,
1001
+ "data-density": props.density,
992
1002
  className: cx("aura-table", scrolledX[0] && "is-scrolled-x", refreshing && "is-refreshing", props.className)
993
1003
  },
994
1004
  refreshing ? /* @__PURE__ */ React.createElement("span", { className: "aura-table__busy-bar", "aria-hidden": true }) : null,
@@ -1,12 +1,13 @@
1
1
  'use client';
2
2
  import * as React from "react";
3
- import { useStrings } from "./locale.js";
3
+ import { useStrings, useDensity } from "./locale.js";
4
4
  import { createPortal } from "react-dom";
5
5
  import { cx, uid, useMergedRef } from "./internal.js";
6
6
  import { IconButton } from "./IconButton.js";
7
7
  import { useModal } from "./useModal.js";
8
8
  const Dialog = React.forwardRef(function Dialog2(props, ref) {
9
9
  const t = useStrings();
10
+ const density = useDensity();
10
11
  const own = React.useRef(null), merged = useMergedRef(ref, own), titleId = uid(), descId = uid();
11
12
  function close() {
12
13
  if (props.dismissible !== false && props.onClose) props.onClose();
@@ -19,7 +20,7 @@ const Dialog = React.forwardRef(function Dialog2(props, ref) {
19
20
  });
20
21
  if (!modal.ready) return null;
21
22
  return createPortal(
22
- /* @__PURE__ */ React.createElement("div", { className: "aura-dialog-layer", onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React.createElement("div", { className: "aura-scrim", onClick: close, "aria-hidden": true }), /* @__PURE__ */ React.createElement(
23
+ /* @__PURE__ */ React.createElement("div", { className: "aura-dialog-layer", "data-density": density, onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React.createElement("div", { className: "aura-scrim", onClick: close, "aria-hidden": true }), /* @__PURE__ */ React.createElement(
23
24
  "div",
24
25
  {
25
26
  ref: merged,
@@ -40,6 +41,7 @@ const Dialog = React.forwardRef(function Dialog2(props, ref) {
40
41
  });
41
42
  const Drawer = React.forwardRef(function Drawer2(props, ref) {
42
43
  const t = useStrings();
44
+ const density = useDensity();
43
45
  const own = React.useRef(null), merged = useMergedRef(ref, own), titleId = uid(), descId = uid();
44
46
  function close() {
45
47
  if (props.dismissible !== false && props.onClose) props.onClose();
@@ -53,7 +55,7 @@ const Drawer = React.forwardRef(function Drawer2(props, ref) {
53
55
  if (!modal.ready) return null;
54
56
  const side = props.side === "left" ? "left" : "right";
55
57
  return createPortal(
56
- /* @__PURE__ */ React.createElement("div", { className: "aura-dialog-layer aura-drawer-layer", onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React.createElement("div", { className: "aura-scrim", onClick: close, "aria-hidden": true }), /* @__PURE__ */ React.createElement(
58
+ /* @__PURE__ */ React.createElement("div", { className: "aura-dialog-layer aura-drawer-layer", "data-density": density, onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React.createElement("div", { className: "aura-scrim", onClick: close, "aria-hidden": true }), /* @__PURE__ */ React.createElement(
57
59
  "div",
58
60
  {
59
61
  ref: merged,
@@ -0,0 +1,112 @@
1
+ 'use client';
2
+ import * as React from "react";
3
+ import { Icon } from "./Icon.js";
4
+ import { IconButton } from "./IconButton.js";
5
+ import { Tag } from "./Tag.js";
6
+ import { cx, uid } from "./internal.js";
7
+ import { useStrings } from "./locale.js";
8
+ const FilterBar = React.forwardRef(function FilterBar2(props, ref) {
9
+ const t = useStrings();
10
+ const id = uid();
11
+ const hasSearch = !!props.onSearchChange;
12
+ const draft = React.useState(props.search || "");
13
+ const timer = React.useRef(null);
14
+ const sent = React.useRef(props.search || "");
15
+ const onChange = React.useRef(props.onSearchChange);
16
+ onChange.current = props.onSearchChange;
17
+ React.useEffect(
18
+ function() {
19
+ if ((props.search || "") !== sent.current) {
20
+ sent.current = props.search || "";
21
+ draft[1](props.search || "");
22
+ }
23
+ },
24
+ [props.search]
25
+ );
26
+ React.useEffect(function() {
27
+ return function() {
28
+ if (timer.current) clearTimeout(timer.current);
29
+ };
30
+ }, []);
31
+ function send(v) {
32
+ if (timer.current) clearTimeout(timer.current);
33
+ timer.current = null;
34
+ if (v === sent.current) return;
35
+ sent.current = v;
36
+ if (onChange.current) onChange.current(v);
37
+ }
38
+ function type(v) {
39
+ draft[1](v);
40
+ if (timer.current) clearTimeout(timer.current);
41
+ timer.current = setTimeout(
42
+ function() {
43
+ send(v);
44
+ },
45
+ props.searchDelay == null ? 300 : props.searchDelay
46
+ );
47
+ }
48
+ const filters = props.filters || [];
49
+ const anything = filters.length > 0 || !!(props.search || draft[0]);
50
+ const count = props.resultCount == null ? null : typeof props.resultCount === "number" ? t.results(props.resultCount) : props.resultCount;
51
+ return /* @__PURE__ */ React.createElement(
52
+ "div",
53
+ {
54
+ ref,
55
+ role: "region",
56
+ "aria-label": props.label || t.filters,
57
+ className: cx("aura-filterbar", props.className)
58
+ },
59
+ /* @__PURE__ */ React.createElement("div", { className: "aura-filterbar__row" }, hasSearch ? /* @__PURE__ */ React.createElement("div", { className: "aura-input has-icon aura-filterbar__search" }, /* @__PURE__ */ React.createElement(Icon, { name: "search", className: "aura-input__icon" }), /* @__PURE__ */ React.createElement(
60
+ "input",
61
+ {
62
+ id,
63
+ type: "search",
64
+ className: "aura-input__control",
65
+ "aria-label": props.searchLabel || t.search,
66
+ placeholder: props.searchPlaceholder || props.searchLabel || t.search,
67
+ value: draft[0],
68
+ onChange: function(e) {
69
+ type(e.target.value);
70
+ },
71
+ onKeyDown: function(e) {
72
+ if (e.key === "Enter") send(draft[0]);
73
+ if (e.key === "Escape" && draft[0]) {
74
+ e.preventDefault();
75
+ draft[1]("");
76
+ send("");
77
+ }
78
+ }
79
+ }
80
+ ), draft[0] ? /* @__PURE__ */ React.createElement(
81
+ IconButton,
82
+ {
83
+ icon: "x",
84
+ label: t.clear((props.searchLabel || t.search).toLowerCase()),
85
+ className: "aura-filterbar__clear-search",
86
+ onClick: function() {
87
+ draft[1]("");
88
+ send("");
89
+ }
90
+ }
91
+ ) : null) : null, props.children ? /* @__PURE__ */ React.createElement("div", { className: "aura-filterbar__controls" }, props.children) : null, /* @__PURE__ */ React.createElement("span", { className: "aura-filterbar__spacer" }), count != null ? /* @__PURE__ */ React.createElement("span", { className: "aura-filterbar__count", "aria-live": "polite" }, count) : null, props.actions ? /* @__PURE__ */ React.createElement("div", { className: "aura-filterbar__actions" }, props.actions) : null),
92
+ filters.length || props.onClearAll && anything ? /* @__PURE__ */ React.createElement("div", { className: "aura-filterbar__chips" }, filters.map(function(f) {
93
+ return /* @__PURE__ */ React.createElement(Tag, { key: f.id, onRemove: f.onRemove }, f.label);
94
+ }), props.onClearAll && anything ? /* @__PURE__ */ React.createElement(
95
+ "button",
96
+ {
97
+ type: "button",
98
+ className: "aura-filterbar__clear",
99
+ onClick: function() {
100
+ draft[1]("");
101
+ sent.current = "";
102
+ if (timer.current) clearTimeout(timer.current);
103
+ props.onClearAll();
104
+ }
105
+ },
106
+ t.clearFilters
107
+ ) : null) : null
108
+ );
109
+ });
110
+ export {
111
+ FilterBar
112
+ };
@@ -0,0 +1,76 @@
1
+ 'use client';
2
+ import * as React from "react";
3
+ import { Icon } from "./Icon.js";
4
+ import { cx, uid } from "./internal.js";
5
+ import { useStrings } from "./locale.js";
6
+ function items(errors) {
7
+ if (Array.isArray(errors)) return errors;
8
+ const out = [];
9
+ for (const k in errors || {}) {
10
+ const e = errors[k];
11
+ if (e && e.message) out.push({ field: k, message: e.message });
12
+ }
13
+ return out;
14
+ }
15
+ function fieldElement(field) {
16
+ if (typeof document === "undefined") return null;
17
+ return document.getElementById(field) || document.querySelector('[name="' + field.replace(/"/g, '\\"') + '"]');
18
+ }
19
+ const FormErrorSummary = React.forwardRef(
20
+ function FormErrorSummary2(props, ref) {
21
+ const t = useStrings();
22
+ const auto = uid(), id = props.id || auto;
23
+ const list = items(props.errors);
24
+ const box = React.useRef(null);
25
+ React.useEffect(
26
+ function() {
27
+ const has = list.length > 0;
28
+ if (has && box.current) box.current.focus();
29
+ },
30
+ /* focus when errors first appear, and again whenever focusKey changes (each submit) */
31
+ // eslint-disable-next-line react-hooks/exhaustive-deps
32
+ [list.length > 0, props.focusKey]
33
+ );
34
+ if (!list.length) return null;
35
+ return /* @__PURE__ */ React.createElement(
36
+ "div",
37
+ {
38
+ ref: function(el) {
39
+ box.current = el;
40
+ if (typeof ref === "function") ref(el);
41
+ else if (ref) ref.current = el;
42
+ },
43
+ id,
44
+ tabIndex: -1,
45
+ role: "alert",
46
+ "aria-labelledby": id + "-title",
47
+ className: cx("aura-alert aura-alert--danger aura-error-summary", props.className)
48
+ },
49
+ /* @__PURE__ */ React.createElement(Icon, { name: "circle-alert", className: "aura-alert__icon" }),
50
+ /* @__PURE__ */ React.createElement("div", { className: "aura-alert__body" }, /* @__PURE__ */ React.createElement("h2", { className: "aura-alert__title", id: id + "-title" }, props.title || t.errorSummary(list.length)), /* @__PURE__ */ React.createElement("ul", { className: "aura-error-summary__list" }, list.map(function(e) {
51
+ return /* @__PURE__ */ React.createElement("li", { key: e.field }, /* @__PURE__ */ React.createElement(
52
+ "a",
53
+ {
54
+ href: "#" + e.field,
55
+ onClick: function(ev) {
56
+ ev.preventDefault();
57
+ if (props.onSelect) {
58
+ props.onSelect(e.field);
59
+ return;
60
+ }
61
+ const el = fieldElement(e.field);
62
+ if (el) {
63
+ el.scrollIntoView({ block: "center" });
64
+ el.focus();
65
+ }
66
+ }
67
+ },
68
+ e.message
69
+ ));
70
+ })))
71
+ );
72
+ }
73
+ );
74
+ export {
75
+ FormErrorSummary
76
+ };
@@ -0,0 +1,38 @@
1
+ 'use client';
2
+ import * as React from "react";
3
+ import { TextField } from "./TextField.js";
4
+ import { IconButton } from "./IconButton.js";
5
+ import { cx, omit } from "./internal.js";
6
+ import { useStrings } from "./locale.js";
7
+ const PasswordField = React.forwardRef(function PasswordField2(props, ref) {
8
+ const t = useStrings();
9
+ const shown = React.useState(false);
10
+ const rest = omit(props, ["toggle", "className"]);
11
+ return /* @__PURE__ */ React.createElement(
12
+ TextField,
13
+ {
14
+ autoComplete: "current-password",
15
+ ...rest,
16
+ ref,
17
+ type: shown[0] ? "text" : "password",
18
+ spellCheck: false,
19
+ autoCapitalize: "none",
20
+ className: cx("aura-password", props.className),
21
+ suffix: props.toggle === false ? void 0 : /* @__PURE__ */ React.createElement(
22
+ IconButton,
23
+ {
24
+ icon: shown[0] ? "eye-off" : "eye",
25
+ label: t.showPassword,
26
+ "aria-pressed": shown[0],
27
+ disabled: props.disabled,
28
+ onClick: function() {
29
+ shown[1](!shown[0]);
30
+ }
31
+ }
32
+ )
33
+ }
34
+ );
35
+ });
36
+ export {
37
+ PasswordField
38
+ };
@@ -11,7 +11,7 @@ import {
11
11
  useMergedRef,
12
12
  useMounted
13
13
  } from "./internal.js";
14
- import { useStrings } from "./locale.js";
14
+ import { useDensity, useStrings } from "./locale.js";
15
15
  import { createPortal } from "react-dom";
16
16
  function position(anchor, pop, placement) {
17
17
  const r = anchor.getBoundingClientRect(), pw = pop.offsetWidth, ph = pop.offsetHeight, vw = window.innerWidth, vh = window.innerHeight, gap = 6;
@@ -24,6 +24,7 @@ function position(anchor, pop, placement) {
24
24
  }
25
25
  const Popover = React.forwardRef(function Popover2(props, ref) {
26
26
  const t = useStrings();
27
+ const density = useDensity();
27
28
  const auto = uid(), id = props.id || auto;
28
29
  const st = useMaybeControlled(props.open, !!props.defaultOpen, props.onOpenChange);
29
30
  const open = !!st[0];
@@ -81,6 +82,7 @@ const Popover = React.forwardRef(function Popover2(props, ref) {
81
82
  {
82
83
  ref: popMerged,
83
84
  id,
85
+ "data-density": density,
84
86
  role: "dialog",
85
87
  "aria-modal": false,
86
88
  "aria-label": props.title ? void 0 : props.label,
@@ -16,15 +16,40 @@ function emitToasts() {
16
16
  f(toastState.list.slice());
17
17
  });
18
18
  }
19
- function toast(opts) {
20
- if (typeof opts === "string") opts = { title: opts };
19
+ function show(opts) {
21
20
  const id = opts.id || "t" + ++toastState.n;
22
- toastState.list = toastState.list.filter(function(t) {
23
- return t.id !== id;
24
- }).concat([Object.assign({ tone: "info" }, opts, { id })]).slice(-3);
21
+ const at = toastState.list.findIndex(function(t) {
22
+ return t.id === id;
23
+ });
24
+ const entry = Object.assign({ tone: "info" }, opts, {
25
+ id,
26
+ loading: !!opts.loading,
27
+ rev: at >= 0 ? toastState.list[at].rev + 1 : 0
28
+ });
29
+ if (at >= 0) {
30
+ toastState.list = toastState.list.slice();
31
+ toastState.list[at] = entry;
32
+ } else toastState.list = toastState.list.concat([entry]).slice(-3);
25
33
  emitToasts();
26
34
  return id;
27
35
  }
36
+ function toast(opts) {
37
+ return show(typeof opts === "string" ? { title: opts } : opts);
38
+ }
39
+ function shorthand(tone) {
40
+ return function(title, opts) {
41
+ return show(Object.assign({}, opts, { title, tone }));
42
+ };
43
+ }
44
+ toast.success = shorthand("success");
45
+ toast.error = shorthand("danger");
46
+ toast.warning = shorthand("warning");
47
+ toast.info = shorthand("info");
48
+ toast.loading = function(title, opts) {
49
+ return show(
50
+ Object.assign({}, opts, { title, tone: "info", loading: true, duration: Infinity })
51
+ );
52
+ };
28
53
  toast.dismiss = function(id) {
29
54
  toastState.list = toastState.list.filter(function(t) {
30
55
  return t.id !== id;
@@ -34,35 +59,56 @@ toast.dismiss = function(id) {
34
59
  function ToastItem(props) {
35
60
  const str = useStrings();
36
61
  const t = props.toast, timer = React.useRef(null), left = React.useRef(t.duration || 5e3), since = React.useRef(0);
62
+ const hover = React.useRef(false);
37
63
  function start() {
38
- if (left.current === Infinity) return;
64
+ if (left.current === Infinity || timer.current) return;
39
65
  since.current = Date.now();
40
66
  timer.current = setTimeout(function() {
41
67
  toast.dismiss(t.id);
42
68
  }, left.current);
43
69
  }
70
+ function resume() {
71
+ hover.current = false;
72
+ start();
73
+ }
44
74
  function pause() {
75
+ hover.current = true;
45
76
  if (timer.current) {
46
77
  clearTimeout(timer.current);
47
78
  timer.current = null;
48
79
  left.current -= Date.now() - since.current;
49
80
  }
50
81
  }
51
- React.useEffect(function() {
52
- start();
53
- return pause;
54
- }, []);
82
+ React.useEffect(
83
+ function() {
84
+ if (timer.current) clearTimeout(timer.current);
85
+ timer.current = null;
86
+ left.current = t.duration || 5e3;
87
+ if (!hover.current) start();
88
+ return function() {
89
+ if (timer.current) clearTimeout(timer.current);
90
+ };
91
+ },
92
+ [t.rev]
93
+ );
55
94
  return /* @__PURE__ */ React.createElement(
56
95
  "div",
57
96
  {
58
- className: cx("aura-toast", "aura-toast--" + t.tone),
97
+ className: cx("aura-toast", "aura-toast--" + t.tone, t.loading && "is-loading"),
59
98
  role: t.tone === "danger" ? "alert" : "status",
99
+ "aria-busy": t.loading || void 0,
60
100
  onMouseEnter: pause,
61
- onMouseLeave: start,
101
+ onMouseLeave: resume,
62
102
  onFocus: pause,
63
- onBlur: start
103
+ onBlur: resume
64
104
  },
65
- /* @__PURE__ */ React.createElement(Icon, { name: ALERT_ICON[t.tone] || "info", className: "aura-toast__icon" }),
105
+ /* @__PURE__ */ React.createElement(
106
+ Icon,
107
+ {
108
+ name: t.loading ? "loader-circle" : ALERT_ICON[t.tone] || "info",
109
+ className: cx("aura-toast__icon", t.loading && "aura-spin")
110
+ }
111
+ ),
66
112
  /* @__PURE__ */ React.createElement("div", { className: "aura-toast__body" }, /* @__PURE__ */ React.createElement("p", { className: "aura-toast__title" }, t.title), t.description ? /* @__PURE__ */ React.createElement("p", { className: "aura-toast__text" }, t.description) : null),
67
113
  t.action ? /* @__PURE__ */ React.createElement(
68
114
  "button",