@apliteni/apliteni-ui 0.30.0 → 0.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.
@@ -83,6 +83,49 @@ function Card({ title, sub, level = 2, children }) {
83
83
  ] });
84
84
  }
85
85
 
86
+ // src/primitives/StatBand.tsx
87
+ import { useId } from "react";
88
+ import { Fragment, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
89
+ var GLYPH = { up: "arrowUp", down: "arrowDown", flat: "minus" };
90
+ var directionOf = (text) => {
91
+ const t = text.trim();
92
+ return /^[-−–(]/.test(t) ? "down" : /^\+/.test(t) ? "up" : "flat";
93
+ };
94
+ var hasChange = (d) => !!d && d.value != null && d.value !== "";
95
+ function Delta({ delta, basisId }) {
96
+ if (!hasChange(delta)) {
97
+ return /* @__PURE__ */ jsx5("dd", { className: "ui-stat__delta ui-stat__delta--none", children: delta.none || "No earlier figure" });
98
+ }
99
+ const dir = delta.direction && GLYPH[delta.direction] ? delta.direction : directionOf(delta.value);
100
+ return /* @__PURE__ */ jsxs3("dd", { className: "ui-stat__delta", "aria-describedby": !delta.basis && basisId ? basisId : void 0, children: [
101
+ /* @__PURE__ */ jsx5(Icon, { name: GLYPH[dir] }),
102
+ /* @__PURE__ */ jsx5("span", { className: "ui-stat__change", children: delta.value }),
103
+ delta.basis ? /* @__PURE__ */ jsxs3(Fragment, { children: [
104
+ " ",
105
+ /* @__PURE__ */ jsx5("span", { className: "ui-stat__basis", children: delta.basis })
106
+ ] }) : null
107
+ ] });
108
+ }
109
+ function StatBand({ stats, variant = "tiles", basis, label, id }) {
110
+ const auto = useId();
111
+ const v = ["band", "tiles", "open"].includes(variant) ? variant : "tiles";
112
+ const basisId = basis ? `${id || auto}-basis` : void 0;
113
+ const root = ["ui-stats", `ui-stats--${v}`, v === "band" && "ui-card"].filter(Boolean).join(" ");
114
+ return /* @__PURE__ */ jsxs3("div", { className: root, role: label ? "group" : void 0, "aria-label": label || void 0, children: [
115
+ basis ? /* @__PURE__ */ jsx5("p", { className: "ui-stats__basis", id: basisId, children: basis }) : null,
116
+ /* @__PURE__ */ jsx5("dl", { className: "ui-stats__list", children: stats.map((s, i) => {
117
+ const tone = hasChange(s.delta) && (s.delta.tone === "good" || s.delta.tone === "bad") ? s.delta.tone : "";
118
+ const cls = ["ui-stat", tone && `ui-stat--${tone}`, v === "tiles" && "ui-card ui-card--pad-sm"].filter(Boolean).join(" ");
119
+ return /* @__PURE__ */ jsxs3("div", { className: cls, children: [
120
+ /* @__PURE__ */ jsx5("dt", { className: "ui-stat__label", children: s.label }),
121
+ /* @__PURE__ */ jsx5("dd", { className: "ui-stat__value", children: s.value }),
122
+ s.delta ? /* @__PURE__ */ jsx5(Delta, { delta: s.delta, basisId }) : null,
123
+ s.trend ? /* @__PURE__ */ jsx5("dd", { className: "ui-stat__trend", children: s.trend }) : null
124
+ ] }, `${s.label}-${i}`);
125
+ }) })
126
+ ] });
127
+ }
128
+
86
129
  // src/Modal.tsx
87
130
  import { useRef as useRef2 } from "react";
88
131
  import { createPortal } from "react-dom";
@@ -297,7 +340,7 @@ function useDialog(active, { root, panel, body }, onClose) {
297
340
  }
298
341
 
299
342
  // src/Modal.tsx
300
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
343
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
301
344
  function Modal({ open, title, onClose, footer, children }) {
302
345
  const root = useRef2(null);
303
346
  const panel = useRef2(null);
@@ -306,22 +349,22 @@ function Modal({ open, title, onClose, footer, children }) {
306
349
  const scope = useDialog(shown, { root, panel, body }, onClose);
307
350
  if (!mounted) return null;
308
351
  return createPortal(
309
- /* @__PURE__ */ jsx5(DialogScope.Provider, { value: scope, children: /* @__PURE__ */ jsx5("div", { className: shown ? "rx-scrim is-open" : "rx-scrim", ref: root, onMouseDown: dismissOnScrim(onClose), children: /* @__PURE__ */ jsxs3("div", { className: "rx-modal", role: "dialog", "aria-modal": "true", "aria-label": title, tabIndex: -1, ref: panel, children: [
310
- /* @__PURE__ */ jsxs3("div", { className: "rx-modal__head", children: [
311
- /* @__PURE__ */ jsx5("div", { className: "rx-modal__title", children: title }),
312
- /* @__PURE__ */ jsx5(Button, { variant: "ghost", size: "sm", iconOnly: true, icon: "x", "aria-label": "Close", onClick: onClose })
352
+ /* @__PURE__ */ jsx6(DialogScope.Provider, { value: scope, children: /* @__PURE__ */ jsx6("div", { className: shown ? "rx-scrim is-open" : "rx-scrim", ref: root, onMouseDown: dismissOnScrim(onClose), children: /* @__PURE__ */ jsxs4("div", { className: "rx-modal", role: "dialog", "aria-modal": "true", "aria-label": title, tabIndex: -1, ref: panel, children: [
353
+ /* @__PURE__ */ jsxs4("div", { className: "rx-modal__head", children: [
354
+ /* @__PURE__ */ jsx6("div", { className: "rx-modal__title", children: title }),
355
+ /* @__PURE__ */ jsx6(Button, { variant: "ghost", size: "sm", iconOnly: true, icon: "x", "aria-label": "Close", onClick: onClose })
313
356
  ] }),
314
- /* @__PURE__ */ jsx5("div", { className: "rx-modal__body", ref: body, children }),
315
- footer && /* @__PURE__ */ jsx5("div", { className: "rx-modal__foot", children: footer })
357
+ /* @__PURE__ */ jsx6("div", { className: "rx-modal__body", ref: body, children }),
358
+ footer && /* @__PURE__ */ jsx6("div", { className: "rx-modal__foot", children: footer })
316
359
  ] }) }) }),
317
360
  document.body
318
361
  );
319
362
  }
320
363
 
321
364
  // src/Drawer.tsx
322
- import { useId, useRef as useRef3 } from "react";
365
+ import { useId as useId2, useRef as useRef3 } from "react";
323
366
  import { createPortal as createPortal2 } from "react-dom";
324
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
367
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
325
368
  function Drawer({
326
369
  open,
327
370
  title,
@@ -335,15 +378,15 @@ function Drawer({
335
378
  const root = useRef3(null);
336
379
  const panel = useRef3(null);
337
380
  const body = useRef3(null);
338
- const titleId = useId();
381
+ const titleId = useId2();
339
382
  const { mounted, shown } = usePresence(open, root, panel);
340
383
  const scope = useDialog(shown, { root, panel, body }, onClose);
341
384
  if (!mounted) return null;
342
385
  const cls = ["ui-drawer", `ui-drawer--${side}`, `ui-drawer--${size}`, shown && "is-open"].filter(Boolean).join(" ");
343
386
  return createPortal2(
344
- /* @__PURE__ */ jsx6(DialogScope.Provider, { value: scope, children: /* @__PURE__ */ jsxs4("div", { className: cls, ref: root, children: [
345
- /* @__PURE__ */ jsx6("div", { className: "ui-drawer__scrim", onMouseDown: dismissOnScrim(onClose) }),
346
- /* @__PURE__ */ jsxs4(
387
+ /* @__PURE__ */ jsx7(DialogScope.Provider, { value: scope, children: /* @__PURE__ */ jsxs5("div", { className: cls, ref: root, children: [
388
+ /* @__PURE__ */ jsx7("div", { className: "ui-drawer__scrim", onMouseDown: dismissOnScrim(onClose) }),
389
+ /* @__PURE__ */ jsxs5(
347
390
  "aside",
348
391
  {
349
392
  className: "ui-drawer__panel",
@@ -353,12 +396,12 @@ function Drawer({
353
396
  tabIndex: -1,
354
397
  ref: panel,
355
398
  children: [
356
- /* @__PURE__ */ jsxs4("header", { className: "ui-drawer__header", children: [
357
- /* @__PURE__ */ jsx6("h2", { className: "ui-drawer__title", id: titleId, children: title }),
358
- /* @__PURE__ */ jsx6("button", { type: "button", className: "ui-drawer__close", "aria-label": closeLabel, onClick: onClose, children: /* @__PURE__ */ jsx6(Icon, { name: "x" }) })
399
+ /* @__PURE__ */ jsxs5("header", { className: "ui-drawer__header", children: [
400
+ /* @__PURE__ */ jsx7("h2", { className: "ui-drawer__title", id: titleId, children: title }),
401
+ /* @__PURE__ */ jsx7("button", { type: "button", className: "ui-drawer__close", "aria-label": closeLabel, onClick: onClose, children: /* @__PURE__ */ jsx7(Icon, { name: "x" }) })
359
402
  ] }),
360
- /* @__PURE__ */ jsx6("div", { className: "ui-drawer__body", ref: body, children }),
361
- footer && /* @__PURE__ */ jsx6("footer", { className: "ui-drawer__footer", children: footer })
403
+ /* @__PURE__ */ jsx7("div", { className: "ui-drawer__body", ref: body, children }),
404
+ footer && /* @__PURE__ */ jsx7("footer", { className: "ui-drawer__footer", children: footer })
362
405
  ]
363
406
  }
364
407
  )
@@ -368,12 +411,12 @@ function Drawer({
368
411
  }
369
412
 
370
413
  // src/CommandPalette.tsx
371
- import { useCallback, useEffect as useEffect2, useId as useId2, useMemo, useRef as useRef4, useState as useState2 } from "react";
414
+ import { useCallback, useEffect as useEffect2, useId as useId3, useMemo, useRef as useRef4, useState as useState2 } from "react";
372
415
  import { createPortal as createPortal3 } from "react-dom";
373
416
  import { rankGroups, icon as icon2 } from "@apliteni/apliteni-ui";
374
- import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
417
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
375
418
  var cx2 = (...a) => a.filter(Boolean).join(" ");
376
- var Glyph = ({ name, className }) => /* @__PURE__ */ jsx7("span", { className, "aria-hidden": "true", dangerouslySetInnerHTML: { __html: icon2(name) } });
419
+ var Glyph = ({ name, className }) => /* @__PURE__ */ jsx8("span", { className, "aria-hidden": "true", dangerouslySetInnerHTML: { __html: icon2(name) } });
377
420
  var keysOf = (shortcut) => shortcut == null ? [] : Array.isArray(shortcut) ? shortcut : [shortcut];
378
421
  function CommandPalette({
379
422
  open,
@@ -388,7 +431,7 @@ function CommandPalette({
388
431
  rank = true,
389
432
  onQueryChange
390
433
  }) {
391
- const uid = useId2().replace(/:/g, "");
434
+ const uid = useId3().replace(/:/g, "");
392
435
  const root = useRef4(null);
393
436
  const panel = useRef4(null);
394
437
  const search = useRef4(null);
@@ -438,9 +481,9 @@ function CommandPalette({
438
481
  const listId = `${uid}-list`;
439
482
  let n = 0;
440
483
  return createPortal3(
441
- /* @__PURE__ */ jsx7(DialogScope.Provider, { value: scope, children: /* @__PURE__ */ jsxs5("div", { className: cx2("ui-cmdk", density === "roomy" && "ui-cmdk--roomy", "is-open"), ref: root, children: [
442
- /* @__PURE__ */ jsx7("div", { className: "ui-cmdk__scrim", onMouseDown: dismissOnScrim(onClose) }),
443
- /* @__PURE__ */ jsxs5(
484
+ /* @__PURE__ */ jsx8(DialogScope.Provider, { value: scope, children: /* @__PURE__ */ jsxs6("div", { className: cx2("ui-cmdk", density === "roomy" && "ui-cmdk--roomy", "is-open"), ref: root, children: [
485
+ /* @__PURE__ */ jsx8("div", { className: "ui-cmdk__scrim", onMouseDown: dismissOnScrim(onClose) }),
486
+ /* @__PURE__ */ jsxs6(
444
487
  "div",
445
488
  {
446
489
  className: "ui-cmdk__panel",
@@ -451,9 +494,9 @@ function CommandPalette({
451
494
  ref: panel,
452
495
  onKeyDown,
453
496
  children: [
454
- /* @__PURE__ */ jsxs5("div", { className: "ui-cmdk__search", ref: search, children: [
455
- /* @__PURE__ */ jsx7(Glyph, { name: "search", className: "ui-cmdk__search-ic" }),
456
- /* @__PURE__ */ jsx7(
497
+ /* @__PURE__ */ jsxs6("div", { className: "ui-cmdk__search", ref: search, children: [
498
+ /* @__PURE__ */ jsx8(Glyph, { name: "search", className: "ui-cmdk__search-ic" }),
499
+ /* @__PURE__ */ jsx8(
457
500
  "input",
458
501
  {
459
502
  className: "ui-cmdk__input",
@@ -475,19 +518,19 @@ function CommandPalette({
475
518
  }
476
519
  )
477
520
  ] }),
478
- /* @__PURE__ */ jsx7("div", { className: "ui-cmdk__list", id: listId, role: "listbox", "aria-label": `${label} results`, children: shown.map((group, gi) => /* @__PURE__ */ jsxs5(
521
+ /* @__PURE__ */ jsx8("div", { className: "ui-cmdk__list", id: listId, role: "listbox", "aria-label": `${label} results`, children: shown.map((group, gi) => /* @__PURE__ */ jsxs6(
479
522
  "div",
480
523
  {
481
524
  className: "ui-cmdk__group",
482
525
  role: "group",
483
526
  "aria-labelledby": group.label ? `${uid}-g${gi}` : void 0,
484
527
  children: [
485
- group.label && /* @__PURE__ */ jsx7("div", { className: "ui-cmdk__group-head", id: `${uid}-g${gi}`, children: group.label }),
528
+ group.label && /* @__PURE__ */ jsx8("div", { className: "ui-cmdk__group-head", id: `${uid}-g${gi}`, children: group.label }),
486
529
  group.items.map((item) => {
487
530
  const disabled = isDisabled(item);
488
531
  const id = `${uid}-o${n++}`;
489
532
  const on = item === active;
490
- return /* @__PURE__ */ jsxs5(
533
+ return /* @__PURE__ */ jsxs6(
491
534
  "div",
492
535
  {
493
536
  id,
@@ -507,13 +550,13 @@ function CommandPalette({
507
550
  },
508
551
  onClick: (e) => run(item, e.metaKey || e.ctrlKey),
509
552
  children: [
510
- item.icon && /* @__PURE__ */ jsx7(Glyph, { name: item.icon, className: "ui-cmdk__ic" }),
511
- /* @__PURE__ */ jsxs5("span", { className: "ui-cmdk__main", children: [
512
- /* @__PURE__ */ jsx7("span", { className: "ui-cmdk__label", children: item.label }),
513
- item.description && /* @__PURE__ */ jsx7("span", { className: "ui-cmdk__desc", children: item.description })
553
+ item.icon && /* @__PURE__ */ jsx8(Glyph, { name: item.icon, className: "ui-cmdk__ic" }),
554
+ /* @__PURE__ */ jsxs6("span", { className: "ui-cmdk__main", children: [
555
+ /* @__PURE__ */ jsx8("span", { className: "ui-cmdk__label", children: item.label }),
556
+ item.description && /* @__PURE__ */ jsx8("span", { className: "ui-cmdk__desc", children: item.description })
514
557
  ] }),
515
- item.badge && /* @__PURE__ */ jsx7("span", { className: "ui-cmdk__badge", children: item.badge }),
516
- keysOf(item.shortcut).length > 0 && /* @__PURE__ */ jsx7("span", { className: "ui-cmdk__keys", "aria-hidden": "true", children: keysOf(item.shortcut).map((k) => /* @__PURE__ */ jsx7("kbd", { className: "ui-cmdk__key", children: k }, k)) })
558
+ item.badge && /* @__PURE__ */ jsx8("span", { className: "ui-cmdk__badge", children: item.badge }),
559
+ keysOf(item.shortcut).length > 0 && /* @__PURE__ */ jsx8("span", { className: "ui-cmdk__keys", "aria-hidden": "true", children: keysOf(item.shortcut).map((k) => /* @__PURE__ */ jsx8("kbd", { className: "ui-cmdk__key", children: k }, k)) })
517
560
  ]
518
561
  },
519
562
  item.id
@@ -523,20 +566,20 @@ function CommandPalette({
523
566
  },
524
567
  group.label ?? `g${gi}`
525
568
  )) }),
526
- /* @__PURE__ */ jsx7("p", { className: "ui-cmdk__empty", hidden: countOf(shown) > 0, children: empty }),
527
- /* @__PURE__ */ jsx7("p", { className: "ui-sr", role: "status", "aria-live": "polite", children: countOf(shown) === 0 ? "No results" : `${countOf(shown)} result${countOf(shown) === 1 ? "" : "s"}` }),
528
- hint && /* @__PURE__ */ jsxs5("div", { className: "ui-cmdk__foot", "aria-hidden": "true", children: [
529
- /* @__PURE__ */ jsxs5("span", { children: [
530
- /* @__PURE__ */ jsx7("kbd", { className: "ui-cmdk__key", children: "\u2191" }),
531
- /* @__PURE__ */ jsx7("kbd", { className: "ui-cmdk__key", children: "\u2193" }),
569
+ /* @__PURE__ */ jsx8("p", { className: "ui-cmdk__empty", hidden: countOf(shown) > 0, children: empty }),
570
+ /* @__PURE__ */ jsx8("p", { className: "ui-sr", role: "status", "aria-live": "polite", children: countOf(shown) === 0 ? "No results" : `${countOf(shown)} result${countOf(shown) === 1 ? "" : "s"}` }),
571
+ hint && /* @__PURE__ */ jsxs6("div", { className: "ui-cmdk__foot", "aria-hidden": "true", children: [
572
+ /* @__PURE__ */ jsxs6("span", { children: [
573
+ /* @__PURE__ */ jsx8("kbd", { className: "ui-cmdk__key", children: "\u2191" }),
574
+ /* @__PURE__ */ jsx8("kbd", { className: "ui-cmdk__key", children: "\u2193" }),
532
575
  " move"
533
576
  ] }),
534
- /* @__PURE__ */ jsxs5("span", { children: [
535
- /* @__PURE__ */ jsx7("kbd", { className: "ui-cmdk__key", children: "\u21B5" }),
577
+ /* @__PURE__ */ jsxs6("span", { children: [
578
+ /* @__PURE__ */ jsx8("kbd", { className: "ui-cmdk__key", children: "\u21B5" }),
536
579
  " run"
537
580
  ] }),
538
- /* @__PURE__ */ jsxs5("span", { children: [
539
- /* @__PURE__ */ jsx7("kbd", { className: "ui-cmdk__key", children: "esc" }),
581
+ /* @__PURE__ */ jsxs6("span", { children: [
582
+ /* @__PURE__ */ jsx8("kbd", { className: "ui-cmdk__key", children: "esc" }),
540
583
  " close"
541
584
  ] })
542
585
  ] })
@@ -565,9 +608,9 @@ import { useMemo as useMemo2, useState as useState4 } from "react";
565
608
  import { DEFAULT_PAGE_SIZE as DEFAULT_PAGE_SIZE2 } from "@apliteni/apliteni-ui";
566
609
 
567
610
  // src/Pagination.tsx
568
- import { useEffect as useEffect3, useId as useId3, useRef as useRef5, useState as useState3 } from "react";
611
+ import { useEffect as useEffect3, useId as useId4, useRef as useRef5, useState as useState3 } from "react";
569
612
  import { DEFAULT_PAGE_SIZE } from "@apliteni/apliteni-ui";
570
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
613
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
571
614
  var GHOST_SM = "ui-btn ui-btn--ghost ui-btn--sm";
572
615
  var VARIANTS = ["steps", "numbered", "jump"];
573
616
  var cx3 = (...a) => a.filter(Boolean).join(" ");
@@ -609,7 +652,7 @@ function Pagination({
609
652
  onPageChange,
610
653
  onPageSizeChange
611
654
  }) {
612
- const auto = useId3();
655
+ const auto = useId4();
613
656
  const [draft, setDraft] = useState3("");
614
657
  const [drafting, setDrafting] = useState3(false);
615
658
  const nav = useRef5(null);
@@ -659,7 +702,7 @@ function Pagination({
659
702
  const step = (label_, target, disabled) => (
660
703
  // A control at an end is disabled and stays where it is: removing it slides
661
704
  // the next control under a pointer already travelling toward it.
662
- /* @__PURE__ */ jsx8(
705
+ /* @__PURE__ */ jsx9(
663
706
  "button",
664
707
  {
665
708
  type: "button",
@@ -698,14 +741,14 @@ function Pagination({
698
741
  if (single) {
699
742
  steps = null;
700
743
  } else if (!counted) {
701
- steps = /* @__PURE__ */ jsxs6("div", { className: "ui-pager__steps", children: [
744
+ steps = /* @__PURE__ */ jsxs7("div", { className: "ui-pager__steps", children: [
702
745
  step("Prev", Math.max(1, at - 1), atStart),
703
746
  step("Next", at + 1, !hasMore)
704
747
  ] });
705
748
  } else {
706
749
  let middle = null;
707
750
  if (kind === "numbered") {
708
- middle = slotsFor(at, last).map((n, i) => n == null ? /* @__PURE__ */ jsx8("span", { className: "ui-pager__gap", "aria-hidden": "true", children: "\u2026" }, `gap${i}`) : /* @__PURE__ */ jsx8(
751
+ middle = slotsFor(at, last).map((n, i) => n == null ? /* @__PURE__ */ jsx9("span", { className: "ui-pager__gap", "aria-hidden": "true", children: "\u2026" }, `gap${i}`) : /* @__PURE__ */ jsx9(
709
752
  "button",
710
753
  {
711
754
  type: "button",
@@ -720,9 +763,9 @@ function Pagination({
720
763
  n
721
764
  ));
722
765
  } else if (kind === "jump") {
723
- middle = /* @__PURE__ */ jsxs6("span", { className: "ui-pager__jump", children: [
724
- /* @__PURE__ */ jsx8("label", { htmlFor: `${uid}-jump`, children: "Page" }),
725
- /* @__PURE__ */ jsx8(
766
+ middle = /* @__PURE__ */ jsxs7("span", { className: "ui-pager__jump", children: [
767
+ /* @__PURE__ */ jsx9("label", { htmlFor: `${uid}-jump`, children: "Page" }),
768
+ /* @__PURE__ */ jsx9(
726
769
  "input",
727
770
  {
728
771
  className: "ui-input ui-pager__jump-input",
@@ -745,13 +788,13 @@ function Pagination({
745
788
  }
746
789
  }
747
790
  ),
748
- /* @__PURE__ */ jsxs6("span", { className: "ui-pager__jump-of", children: [
791
+ /* @__PURE__ */ jsxs7("span", { className: "ui-pager__jump-of", children: [
749
792
  "of ",
750
793
  fmt(last)
751
794
  ] })
752
795
  ] });
753
796
  }
754
- steps = /* @__PURE__ */ jsxs6("div", { className: "ui-pager__steps", children: [
797
+ steps = /* @__PURE__ */ jsxs7("div", { className: "ui-pager__steps", children: [
755
798
  ends ? step("First", 1, atStart) : null,
756
799
  step("Prev", Math.max(1, at - 1), atStart),
757
800
  middle,
@@ -759,7 +802,7 @@ function Pagination({
759
802
  ends ? step("Last", last, atEnd) : null
760
803
  ] });
761
804
  }
762
- return /* @__PURE__ */ jsxs6(
805
+ return /* @__PURE__ */ jsxs7(
763
806
  "nav",
764
807
  {
765
808
  ref: nav,
@@ -767,10 +810,10 @@ function Pagination({
767
810
  "aria-label": label,
768
811
  "aria-busy": loading ? true : void 0,
769
812
  children: [
770
- /* @__PURE__ */ jsx8("p", { className: "ui-pager__status", "aria-live": "polite", "aria-atomic": "true", children: status }),
771
- sizes.length ? /* @__PURE__ */ jsxs6("div", { className: "ui-pager__size", children: [
772
- /* @__PURE__ */ jsx8("label", { className: "ui-pager__size-label", htmlFor: `${uid}-size`, children: "Rows" }),
773
- /* @__PURE__ */ jsx8(
813
+ /* @__PURE__ */ jsx9("p", { className: "ui-pager__status", "aria-live": "polite", "aria-atomic": "true", children: status }),
814
+ sizes.length ? /* @__PURE__ */ jsxs7("div", { className: "ui-pager__size", children: [
815
+ /* @__PURE__ */ jsx9("label", { className: "ui-pager__size-label", htmlFor: `${uid}-size`, children: "Rows" }),
816
+ /* @__PURE__ */ jsx9(
774
817
  "select",
775
818
  {
776
819
  className: "ui-select ui-pager__size-select",
@@ -778,7 +821,7 @@ function Pagination({
778
821
  disabled: loading,
779
822
  value: size,
780
823
  onChange: (e) => onPageSizeChange?.(Number(e.target.value)),
781
- children: sizes.map((s) => /* @__PURE__ */ jsx8("option", { value: s, children: fmt(s) }, s))
824
+ children: sizes.map((s) => /* @__PURE__ */ jsx9("option", { value: s, children: fmt(s) }, s))
782
825
  }
783
826
  )
784
827
  ] }) : null,
@@ -789,7 +832,7 @@ function Pagination({
789
832
  }
790
833
 
791
834
  // src/DataTable.tsx
792
- import { Fragment, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
835
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
793
836
  var NO_SORT = { key: void 0, dir: -1 };
794
837
  function sortTableRows(rows, sort) {
795
838
  if (sort.key === void 0) return [...rows];
@@ -852,15 +895,15 @@ function DataTable({
852
895
  };
853
896
  const caret = (k) => sort.key === k ? sort.dir === 1 ? " \u25B2" : " \u25BC" : " \u2195";
854
897
  const pageAllOn = selectable && slice.length > 0 && slice.every((r) => selected.has(r.name));
855
- return /* @__PURE__ */ jsxs7(Fragment, { children: [
856
- /* @__PURE__ */ jsxs7(
898
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
899
+ /* @__PURE__ */ jsxs8(
857
900
  "table",
858
901
  {
859
902
  className: "ui-table ui-table--hover ui-table--zebra",
860
903
  "aria-busy": loading || void 0,
861
904
  children: [
862
- /* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsxs7("tr", { children: [
863
- selectable ? /* @__PURE__ */ jsx9("th", { scope: "col", children: /* @__PURE__ */ jsx9(
905
+ /* @__PURE__ */ jsx10("thead", { children: /* @__PURE__ */ jsxs8("tr", { children: [
906
+ selectable ? /* @__PURE__ */ jsx10("th", { scope: "col", children: /* @__PURE__ */ jsx10(
864
907
  "input",
865
908
  {
866
909
  type: "checkbox",
@@ -873,23 +916,23 @@ function DataTable({
873
916
  // The sort control is a real <button> inside the header cell. It used to be
874
917
  // role="button" ON the <th>, which threw away the columnheader role and put
875
918
  // aria-sort on a role that forbids it.
876
- /* @__PURE__ */ jsx9(
919
+ /* @__PURE__ */ jsx10(
877
920
  "th",
878
921
  {
879
922
  scope: "col",
880
923
  className: [c.num && "ui-table__num", c.sortable && "rx-sortable"].filter(Boolean).join(" "),
881
924
  "aria-sort": sort.key === c.key ? sort.dir === 1 ? "ascending" : "descending" : c.sortable && sortIsKnown ? "none" : void 0,
882
- children: c.sortable ? /* @__PURE__ */ jsxs7("button", { type: "button", className: "rx-sort", onClick: () => onSort(c.key), children: [
925
+ children: c.sortable ? /* @__PURE__ */ jsxs8("button", { type: "button", className: "rx-sort", onClick: () => onSort(c.key), children: [
883
926
  c.label,
884
- /* @__PURE__ */ jsx9("span", { className: "rx-caret", "aria-hidden": "true", children: caret(c.key) })
927
+ /* @__PURE__ */ jsx10("span", { className: "rx-caret", "aria-hidden": "true", children: caret(c.key) })
885
928
  ] }) : c.label
886
929
  },
887
930
  c.key
888
931
  )
889
932
  ))
890
933
  ] }) }),
891
- /* @__PURE__ */ jsx9("tbody", { children: slice.map((r) => /* @__PURE__ */ jsxs7("tr", { children: [
892
- selectable ? /* @__PURE__ */ jsx9("td", { children: /* @__PURE__ */ jsx9(
934
+ /* @__PURE__ */ jsx10("tbody", { children: slice.map((r) => /* @__PURE__ */ jsxs8("tr", { children: [
935
+ selectable ? /* @__PURE__ */ jsx10("td", { children: /* @__PURE__ */ jsx10(
893
936
  "input",
894
937
  {
895
938
  type: "checkbox",
@@ -898,12 +941,12 @@ function DataTable({
898
941
  onChange: () => onToggle(r.name)
899
942
  }
900
943
  ) }) : null,
901
- columns.map((c) => /* @__PURE__ */ jsx9("td", { className: c.num ? "ui-table__num" : void 0, children: c.render ? c.render(r) : String(r[c.key]) }, c.key))
944
+ columns.map((c) => /* @__PURE__ */ jsx10("td", { className: c.num ? "ui-table__num" : void 0, children: c.render ? c.render(r) : String(r[c.key]) }, c.key))
902
945
  ] }, r.name)) })
903
946
  ]
904
947
  }
905
948
  ),
906
- pager ? /* @__PURE__ */ jsx9(
949
+ pager ? /* @__PURE__ */ jsx10(
907
950
  Pagination,
908
951
  {
909
952
  page,
@@ -928,7 +971,7 @@ function DataTable({
928
971
  import { PAGE_SIZES as KIT_PAGE_SIZES, DEFAULT_PAGE_SIZE as KIT_DEFAULT_PAGE_SIZE } from "@apliteni/apliteni-ui";
929
972
 
930
973
  // src/Loading.tsx
931
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
974
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
932
975
  var cx4 = (...a) => a.filter(Boolean).join(" ");
933
976
  function Skeleton({ lines = 3, width, height, radius, className }) {
934
977
  const widths = Array.isArray(lines) ? lines : null;
@@ -938,19 +981,19 @@ function Skeleton({ lines = 3, width, height, radius, className }) {
938
981
  if (!w && !height && !radius) return void 0;
939
982
  return { ...w && { width: w }, ...height && { height }, ...radius && { borderRadius: radius } };
940
983
  };
941
- return /* @__PURE__ */ jsx10("div", { className: cx4("ui-skel", className), "aria-hidden": "true", children: Array.from({ length: n }, (_, i) => /* @__PURE__ */ jsx10("span", { className: "ui-skel__bar m-skeleton", style: styleFor(i) }, i)) });
984
+ return /* @__PURE__ */ jsx11("div", { className: cx4("ui-skel", className), "aria-hidden": "true", children: Array.from({ length: n }, (_, i) => /* @__PURE__ */ jsx11("span", { className: "ui-skel__bar m-skeleton", style: styleFor(i) }, i)) });
942
985
  }
943
986
  function SkeletonTable({ rows = 5, cols = 4, head = true }) {
944
- const cells = Array.from({ length: Math.max(1, cols) }, (_, i) => /* @__PURE__ */ jsx10("span", { className: "ui-skel__bar m-skeleton" }, i));
945
- return /* @__PURE__ */ jsxs8(
987
+ const cells = Array.from({ length: Math.max(1, cols) }, (_, i) => /* @__PURE__ */ jsx11("span", { className: "ui-skel__bar m-skeleton" }, i));
988
+ return /* @__PURE__ */ jsxs9(
946
989
  "div",
947
990
  {
948
991
  className: "ui-skel ui-skel--table",
949
992
  style: { "--skel-cols": Math.max(1, cols) },
950
993
  "aria-hidden": "true",
951
994
  children: [
952
- head && /* @__PURE__ */ jsx10("div", { className: "ui-skel__row ui-skel__row--head", children: cells }),
953
- Array.from({ length: Math.max(1, rows) }, (_, i) => /* @__PURE__ */ jsx10("div", { className: "ui-skel__row", children: cells }, i))
995
+ head && /* @__PURE__ */ jsx11("div", { className: "ui-skel__row ui-skel__row--head", children: cells }),
996
+ Array.from({ length: Math.max(1, rows) }, (_, i) => /* @__PURE__ */ jsx11("div", { className: "ui-skel__row", children: cells }, i))
954
997
  ]
955
998
  }
956
999
  );
@@ -963,7 +1006,7 @@ function BusyRegion({
963
1006
  className,
964
1007
  children
965
1008
  }) {
966
- return /* @__PURE__ */ jsxs8(
1009
+ return /* @__PURE__ */ jsxs9(
967
1010
  "div",
968
1011
  {
969
1012
  className: cx4("ui-busy", className),
@@ -971,8 +1014,8 @@ function BusyRegion({
971
1014
  "aria-live": "polite",
972
1015
  "aria-busy": busy,
973
1016
  children: [
974
- /* @__PURE__ */ jsx10("span", { className: "ui-sr", children: busy ? label : message }),
975
- /* @__PURE__ */ jsx10("div", { className: "ui-busy__body", children: busy ? placeholder ?? /* @__PURE__ */ jsx10(Skeleton, { lines: 3 }) : children })
1017
+ /* @__PURE__ */ jsx11("span", { className: "ui-sr", children: busy ? label : message }),
1018
+ /* @__PURE__ */ jsx11("div", { className: "ui-busy__body", children: busy ? placeholder ?? /* @__PURE__ */ jsx11(Skeleton, { lines: 3 }) : children })
976
1019
  ]
977
1020
  }
978
1021
  );
@@ -985,15 +1028,15 @@ function Denied({
985
1028
  className,
986
1029
  children
987
1030
  }) {
988
- return /* @__PURE__ */ jsxs8("div", { className: cx4("ui-denied", className), children: [
989
- /* @__PURE__ */ jsx10("div", { className: "ui-denied__seal", children: /* @__PURE__ */ jsx10(Icon, { name: icon3 }) }),
990
- /* @__PURE__ */ jsx10("div", { className: "ui-denied__title", children: title }),
991
- sub && /* @__PURE__ */ jsx10("div", { className: "ui-denied__sub", children: sub }),
992
- need && /* @__PURE__ */ jsxs8("div", { className: "ui-denied__need", children: [
1031
+ return /* @__PURE__ */ jsxs9("div", { className: cx4("ui-denied", className), children: [
1032
+ /* @__PURE__ */ jsx11("div", { className: "ui-denied__seal", children: /* @__PURE__ */ jsx11(Icon, { name: icon3 }) }),
1033
+ /* @__PURE__ */ jsx11("div", { className: "ui-denied__title", children: title }),
1034
+ sub && /* @__PURE__ */ jsx11("div", { className: "ui-denied__sub", children: sub }),
1035
+ need && /* @__PURE__ */ jsxs9("div", { className: "ui-denied__need", children: [
993
1036
  "Needs ",
994
- /* @__PURE__ */ jsx10("code", { className: "ui-code", children: need })
1037
+ /* @__PURE__ */ jsx11("code", { className: "ui-code", children: need })
995
1038
  ] }),
996
- children && /* @__PURE__ */ jsx10("div", { className: "ui-denied__actions", children })
1039
+ children && /* @__PURE__ */ jsx11("div", { className: "ui-denied__actions", children })
997
1040
  ] });
998
1041
  }
999
1042
 
@@ -1016,5 +1059,6 @@ export {
1016
1059
  Pagination,
1017
1060
  Skeleton,
1018
1061
  SkeletonTable,
1062
+ StatBand,
1019
1063
  sortTableRows
1020
1064
  };
@@ -2,9 +2,9 @@
2
2
  // viz/ server-render idiom so the portal can adopt them with no framework.
3
3
  import { icon } from '../assets/icons.js';
4
4
  import { illo } from '../assets/illustrations.js';
5
-
5
+ const HTML_ENTITIES = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' };
6
6
  const cx = (...a) => a.filter(Boolean).join(' ');
7
- export const esc = (s) => String(s == null ? '' : s).replace(/[&<>"]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]));
7
+ export const esc = (s) => String(s == null ? '' : s).replace(/[&<>"]/g, (c) => HTML_ENTITIES[c]);
8
8
 
9
9
  // ---- Button --------------------------------------------------------------
10
10
  // `iconSvg` is a raw leading-icon SVG string (trusted markup, not escaped) for
@@ -236,29 +236,12 @@ export function pagination({
236
236
  }
237
237
 
238
238
  /**
239
- * Make a rendered pager work.
239
+ * Make a rendered pager work: delegate the steps, the size control and the jump
240
+ * input from `root`, so a pager re-rendered underneath stays wired.
240
241
  *
241
- * Every control the factory draws is inert markup until this runs: the steps
242
- * carry `data-page` and nothing reads it, the size control is a `<select>` with
243
- * no handler, and the jump input is an `<input>` with no handler. Shipping the
244
- * `jump` variant without this meant shipping the one control that reaches an
245
- * arbitrary page and having it do nothing.
242
+ * @returns {() => void} a function that removes the listeners.
246
243
  *
247
- * It reads the class contract rather than hooks of its own — `.ui-pager__step`,
248
- * `.ui-pager__page`, `.ui-pager__size-select`, `.ui-pager__jump-input` — so the
249
- * markup is exactly what `pagination()` already returns and the React component
250
- * is still class-for-class identical to it.
251
- *
252
- * const pager = wirePagination(root, {
253
- * onPage: (page) => load({ page }),
254
- * onPageSize: (size) => load({ page: 1, size }),
255
- * });
256
- *
257
- * Listeners are delegated from `root`, so a pager re-rendered underneath stays
258
- * wired. Returns a function that removes them.
259
- *
260
- * A step rendered as an `<a href>` is left alone: it is a link, the browser owns
261
- * it, and calling it back as well would navigate twice.
244
+ * why: CONTRIBUTING.md#pagination-event-wiring
262
245
  */
263
246
  export function wirePagination(root = document, { onPage, onPageSize } = {}) {
264
247
  const scope = typeof root === 'string' ? document.querySelector(root) : root;
@@ -321,18 +304,13 @@ export function wirePagination(root = document, { onPage, onPageSize } = {}) {
321
304
  }
322
305
 
323
306
  /**
324
- * Rewrite a pager's row range, in place. THIS is the announcement.
307
+ * Rewrite a pager's row range in place, so the live region it already holds is
308
+ * the thing that announces — as `setBusy()` does.
325
309
  *
326
- * The factory returns a whole `<nav>`, so the obvious way to show a new page is
327
- * to replace it which inserts a brand-new live region that already contains its
328
- * text, and several screen readers say nothing at all about a region that arrived
329
- * with its content. The kit has met this before and answered it the same way:
330
- * `setBusy()` rewrites the line its region already holds rather than inserting a
331
- * new one. why: docs/specification.md#pending-and-denied-states
310
+ * @returns {Element|null} the status element, or null when there is nothing to
311
+ * updatesafe against a torn-down view.
332
312
  *
333
- * So a consumer re-rendering a pager should hand the new range here instead of
334
- * relying on the replacement to speak. Returns the status element, or null when
335
- * there is nothing to update — safe against a torn-down view.
313
+ * why: CONTRIBUTING.md#pagination-status-updates
336
314
  */
337
315
  export function setPagerStatus(root, text) {
338
316
  const el = typeof root === 'string' ? document.querySelector(root) : root;