@jirawatpyk/aura-react 5.13.0 → 5.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.
package/README.md CHANGED
@@ -186,6 +186,19 @@ DataTable with sort and page in the URL (4.17): `onStateChange={({ sort, page })
186
186
  - **Time zone**: `timeZone` on AuraProvider, DatePicker, DateRangePicker and Calendar decides "today" (the marker, `min`/`max="today"`, the first month shown); or pass `today` as an ISO date. `todayIn('Asia/Bangkok')` is exported from the root and `/server`.
187
187
  - **Toasts** queue past three instead of dropping: six in a row all show, in order, three at a time.
188
188
 
189
+ ## 5.14 — Chamber-OS addenda 15–16, the small items
190
+
191
+ - **EmptyState `headingLevel={false}`** (item 86): the title is a `<p>`, so an empty state inside a card or list adds nothing to the page's heading outline. EmptyState has no live-region role unless you pass `role`.
192
+ - **Stat from `/server`, with `headingLevel`** (item 88): `import { Stat } from '@jirawatpyk/aura-react/server'` renders the same HTML as the root Stat (no `onClick` there; `href` renders `linkComponent`, else `<a>`), `loading` included. `headingLevel={2}` makes the label a heading — on a link tile too; with `onClick` it stays a span (a heading can't sit in a button) and a dev notice says so.
193
+ - **Drawer body scroll padding** (item 91): a field scrolled into view in a Drawer (focus, a phone's keyboard) keeps 16px clear of the head and the foot, so its focus ring is never cut (WCAG 2.4.11).
194
+ - **AppShell `contentPadding={false}`** (item 97): `<main>` has no padding, for pages whose containers own the padding and column.
195
+ - **Button keeps a passed `aria-disabled`** (item 102): `<Button aria-disabled>` stays focusable and in the Tab order, is announced as unavailable, looks unavailable (disabled opacity, not-allowed cursor) and ignores clicks and Enter — a gated confirm. Hovering it changes nothing. `loading` still sets it. In tests, click such a button with `{ force: true }`. This is for buttons: a Button with `href` takes `disabled` (it drops the link and sets `aria-disabled` itself), and IconButton is unchanged.
196
+ - **Avatar from `/server`** (item 103): initials and colour, same HTML as the root Avatar. Falling back to initials when an image fails needs the client.
197
+ - **`--aura-shell-bar-height`** (item 104): the top bar's height on `.aura-shell`, for sticky offsets under it — `top: var(--aura-shell-bar-height)`. AppShell measures the bar, so a header that wraps to two lines on a phone gives its real height; the stylesheet's 56px covers the server's HTML until then. It is set inline on the shell, so read it rather than override it. It is 0 when there is no bar (no `header`, no `nav`), and from 1024px when the bar only held the menu button. The bar is now `box-sizing: border-box`, so it is 56px everywhere (it was 57px where no border-box reset applied).
198
+ - **`--aura-fg-warning`** (item 106): warning text on the page — amber-800 in light (7.1:1 on surface, 6.8:1 on canvas), amber-300 in dark (12.3:1, 13.9:1), checked in CI. `--aura-status-*-fg` are only for text on their pill's own fill; the warning one is near-black in dark mode.
199
+ - **DataTable cards start-align every field** (item 108): a column with `align: 'end'` is right-aligned in the grid and starts under its label in the stacked cards, with tabular figures in both. A visible change to cards with amount columns.
200
+ - **Tabs `current="location"`** (item 107): link tabs to sections of the same page mark the current one `aria-current="location"` (default `page`).
201
+
189
202
  ## 5.13 — Chamber-OS addendum 13 (board parity)
190
203
 
191
204
  - **DataTable card options** (item 80): per column, `card: 'hide'` leaves it out of the stacked card (the grid keeps it), `card: 'title' | 'pill' | 'field'` places it (a declared title or pill replaces the automatic one; the automatic title is the first column without a `card` — normally the first column — skipping `actions` columns), and `cardOrder` orders the fields (lowest first; others sort by column index) without touching the grid's order. `hideSelectionInCards` drops the row boxes and the select-all line from the cards only; the selection is kept, and Space doesn't change it while cards show. Screen readers and arrow keys follow the grid's order and skip what the card leaves out (the tab stop too); each remaining field keeps its label; loading skeletons leave out the same columns. With `getRowHref`, the link is the first column's text, so hiding that column in cards leaves the card clickable but without a link to Tab to. All in CSS, so the server HTML is already right.
@@ -674,7 +674,7 @@ window.Aura = (() => {
674
674
  );
675
675
  }
676
676
  function emptyStateElement(props, ref) {
677
- const HT = "h" + (props.headingLevel || 3);
677
+ const HT = props.headingLevel === false ? "p" : "h" + (props.headingLevel || 3);
678
678
  const rest = omit(props, [
679
679
  "title",
680
680
  "description",
@@ -706,6 +706,66 @@ window.Aura = (() => {
706
706
  props.action ? /* @__PURE__ */ React4.createElement("div", { className: "aura-empty__action" }, props.action) : null
707
707
  );
708
708
  }
709
+ function statElement(props, ref, Link) {
710
+ const ch = props.change;
711
+ const dir = ch && (ch.direction || "flat");
712
+ const tone2 = ch && (ch.tone || (dir === "up" ? "positive" : dir === "down" ? "negative" : "neutral"));
713
+ const v = props.value;
714
+ const numeric = typeof v === "number" || typeof v === "string" && /\d/.test(v) && /^[\s\d.,:+\-\u2212%()\u0E3F$\u20AC\u00A3\u00A5kKmMbB]+$/.test(v.replace(/\b[A-Z]{3}\b/g, ""));
715
+ const Tag3 = props.href ? Link : props.onClick ? "button" : "div";
716
+ const interactive = !!(props.href || props.onClick);
717
+ if (props.headingLevel && Tag3 === "button")
718
+ devWarnOnce(
719
+ "stat-heading-button",
720
+ "Stat `headingLevel` is ignored with `onClick`: a heading can\u2019t sit in a button. Use `href`, or put the heading outside."
721
+ );
722
+ const LabelTag = props.headingLevel && Tag3 !== "button" ? "h" + props.headingLevel : "span";
723
+ const HeadTag = LabelTag === "span" ? "span" : "div";
724
+ return /* @__PURE__ */ React4.createElement(
725
+ Tag3,
726
+ {
727
+ ref,
728
+ className: cx("aura-stat", interactive && "is-interactive", props.loading && "is-loading", props.className),
729
+ href: props.href,
730
+ onClick: props.onClick,
731
+ type: Tag3 === "button" ? "button" : void 0,
732
+ "aria-busy": props.loading || void 0
733
+ },
734
+ /* @__PURE__ */ React4.createElement(HeadTag, { className: "aura-stat__head" }, /* @__PURE__ */ React4.createElement(LabelTag, { className: "aura-stat__label" }, props.label), props.icon ? /* @__PURE__ */ React4.createElement("span", { className: "aura-stat__icon" }, /* @__PURE__ */ React4.createElement(Icon, { name: props.icon })) : null),
735
+ props.loading ? /* @__PURE__ */ React4.createElement("span", { className: "aura-stat__value" }, /* @__PURE__ */ React4.createElement("span", { className: "aura-skel aura-stat__skel" })) : /* @__PURE__ */ React4.createElement("span", { className: cx("aura-stat__value", numeric && "is-numeric") }, props.value, props.unit ? /* @__PURE__ */ React4.createElement("span", { className: "aura-stat__unit" }, props.unit) : null),
736
+ ch && !props.loading || props.caption ? /* @__PURE__ */ React4.createElement("span", { className: "aura-stat__foot" }, ch && !props.loading ? /* @__PURE__ */ React4.createElement("span", { className: cx("aura-stat__change", "is-" + tone2) }, /* @__PURE__ */ React4.createElement(
737
+ Icon,
738
+ {
739
+ name: dir === "up" ? /* @__PURE__ */ React4.createElement(IconTrendingUp, null) : dir === "down" ? /* @__PURE__ */ React4.createElement(IconTrendingDown, null) : /* @__PURE__ */ React4.createElement(IconMinus, null),
740
+ size: 14
741
+ }
742
+ ), /* @__PURE__ */ React4.createElement("span", null, ch.value)) : null, ch && ch.label && !props.loading ? /* @__PURE__ */ React4.createElement("span", { className: "aura-stat__caption" }, ch.label) : null, props.caption ? /* @__PURE__ */ React4.createElement("span", { className: "aura-stat__caption" }, props.caption) : null) : null
743
+ );
744
+ }
745
+ var AVATAR_TONES = ["progress", "ready", "neutral", "warning"];
746
+ function initials(name) {
747
+ const parts = String(name || "?").trim().split(/\s+/);
748
+ return ((parts[0] || "?")[0] + (parts.length > 1 ? parts[parts.length - 1][0] : parts[0][1] || "")).toUpperCase();
749
+ }
750
+ function avatarElement(props, ref, broken, onError) {
751
+ const size = props.size || "md";
752
+ let hash = 0;
753
+ String(props.name || "").split("").forEach(function(ch) {
754
+ hash = hash * 31 + ch.charCodeAt(0) >>> 0;
755
+ });
756
+ const tone2 = AVATAR_TONES[hash % AVATAR_TONES.length];
757
+ return /* @__PURE__ */ React4.createElement(
758
+ "span",
759
+ {
760
+ ref,
761
+ className: cx("aura-avatar", "aura-avatar--" + size, "aura-avatar--" + tone2, props.className),
762
+ role: "img",
763
+ "aria-label": props.name + (props.status ? ", " + props.status : "")
764
+ },
765
+ props.src && broken !== props.src ? /* @__PURE__ */ React4.createElement("img", { key: props.src, src: props.src, alt: "", onError }) : /* @__PURE__ */ React4.createElement("span", { "aria-hidden": true }, initials(props.name)),
766
+ props.status === "online" ? /* @__PURE__ */ React4.createElement("span", { className: "aura-avatar__status", "aria-hidden": true }) : null
767
+ );
768
+ }
709
769
  function buttonClass(opts = {}) {
710
770
  return cx(
711
771
  "aura-btn",
@@ -1194,6 +1254,8 @@ window.Aura = (() => {
1194
1254
  const props = all;
1195
1255
  const variant = props.variant || "primary";
1196
1256
  const loading = !!props.loading;
1257
+ const ad = props["aria-disabled"];
1258
+ const blocked = loading || ad === true || ad === "true";
1197
1259
  const rest = omit(props, [
1198
1260
  "variant",
1199
1261
  "className",
@@ -1220,8 +1282,8 @@ window.Aura = (() => {
1220
1282
  className: props.className
1221
1283
  }),
1222
1284
  "aria-busy": loading || void 0,
1223
- "aria-disabled": loading || void 0,
1224
- onClick: loading ? function(e) {
1285
+ "aria-disabled": blocked || void 0,
1286
+ onClick: blocked ? function(e) {
1225
1287
  e.preventDefault();
1226
1288
  } : props.onClick
1227
1289
  },
@@ -6137,7 +6199,7 @@ window.Aura = (() => {
6137
6199
  key: t.id,
6138
6200
  href: t.href,
6139
6201
  className: tabCls(on, own.className),
6140
- "aria-current": on ? "page" : void 0,
6202
+ "aria-current": on ? props.current || "page" : void 0,
6141
6203
  onClick: function(e) {
6142
6204
  if (own.onClick) own.onClick(e);
6143
6205
  if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
@@ -6484,39 +6546,11 @@ window.Aura = (() => {
6484
6546
 
6485
6547
  // src/Avatar.tsx
6486
6548
  var React36 = __toESM(require_react(), 1);
6487
- var AVATAR_TONES = ["progress", "ready", "neutral", "warning"];
6488
- function initials(name) {
6489
- const parts = String(name || "?").trim().split(/\s+/);
6490
- return ((parts[0] || "?")[0] + (parts.length > 1 ? parts[parts.length - 1][0] : parts[0][1] || "")).toUpperCase();
6491
- }
6492
6549
  var Avatar = React36.forwardRef(function Avatar2(props, ref) {
6493
- const size = props.size || "md", errState = React36.useState(null);
6494
- let hash = 0;
6495
- String(props.name || "").split("").forEach(function(ch) {
6496
- hash = hash * 31 + ch.charCodeAt(0) >>> 0;
6550
+ const errState = React36.useState(null);
6551
+ return avatarElement(props, ref, errState[0], function() {
6552
+ errState[1](props.src || null);
6497
6553
  });
6498
- const tone2 = AVATAR_TONES[hash % AVATAR_TONES.length];
6499
- return /* @__PURE__ */ React36.createElement(
6500
- "span",
6501
- {
6502
- ref,
6503
- className: cx("aura-avatar", "aura-avatar--" + size, "aura-avatar--" + tone2, props.className),
6504
- role: "img",
6505
- "aria-label": props.name + (props.status ? ", " + props.status : "")
6506
- },
6507
- props.src && errState[0] !== props.src ? /* @__PURE__ */ React36.createElement(
6508
- "img",
6509
- {
6510
- key: props.src,
6511
- src: props.src,
6512
- alt: "",
6513
- onError: function() {
6514
- errState[1](props.src || null);
6515
- }
6516
- }
6517
- ) : /* @__PURE__ */ React36.createElement("span", { "aria-hidden": true }, initials(props.name)),
6518
- props.status === "online" ? /* @__PURE__ */ React36.createElement("span", { className: "aura-avatar__status", "aria-hidden": true }) : null
6519
- );
6520
6554
  });
6521
6555
 
6522
6556
  // src/Stack.tsx
@@ -6637,6 +6671,27 @@ window.Aura = (() => {
6637
6671
  if (shared) navState[1](id);
6638
6672
  if (navEl && navEl.props.onChange) navEl.props.onChange(id);
6639
6673
  }
6674
+ const rootRef = React41.useRef(null);
6675
+ const barRef = React41.useRef(null);
6676
+ const mergedRef = useMergedRef(ref, rootRef);
6677
+ const hasBar = !!(props.header || props.nav);
6678
+ useIsoLayoutEffect(
6679
+ function() {
6680
+ const root = rootRef.current, bar = barRef.current;
6681
+ if (!root || !bar) return;
6682
+ function sync() {
6683
+ root.style.setProperty("--aura-shell-bar-height", bar.getBoundingClientRect().height + "px");
6684
+ }
6685
+ sync();
6686
+ const ro = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(sync);
6687
+ if (ro) ro.observe(bar);
6688
+ return function() {
6689
+ if (ro) ro.disconnect();
6690
+ root.style.removeProperty("--aura-shell-bar-height");
6691
+ };
6692
+ },
6693
+ [hasBar]
6694
+ );
6640
6695
  const deskNav = navEl && shared ? React41.cloneElement(navEl, { value: navState[0], onChange: onNav }) : props.nav;
6641
6696
  const drawerNav = navEl ? React41.cloneElement(navEl, {
6642
6697
  value: shared ? navState[0] : navEl.props.value,
@@ -6648,32 +6703,56 @@ window.Aura = (() => {
6648
6703
  collapsed: false,
6649
6704
  collapsible: false
6650
6705
  }) : props.nav;
6651
- return /* @__PURE__ */ React41.createElement("div", { ref, className: cx("aura-shell", props.bottomNav && "aura-shell--bottomnav", props.className) }, props.nav ? /* @__PURE__ */ React41.createElement("div", { className: "aura-shell__nav" }, deskNav) : null, props.nav ? /* @__PURE__ */ React41.createElement(
6652
- Drawer,
6706
+ return /* @__PURE__ */ React41.createElement(
6707
+ "div",
6653
6708
  {
6654
- open,
6655
- onClose: function() {
6656
- setOpen(false);
6657
- },
6658
- side: "left",
6659
- size: "nav",
6660
- "aria-label": props.navLabel || t.navigation,
6661
- dismissible: true
6709
+ ref: mergedRef,
6710
+ className: cx(
6711
+ "aura-shell",
6712
+ props.bottomNav && "aura-shell--bottomnav",
6713
+ /* 5.14: no top bar at all, or one that only holds the menu button (gone from lg up): the bar-height token is 0. */
6714
+ !props.header && !props.nav && "aura-shell--no-bar",
6715
+ !props.header && props.nav && "aura-shell--menu-bar",
6716
+ props.className
6717
+ )
6662
6718
  },
6663
- drawerNav
6664
- ) : null, /* @__PURE__ */ React41.createElement("div", { className: "aura-shell__main" }, props.header || props.nav ? /* @__PURE__ */ React41.createElement("header", { className: cx("aura-shell__bar", !props.header && "aura-shell__bar--menu-only") }, props.nav ? /* @__PURE__ */ React41.createElement(
6665
- IconButton,
6666
- {
6667
- className: "aura-shell__menu",
6668
- icon: /* @__PURE__ */ React41.createElement(IconMenu, null),
6669
- label: props.menuLabel || t.openNav,
6670
- size: "md",
6671
- onClick: function() {
6672
- setOpen(true);
6719
+ props.nav ? /* @__PURE__ */ React41.createElement("div", { className: "aura-shell__nav" }, deskNav) : null,
6720
+ props.nav ? /* @__PURE__ */ React41.createElement(
6721
+ Drawer,
6722
+ {
6723
+ open,
6724
+ onClose: function() {
6725
+ setOpen(false);
6726
+ },
6727
+ side: "left",
6728
+ size: "nav",
6729
+ "aria-label": props.navLabel || t.navigation,
6730
+ dismissible: true
6673
6731
  },
6674
- "aria-expanded": open
6675
- }
6676
- ) : null, /* @__PURE__ */ React41.createElement("div", { className: "aura-shell__bar-content" }, props.header)) : null, /* @__PURE__ */ React41.createElement("main", { className: "aura-shell__content", id: props.mainId || "main", tabIndex: -1 }, props.children), props.bottomNav || null));
6732
+ drawerNav
6733
+ ) : null,
6734
+ /* @__PURE__ */ React41.createElement("div", { className: "aura-shell__main" }, props.header || props.nav ? /* @__PURE__ */ React41.createElement("header", { ref: barRef, className: cx("aura-shell__bar", !props.header && "aura-shell__bar--menu-only") }, props.nav ? /* @__PURE__ */ React41.createElement(
6735
+ IconButton,
6736
+ {
6737
+ className: "aura-shell__menu",
6738
+ icon: /* @__PURE__ */ React41.createElement(IconMenu, null),
6739
+ label: props.menuLabel || t.openNav,
6740
+ size: "md",
6741
+ onClick: function() {
6742
+ setOpen(true);
6743
+ },
6744
+ "aria-expanded": open
6745
+ }
6746
+ ) : null, /* @__PURE__ */ React41.createElement("div", { className: "aura-shell__bar-content" }, props.header)) : null, /* @__PURE__ */ React41.createElement(
6747
+ "main",
6748
+ {
6749
+ className: cx("aura-shell__content", props.contentPadding === false && "is-flush"),
6750
+ id: props.mainId || "main",
6751
+ tabIndex: -1
6752
+ },
6753
+ props.children
6754
+ ), props.bottomNav || null)
6755
+ );
6677
6756
  });
6678
6757
 
6679
6758
  // src/ActionBar.tsx
@@ -6977,34 +7056,8 @@ window.Aura = (() => {
6977
7056
  // src/Stat.tsx
6978
7057
  var React47 = __toESM(require_react(), 1);
6979
7058
  var Stat = React47.forwardRef(function Stat2(props, ref) {
6980
- const ch = props.change;
6981
- const dir = ch && (ch.direction || "flat");
6982
- const tone2 = ch && (ch.tone || (dir === "up" ? "positive" : dir === "down" ? "negative" : "neutral"));
6983
7059
  const Link = useLinkComponent(props.linkComponent);
6984
- const v = props.value;
6985
- const numeric = typeof v === "number" || typeof v === "string" && /\d/.test(v) && /^[\s\d.,:+\-\u2212%()\u0E3F$\u20AC\u00A3\u00A5kKmMbB]+$/.test(v.replace(/\b[A-Z]{3}\b/g, ""));
6986
- const Tag3 = props.href ? Link : props.onClick ? "button" : "div";
6987
- const interactive = !!(props.href || props.onClick);
6988
- return /* @__PURE__ */ React47.createElement(
6989
- Tag3,
6990
- {
6991
- ref,
6992
- className: cx("aura-stat", interactive && "is-interactive", props.loading && "is-loading", props.className),
6993
- href: props.href,
6994
- onClick: props.onClick,
6995
- type: Tag3 === "button" ? "button" : void 0,
6996
- "aria-busy": props.loading || void 0
6997
- },
6998
- /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__head" }, /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__label" }, props.label), props.icon ? /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__icon" }, /* @__PURE__ */ React47.createElement(Icon, { name: props.icon })) : null),
6999
- props.loading ? /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__value" }, /* @__PURE__ */ React47.createElement("span", { className: "aura-skel aura-stat__skel" })) : /* @__PURE__ */ React47.createElement("span", { className: cx("aura-stat__value", numeric && "is-numeric") }, props.value, props.unit ? /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__unit" }, props.unit) : null),
7000
- ch && !props.loading || props.caption ? /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__foot" }, ch && !props.loading ? /* @__PURE__ */ React47.createElement("span", { className: cx("aura-stat__change", "is-" + tone2) }, /* @__PURE__ */ React47.createElement(
7001
- Icon,
7002
- {
7003
- name: dir === "up" ? /* @__PURE__ */ React47.createElement(IconTrendingUp, null) : dir === "down" ? /* @__PURE__ */ React47.createElement(IconTrendingDown, null) : /* @__PURE__ */ React47.createElement(IconMinus, null),
7004
- size: 14
7005
- }
7006
- ), /* @__PURE__ */ React47.createElement("span", null, ch.value)) : null, ch && ch.label && !props.loading ? /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__caption" }, ch.label) : null, props.caption ? /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__caption" }, props.caption) : null) : null
7007
- );
7060
+ return statElement(props, ref, Link);
7008
7061
  });
7009
7062
 
7010
7063
  // src/TimePicker.tsx
@@ -656,7 +656,7 @@ function badgeElement(props, ref) {
656
656
  );
657
657
  }
658
658
  function emptyStateElement(props, ref) {
659
- const HT = "h" + (props.headingLevel || 3);
659
+ const HT = props.headingLevel === false ? "p" : "h" + (props.headingLevel || 3);
660
660
  const rest = omit(props, [
661
661
  "title",
662
662
  "description",
@@ -688,6 +688,66 @@ function emptyStateElement(props, ref) {
688
688
  props.action ? /* @__PURE__ */ React4.createElement("div", { className: "aura-empty__action" }, props.action) : null
689
689
  );
690
690
  }
691
+ function statElement(props, ref, Link) {
692
+ const ch = props.change;
693
+ const dir = ch && (ch.direction || "flat");
694
+ const tone2 = ch && (ch.tone || (dir === "up" ? "positive" : dir === "down" ? "negative" : "neutral"));
695
+ const v = props.value;
696
+ const numeric = typeof v === "number" || typeof v === "string" && /\d/.test(v) && /^[\s\d.,:+\-\u2212%()\u0E3F$\u20AC\u00A3\u00A5kKmMbB]+$/.test(v.replace(/\b[A-Z]{3}\b/g, ""));
697
+ const Tag3 = props.href ? Link : props.onClick ? "button" : "div";
698
+ const interactive = !!(props.href || props.onClick);
699
+ if (props.headingLevel && Tag3 === "button")
700
+ devWarnOnce(
701
+ "stat-heading-button",
702
+ "Stat `headingLevel` is ignored with `onClick`: a heading can\u2019t sit in a button. Use `href`, or put the heading outside."
703
+ );
704
+ const LabelTag = props.headingLevel && Tag3 !== "button" ? "h" + props.headingLevel : "span";
705
+ const HeadTag = LabelTag === "span" ? "span" : "div";
706
+ return /* @__PURE__ */ React4.createElement(
707
+ Tag3,
708
+ {
709
+ ref,
710
+ className: cx("aura-stat", interactive && "is-interactive", props.loading && "is-loading", props.className),
711
+ href: props.href,
712
+ onClick: props.onClick,
713
+ type: Tag3 === "button" ? "button" : void 0,
714
+ "aria-busy": props.loading || void 0
715
+ },
716
+ /* @__PURE__ */ React4.createElement(HeadTag, { className: "aura-stat__head" }, /* @__PURE__ */ React4.createElement(LabelTag, { className: "aura-stat__label" }, props.label), props.icon ? /* @__PURE__ */ React4.createElement("span", { className: "aura-stat__icon" }, /* @__PURE__ */ React4.createElement(Icon, { name: props.icon })) : null),
717
+ props.loading ? /* @__PURE__ */ React4.createElement("span", { className: "aura-stat__value" }, /* @__PURE__ */ React4.createElement("span", { className: "aura-skel aura-stat__skel" })) : /* @__PURE__ */ React4.createElement("span", { className: cx("aura-stat__value", numeric && "is-numeric") }, props.value, props.unit ? /* @__PURE__ */ React4.createElement("span", { className: "aura-stat__unit" }, props.unit) : null),
718
+ ch && !props.loading || props.caption ? /* @__PURE__ */ React4.createElement("span", { className: "aura-stat__foot" }, ch && !props.loading ? /* @__PURE__ */ React4.createElement("span", { className: cx("aura-stat__change", "is-" + tone2) }, /* @__PURE__ */ React4.createElement(
719
+ Icon,
720
+ {
721
+ name: dir === "up" ? /* @__PURE__ */ React4.createElement(IconTrendingUp, null) : dir === "down" ? /* @__PURE__ */ React4.createElement(IconTrendingDown, null) : /* @__PURE__ */ React4.createElement(IconMinus, null),
722
+ size: 14
723
+ }
724
+ ), /* @__PURE__ */ React4.createElement("span", null, ch.value)) : null, ch && ch.label && !props.loading ? /* @__PURE__ */ React4.createElement("span", { className: "aura-stat__caption" }, ch.label) : null, props.caption ? /* @__PURE__ */ React4.createElement("span", { className: "aura-stat__caption" }, props.caption) : null) : null
725
+ );
726
+ }
727
+ var AVATAR_TONES = ["progress", "ready", "neutral", "warning"];
728
+ function initials(name) {
729
+ const parts = String(name || "?").trim().split(/\s+/);
730
+ return ((parts[0] || "?")[0] + (parts.length > 1 ? parts[parts.length - 1][0] : parts[0][1] || "")).toUpperCase();
731
+ }
732
+ function avatarElement(props, ref, broken, onError) {
733
+ const size = props.size || "md";
734
+ let hash = 0;
735
+ String(props.name || "").split("").forEach(function(ch) {
736
+ hash = hash * 31 + ch.charCodeAt(0) >>> 0;
737
+ });
738
+ const tone2 = AVATAR_TONES[hash % AVATAR_TONES.length];
739
+ return /* @__PURE__ */ React4.createElement(
740
+ "span",
741
+ {
742
+ ref,
743
+ className: cx("aura-avatar", "aura-avatar--" + size, "aura-avatar--" + tone2, props.className),
744
+ role: "img",
745
+ "aria-label": props.name + (props.status ? ", " + props.status : "")
746
+ },
747
+ props.src && broken !== props.src ? /* @__PURE__ */ React4.createElement("img", { key: props.src, src: props.src, alt: "", onError }) : /* @__PURE__ */ React4.createElement("span", { "aria-hidden": true }, initials(props.name)),
748
+ props.status === "online" ? /* @__PURE__ */ React4.createElement("span", { className: "aura-avatar__status", "aria-hidden": true }) : null
749
+ );
750
+ }
691
751
  function buttonClass(opts = {}) {
692
752
  return cx(
693
753
  "aura-btn",
@@ -1176,6 +1236,8 @@ var Button = React6.forwardRef(
1176
1236
  const props = all;
1177
1237
  const variant = props.variant || "primary";
1178
1238
  const loading = !!props.loading;
1239
+ const ad = props["aria-disabled"];
1240
+ const blocked = loading || ad === true || ad === "true";
1179
1241
  const rest = omit(props, [
1180
1242
  "variant",
1181
1243
  "className",
@@ -1202,8 +1264,8 @@ var Button = React6.forwardRef(
1202
1264
  className: props.className
1203
1265
  }),
1204
1266
  "aria-busy": loading || void 0,
1205
- "aria-disabled": loading || void 0,
1206
- onClick: loading ? function(e) {
1267
+ "aria-disabled": blocked || void 0,
1268
+ onClick: blocked ? function(e) {
1207
1269
  e.preventDefault();
1208
1270
  } : props.onClick
1209
1271
  },
@@ -6119,7 +6181,7 @@ var Tabs = React33.forwardRef(function Tabs2(props, ref) {
6119
6181
  key: t.id,
6120
6182
  href: t.href,
6121
6183
  className: tabCls(on, own.className),
6122
- "aria-current": on ? "page" : void 0,
6184
+ "aria-current": on ? props.current || "page" : void 0,
6123
6185
  onClick: function(e) {
6124
6186
  if (own.onClick) own.onClick(e);
6125
6187
  if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
@@ -6466,39 +6528,11 @@ var Breadcrumb = React35.forwardRef(function Breadcrumb2(props, ref) {
6466
6528
 
6467
6529
  // src/Avatar.tsx
6468
6530
  var React36 = __toESM(require("react"), 1);
6469
- var AVATAR_TONES = ["progress", "ready", "neutral", "warning"];
6470
- function initials(name) {
6471
- const parts = String(name || "?").trim().split(/\s+/);
6472
- return ((parts[0] || "?")[0] + (parts.length > 1 ? parts[parts.length - 1][0] : parts[0][1] || "")).toUpperCase();
6473
- }
6474
6531
  var Avatar = React36.forwardRef(function Avatar2(props, ref) {
6475
- const size = props.size || "md", errState = React36.useState(null);
6476
- let hash = 0;
6477
- String(props.name || "").split("").forEach(function(ch) {
6478
- hash = hash * 31 + ch.charCodeAt(0) >>> 0;
6532
+ const errState = React36.useState(null);
6533
+ return avatarElement(props, ref, errState[0], function() {
6534
+ errState[1](props.src || null);
6479
6535
  });
6480
- const tone2 = AVATAR_TONES[hash % AVATAR_TONES.length];
6481
- return /* @__PURE__ */ React36.createElement(
6482
- "span",
6483
- {
6484
- ref,
6485
- className: cx("aura-avatar", "aura-avatar--" + size, "aura-avatar--" + tone2, props.className),
6486
- role: "img",
6487
- "aria-label": props.name + (props.status ? ", " + props.status : "")
6488
- },
6489
- props.src && errState[0] !== props.src ? /* @__PURE__ */ React36.createElement(
6490
- "img",
6491
- {
6492
- key: props.src,
6493
- src: props.src,
6494
- alt: "",
6495
- onError: function() {
6496
- errState[1](props.src || null);
6497
- }
6498
- }
6499
- ) : /* @__PURE__ */ React36.createElement("span", { "aria-hidden": true }, initials(props.name)),
6500
- props.status === "online" ? /* @__PURE__ */ React36.createElement("span", { className: "aura-avatar__status", "aria-hidden": true }) : null
6501
- );
6502
6536
  });
6503
6537
 
6504
6538
  // src/Stack.tsx
@@ -6619,6 +6653,27 @@ var AppShell = React41.forwardRef(function AppShell2(props, ref) {
6619
6653
  if (shared) navState[1](id);
6620
6654
  if (navEl && navEl.props.onChange) navEl.props.onChange(id);
6621
6655
  }
6656
+ const rootRef = React41.useRef(null);
6657
+ const barRef = React41.useRef(null);
6658
+ const mergedRef = useMergedRef(ref, rootRef);
6659
+ const hasBar = !!(props.header || props.nav);
6660
+ useIsoLayoutEffect(
6661
+ function() {
6662
+ const root = rootRef.current, bar = barRef.current;
6663
+ if (!root || !bar) return;
6664
+ function sync() {
6665
+ root.style.setProperty("--aura-shell-bar-height", bar.getBoundingClientRect().height + "px");
6666
+ }
6667
+ sync();
6668
+ const ro = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(sync);
6669
+ if (ro) ro.observe(bar);
6670
+ return function() {
6671
+ if (ro) ro.disconnect();
6672
+ root.style.removeProperty("--aura-shell-bar-height");
6673
+ };
6674
+ },
6675
+ [hasBar]
6676
+ );
6622
6677
  const deskNav = navEl && shared ? React41.cloneElement(navEl, { value: navState[0], onChange: onNav }) : props.nav;
6623
6678
  const drawerNav = navEl ? React41.cloneElement(navEl, {
6624
6679
  value: shared ? navState[0] : navEl.props.value,
@@ -6630,32 +6685,56 @@ var AppShell = React41.forwardRef(function AppShell2(props, ref) {
6630
6685
  collapsed: false,
6631
6686
  collapsible: false
6632
6687
  }) : props.nav;
6633
- return /* @__PURE__ */ React41.createElement("div", { ref, className: cx("aura-shell", props.bottomNav && "aura-shell--bottomnav", props.className) }, props.nav ? /* @__PURE__ */ React41.createElement("div", { className: "aura-shell__nav" }, deskNav) : null, props.nav ? /* @__PURE__ */ React41.createElement(
6634
- Drawer,
6688
+ return /* @__PURE__ */ React41.createElement(
6689
+ "div",
6635
6690
  {
6636
- open,
6637
- onClose: function() {
6638
- setOpen(false);
6639
- },
6640
- side: "left",
6641
- size: "nav",
6642
- "aria-label": props.navLabel || t.navigation,
6643
- dismissible: true
6691
+ ref: mergedRef,
6692
+ className: cx(
6693
+ "aura-shell",
6694
+ props.bottomNav && "aura-shell--bottomnav",
6695
+ /* 5.14: no top bar at all, or one that only holds the menu button (gone from lg up): the bar-height token is 0. */
6696
+ !props.header && !props.nav && "aura-shell--no-bar",
6697
+ !props.header && props.nav && "aura-shell--menu-bar",
6698
+ props.className
6699
+ )
6644
6700
  },
6645
- drawerNav
6646
- ) : null, /* @__PURE__ */ React41.createElement("div", { className: "aura-shell__main" }, props.header || props.nav ? /* @__PURE__ */ React41.createElement("header", { className: cx("aura-shell__bar", !props.header && "aura-shell__bar--menu-only") }, props.nav ? /* @__PURE__ */ React41.createElement(
6647
- IconButton,
6648
- {
6649
- className: "aura-shell__menu",
6650
- icon: /* @__PURE__ */ React41.createElement(IconMenu, null),
6651
- label: props.menuLabel || t.openNav,
6652
- size: "md",
6653
- onClick: function() {
6654
- setOpen(true);
6701
+ props.nav ? /* @__PURE__ */ React41.createElement("div", { className: "aura-shell__nav" }, deskNav) : null,
6702
+ props.nav ? /* @__PURE__ */ React41.createElement(
6703
+ Drawer,
6704
+ {
6705
+ open,
6706
+ onClose: function() {
6707
+ setOpen(false);
6708
+ },
6709
+ side: "left",
6710
+ size: "nav",
6711
+ "aria-label": props.navLabel || t.navigation,
6712
+ dismissible: true
6655
6713
  },
6656
- "aria-expanded": open
6657
- }
6658
- ) : null, /* @__PURE__ */ React41.createElement("div", { className: "aura-shell__bar-content" }, props.header)) : null, /* @__PURE__ */ React41.createElement("main", { className: "aura-shell__content", id: props.mainId || "main", tabIndex: -1 }, props.children), props.bottomNav || null));
6714
+ drawerNav
6715
+ ) : null,
6716
+ /* @__PURE__ */ React41.createElement("div", { className: "aura-shell__main" }, props.header || props.nav ? /* @__PURE__ */ React41.createElement("header", { ref: barRef, className: cx("aura-shell__bar", !props.header && "aura-shell__bar--menu-only") }, props.nav ? /* @__PURE__ */ React41.createElement(
6717
+ IconButton,
6718
+ {
6719
+ className: "aura-shell__menu",
6720
+ icon: /* @__PURE__ */ React41.createElement(IconMenu, null),
6721
+ label: props.menuLabel || t.openNav,
6722
+ size: "md",
6723
+ onClick: function() {
6724
+ setOpen(true);
6725
+ },
6726
+ "aria-expanded": open
6727
+ }
6728
+ ) : null, /* @__PURE__ */ React41.createElement("div", { className: "aura-shell__bar-content" }, props.header)) : null, /* @__PURE__ */ React41.createElement(
6729
+ "main",
6730
+ {
6731
+ className: cx("aura-shell__content", props.contentPadding === false && "is-flush"),
6732
+ id: props.mainId || "main",
6733
+ tabIndex: -1
6734
+ },
6735
+ props.children
6736
+ ), props.bottomNav || null)
6737
+ );
6659
6738
  });
6660
6739
 
6661
6740
  // src/ActionBar.tsx
@@ -6959,34 +7038,8 @@ var Surface = React46.forwardRef(function Surface2(props, ref) {
6959
7038
  // src/Stat.tsx
6960
7039
  var React47 = __toESM(require("react"), 1);
6961
7040
  var Stat = React47.forwardRef(function Stat2(props, ref) {
6962
- const ch = props.change;
6963
- const dir = ch && (ch.direction || "flat");
6964
- const tone2 = ch && (ch.tone || (dir === "up" ? "positive" : dir === "down" ? "negative" : "neutral"));
6965
7041
  const Link = useLinkComponent(props.linkComponent);
6966
- const v = props.value;
6967
- const numeric = typeof v === "number" || typeof v === "string" && /\d/.test(v) && /^[\s\d.,:+\-\u2212%()\u0E3F$\u20AC\u00A3\u00A5kKmMbB]+$/.test(v.replace(/\b[A-Z]{3}\b/g, ""));
6968
- const Tag3 = props.href ? Link : props.onClick ? "button" : "div";
6969
- const interactive = !!(props.href || props.onClick);
6970
- return /* @__PURE__ */ React47.createElement(
6971
- Tag3,
6972
- {
6973
- ref,
6974
- className: cx("aura-stat", interactive && "is-interactive", props.loading && "is-loading", props.className),
6975
- href: props.href,
6976
- onClick: props.onClick,
6977
- type: Tag3 === "button" ? "button" : void 0,
6978
- "aria-busy": props.loading || void 0
6979
- },
6980
- /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__head" }, /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__label" }, props.label), props.icon ? /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__icon" }, /* @__PURE__ */ React47.createElement(Icon, { name: props.icon })) : null),
6981
- props.loading ? /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__value" }, /* @__PURE__ */ React47.createElement("span", { className: "aura-skel aura-stat__skel" })) : /* @__PURE__ */ React47.createElement("span", { className: cx("aura-stat__value", numeric && "is-numeric") }, props.value, props.unit ? /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__unit" }, props.unit) : null),
6982
- ch && !props.loading || props.caption ? /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__foot" }, ch && !props.loading ? /* @__PURE__ */ React47.createElement("span", { className: cx("aura-stat__change", "is-" + tone2) }, /* @__PURE__ */ React47.createElement(
6983
- Icon,
6984
- {
6985
- name: dir === "up" ? /* @__PURE__ */ React47.createElement(IconTrendingUp, null) : dir === "down" ? /* @__PURE__ */ React47.createElement(IconTrendingDown, null) : /* @__PURE__ */ React47.createElement(IconMinus, null),
6986
- size: 14
6987
- }
6988
- ), /* @__PURE__ */ React47.createElement("span", null, ch.value)) : null, ch && ch.label && !props.loading ? /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__caption" }, ch.label) : null, props.caption ? /* @__PURE__ */ React47.createElement("span", { className: "aura-stat__caption" }, props.caption) : null) : null
6989
- );
7042
+ return statElement(props, ref, Link);
6990
7043
  });
6991
7044
 
6992
7045
  // src/TimePicker.tsx