@jirawatpyk/aura-react 5.31.0 → 5.32.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.
@@ -49,7 +49,6 @@ const Accordion = React.forwardRef(function Accordion2(props, ref) {
49
49
  {
50
50
  type: "button",
51
51
  id: bid,
52
- "aria-labelledby": it.description ? bid + "-title" : void 0,
53
52
  "aria-describedby": it.description ? bid + "-desc" : void 0,
54
53
  "aria-expanded": on,
55
54
  "aria-controls": pid,
@@ -59,7 +58,7 @@ const Accordion = React.forwardRef(function Accordion2(props, ref) {
59
58
  }
60
59
  },
61
60
  it.icon ? /* @__PURE__ */ React.createElement(Icon, { name: it.icon, className: "aura-accordion__lead" }) : null,
62
- /* @__PURE__ */ React.createElement("span", { className: "aura-accordion__title" }, /* @__PURE__ */ React.createElement("span", { id: it.description ? bid + "-title" : void 0 }, it.title), it.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-accordion__desc", id: bid + "-desc" }, it.description) : null),
61
+ /* @__PURE__ */ React.createElement("span", { className: "aura-accordion__title" }, /* @__PURE__ */ React.createElement("span", { id: it.description ? bid + "-title" : void 0 }, it.title), it.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-accordion__desc", id: bid + "-desc", "aria-hidden": true }, it.description) : null),
63
62
  /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconChevronDown, null), className: "aura-accordion__chevron" })
64
63
  )), /* @__PURE__ */ React.createElement(
65
64
  "div",
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import * as React from "react";
3
3
  import { Icon } from "./Icon.js";
4
- import { cx, plainClick, useMaybeControlled } from "./internal.js";
4
+ import { cx, devWarnOnce, plainClick, useMaybeControlled } from "./internal.js";
5
5
  import { useLinkComponent, useStrings } from "./locale.js";
6
6
  const BottomNav = React.forwardRef(function BottomNav2(props, ref) {
7
7
  const t = useStrings();
@@ -21,13 +21,18 @@ const BottomNav = React.forwardRef(function BottomNav2(props, ref) {
21
21
  "aria-label": props.label || t.mainNav
22
22
  },
23
23
  /* @__PURE__ */ React.createElement("ul", { className: "aura-bottomnav__list" }, props.items.map(function(it) {
24
+ if (it.ariaLabel && typeof it.label === "string" && it.ariaLabel.toLowerCase().indexOf(it.label.toLowerCase()) < 0)
25
+ devWarnOnce(
26
+ "bottomnav-label-" + it.id,
27
+ 'BottomNav: ariaLabel "' + it.ariaLabel + '" should contain the visible label "' + it.label + '" (WCAG 2.5.3).'
28
+ );
24
29
  const on = active === it.id;
25
30
  const count = it.count != null && it.count > 0 ? it.count > 99 ? "99+" : String(it.count) : null;
26
31
  const suffix = count || it.badge && it.badgeLabel ? " (" + (count || it.badgeLabel) + ")" : "";
27
32
  const common = {
28
33
  className: cx("aura-bottomnav__item", on && "is-active"),
29
- /* 5.7: a full name when the label is shortened; the count / badge words still follow. */
30
- "aria-label": it.ariaLabel ? it.ariaLabel + suffix : void 0,
34
+ /* 5.7: a full name when the label is shortened; the count / badge words still follow. 5.32 (WCAG 2.5.3;
35
+ * axe 4.14): as content — the full name sr-only, the short label aria-hidden — not aria-label. */
31
36
  "aria-current": on ? "page" : void 0,
32
37
  onClick: function(e) {
33
38
  if (!it.href) e.preventDefault();
@@ -37,7 +42,8 @@ const BottomNav = React.forwardRef(function BottomNav2(props, ref) {
37
42
  };
38
43
  const inner = [
39
44
  /* @__PURE__ */ React.createElement("span", { key: "i", className: "aura-bottomnav__icon" }, /* @__PURE__ */ React.createElement(Icon, { name: it.icon, size: "md" }), count ? /* @__PURE__ */ React.createElement("span", { className: "aura-bottomnav__count", "aria-hidden": "true" }, count) : it.badge ? /* @__PURE__ */ React.createElement("span", { className: "aura-bottomnav__dot", "aria-hidden": "true" }) : null),
40
- /* @__PURE__ */ React.createElement("span", { key: "l", className: "aura-bottomnav__label" }, it.label),
45
+ it.ariaLabel ? /* @__PURE__ */ React.createElement("span", { key: "n", className: "aura-sr-only" }, it.ariaLabel + suffix) : null,
46
+ /* @__PURE__ */ React.createElement("span", { key: "l", className: "aura-bottomnav__label", "aria-hidden": it.ariaLabel ? true : void 0 }, it.label),
41
47
  suffix && !it.ariaLabel ? /* @__PURE__ */ React.createElement("span", { key: "s", className: "aura-sr-only" }, suffix) : null
42
48
  ];
43
49
  return /* @__PURE__ */ React.createElement("li", { key: it.id, className: "aura-bottomnav__cell" }, it.href ? /* @__PURE__ */ React.createElement(Link, { href: it.href, ...common }, inner) : /* @__PURE__ */ React.createElement("button", { type: "button", ...common }, inner));
@@ -234,7 +234,6 @@ const Combobox = React.forwardRef(
234
234
  id: optId(i),
235
235
  role: "option",
236
236
  "data-idx": i,
237
- "aria-labelledby": o.description ? optId(i) + "-label" : void 0,
238
237
  "aria-describedby": o.description ? optId(i) + "-desc" : void 0,
239
238
  "aria-selected": isSel,
240
239
  "aria-disabled": blocked || void 0,
@@ -255,7 +254,7 @@ const Combobox = React.forwardRef(
255
254
  }
256
255
  },
257
256
  o.icon ? /* @__PURE__ */ React.createElement(Icon, { name: o.icon }) : null,
258
- /* @__PURE__ */ React.createElement("span", { className: "aura-combo__text" }, /* @__PURE__ */ React.createElement("span", { className: "aura-combo__label", id: o.description ? optId(i) + "-label" : void 0 }, o.label), o.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-combo__desc", id: optId(i) + "-desc" }, o.description) : null),
257
+ /* @__PURE__ */ React.createElement("span", { className: "aura-combo__text" }, /* @__PURE__ */ React.createElement("span", { className: "aura-combo__label", id: o.description ? optId(i) + "-label" : void 0 }, o.label), o.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-combo__desc", id: optId(i) + "-desc", "aria-hidden": true }, o.description) : null),
259
258
  isSel ? /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconCheck, null), className: "aura-combo__check" }) : null
260
259
  );
261
260
  }
@@ -202,7 +202,6 @@ function Command(props) {
202
202
  key: it.id,
203
203
  id: optId(n),
204
204
  role: "option",
205
- "aria-labelledby": it.description ? optId(n) + "-label" + (it.shortcut ? " " + optId(n) + "-kbd" : "") : void 0,
206
205
  "aria-describedby": it.description ? optId(n) + "-desc" : void 0,
207
206
  "aria-selected": n === active,
208
207
  "aria-disabled": it.disabled || void 0,
@@ -222,7 +221,7 @@ function Command(props) {
222
221
  }
223
222
  },
224
223
  it.icon ? /* @__PURE__ */ React.createElement(Icon, { name: it.icon }) : null,
225
- /* @__PURE__ */ React.createElement("span", { className: "aura-command__text" }, /* @__PURE__ */ React.createElement("span", { className: "aura-command__label", id: it.description ? optId(n) + "-label" : void 0 }, it.label), it.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-command__desc", id: optId(n) + "-desc" }, it.description) : null),
224
+ /* @__PURE__ */ React.createElement("span", { className: "aura-command__text" }, /* @__PURE__ */ React.createElement("span", { className: "aura-command__label", id: it.description ? optId(n) + "-label" : void 0 }, it.label), it.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-command__desc", id: optId(n) + "-desc", "aria-hidden": true }, it.description) : null),
226
225
  it.shortcut ? /* @__PURE__ */ React.createElement("kbd", { className: "aura-command__kbd", id: it.description ? optId(n) + "-kbd" : void 0 }, it.shortcut) : null
227
226
  );
228
227
  })
@@ -48,6 +48,7 @@ const FilterDateRange = React.forwardRef(
48
48
  choose(a <= iso ? { start: a, end: iso } : { start: iso, end: a });
49
49
  }
50
50
  const isAny = !v.start || !v.end;
51
+ const own = props["aria-label"] || void 0;
51
52
  const attrs = {};
52
53
  Object.keys(props).forEach(function(k) {
53
54
  if (k === "style" || /^(aria|data)-/.test(k) && k !== "aria-label")
@@ -136,7 +137,7 @@ const FilterDateRange = React.forwardRef(
136
137
  type: "button",
137
138
  className: "aura-filterselect__face",
138
139
  disabled: props.disabled,
139
- "aria-label": props["aria-label"] || props.label + ": " + value,
140
+ "aria-label": own || void 0,
140
141
  "aria-haspopup": "dialog",
141
142
  "aria-expanded": pop.open,
142
143
  "aria-controls": pop.open ? dialogId : void 0,
@@ -145,8 +146,9 @@ const FilterDateRange = React.forwardRef(
145
146
  draft[1](null);
146
147
  }
147
148
  },
148
- /* @__PURE__ */ React.createElement("span", { className: "aura-filterselect__name" }, props.label),
149
- /* @__PURE__ */ React.createElement("span", { className: "aura-filterselect__value" }, value),
149
+ own ? null : /* @__PURE__ */ React.createElement("span", { className: "aura-sr-only" }, props.label + ": " + value),
150
+ /* @__PURE__ */ React.createElement("span", { className: "aura-filterselect__name", "aria-hidden": own ? void 0 : true }, props.label),
151
+ /* @__PURE__ */ React.createElement("span", { className: "aura-filterselect__value", "aria-hidden": own ? void 0 : true }, value),
150
152
  /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconChevronDown, null), className: "aura-filterselect__chevron" })
151
153
  )
152
154
  ), cal);
@@ -2,8 +2,11 @@
2
2
  import * as React from "react";
3
3
  import { cx, omit } from "./internal.js";
4
4
  import { Icon } from "./Icon.js";
5
+ import { useAutoTip } from "./autoTip.js";
5
6
  const IconButton = React.forwardRef(function IconButton2(props, ref) {
6
7
  const rest = omit(props, ["icon", "label", "className", "size", "tone", "touchHeight"]);
8
+ useAutoTip();
9
+ const own = props.title !== void 0;
7
10
  return /* @__PURE__ */ React.createElement(
8
11
  "button",
9
12
  {
@@ -11,7 +14,7 @@ const IconButton = React.forwardRef(function IconButton2(props, ref) {
11
14
  ref,
12
15
  type: props.type || "button",
13
16
  "aria-label": props.label,
14
- title: props.label,
17
+ "data-aura-tip": own ? void 0 : props.label,
15
18
  className: cx(
16
19
  "aura-icon-btn",
17
20
  props.tone === "danger" && "aura-icon-btn--danger",
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+ import * as React from "react";
3
+ import { cx } from "./classes.js";
4
+ const h = React.createElement;
5
+ const PageHeader = React.forwardRef(function PageHeader2(props, ref) {
6
+ const attrs = {};
7
+ Object.keys(props).forEach(function(k) {
8
+ if (k === "id" || k === "lang" || k === "dir" || k === "role" || /^(aria|data)-/.test(k))
9
+ attrs[k] = props[k];
10
+ });
11
+ const level = props.headingLevel || 1;
12
+ return h(
13
+ "div",
14
+ Object.assign(attrs, { ref, className: cx("aura-page-header", props.className), style: props.style }),
15
+ h(
16
+ "div",
17
+ { className: "aura-page-header__text" },
18
+ props.eyebrow != null && props.eyebrow !== false ? h("p", { className: "aura-page-header__eyebrow" }, props.eyebrow) : null,
19
+ h("h" + level, { className: "aura-page-header__title", id: props.titleId }, props.title),
20
+ props.meta != null && props.meta !== false ? h("div", { className: "aura-page-header__meta" }, props.meta) : null
21
+ ),
22
+ props.actions != null && props.actions !== false ? h("div", { className: "aura-page-header__actions" }, props.actions) : null
23
+ );
24
+ });
25
+ export {
26
+ PageHeader
27
+ };
@@ -3,6 +3,7 @@ import * as React from "react";
3
3
  import { Icon } from "./Icon.js";
4
4
  import { IconButton } from "./IconButton.js";
5
5
  import { cx, plainClick, useMaybeControlled } from "./internal.js";
6
+ import { useAutoTip } from "./autoTip.js";
6
7
  import { useLinkComponent, useStrings } from "./locale.js";
7
8
  import { IconChevronLeft, IconChevronRight } from "./icons.js";
8
9
  function pageList(page, count, sib) {
@@ -26,6 +27,7 @@ function pageList(page, count, sib) {
26
27
  }
27
28
  const Pagination = React.forwardRef(function Pagination2(props, ref) {
28
29
  const t = useStrings();
30
+ useAutoTip();
29
31
  const count = Math.max(1, props.pageCount || 1);
30
32
  const st = useMaybeControlled(props.page, props.defaultPage || 1, props.onChange);
31
33
  const page = Math.min(count, Math.max(1, st[0]));
@@ -88,7 +90,7 @@ const Pagination = React.forwardRef(function Pagination2(props, ref) {
88
90
  rel,
89
91
  className: "aura-icon-btn",
90
92
  "aria-label": label,
91
- title: label,
93
+ "data-aura-tip": label,
92
94
  onClick: function(e) {
93
95
  if (props.onChange && plainClick(e)) {
94
96
  e.preventDefault();
@@ -2,12 +2,14 @@
2
2
  import * as React from "react";
3
3
  import { Icon } from "./Icon.js";
4
4
  import { cx, uid, useMaybeControlled } from "./internal.js";
5
+ import { useAutoTip } from "./autoTip.js";
5
6
  function toOpt(o) {
6
7
  return typeof o === "object" ? o : { value: o, label: o };
7
8
  }
8
9
  const SegmentedControl = React.forwardRef(
9
10
  function SegmentedControl2(props, ref) {
10
11
  const options = (props.options || []).map(toOpt);
12
+ useAutoTip();
11
13
  const firstEnabled = options.filter(function(o) {
12
14
  return !o.disabled;
13
15
  })[0];
@@ -81,7 +83,7 @@ const SegmentedControl = React.forwardRef(
81
83
  role: "radio",
82
84
  "aria-checked": on,
83
85
  "aria-label": o.iconOnly ? o.label : void 0,
84
- title: o.iconOnly ? o.label : void 0,
86
+ "data-aura-tip": o.iconOnly ? o.label : void 0,
85
87
  tabIndex: i === tabIdx && !props.disabled ? 0 : -1,
86
88
  disabled: off,
87
89
  className: cx("aura-segmented__option", on && "is-selected", o.iconOnly && "is-icon"),
@@ -176,6 +176,7 @@ const SideNav = React.forwardRef(function SideNav2(props, ref) {
176
176
  "aura-nav",
177
177
  collapsed && "aura-nav--collapsed",
178
178
  props.bordered === false && "aura-nav--borderless",
179
+ props.headerDivider && props.header && "aura-nav--header-divider",
179
180
  props.chevron === "right" && "aura-nav--chevron-right",
180
181
  props.className
181
182
  ),
@@ -30,7 +30,15 @@ const Stepper = React.forwardRef(function Stepper2(props, ref) {
30
30
  const clickable = state === "done" && !!props.onStepClick;
31
31
  const named = clickable && (typeof s.label === "string" || typeof s.label === "number") && String(s.label).trim() !== "";
32
32
  const descId = idBase + "-step-" + i;
33
- const text = /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__text" }, /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__label" }, s.label, note ? /* @__PURE__ */ React.createElement("span", { className: "aura-sr-only" }, ", " + note) : null), s.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__desc", id: named ? descId : void 0 }, s.description) : null);
33
+ const text = /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__text" }, named ? /* @__PURE__ */ React.createElement("span", { className: "aura-sr-only" }, String(s.label) + (note ? ", " + note : "")) : null, /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__label", "aria-hidden": named ? true : void 0 }, s.label, note && !named ? /* @__PURE__ */ React.createElement("span", { className: "aura-sr-only" }, ", " + note) : null), s.description ? /* @__PURE__ */ React.createElement(
34
+ "span",
35
+ {
36
+ className: "aura-stepper__desc",
37
+ id: named ? descId : void 0,
38
+ "aria-hidden": named ? true : void 0
39
+ },
40
+ s.description
41
+ ) : null);
34
42
  return /* @__PURE__ */ React.createElement(
35
43
  "li",
36
44
  {
@@ -43,7 +51,6 @@ const Stepper = React.forwardRef(function Stepper2(props, ref) {
43
51
  {
44
52
  type: "button",
45
53
  className: "aura-stepper__step aura-focusable",
46
- "aria-label": named ? String(s.label) + (note ? ", " + note : "") : void 0,
47
54
  "aria-describedby": named && s.description ? descId : void 0,
48
55
  onClick: function() {
49
56
  props.onStepClick(s.id);
package/dist/esm/Tag.js CHANGED
@@ -3,6 +3,7 @@ import * as React from "react";
3
3
  import { Icon } from "./Icon.js";
4
4
  import { cx, devWarnOnce, omit, useIsoLayoutEffect } from "./internal.js";
5
5
  import { Tooltip } from "./Tooltip.js";
6
+ import { useAutoTip } from "./autoTip.js";
6
7
  import { useStrings } from "./locale.js";
7
8
  import { IconCheck, IconX } from "./icons.js";
8
9
  function textOf(node, all) {
@@ -32,6 +33,7 @@ const Tag = React.forwardRef(function Tag2(props, ref) {
32
33
  "touchHeight"
33
34
  ]);
34
35
  const textRef = React.useRef(null);
36
+ useAutoTip();
35
37
  const cutState = React.useState(false), isCut = cutState[0];
36
38
  const check = React.useCallback(function() {
37
39
  const el = textRef.current;
@@ -53,7 +55,7 @@ const Tag = React.forwardRef(function Tag2(props, ref) {
53
55
  const fullText = isCut ? textOf(props.children, true).replace(/\s+/g, " ").trim() : "";
54
56
  const inner = [
55
57
  props.icon ? /* @__PURE__ */ React.createElement(Icon, { key: "i", name: props.icon, size: 14 }) : null,
56
- /* @__PURE__ */ React.createElement("span", { key: "t", ref: textRef, className: "aura-tag__text", title: fullText || void 0 }, props.children)
58
+ /* @__PURE__ */ React.createElement("span", { key: "t", ref: textRef, className: "aura-tag__text", "data-aura-tip": fullText || void 0 }, props.children)
57
59
  ];
58
60
  if (selectable) {
59
61
  return /* @__PURE__ */ React.createElement(
@@ -0,0 +1,182 @@
1
+ 'use client';
2
+ import * as React from "react";
3
+ const ATTR = "data-aura-tip";
4
+ let installed = false;
5
+ let tip = null;
6
+ let cur = null;
7
+ let byFocus = false;
8
+ let hiding = false;
9
+ let timer;
10
+ let watch = null;
11
+ let gone = null;
12
+ function targetOf(e) {
13
+ const path = typeof e.composedPath === "function" ? e.composedPath() : null;
14
+ return path && path.length ? path[0] : e.target;
15
+ }
16
+ function source(t) {
17
+ if (!t || typeof t.closest !== "function") return null;
18
+ const el = t;
19
+ if (el.closest(".aura-tooltip-anchor")) return null;
20
+ let s = el.closest("[" + ATTR + "]");
21
+ if (!s) {
22
+ const tag = el.closest(".aura-tag");
23
+ if (tag) s = tag.querySelector(".aura-tag__text[" + ATTR + "]");
24
+ }
25
+ if (!s || !s.getAttribute(ATTR) || !s.isConnected) return null;
26
+ if (s.getAttribute("aria-expanded") === "true") return null;
27
+ return s;
28
+ }
29
+ function place() {
30
+ if (!tip || !cur) return;
31
+ if (!cur.isConnected) {
32
+ hide();
33
+ return;
34
+ }
35
+ const r = cur.getBoundingClientRect(), t = tip.getBoundingClientRect(), vw = window.innerWidth, vh = window.innerHeight, gap = 8, m = 8;
36
+ let top = r.top - t.height - gap;
37
+ if (top < m && r.bottom + gap + t.height <= vh - m) top = r.bottom + gap;
38
+ top = Math.max(m, Math.min(top, vh - t.height - m));
39
+ const left = Math.max(m, Math.min(r.left + r.width / 2 - t.width / 2, vw - t.width - m));
40
+ tip.style.top = top + "px";
41
+ tip.style.left = left + "px";
42
+ }
43
+ function inZone(x, y) {
44
+ if (!cur || !tip || !up()) return false;
45
+ const a = cur.getBoundingClientRect(), b = tip.getBoundingClientRect();
46
+ return x >= Math.min(a.left, b.left) - 1 && x <= Math.max(a.right, b.right) + 1 && y >= Math.min(a.top, b.top) - 1 && y <= Math.max(a.bottom, b.bottom) + 1;
47
+ }
48
+ function stop() {
49
+ clearTimeout(timer);
50
+ hiding = false;
51
+ }
52
+ function show(s, focus) {
53
+ stop();
54
+ if (!tip) {
55
+ tip = document.createElement("div");
56
+ tip.className = "aura-tooltip aura-tooltip--auto";
57
+ tip.setAttribute("aria-hidden", "true");
58
+ }
59
+ cur = s;
60
+ byFocus = focus;
61
+ tip.textContent = s.getAttribute(ATTR);
62
+ const lang = s.closest("[lang]"), dir = s.closest("[dir]");
63
+ tip.lang = lang ? lang.getAttribute("lang") || "" : "";
64
+ tip.dir = dir ? dir.getAttribute("dir") || "" : "";
65
+ tip.style.top = tip.style.left = "-9999px";
66
+ if (!tip.isConnected) document.body.appendChild(tip);
67
+ place();
68
+ if (typeof MutationObserver === "undefined") return;
69
+ if (watch) watch.disconnect();
70
+ watch = new MutationObserver(function() {
71
+ if (!cur || !tip) return;
72
+ const text = cur.getAttribute(ATTR);
73
+ if (!text || cur.getAttribute("aria-expanded") === "true") hide();
74
+ else {
75
+ tip.textContent = text;
76
+ place();
77
+ }
78
+ });
79
+ watch.observe(s, { attributes: true, attributeFilter: [ATTR, "aria-expanded"] });
80
+ if (!gone) {
81
+ gone = new MutationObserver(function() {
82
+ if (cur && !cur.isConnected) hide();
83
+ });
84
+ gone.observe(document.body, { childList: true, subtree: true });
85
+ }
86
+ }
87
+ function hide() {
88
+ stop();
89
+ if (watch) watch.disconnect();
90
+ if (gone) gone.disconnect();
91
+ watch = gone = null;
92
+ cur = null;
93
+ byFocus = false;
94
+ if (tip && tip.isConnected) tip.remove();
95
+ }
96
+ function hideSoon() {
97
+ if (hiding) return;
98
+ clearTimeout(timer);
99
+ hiding = true;
100
+ timer = setTimeout(hide, 120);
101
+ }
102
+ function up() {
103
+ return !!(tip && tip.isConnected);
104
+ }
105
+ function focusVisible(el) {
106
+ try {
107
+ return el.matches(":focus-visible");
108
+ } catch (e) {
109
+ return true;
110
+ }
111
+ }
112
+ function installAutoTip() {
113
+ if (installed || typeof document === "undefined") return;
114
+ installed = true;
115
+ const w = window;
116
+ if (w.__auraAutoTip) return;
117
+ w.__auraAutoTip = true;
118
+ document.addEventListener("pointerover", function(e) {
119
+ if (e.pointerType === "touch") return;
120
+ const s = source(targetOf(e));
121
+ if (s) {
122
+ if (s === cur && up()) {
123
+ stop();
124
+ return;
125
+ }
126
+ stop();
127
+ if (up()) show(s, false);
128
+ else
129
+ timer = setTimeout(function() {
130
+ if (source(s) === s) show(s, false);
131
+ }, 400);
132
+ return;
133
+ }
134
+ if (!cur) stop();
135
+ else if (!byFocus && !inZone(e.clientX, e.clientY)) hideSoon();
136
+ });
137
+ document.addEventListener("pointermove", function(e) {
138
+ if (!cur || byFocus || e.pointerType === "touch") return;
139
+ if (inZone(e.clientX, e.clientY)) stop();
140
+ else hideSoon();
141
+ });
142
+ document.addEventListener("pointerout", function(e) {
143
+ if (!cur) {
144
+ if (!e.relatedTarget) stop();
145
+ return;
146
+ }
147
+ if (byFocus) return;
148
+ const to = e.relatedTarget;
149
+ if (to && (cur.contains(to) || inZone(e.clientX, e.clientY))) return;
150
+ hideSoon();
151
+ });
152
+ document.addEventListener("focusin", function(e) {
153
+ const t = targetOf(e), s = source(t);
154
+ if (s && focusVisible(t)) show(s, true);
155
+ else if (cur) hide();
156
+ });
157
+ document.addEventListener("focusout", function(e) {
158
+ const t = targetOf(e);
159
+ if (cur && byFocus && t && (cur === t || cur.contains(t) || t.contains(cur))) hide();
160
+ });
161
+ document.addEventListener("pointerdown", hide);
162
+ document.addEventListener("keydown", function(e) {
163
+ if (e.key === "Escape" && up()) hide();
164
+ });
165
+ window.addEventListener(
166
+ "scroll",
167
+ function() {
168
+ if (up()) place();
169
+ },
170
+ true
171
+ );
172
+ window.addEventListener("resize", function() {
173
+ if (up()) place();
174
+ });
175
+ }
176
+ function useAutoTip() {
177
+ React.useEffect(installAutoTip, []);
178
+ }
179
+ export {
180
+ installAutoTip,
181
+ useAutoTip
182
+ };
package/dist/esm/index.js CHANGED
@@ -35,6 +35,7 @@ import { Avatar } from "./Avatar.js";
35
35
  import { Stack } from "./Stack.js";
36
36
  import { Grid } from "./Grid.js";
37
37
  import { Container } from "./Container.js";
38
+ import { PageHeader } from "./PageHeader.js";
38
39
  import { AppShell } from "./AppShell.js";
39
40
  import { ActionBar } from "./ActionBar.js";
40
41
  import { Separator } from "./Separator.js";
@@ -98,6 +99,7 @@ export {
98
99
  IconButton,
99
100
  Menu,
100
101
  NumberField,
102
+ PageHeader,
101
103
  Pagination,
102
104
  PasswordField,
103
105
  Popover,
package/dist/index.d.cts CHANGED
@@ -344,7 +344,8 @@ export interface IconButtonProps extends React$1.ButtonHTMLAttributes<HTMLButton
344
344
  icon: IconInput;
345
345
  /** `danger` colours the icon and hover for a destructive action (Delete, Revoke). */
346
346
  tone?: "neutral" | "danger" | undefined;
347
- /** Accessible name and tooltip (required). */
347
+ /** Accessible name and tooltip (required). Since 5.32 the tooltip is AURA's own (hover and keyboard focus), not a
348
+ * native `title`; pass `title` yourself to keep a native one instead. */
348
349
  label: string;
349
350
  /** Icon size. Default `sm` (16). */
350
351
  size?: "sm" | "md" | undefined;
@@ -828,6 +829,10 @@ export interface SideNavProps {
828
829
  defaultValue?: string | undefined;
829
830
  onChange?: ((id: string) => void) | undefined;
830
831
  header?: React$1.ReactNode | undefined;
832
+ /** 5.32 (DxT Monitor request 1): a full-width 1px aura-border-default line under `header`, with the board's metrics:
833
+ * the header at least 72px tall with its content 20px from the top and start, the line at its foot, and 16px
834
+ * before the first item. Default `false`. */
835
+ headerDivider?: boolean | undefined;
831
836
  footer?: React$1.ReactNode | undefined;
832
837
  /** Default "Main". */
833
838
  label?: string | undefined;
@@ -1214,7 +1219,9 @@ export interface AppShellProps {
1214
1219
  /** A BottomNav for phones (4.19). Shown below its `hideFrom` breakpoint; the content keeps room for it, and a
1215
1220
  * viewport ActionBar sits on top of it. With a `bottomNav` and no `nav`, there is no menu button. */
1216
1221
  bottomNav?: React$1.ReactElement | undefined;
1217
- /** `false` (5.14): `<main>` has no padding, for pages whose containers own the page padding and column. */
1222
+ /** The content padding is the page margin: 16px at the sides and 24px top and bottom on phones, 24px from 640px,
1223
+ * 32px from 1024px. Pages don't add their own (a PageHeader has none, 5.32). `false` (5.14): `<main>` has no
1224
+ * padding, for pages whose containers own the page padding and column. */
1218
1225
  contentPadding?: boolean | undefined;
1219
1226
  className?: string | undefined;
1220
1227
  }
@@ -1338,7 +1345,8 @@ export interface BottomNavItem {
1338
1345
  /** Read after the label when `badge` is set, e.g. "new". */
1339
1346
  badgeLabel?: string | undefined;
1340
1347
  /** The full name for screen readers when `label` is shortened to fit (5.7), e.g. label "Konto", ariaLabel
1341
- * "Mitt konto". Include the visible label in it (WCAG 2.5.3). The count or badge is still appended. */
1348
+ * "Mitt konto". Include the visible label in it (WCAG 2.5.3); a dev warning says when it doesn't (5.32). The count or badge is
1349
+ * still appended. */
1342
1350
  ariaLabel?: string | undefined;
1343
1351
  }
1344
1352
  export interface BottomNavProps {
@@ -1490,7 +1498,10 @@ export interface ProgressProps {
1490
1498
  className?: string | undefined;
1491
1499
  }
1492
1500
  export interface SkeletonProps {
1493
- variant?: "text" | "rect" | "circle" | undefined;
1501
+ /** 5.32 (DxT Monitor request 4): `pill` is a StatusPill's size (20px tall, 22px under `lang="th"`, 72px wide) and
1502
+ * `badge` a Badge's (20px, 56px wide), both fully rounded, so the row doesn't move when the real one arrives.
1503
+ * `width` overrides the width. */
1504
+ variant?: "text" | "rect" | "circle" | "pill" | "badge" | undefined;
1494
1505
  /** text only: number of lines (the last is shorter). */
1495
1506
  lines?: number | undefined;
1496
1507
  width?: number | string | undefined;
@@ -1783,6 +1794,28 @@ export interface ContainerProps extends Pick<React$1.HTMLAttributes<HTMLElement>
1783
1794
  }
1784
1795
  /** Container — centres content up to aura-container-max (1280px) with responsive side padding. */
1785
1796
  export declare const Container: React$1.ForwardRefExoticComponent<ContainerProps & React$1.RefAttributes<HTMLElement>>;
1797
+ /** PageHeader (5.32, DxT Monitor request 3): the top of a page — an eyebrow, the page's heading, a meta line and the
1798
+ * page's actions. It has no outer padding: inside AppShell, the content padding is the page margin (16px at the sides
1799
+ * and 24px top and bottom on phones, 24px from 640px, 32px from 1024px). Stateless, so it is also exported from
1800
+ * `/server`. It renders a plain `div`: to name a region by its title, put `aria-labelledby={titleId}` on your own
1801
+ * `<section>`, or pass `role="region"` with it. */
1802
+ export interface PageHeaderProps extends Pick<React$1.HTMLAttributes<HTMLElement>, "id" | "lang" | "dir" | "role">, React$1.AriaAttributes, DataAttributes {
1803
+ /** Small mono line above the title — a section or breadcrumb-like context ("Monitor / Sites"). */
1804
+ eyebrow?: React$1.ReactNode | undefined;
1805
+ /** The page's heading. */
1806
+ title: React$1.ReactNode;
1807
+ /** A line under the title — counts, last update, owner. */
1808
+ meta?: React$1.ReactNode | undefined;
1809
+ /** Buttons for the page, bottom-aligned at the end; they wrap below the text on a narrow screen. */
1810
+ actions?: React$1.ReactNode | undefined;
1811
+ /** Default 1. Use 2 when the page already has an h1 (e.g. in the AppShell bar). */
1812
+ headingLevel?: 1 | 2 | 3 | undefined;
1813
+ /** The heading's id, for `aria-labelledby` on the page's region. */
1814
+ titleId?: string | undefined;
1815
+ className?: string | undefined;
1816
+ style?: React$1.CSSProperties | undefined;
1817
+ }
1818
+ export declare const PageHeader: React$1.ForwardRefExoticComponent<PageHeaderProps & React$1.RefAttributes<HTMLDivElement>>;
1786
1819
  /** AppShell — side navigation + top bar + content. The nav is fixed from lg (1024px) up and a Drawer below it.
1787
1820
  * Which one shows is decided in CSS (4.16), so the server's HTML is already right on a phone and nothing shifts on
1788
1821
  * hydration; JavaScript only opens and closes the drawer. */