@jirawatpyk/aura-react 5.0.1 → 5.1.1

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.
Files changed (47) hide show
  1. package/README.md +8 -0
  2. package/dist/aura.bundle.js +464 -154
  3. package/dist/cjs/index.cjs +464 -154
  4. package/dist/esm/AppShell.js +10 -2
  5. package/dist/esm/Avatar.js +4 -3
  6. package/dist/esm/BottomNav.js +2 -1
  7. package/dist/esm/Checkbox.js +13 -4
  8. package/dist/esm/Combobox.js +19 -19
  9. package/dist/esm/Command.js +1 -0
  10. package/dist/esm/DataTable.js +46 -14
  11. package/dist/esm/DatePicker.js +81 -18
  12. package/dist/esm/DropdownMenu.js +12 -0
  13. package/dist/esm/FileUpload.js +15 -0
  14. package/dist/esm/FormErrorSummary.js +19 -4
  15. package/dist/esm/Icon.js +1 -1
  16. package/dist/esm/Menu.js +20 -7
  17. package/dist/esm/NumberField.js +3 -1
  18. package/dist/esm/Pagination.js +3 -3
  19. package/dist/esm/Popover.js +11 -1
  20. package/dist/esm/RadioGroup.js +14 -1
  21. package/dist/esm/SideNav.js +8 -2
  22. package/dist/esm/StatusPill.js +1 -0
  23. package/dist/esm/Switch.js +1 -1
  24. package/dist/esm/Table.js +3 -2
  25. package/dist/esm/Tabs.js +11 -7
  26. package/dist/esm/TextField.js +2 -2
  27. package/dist/esm/Textarea.js +2 -2
  28. package/dist/esm/ThemeStyle.js +13 -3
  29. package/dist/esm/Toaster.js +1 -0
  30. package/dist/esm/Tooltip.js +54 -29
  31. package/dist/esm/colorScheme.js +7 -3
  32. package/dist/esm/colorSchemeScript.js +3 -3
  33. package/dist/esm/dates.js +6 -3
  34. package/dist/esm/internal.js +32 -7
  35. package/dist/esm/locale.js +6 -5
  36. package/dist/esm/status.js +2 -1
  37. package/dist/esm/theme.js +3 -1
  38. package/dist/esm/useModal.js +42 -9
  39. package/dist/index.d.cts +1508 -0
  40. package/dist/index.d.ts +13 -6
  41. package/dist/server/index.cjs +7 -4
  42. package/dist/server/index.d.cts +176 -0
  43. package/dist/server/index.d.ts +2 -1
  44. package/dist/server/index.js +7 -4
  45. package/dist/styles.css +23 -3
  46. package/dist/styles.layer.css +23 -3
  47. package/package.json +19 -8
package/dist/esm/Table.js CHANGED
@@ -26,8 +26,9 @@ const Table = React.forwardRef(function Table2(props, ref) {
26
26
  className: "aura-tbl-wrap",
27
27
  "data-density": density,
28
28
  tabIndex: scrolls ? 0 : void 0,
29
- role: scrolls ? "region" : void 0,
30
- "aria-labelledby": scrolls && caption != null ? capId : void 0
29
+ role: scrolls && (caption != null || rest["aria-label"]) ? "region" : void 0,
30
+ "aria-labelledby": scrolls && caption != null ? capId : void 0,
31
+ "aria-label": scrolls && caption == null ? rest["aria-label"] : void 0
31
32
  },
32
33
  /* @__PURE__ */ React.createElement("table", { ref, className: cx("aura-tbl", className), ...rest }, caption != null ? /* @__PURE__ */ React.createElement("caption", { id: capId, className: cx("aura-tbl__caption", captionHidden && "aura-sr-only") }, caption) : null, children)
33
34
  );
package/dist/esm/Tabs.js CHANGED
@@ -5,19 +5,23 @@ import { cx, uid, useMaybeControlled } from "./internal.js";
5
5
  import { useLinkComponent } from "./locale.js";
6
6
  const Tabs = React.forwardRef(function Tabs2(props, ref) {
7
7
  const items = props.tabs || [], base = uid();
8
+ const asLinks = items.length > 0 && items.every(function(t) {
9
+ return !!t.href;
10
+ });
11
+ const firstOn = items.filter(function(t) {
12
+ return !t.disabled;
13
+ })[0];
8
14
  const st = useMaybeControlled(
9
15
  props.value,
10
- props.defaultValue || items[0] && items[0].id,
16
+ props.defaultValue || (asLinks ? void 0 : firstOn && firstOn.id),
11
17
  props.onChange
12
18
  );
19
+ const cur = asLinks && "value" in props ? props.value : st[0];
13
20
  const refs = React.useRef({});
14
21
  const current = items.filter(function(t) {
15
- return t.id === st[0];
16
- })[0] || items[0];
22
+ return t.id === cur;
23
+ })[0] || firstOn || items[0];
17
24
  const Link = useLinkComponent(props.linkComponent);
18
- const asLinks = items.length > 0 && items.every(function(t) {
19
- return !!t.href;
20
- });
21
25
  if (asLinks)
22
26
  return /* @__PURE__ */ React.createElement(
23
27
  "nav",
@@ -27,7 +31,7 @@ const Tabs = React.forwardRef(function Tabs2(props, ref) {
27
31
  className: cx("aura-tabs aura-tabs--links", props.className)
28
32
  },
29
33
  /* @__PURE__ */ React.createElement("div", { className: "aura-tabs__list" }, items.map(function(t) {
30
- const on = t.id === st[0];
34
+ const on = t.id === cur;
31
35
  const inner = [
32
36
  t.icon ? /* @__PURE__ */ React.createElement(Icon, { key: "i", name: t.icon }) : null,
33
37
  t.label,
@@ -28,8 +28,8 @@ const TextField = React.forwardRef(function TextField2(props, ref) {
28
28
  className: "aura-input__control",
29
29
  placeholder: props.placeholder,
30
30
  required: props.required,
31
- "aria-invalid": props.error ? true : void 0,
32
- "aria-describedby": describedBy(id, props)
31
+ "aria-invalid": props.error ? true : rest["aria-invalid"],
32
+ "aria-describedby": [describedBy(id, props), rest["aria-describedby"]].filter(Boolean).join(" ") || void 0
33
33
  }
34
34
  ), props.suffix ? /* @__PURE__ */ React.createElement("span", { className: "aura-input__suffix" }, props.suffix) : null)
35
35
  );
@@ -27,8 +27,8 @@ const Textarea = React.forwardRef(function Textarea2(props, ref) {
27
27
  className: "aura-input aura-textarea",
28
28
  placeholder: props.placeholder,
29
29
  required: props.required,
30
- "aria-invalid": props.error ? true : void 0,
31
- "aria-describedby": describedBy(id, props)
30
+ "aria-invalid": props.error ? true : rest["aria-invalid"],
31
+ "aria-describedby": [describedBy(id, props), rest["aria-describedby"]].filter(Boolean).join(" ") || void 0
32
32
  }
33
33
  )
34
34
  );
@@ -1,15 +1,25 @@
1
1
  'use client';
2
2
  import * as React from "react";
3
3
  import { createTheme } from "./theme.js";
4
+ import { devWarnOnce } from "./internal.js";
4
5
  function ThemeStyle(props) {
5
6
  const css = React.useMemo(
6
7
  function() {
7
- return createTheme({ brand: props.brand, signal: props.signal, primary: props.primary, name: props.name }).css(
8
- props.selector
9
- );
8
+ try {
9
+ return createTheme({
10
+ brand: props.brand,
11
+ signal: props.signal,
12
+ primary: props.primary,
13
+ name: props.name
14
+ }).css(props.selector);
15
+ } catch (e) {
16
+ devWarnOnce("theme-style", "ThemeStyle ignored: " + e.message);
17
+ return null;
18
+ }
10
19
  },
11
20
  [props.brand, props.signal, props.primary, props.name, props.selector]
12
21
  );
22
+ if (css === null) return null;
13
23
  return /* @__PURE__ */ React.createElement("style", { "data-aura-theme": props.name || props.brand, dangerouslySetInnerHTML: { __html: css } });
14
24
  }
15
25
  export {
@@ -158,6 +158,7 @@ function Toaster(props) {
158
158
  const s = React.useState(toastState.list);
159
159
  React.useEffect(function() {
160
160
  toastState.subs.push(s[1]);
161
+ s[1](toastState.list.slice());
161
162
  return function() {
162
163
  toastState.subs = toastState.subs.filter(function(f) {
163
164
  return f !== s[1];
@@ -1,11 +1,11 @@
1
1
  'use client';
2
2
  import * as React from "react";
3
- import { uid, useIsoLayoutEffect, useMergedRef } from "./internal.js";
3
+ import { uid, useIsoLayoutEffect, useMergedRef, useMounted } from "./internal.js";
4
4
  import { createPortal } from "react-dom";
5
5
  const Tooltip = React.forwardRef(function Tooltip2(props, ref) {
6
6
  const id = uid(), st = React.useState(false), open = props.open !== void 0 ? props.open : st[0], set = st[1];
7
7
  const anchor = React.useRef(null), anchorMerged = useMergedRef(ref, anchor), tip = React.useRef(null), timer = React.useRef(void 0);
8
- const pos = React.useState(null);
8
+ const pos = React.useState(null), mounted = useMounted();
9
9
  function show(now) {
10
10
  clearTimeout(timer.current);
11
11
  timer.current = setTimeout(
@@ -19,34 +19,55 @@ const Tooltip = React.forwardRef(function Tooltip2(props, ref) {
19
19
  clearTimeout(timer.current);
20
20
  set(false);
21
21
  }
22
+ function hideSoon() {
23
+ clearTimeout(timer.current);
24
+ timer.current = setTimeout(function() {
25
+ set(false);
26
+ }, 120);
27
+ }
28
+ React.useEffect(function() {
29
+ return function() {
30
+ clearTimeout(timer.current);
31
+ };
32
+ }, []);
33
+ function place() {
34
+ if (!anchor.current || !tip.current) return;
35
+ const r = anchor.current.getBoundingClientRect(), t = tip.current.getBoundingClientRect();
36
+ const vw = window.innerWidth, vh = window.innerHeight, gap = 8, m = 8;
37
+ let side = props.side || "top";
38
+ if (side === "top" && r.top - t.height - gap < m && r.bottom + gap + t.height <= vh - m) side = "bottom";
39
+ else if (side === "bottom" && r.bottom + gap + t.height > vh - m && r.top - t.height - gap >= m) side = "top";
40
+ else if (side === "left" && r.left - t.width - gap < m && r.right + gap + t.width <= vw - m) side = "right";
41
+ else if (side === "right" && r.right + gap + t.width > vw - m && r.left - t.width - gap >= m) side = "left";
42
+ const clampX = function(x) {
43
+ return Math.max(m, Math.min(x, vw - t.width - m));
44
+ };
45
+ const clampY = function(y) {
46
+ return Math.max(m, Math.min(y, vh - t.height - m));
47
+ };
48
+ let top, left;
49
+ if (side === "top" || side === "bottom") {
50
+ top = clampY(side === "top" ? r.top - t.height - gap : r.bottom + gap);
51
+ left = clampX(r.left + r.width / 2 - t.width / 2);
52
+ } else {
53
+ left = side === "left" ? r.left - t.width - gap : r.right + gap;
54
+ left = clampX(left);
55
+ top = clampY(r.top + r.height / 2 - t.height / 2);
56
+ }
57
+ pos[1]({ top, left });
58
+ }
22
59
  useIsoLayoutEffect(
23
60
  function() {
24
- if (!open || !anchor.current || !tip.current) return;
25
- const r = anchor.current.getBoundingClientRect(), t = tip.current.getBoundingClientRect();
26
- const vw = window.innerWidth, vh = window.innerHeight, gap = 8, m = 8;
27
- let side = props.side || "top";
28
- if (side === "top" && r.top - t.height - gap < m && r.bottom + gap + t.height <= vh - m) side = "bottom";
29
- else if (side === "bottom" && r.bottom + gap + t.height > vh - m && r.top - t.height - gap >= m) side = "top";
30
- else if (side === "left" && r.left - t.width - gap < m && r.right + gap + t.width <= vw - m) side = "right";
31
- else if (side === "right" && r.right + gap + t.width > vw - m && r.left - t.width - gap >= m) side = "left";
32
- const clampX = function(x) {
33
- return Math.max(m, Math.min(x, vw - t.width - m));
34
- };
35
- const clampY = function(y) {
36
- return Math.max(m, Math.min(y, vh - t.height - m));
61
+ if (!open) return;
62
+ place();
63
+ window.addEventListener("scroll", place, true);
64
+ window.addEventListener("resize", place);
65
+ return function() {
66
+ window.removeEventListener("scroll", place, true);
67
+ window.removeEventListener("resize", place);
37
68
  };
38
- let top, left;
39
- if (side === "top" || side === "bottom") {
40
- top = clampY(side === "top" ? r.top - t.height - gap : r.bottom + gap);
41
- left = clampX(r.left + r.width / 2 - t.width / 2);
42
- } else {
43
- left = side === "left" ? r.left - t.width - gap : r.right + gap;
44
- left = clampX(left);
45
- top = clampY(r.top + r.height / 2 - t.height / 2);
46
- }
47
- pos[1]({ top, left });
48
69
  },
49
- [open]
70
+ [open, mounted]
50
71
  );
51
72
  React.useEffect(
52
73
  function() {
@@ -70,7 +91,7 @@ const Tooltip = React.forwardRef(function Tooltip2(props, ref) {
70
91
  onMouseEnter: function() {
71
92
  show(false);
72
93
  },
73
- onMouseLeave: hide,
94
+ onMouseLeave: hideSoon,
74
95
  onFocus: function() {
75
96
  show(true);
76
97
  },
@@ -78,14 +99,18 @@ const Tooltip = React.forwardRef(function Tooltip2(props, ref) {
78
99
  },
79
100
  React.cloneElement(child, { "aria-describedby": open ? id : child.props["aria-describedby"] })
80
101
  );
81
- return /* @__PURE__ */ React.createElement(React.Fragment, null, trigger, open ? createPortal(
102
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, trigger, open && mounted ? createPortal(
82
103
  /* @__PURE__ */ React.createElement(
83
104
  "div",
84
105
  {
85
106
  ref: tip,
86
107
  id,
87
108
  role: "tooltip",
88
- className: "aura-tooltip",
109
+ onMouseEnter: function() {
110
+ clearTimeout(timer.current);
111
+ },
112
+ onMouseLeave: hideSoon,
113
+ className: "aura-tooltip aura-tooltip--hoverable",
89
114
  style: { top: pos[0] ? pos[0].top : -9999, left: pos[0] ? pos[0].left : -9999 }
90
115
  },
91
116
  props.content
@@ -37,16 +37,20 @@ function useColorScheme(options) {
37
37
  if (readScheme(fallback) === "system") apply("system");
38
38
  cb();
39
39
  }
40
+ function onStorage(e) {
41
+ if (e.key === key) apply(valid(e.newValue) ? e.newValue : fallback);
42
+ cb();
43
+ }
40
44
  if (mq) mq.addEventListener("change", onSystem);
41
45
  window.addEventListener(EVENT, cb);
42
- window.addEventListener("storage", cb);
46
+ window.addEventListener("storage", onStorage);
43
47
  return function() {
44
48
  if (mq) mq.removeEventListener("change", onSystem);
45
49
  window.removeEventListener(EVENT, cb);
46
- window.removeEventListener("storage", cb);
50
+ window.removeEventListener("storage", onStorage);
47
51
  };
48
52
  },
49
- [fallback]
53
+ [fallback, key]
50
54
  );
51
55
  const snapshot = React.useSyncExternalStore(
52
56
  subscribe,
@@ -6,9 +6,9 @@ function valid(v) {
6
6
  return typeof v === "string" && SCHEMES.indexOf(v) >= 0;
7
7
  }
8
8
  function colorSchemeScript(options) {
9
- const key = JSON.stringify(options && options.storageKey || DEFAULT_KEY);
10
- const fallback = JSON.stringify(options && options.defaultScheme || "system");
11
- return "(function(){try{var s=localStorage.getItem(" + key + ");if(s!=='light'&&s!=='dark'&&s!=='system')s=" + fallback + `;var d=document.documentElement;d.setAttribute("data-theme",s);var dark=s==='dark'||(s==='system'&&window.matchMedia('(prefers-color-scheme: dark)').matches);d.classList.toggle("dark",dark);}catch(e){}})();`;
9
+ const key = JSON.stringify(options && options.storageKey || DEFAULT_KEY).replace(/</g, "\\u003c");
10
+ const fallback = JSON.stringify(options && options.defaultScheme || "system").replace(/</g, "\\u003c");
11
+ return "(function(){try{var s=localStorage.getItem(" + key + ");if(s!=='light'&&s!=='dark'&&s!=='system')s=" + fallback + `;var d=document.documentElement;d.setAttribute("data-theme",s);var m=window.matchMedia('(prefers-color-scheme: dark)');d.classList.toggle('dark',s==='dark'||(s==='system'&&m.matches));if(m.addEventListener)m.addEventListener('change',function(e){if(d.getAttribute('data-theme')==='system')d.classList.toggle('dark',e.matches)});}catch(e){}})();`;
12
12
  }
13
13
  export {
14
14
  DEFAULT_KEY,
package/dist/esm/dates.js CHANGED
@@ -46,7 +46,8 @@ const DATE_TEXT = {
46
46
  rangePlaceholder: "\u0E27\u0E27/\u0E14\u0E14/\u0E1B\u0E1B\u0E1B\u0E1B \u2013 \u0E27\u0E27/\u0E14\u0E14/\u0E1B\u0E1B\u0E1B\u0E1B",
47
47
  clearDates: "\u0E25\u0E49\u0E32\u0E07\u0E0A\u0E48\u0E27\u0E07\u0E27\u0E31\u0E19\u0E17\u0E35\u0E48",
48
48
  chooseStart: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E27\u0E31\u0E19\u0E40\u0E23\u0E34\u0E48\u0E21\u0E15\u0E49\u0E19",
49
- chooseEnd: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E27\u0E31\u0E19\u0E2A\u0E34\u0E49\u0E19\u0E2A\u0E38\u0E14"
49
+ chooseEnd: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E27\u0E31\u0E19\u0E2A\u0E34\u0E49\u0E19\u0E2A\u0E38\u0E14",
50
+ dateUnavailable: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E27\u0E31\u0E19\u0E17\u0E35\u0E48\u0E19\u0E35\u0E49\u0E44\u0E21\u0E48\u0E44\u0E14\u0E49 \u0E25\u0E2D\u0E07\u0E40\u0E1B\u0E34\u0E14\u0E1B\u0E0F\u0E34\u0E17\u0E34\u0E19\u0E14\u0E39\u0E27\u0E31\u0E19\u0E17\u0E35\u0E48\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E44\u0E14\u0E49"
50
51
  },
51
52
  en: {
52
53
  prevYears: "Previous years",
@@ -63,7 +64,8 @@ const DATE_TEXT = {
63
64
  rangePlaceholder: "DD/MM/YYYY \u2013 DD/MM/YYYY",
64
65
  clearDates: "Clear dates",
65
66
  chooseStart: "Choose the start date",
66
- chooseEnd: "Choose the end date"
67
+ chooseEnd: "Choose the end date",
68
+ dateUnavailable: "That date can't be chosen. Open the calendar to see the dates you can pick."
67
69
  },
68
70
  sv: {
69
71
  prevYears: "Tidigare \xE5r",
@@ -80,7 +82,8 @@ const DATE_TEXT = {
80
82
  rangePlaceholder: "\xE5\xE5\xE5\xE5-mm-dd \u2013 \xE5\xE5\xE5\xE5-mm-dd",
81
83
  clearDates: "Rensa datumen",
82
84
  chooseStart: "V\xE4lj startdatum",
83
- chooseEnd: "V\xE4lj slutdatum"
85
+ chooseEnd: "V\xE4lj slutdatum",
86
+ dateUnavailable: "Det datumet g\xE5r inte att v\xE4lja. \xD6ppna kalendern f\xF6r att se vilka datum som g\xE5r."
84
87
  }
85
88
  };
86
89
  function dateText(locale) {
@@ -12,14 +12,21 @@ function omit(src, keys) {
12
12
  }
13
13
  function useMaybeControlled(value, initial, onChange) {
14
14
  const s = React.useState(initial);
15
+ const wasSet = React.useState(value !== void 0);
16
+ if (value !== void 0 && !wasSet[0]) wasSet[1](true);
17
+ else if (value === void 0 && wasSet[0]) {
18
+ wasSet[1](false);
19
+ s[1](initial);
20
+ }
15
21
  const controlled = value !== void 0;
16
- return [
17
- controlled ? value : s[0],
18
- function(next) {
19
- if (!controlled) s[1](next);
20
- if (onChange) onChange(next);
21
- }
22
- ];
22
+ const latest = React.useRef({ controlled, onChange, set: s[1] });
23
+ latest.current = { controlled, onChange, set: s[1] };
24
+ const set = React.useCallback(function(next) {
25
+ const l = latest.current;
26
+ if (!l.controlled) l.set(next);
27
+ if (l.onChange) l.onChange(next);
28
+ }, []);
29
+ return [controlled ? value : s[0], set];
23
30
  }
24
31
  const collator = typeof Intl !== "undefined" ? new Intl.Collator(["th", "en"], { numeric: true, sensitivity: "base" }) : null;
25
32
  function compare(a, b) {
@@ -83,12 +90,30 @@ const TONES = ["neutral", "accent", "success", "warning", "danger"];
83
90
  function tone(t) {
84
91
  return TONES.indexOf(t) >= 0 ? t : "neutral";
85
92
  }
93
+ const warned = {};
94
+ function plainClick(e) {
95
+ return !e.defaultPrevented && e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey;
96
+ }
97
+ function devWarnOnce(key, message) {
98
+ if (warned[key]) return;
99
+ let dev = false;
100
+ try {
101
+ dev = true;
102
+ } catch (e) {
103
+ dev = false;
104
+ }
105
+ if (!dev) return;
106
+ warned[key] = true;
107
+ console.warn("[AURA] " + message);
108
+ }
86
109
  export {
87
110
  FOCUSABLE,
88
111
  collator,
89
112
  compare,
90
113
  cx,
114
+ devWarnOnce,
91
115
  omit,
116
+ plainClick,
92
117
  tone,
93
118
  trapTab,
94
119
  uid,
@@ -8,17 +8,18 @@ function AuraProvider(props) {
8
8
  const timeZone = props.timeZone || outer && outer.timeZone || null;
9
9
  const value = React.useMemo(
10
10
  function() {
11
- const base = props.locale && STRINGS[props.locale] || STRINGS.en;
11
+ const own = !!props.locale || !outer || !outer.locale;
12
+ const base = own ? props.locale && STRINGS[props.locale] || STRINGS.en : outer.strings;
12
13
  return {
13
- locale: props.locale || "en",
14
- calendar: props.calendar || null,
14
+ locale: props.locale || outer && outer.locale || "en",
15
+ calendar: props.calendar || (own ? null : outer.calendar),
15
16
  strings: props.strings ? Object.assign({}, base, props.strings) : base,
16
- linkComponent: props.linkComponent || null,
17
+ linkComponent: props.linkComponent || outer && outer.linkComponent || null,
17
18
  density,
18
19
  timeZone
19
20
  };
20
21
  },
21
- [props.locale, props.calendar, props.strings, props.linkComponent, density, timeZone]
22
+ [props.locale, props.calendar, props.strings, props.linkComponent, density, timeZone, outer]
22
23
  );
23
24
  return /* @__PURE__ */ React.createElement(LocaleContext.Provider, { value }, props.density ? /* @__PURE__ */ React.createElement("div", { className: "aura-density", "data-density": props.density }, props.children) : props.children);
24
25
  }
@@ -2,6 +2,7 @@
2
2
  const TONE_WORDS = {
3
3
  ready: ["ready", "done", "complete", "completed", "approved", "live", "passed"],
4
4
  progress: ["in progress", "in review", "review", "syncing", "running", "pending"],
5
+ warning: ["warning", "problem", "degraded", "at risk"],
5
6
  blocked: ["blocked", "failed", "error", "rejected", "on hold", "cancelled"]
6
7
  };
7
8
  function toneFor(status) {
@@ -9,7 +10,7 @@ function toneFor(status) {
9
10
  for (const t in TONE_WORDS) if (TONE_WORDS[t].indexOf(s) >= 0) return t;
10
11
  return "neutral";
11
12
  }
12
- const TONE_ORDER = { neutral: 0, progress: 1, ready: 2, blocked: 3 };
13
+ const TONE_ORDER = { neutral: 0, progress: 1, ready: 2, warning: 3, blocked: 4 };
13
14
  export {
14
15
  TONE_ORDER,
15
16
  toneFor as statusTone,
package/dist/esm/theme.js CHANGED
@@ -245,6 +245,8 @@ function createTheme(opts) {
245
245
  }
246
246
  if (s) check("light", "ink", "accent-lime (signal)", 4.5, INK, s[200]);
247
247
  function css(selector) {
248
+ if (selector !== void 0 && /[<{};@\r\n]|\/\*|\*\//.test(selector))
249
+ throw new Error('createTheme: "' + selector + '" is not a plain CSS selector');
248
250
  const light = selector ? selector : ':root, [data-theme="light"]';
249
251
  const dark = selector ? selector + ".dark, .dark " + selector + ", " + selector + '[data-theme="dark"], [data-theme="dark"] ' + selector : '.dark, [data-theme="dark"]';
250
252
  const system = selector ? '[data-theme="system"] ' + selector + ", " + selector + '[data-theme="system"]' : '[data-theme="system"]';
@@ -253,7 +255,7 @@ function createTheme(opts) {
253
255
  return " --aura-" + k + ": " + m[k] + ";";
254
256
  }).join("\n") + "\n}";
255
257
  }
256
- return "/* AURA theme" + (o.name ? ' "' + o.name + '"' : "") + ": brand " + o.brand + (o.signal ? ", signal " + o.signal : "") + (o.primary === "brand" ? ", brand primary buttons" : "") + ". Load after aura.css. Generated by createTheme. */\n" + block(light, L) + "\n" + block(dark, D) + "\n@media (prefers-color-scheme: dark) {\n" + block(system, D) + "\n}\n";
258
+ return "/* AURA theme" + (o.name ? ' "' + String(o.name).replace(/[*/<>\\]/g, "") + '"' : "") + ": brand " + o.brand + (o.signal ? ", signal " + o.signal : "") + (o.primary === "brand" ? ", brand primary buttons" : "") + ". Load after aura.css. Generated by createTheme. */\n" + block(light, L) + "\n" + block(dark, D) + "\n@media (prefers-color-scheme: dark) {\n" + block(system, D) + "\n}\n";
257
259
  }
258
260
  return {
259
261
  name: o.name || null,
@@ -1,6 +1,29 @@
1
1
  'use client';
2
2
  import * as React from "react";
3
3
  import { FOCUSABLE, useMounted } from "./internal.js";
4
+ let locks = 0, savedOverflow = "";
5
+ function lockScroll() {
6
+ const body = document.body;
7
+ if (locks === 0) savedOverflow = body.style.overflow;
8
+ locks++;
9
+ body.style.overflow = "hidden";
10
+ let done = false;
11
+ return function() {
12
+ if (done) return;
13
+ done = true;
14
+ locks--;
15
+ if (locks === 0) body.style.overflow = savedOverflow;
16
+ };
17
+ }
18
+ function tabbable(el) {
19
+ const all = Array.prototype.filter.call(el.querySelectorAll(FOCUSABLE), function(c) {
20
+ return c.tabIndex >= 0;
21
+ });
22
+ const shown = all.filter(function(c) {
23
+ return c.getClientRects().length > 0;
24
+ });
25
+ return shown.length ? shown : all;
26
+ }
4
27
  function useModal(open, ref, opts) {
5
28
  const mounted = useMounted();
6
29
  const prev = React.useRef(null);
@@ -9,13 +32,22 @@ function useModal(open, ref, opts) {
9
32
  function() {
10
33
  if (!open || !mounted) return;
11
34
  prev.current = document.activeElement;
12
- const body = document.body, overflow = body.style.overflow;
13
- body.style.overflow = "hidden";
35
+ const unlock = lockScroll();
14
36
  const el = ref.current;
15
- const target = el && (el.querySelector("[data-autofocus]") || el.querySelector(o.bodySelector + " " + FOCUSABLE) || el.querySelector(o.footSelector + " " + FOCUSABLE) || el);
37
+ const firstIn = function(scope) {
38
+ if (!scope || !el) return null;
39
+ const sel = FOCUSABLE.split(",").map(function(s) {
40
+ return scope + " " + s;
41
+ }).join(",");
42
+ const list = el.querySelectorAll(sel);
43
+ for (let i = 0; i < list.length; i++)
44
+ if (list[i].tabIndex >= 0 && list[i].getClientRects().length > 0) return list[i];
45
+ return null;
46
+ };
47
+ const target = el && (el.querySelector("[data-autofocus]") || firstIn(o.bodySelector) || firstIn(o.footSelector) || el);
16
48
  if (target && o.autoFocus !== false) target.focus();
17
49
  return function() {
18
- body.style.overflow = overflow;
50
+ unlock();
19
51
  if (prev.current && prev.current.focus) prev.current.focus();
20
52
  };
21
53
  },
@@ -27,17 +59,18 @@ function useModal(open, ref, opts) {
27
59
  if (o.onEscape) o.onEscape();
28
60
  return;
29
61
  }
30
- if (e.key !== "Tab" || !ref.current) return;
31
- const list = Array.prototype.slice.call(ref.current.querySelectorAll(FOCUSABLE));
62
+ if (e.key !== "Tab" || !ref.current || e.defaultPrevented) return;
63
+ if (!ref.current.contains(e.target)) return;
64
+ const list = tabbable(ref.current);
32
65
  if (!list.length) {
33
66
  e.preventDefault();
34
67
  return;
35
68
  }
36
- const first = list[0], last = list[list.length - 1];
37
- if (e.shiftKey && (document.activeElement === first || document.activeElement === ref.current)) {
69
+ const first = list[0], last = list[list.length - 1], active = document.activeElement, stray = active === ref.current;
70
+ if (e.shiftKey && (active === first || stray)) {
38
71
  e.preventDefault();
39
72
  last.focus();
40
- } else if (!e.shiftKey && document.activeElement === last) {
73
+ } else if (!e.shiftKey && (active === last || stray)) {
41
74
  e.preventDefault();
42
75
  first.focus();
43
76
  }