react-os-shell 4.41.0 → 4.55.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.
Files changed (57) hide show
  1. package/README.md +2 -1
  2. package/dist/{Browser-TKBH4DZ3.js → Browser-WHBSYGLC.js} +6 -6
  3. package/dist/{Browser-TKBH4DZ3.js.map → Browser-WHBSYGLC.js.map} +1 -1
  4. package/dist/{Calculator-XERXEV35.js → Calculator-PHKNMRLX.js} +5 -5
  5. package/dist/{Calculator-XERXEV35.js.map → Calculator-PHKNMRLX.js.map} +1 -1
  6. package/dist/{CurrencyConverter-OSZISLHT.js → CurrencyConverter-MEG7RHEK.js} +5 -5
  7. package/dist/{CurrencyConverter-OSZISLHT.js.map → CurrencyConverter-MEG7RHEK.js.map} +1 -1
  8. package/dist/{Documents-YRTLDTLX.js → Documents-Y32RNCTU.js} +7 -7
  9. package/dist/{Documents-YRTLDTLX.js.map → Documents-Y32RNCTU.js.map} +1 -1
  10. package/dist/{Files-WHYCDRYU.js → Files-LTG2S2KU.js} +8 -8
  11. package/dist/{Files-WHYCDRYU.js.map → Files-LTG2S2KU.js.map} +1 -1
  12. package/dist/{Notepad-6UAWRNZH.js → Notepad-P7J2OAXD.js} +7 -7
  13. package/dist/{Notepad-6UAWRNZH.js.map → Notepad-P7J2OAXD.js.map} +1 -1
  14. package/dist/{PomodoroTimer-5ZSIG7SQ.js → PomodoroTimer-5B5AFXQH.js} +7 -7
  15. package/dist/{PomodoroTimer-5ZSIG7SQ.js.map → PomodoroTimer-5B5AFXQH.js.map} +1 -1
  16. package/dist/{Preview-VBIWWJLK.js → Preview-XZQ55YGY.js} +8 -8
  17. package/dist/{Preview-VBIWWJLK.js.map → Preview-XZQ55YGY.js.map} +1 -1
  18. package/dist/{Spreadsheet-GRD4P3OI.js → Spreadsheet-KKSLCSIK.js} +6 -6
  19. package/dist/{Spreadsheet-GRD4P3OI.js.map → Spreadsheet-KKSLCSIK.js.map} +1 -1
  20. package/dist/{Stock-4BORXILI.js → Stock-KTWRNBI6.js} +5 -5
  21. package/dist/{Stock-4BORXILI.js.map → Stock-KTWRNBI6.js.map} +1 -1
  22. package/dist/{Weather-ZPPSEUIY.js → Weather-B7MO7N4N.js} +5 -5
  23. package/dist/{Weather-ZPPSEUIY.js.map → Weather-B7MO7N4N.js.map} +1 -1
  24. package/dist/{WorldClock-LBKYWDWE.js → WorldClock-M5MEV74F.js} +5 -5
  25. package/dist/{WorldClock-LBKYWDWE.js.map → WorldClock-M5MEV74F.js.map} +1 -1
  26. package/dist/apps/index.js +16 -16
  27. package/dist/{chunk-22BNPLM4.js → chunk-3OCL3DB4.js} +3 -3
  28. package/dist/{chunk-22BNPLM4.js.map → chunk-3OCL3DB4.js.map} +1 -1
  29. package/dist/{chunk-GUWAKATU.js → chunk-5S65ZC5W.js} +3 -3
  30. package/dist/{chunk-GUWAKATU.js.map → chunk-5S65ZC5W.js.map} +1 -1
  31. package/dist/{chunk-D4WFGAUW.js → chunk-CTTHBTOX.js} +326 -61
  32. package/dist/chunk-CTTHBTOX.js.map +1 -0
  33. package/dist/{chunk-X5PZYTAK.js → chunk-ECE3B3FZ.js} +3 -3
  34. package/dist/{chunk-X5PZYTAK.js.map → chunk-ECE3B3FZ.js.map} +1 -1
  35. package/dist/{chunk-X6DOWIY2.js → chunk-FHPPPDHL.js} +3 -3
  36. package/dist/{chunk-X6DOWIY2.js.map → chunk-FHPPPDHL.js.map} +1 -1
  37. package/dist/{chunk-OI253GOA.js → chunk-IOCOH2QQ.js} +4 -4
  38. package/dist/{chunk-OI253GOA.js.map → chunk-IOCOH2QQ.js.map} +1 -1
  39. package/dist/chunk-LCLB7YBY.js +7 -0
  40. package/dist/{chunk-TQFUNASX.js.map → chunk-LCLB7YBY.js.map} +1 -1
  41. package/dist/{chunk-COTCIYLS.js → chunk-S3ZIBWRY.js} +4 -4
  42. package/dist/{chunk-COTCIYLS.js.map → chunk-S3ZIBWRY.js.map} +1 -1
  43. package/dist/{chunk-4AKVHY4B.js → chunk-TK6HI2U4.js} +23 -3
  44. package/dist/chunk-TK6HI2U4.js.map +1 -0
  45. package/dist/{chunk-C22EFSND.js → chunk-ZCDFNZQS.js} +21 -6
  46. package/dist/chunk-ZCDFNZQS.js.map +1 -0
  47. package/dist/{index-B4RRJ5bp.d.ts → index-BE0z2D6y.d.ts} +212 -8
  48. package/dist/index.d.ts +2 -2
  49. package/dist/index.js +14 -14
  50. package/dist/ui/index.d.ts +1 -1
  51. package/dist/ui/index.js +4 -4
  52. package/dist/ui.css +117 -43
  53. package/package.json +1 -1
  54. package/dist/chunk-4AKVHY4B.js.map +0 -1
  55. package/dist/chunk-C22EFSND.js.map +0 -1
  56. package/dist/chunk-D4WFGAUW.js.map +0 -1
  57. package/dist/chunk-TQFUNASX.js +0 -7
@@ -1,9 +1,9 @@
1
1
  import { Breadcrumbs } from './chunk-HNH7FOYB.js';
2
2
  import { SidebarLayout } from './chunk-YG45DAUT.js';
3
- import { useIsMobile, useShellPrefs } from './chunk-C22EFSND.js';
3
+ import { useIsMobile, PopupMenu, PopupMenuItem, useShellPrefs } from './chunk-ZCDFNZQS.js';
4
4
  import { registerModalEscapeInterceptor, useFocusTrap, useScrollLock } from './chunk-OXLLRKBC.js';
5
5
  import { glassStyle } from './chunk-3CUSBZWG.js';
6
- import { forwardRef, useId, useCallback, useState, useRef, useEffect, createContext, useLayoutEffect, useMemo, Children, isValidElement, cloneElement, useContext, Fragment as Fragment$1, useSyncExternalStore } from 'react';
6
+ import { forwardRef, useId, useCallback, useState, useRef, useEffect, createContext, useLayoutEffect, useMemo, Children, isValidElement, cloneElement, useContext, Fragment as Fragment$1, Component, useSyncExternalStore } from 'react';
7
7
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
8
  import { createPortal } from 'react-dom';
9
9
 
@@ -619,10 +619,23 @@ function FormField({
619
619
  return /* @__PURE__ */ jsxs("div", { className, children: [
620
620
  label && /* @__PURE__ */ jsxs("label", { id: labelId, htmlFor, className: "mb-1 block text-xs font-medium text-gray-600", children: [
621
621
  label,
622
- required && /* @__PURE__ */ jsx("span", { className: "ml-0.5 text-red-500", children: "*" })
622
+ required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-0.5 text-red-500", children: "*" })
623
623
  ] }),
624
624
  children,
625
- error ? /* @__PURE__ */ jsx("p", { id: htmlFor ? `${htmlFor}-error` : void 0, className: "mt-1 text-[11px] text-red-600", children: error }) : hint ? /* @__PURE__ */ jsx("p", { id: htmlFor ? `${htmlFor}-hint` : void 0, className: "mt-1 text-[11px] text-gray-400", children: hint }) : null
625
+ error ? (
626
+ // A validation message appears after the user submits, without focus
627
+ // moving to it. Without a live region it reaches nobody — and this is
628
+ // the one status message a form cannot afford to lose.
629
+ /* @__PURE__ */ jsx(
630
+ "p",
631
+ {
632
+ id: htmlFor ? `${htmlFor}-error` : void 0,
633
+ role: "alert",
634
+ className: "mt-1 text-[11px] text-red-600",
635
+ children: error
636
+ }
637
+ )
638
+ ) : hint ? /* @__PURE__ */ jsx("p", { id: htmlFor ? `${htmlFor}-hint` : void 0, className: "mt-1 text-[11px] text-gray-400", children: hint }) : null
626
639
  ] });
627
640
  }
628
641
  function Label({ required, children, className = "", ...rest }) {
@@ -2192,13 +2205,52 @@ var PADDING = {
2192
2205
  md: { body: "p-4", edge: "px-4 py-3" },
2193
2206
  lg: { body: "p-6", edge: "px-6 py-4" }
2194
2207
  };
2195
- function Card({ children, header, footer, padded = true, padding, className = "" }) {
2208
+ function Card({
2209
+ children,
2210
+ header,
2211
+ footer,
2212
+ padded = true,
2213
+ padding,
2214
+ headingLevel,
2215
+ headerActions,
2216
+ "aria-label": ariaLabel,
2217
+ className = "",
2218
+ bodyClassName = "",
2219
+ style
2220
+ }) {
2196
2221
  const p = PADDING[padding ?? (padded ? "md" : "none")];
2197
- return /* @__PURE__ */ jsxs("div", { className: `rounded-lg border border-gray-200 bg-white shadow-sm ${className}`.trim(), children: [
2198
- header && /* @__PURE__ */ jsx("div", { className: `border-b border-gray-100 ${p.edge} text-sm font-semibold text-gray-900`, children: header }),
2199
- /* @__PURE__ */ jsx("div", { className: p.body, children }),
2200
- footer && /* @__PURE__ */ jsx("div", { className: `border-t border-gray-100 ${p.edge}`, children: footer })
2201
- ] });
2222
+ const headingId = useId();
2223
+ const titled = headingLevel != null && header != null;
2224
+ const Heading = `h${headingLevel}`;
2225
+ const named = titled || ariaLabel != null && ariaLabel !== "";
2226
+ const Root = named ? "section" : "div";
2227
+ const surface = `rounded-lg border border-gray-200 bg-white shadow-sm ${className}`.trim();
2228
+ const headerCls = `border-b border-gray-100 ${p.edge} text-sm font-semibold text-gray-900`;
2229
+ return /* @__PURE__ */ jsxs(
2230
+ Root,
2231
+ {
2232
+ className: surface,
2233
+ style,
2234
+ ...titled ? { "aria-labelledby": headingId } : named ? { "aria-label": ariaLabel } : {},
2235
+ children: [
2236
+ header != null && (headerActions != null ? (
2237
+ // With actions the row has to be a flex container, so the heading is
2238
+ // nested rather than being the row itself — and only the title is
2239
+ // inside it.
2240
+ /* @__PURE__ */ jsxs("div", { className: `${headerCls} flex items-center justify-between gap-3`, children: [
2241
+ titled ? /* @__PURE__ */ jsx(Heading, { id: headingId, children: header }) : /* @__PURE__ */ jsx("span", { children: header }),
2242
+ headerActions
2243
+ ] })
2244
+ ) : titled ? (
2245
+ // Without them the heading carries the row's own styling rather than
2246
+ // nesting a second element, so the title is not two boxes deep.
2247
+ /* @__PURE__ */ jsx(Heading, { id: headingId, className: headerCls, children: header })
2248
+ ) : /* @__PURE__ */ jsx("div", { className: headerCls, children: header })),
2249
+ /* @__PURE__ */ jsx("div", { className: [p.body, bodyClassName].filter(Boolean).join(" "), children }),
2250
+ footer && /* @__PURE__ */ jsx("div", { className: `border-t border-gray-100 ${p.edge}`, children: footer })
2251
+ ]
2252
+ }
2253
+ );
2202
2254
  }
2203
2255
  function StatCard({ label, value, icon, delta, className = "" }) {
2204
2256
  const deltaColor = delta?.direction === "up" ? "text-green-600" : delta?.direction === "down" ? "text-red-600" : "text-gray-500";
@@ -4010,6 +4062,133 @@ function TopNav({ items, activeKey, onSelect, brand, actions, className }) {
4010
4062
  }
4011
4063
  );
4012
4064
  }
4065
+ function DropdownMenu({
4066
+ trigger,
4067
+ items,
4068
+ align = "end",
4069
+ "aria-label": ariaLabel,
4070
+ className = "",
4071
+ disabled
4072
+ }) {
4073
+ const [open, setOpen] = useState(false);
4074
+ const [active, setActive] = useState(0);
4075
+ const wrapRef = useRef(null);
4076
+ const triggerRef = useRef(null);
4077
+ const itemRefs = useRef([]);
4078
+ const menuId = useId();
4079
+ const close = useCallback((restoreFocus) => {
4080
+ setOpen(false);
4081
+ if (restoreFocus) triggerRef.current?.focus();
4082
+ }, []);
4083
+ const enabledFrom = (from, step) => {
4084
+ for (let i = 0; i < items.length; i += 1) {
4085
+ const at = ((from + step * i) % items.length + items.length) % items.length;
4086
+ if (!items[at]?.disabled) return at;
4087
+ }
4088
+ return from;
4089
+ };
4090
+ useEffect(() => {
4091
+ if (!open) return;
4092
+ const onPointerDown = (e) => {
4093
+ if (!wrapRef.current?.contains(e.target)) close(false);
4094
+ };
4095
+ document.addEventListener("pointerdown", onPointerDown, true);
4096
+ return () => document.removeEventListener("pointerdown", onPointerDown, true);
4097
+ }, [open, close]);
4098
+ useEffect(() => {
4099
+ if (open) itemRefs.current[active]?.focus();
4100
+ }, [open, active]);
4101
+ useEffect(() => {
4102
+ if (!open) return;
4103
+ return registerModalEscapeInterceptor((event) => {
4104
+ if (event.key !== "Escape") return false;
4105
+ close(true);
4106
+ return true;
4107
+ });
4108
+ }, [open, close]);
4109
+ const openAt = (index) => {
4110
+ setActive(index);
4111
+ setOpen(true);
4112
+ };
4113
+ const onTriggerKeyDown = (e) => {
4114
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
4115
+ e.preventDefault();
4116
+ openAt(enabledFrom(0, 1));
4117
+ } else if (e.key === "ArrowUp") {
4118
+ e.preventDefault();
4119
+ openAt(enabledFrom(items.length - 1, -1));
4120
+ }
4121
+ };
4122
+ const onMenuKeyDown = (e) => {
4123
+ if (e.key === "ArrowDown") {
4124
+ e.preventDefault();
4125
+ setActive(enabledFrom(active + 1, 1));
4126
+ } else if (e.key === "ArrowUp") {
4127
+ e.preventDefault();
4128
+ setActive(enabledFrom(active - 1, -1));
4129
+ } else if (e.key === "Home") {
4130
+ e.preventDefault();
4131
+ setActive(enabledFrom(0, 1));
4132
+ } else if (e.key === "End") {
4133
+ e.preventDefault();
4134
+ setActive(enabledFrom(items.length - 1, -1));
4135
+ } else if (e.key === "Tab") {
4136
+ e.preventDefault();
4137
+ close(true);
4138
+ }
4139
+ };
4140
+ const select = (item) => {
4141
+ if (item.disabled) return;
4142
+ close(true);
4143
+ item.onSelect();
4144
+ };
4145
+ return /* @__PURE__ */ jsxs("div", { ref: wrapRef, className: `relative inline-flex ${className}`.trim(), children: [
4146
+ /* @__PURE__ */ jsx(
4147
+ "button",
4148
+ {
4149
+ ref: triggerRef,
4150
+ type: "button",
4151
+ "aria-haspopup": "menu",
4152
+ "aria-expanded": open,
4153
+ "aria-controls": open ? menuId : void 0,
4154
+ "aria-label": ariaLabel,
4155
+ disabled,
4156
+ onClick: () => open ? close(false) : openAt(enabledFrom(0, 1)),
4157
+ onKeyDown: onTriggerKeyDown,
4158
+ className: "inline-flex items-center justify-center rounded-md text-gray-600 transition-colors hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400 disabled:cursor-not-allowed disabled:opacity-60",
4159
+ children: trigger
4160
+ }
4161
+ ),
4162
+ open && /* @__PURE__ */ jsx(
4163
+ "div",
4164
+ {
4165
+ className: `absolute top-full z-50 mt-1 ${align === "end" ? "right-0" : "left-0"}`,
4166
+ onKeyDown: onMenuKeyDown,
4167
+ children: /* @__PURE__ */ jsx(PopupMenu, { portal: false, minWidth: 180, children: /* @__PURE__ */ jsx("div", { id: menuId, role: "menu", "aria-orientation": "vertical", "aria-label": ariaLabel, children: items.map((item, i) => /* @__PURE__ */ jsxs(
4168
+ PopupMenuItem,
4169
+ {
4170
+ ref: (el) => {
4171
+ itemRefs.current[i] = el;
4172
+ },
4173
+ role: "menuitem",
4174
+ tabIndex: i === active ? 0 : -1,
4175
+ danger: item.danger,
4176
+ disabled: item.disabled,
4177
+ onMouseEnter: () => {
4178
+ if (!item.disabled) setActive(i);
4179
+ },
4180
+ onClick: () => select(item),
4181
+ children: [
4182
+ item.icon,
4183
+ item.label
4184
+ ]
4185
+ },
4186
+ item.key
4187
+ )) }) })
4188
+ }
4189
+ )
4190
+ ] });
4191
+ }
4013
4192
  var SIZE_PX2 = { sm: 320, md: 448, lg: 640 };
4014
4193
  var SIDE_POSITION = {
4015
4194
  right: "inset-y-0 right-0",
@@ -4453,6 +4632,7 @@ function Pagination({
4453
4632
  showEdges && /* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Last page", disabled: page === pageCount, onClick: () => go(pageCount), className: `${BTN} text-gray-600 hover:bg-gray-100`, children: "\xBB" })
4454
4633
  ] });
4455
4634
  }
4635
+ var isGroup = (h) => Array.isArray(h.columns);
4456
4636
  var PAD = {
4457
4637
  sm: "px-2 py-1.5",
4458
4638
  md: "px-3 py-2"
@@ -4475,6 +4655,7 @@ function DataTable({
4475
4655
  sort = null,
4476
4656
  onSortChange,
4477
4657
  pagination,
4658
+ caption,
4478
4659
  loading = false,
4479
4660
  bordered = false,
4480
4661
  size = "md",
@@ -4492,13 +4673,52 @@ function DataTable({
4492
4673
  if (!onSortChange) return;
4493
4674
  const field = fieldOf(col);
4494
4675
  const current = directionFor(col);
4495
- if (current === null) onSortChange({ field, direction: "asc" });
4496
- else if (current === "asc") onSortChange({ field, direction: "desc" });
4676
+ const first = col.sortFirst ?? "asc";
4677
+ const second = first === "asc" ? "desc" : "asc";
4678
+ if (current === null) onSortChange({ field, direction: first });
4679
+ else if (current === first) onSortChange({ field, direction: second });
4497
4680
  else onSortChange(null);
4498
4681
  };
4682
+ const leaves = columns.flatMap((h) => isGroup(h) ? h.columns : [h]);
4683
+ const grouped = columns.some(isGroup);
4499
4684
  const cellBase = `${PAD[size]} ${size === "sm" ? "text-xs" : "text-sm"}`;
4685
+ const headerCell = (col, ci, rowSpan) => {
4686
+ const dir = directionFor(col);
4687
+ const align = ALIGN2[col.align ?? (col.numeric ? "right" : "left")];
4688
+ const pinned = col.fixed === "left";
4689
+ return /* @__PURE__ */ jsx(
4690
+ "th",
4691
+ {
4692
+ scope: "col",
4693
+ rowSpan: rowSpan > 1 ? rowSpan : void 0,
4694
+ "aria-sort": dir === "asc" ? "ascending" : dir === "desc" ? "descending" : col.sortable && onSortChange ? "none" : void 0,
4695
+ style: { width: col.width, ...pinned ? { left: offsets[ci] ?? 0 } : {} },
4696
+ className: [
4697
+ cellBase,
4698
+ align,
4699
+ "font-semibold uppercase tracking-wide text-gray-500",
4700
+ size === "sm" ? "text-[10px]" : "text-xs",
4701
+ pinned ? "sticky z-10 bg-gray-50" : "",
4702
+ col.headerClassName ?? ""
4703
+ ].filter(Boolean).join(" "),
4704
+ children: col.sortable && onSortChange ? /* @__PURE__ */ jsxs(
4705
+ "button",
4706
+ {
4707
+ type: "button",
4708
+ onClick: () => toggleSort(col),
4709
+ className: `inline-flex items-center font-semibold uppercase tracking-wide hover:text-gray-700 ${align}`,
4710
+ children: [
4711
+ col.title,
4712
+ /* @__PURE__ */ jsx(SortIcon, { direction: dir })
4713
+ ]
4714
+ }
4715
+ ) : col.title
4716
+ },
4717
+ col.key
4718
+ );
4719
+ };
4500
4720
  let fixedOffset = 0;
4501
- const offsets = columns.map((col) => {
4721
+ const offsets = leaves.map((col) => {
4502
4722
  if (col.fixed !== "left") return null;
4503
4723
  const at = fixedOffset;
4504
4724
  fixedOffset += col.width ?? 0;
@@ -4514,56 +4734,56 @@ function DataTable({
4514
4734
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading" })
4515
4735
  ] }),
4516
4736
  /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse tabular-nums", style: minWidth ? { minWidth } : void 0, children: [
4517
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-gray-200 bg-gray-50", children: columns.map((col, ci) => {
4518
- const dir = directionFor(col);
4519
- const align = ALIGN2[col.align ?? "left"];
4520
- const pinned = col.fixed === "left";
4521
- return /* @__PURE__ */ jsx(
4522
- "th",
4523
- {
4524
- scope: "col",
4525
- "aria-sort": dir === "asc" ? "ascending" : dir === "desc" ? "descending" : void 0,
4526
- style: {
4527
- width: col.width,
4528
- ...pinned ? { left: offsets[ci] ?? 0 } : {}
4529
- },
4530
- className: [
4531
- cellBase,
4532
- align,
4533
- "font-semibold uppercase tracking-wide text-gray-500",
4534
- size === "sm" ? "text-[10px]" : "text-xs",
4535
- pinned ? "sticky z-10 bg-gray-50" : "",
4536
- col.headerClassName ?? ""
4537
- ].filter(Boolean).join(" "),
4538
- children: col.sortable && onSortChange ? /* @__PURE__ */ jsxs(
4539
- "button",
4737
+ caption && /* @__PURE__ */ jsx("caption", { className: "sr-only", children: caption }),
4738
+ /* @__PURE__ */ jsxs("thead", { children: [
4739
+ /* @__PURE__ */ jsx("tr", { className: "border-b border-gray-200 bg-gray-50", children: columns.map((h, hi) => {
4740
+ if (isGroup(h)) {
4741
+ return /* @__PURE__ */ jsx(
4742
+ "th",
4540
4743
  {
4541
- type: "button",
4542
- onClick: () => toggleSort(col),
4543
- className: `inline-flex items-center font-semibold uppercase tracking-wide hover:text-gray-700 ${align}`,
4544
- children: [
4545
- col.title,
4546
- /* @__PURE__ */ jsx(SortIcon, { direction: dir })
4547
- ]
4548
- }
4549
- ) : col.title
4550
- },
4551
- col.key
4552
- );
4553
- }) }) }),
4554
- /* @__PURE__ */ jsx("tbody", { children: data.length === 0 && !loading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: columns.length, className: `${cellBase} py-10 text-center text-gray-500`, children: emptyText }) }) : data.map((row, i) => {
4744
+ scope: "colgroup",
4745
+ colSpan: h.columns.length,
4746
+ className: [
4747
+ cellBase,
4748
+ "text-center",
4749
+ "font-semibold uppercase tracking-wide text-gray-500",
4750
+ size === "sm" ? "text-[10px]" : "text-xs",
4751
+ "border-b border-gray-200",
4752
+ h.headerClassName ?? ""
4753
+ ].filter(Boolean).join(" "),
4754
+ children: h.title
4755
+ },
4756
+ h.key ?? `group-${hi}`
4757
+ );
4758
+ }
4759
+ return headerCell(h, leaves.indexOf(h), grouped ? 2 : 1);
4760
+ }) }),
4761
+ grouped && /* @__PURE__ */ jsx("tr", { className: "border-b border-gray-200 bg-gray-50", children: columns.flatMap((h) => isGroup(h) ? h.columns : []).map(
4762
+ (col) => headerCell(col, leaves.indexOf(col), 1)
4763
+ ) })
4764
+ ] }),
4765
+ /* @__PURE__ */ jsx("tbody", { children: data.length === 0 && !loading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: leaves.length, className: `${cellBase} py-10 text-center text-gray-500`, children: emptyText }) }) : data.map((row, i) => {
4555
4766
  const rowProps = onRow?.(row, i);
4556
4767
  return /* @__PURE__ */ jsx(
4557
4768
  "tr",
4558
4769
  {
4559
4770
  onClick: rowProps?.onClick,
4771
+ ...rowProps?.onClick ? {
4772
+ tabIndex: 0,
4773
+ onKeyDown: (e) => {
4774
+ if (e.target !== e.currentTarget) return;
4775
+ if (e.key !== "Enter" && e.key !== " ") return;
4776
+ e.preventDefault();
4777
+ rowProps.onClick?.();
4778
+ }
4779
+ } : {},
4560
4780
  className: [
4561
4781
  "border-b border-gray-100 last:border-b-0",
4562
- rowProps?.onClick ? "cursor-pointer hover:bg-gray-50" : "",
4782
+ rowProps?.onClick ? "cursor-pointer hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-400" : "",
4563
4783
  rowClassName?.(row, i) ?? "",
4564
4784
  rowProps?.className ?? ""
4565
4785
  ].filter(Boolean).join(" "),
4566
- children: columns.map((col, ci) => {
4786
+ children: leaves.map((col, ci) => {
4567
4787
  const pinned = col.fixed === "left";
4568
4788
  return /* @__PURE__ */ jsx(
4569
4789
  "td",
@@ -4571,7 +4791,8 @@ function DataTable({
4571
4791
  style: pinned ? { left: offsets[ci] ?? 0 } : void 0,
4572
4792
  className: [
4573
4793
  cellBase,
4574
- ALIGN2[col.align ?? "left"],
4794
+ ALIGN2[col.align ?? (col.numeric ? "right" : "left")],
4795
+ col.numeric ? "font-mono" : "",
4575
4796
  "text-gray-900",
4576
4797
  col.ellipsis ? "max-w-0 truncate" : "",
4577
4798
  // A pinned cell needs its own background or the
@@ -5293,18 +5514,62 @@ var COPY2 = {
5293
5514
  404: { title: "Page not found", body: "The page you're looking for doesn't exist or was moved." },
5294
5515
  500: { title: "Something went wrong", body: "An unexpected error occurred. Please try again." }
5295
5516
  };
5296
- function ErrorPage({ code = 404 }) {
5517
+ function ErrorPage({ code = 404, actions }) {
5297
5518
  const c = COPY2[code];
5298
5519
  return /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col items-center justify-center bg-gray-50 p-6 text-center", children: [
5299
5520
  /* @__PURE__ */ jsx("div", { className: "text-7xl font-bold tracking-tight text-gray-400", children: code }),
5300
5521
  /* @__PURE__ */ jsx("h1", { className: "mt-2 text-xl font-semibold text-gray-900", children: c.title }),
5301
5522
  /* @__PURE__ */ jsx("p", { className: "mt-1 max-w-sm text-sm text-gray-500", children: c.body }),
5302
- /* @__PURE__ */ jsxs("div", { className: "mt-6 flex items-center gap-2", children: [
5303
- /* @__PURE__ */ jsx(Button_default, { variant: "secondary", children: "Go back" }),
5304
- /* @__PURE__ */ jsx(Button_default, { children: "Take me home" })
5305
- ] })
5523
+ actions && /* @__PURE__ */ jsx("div", { className: "mt-6 flex items-center gap-2", children: actions })
5306
5524
  ] });
5307
5525
  }
5526
+ var ErrorBoundary = class extends Component {
5527
+ state = { error: null };
5528
+ static getDerivedStateFromError(error) {
5529
+ return { error };
5530
+ }
5531
+ componentDidCatch(error, info) {
5532
+ console.error("ErrorBoundary caught an error:", error, info);
5533
+ this.props.onError?.(error, info);
5534
+ }
5535
+ componentDidUpdate(prev) {
5536
+ if (!this.state.error) return;
5537
+ const a = prev.resetKeys ?? [];
5538
+ const b = this.props.resetKeys ?? [];
5539
+ if (a.length !== b.length || a.some((k, i) => !Object.is(k, b[i]))) {
5540
+ this.reset();
5541
+ }
5542
+ }
5543
+ reset = () => this.setState({ error: null });
5544
+ render() {
5545
+ const { error } = this.state;
5546
+ if (!error) return this.props.children;
5547
+ if (this.props.fallback) return this.props.fallback(error, this.reset);
5548
+ return (
5549
+ // A column, not a bare wrapper: `ErrorPage` is `h-full`, so a `<details>`
5550
+ // rendered as its sibling inside an `h-full` box starts at the 100% mark
5551
+ // and sits below the fold — unreachable under an `overflow-hidden`
5552
+ // ancestor, which is the usual app shell. As a flex child it takes the
5553
+ // room it needs and the page gives it back.
5554
+ /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col", children: [
5555
+ /* @__PURE__ */ jsx("div", { role: "alert", className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx(
5556
+ ErrorPage,
5557
+ {
5558
+ code: 500,
5559
+ actions: /* @__PURE__ */ jsxs(Fragment, { children: [
5560
+ /* @__PURE__ */ jsx(Button_default, { onClick: this.reset, children: "Try again" }),
5561
+ this.props.actions
5562
+ ] })
5563
+ }
5564
+ ) }),
5565
+ this.props.showDetails && /* @__PURE__ */ jsxs("details", { className: "mx-auto w-full max-w-2xl shrink-0 px-6 pb-6", children: [
5566
+ /* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-sm font-medium text-gray-600", children: "Error details" }),
5567
+ /* @__PURE__ */ jsx("pre", { className: "mt-2 overflow-auto rounded-md bg-gray-100 p-3 text-xs text-gray-700", children: error.stack ?? error.message })
5568
+ ] })
5569
+ ] })
5570
+ );
5571
+ }
5572
+ };
5308
5573
  function subscribeMediaQuery(cb) {
5309
5574
  const mq = window.matchMedia("(prefers-color-scheme: dark)");
5310
5575
  mq.addEventListener("change", cb);
@@ -5438,6 +5703,6 @@ var ALT_SHIFT_D = isMac ? "\u2325\u21E7D" : "Alt+Shift+D";
5438
5703
  var ALT_SHIFT_E = isMac ? "\u2325\u21E7E" : "Alt+Shift+E";
5439
5704
  var ALT_SHIFT_N = isMac ? "\u2325\u21E7N" : "Alt+Shift+N";
5440
5705
 
5441
- export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N, Accordion, AuthScreen, Avatar, AvatarGroup, Banner, BarChart, Button_default, CMD_A, CMD_DOT, CMD_ENTER, CMD_K, CMD_S, CMD_SHIFT_Z, CMD_Z, Card, ChatTemplate, Checkbox_default, CheckoutTemplate, ColoredBadge, ContainerFillChart, CountBadge, DashboardTemplate, DataTable, DataTablePage, DatePicker_default, DateRangePicker, DescriptionList, Divider, DonutChart, Drawer, ENTER, EmailTemplate, EmptyState, ErrorPage, FilePicker, FormField, FormLayoutPage, GROUP_COLORS, GalleryTemplate, GlobalSearch, Grid, HelpCenter, INPUT_BASE, INPUT_SIZES, IconButton_default, Inline, InputNumber_default, Input_default, Kanban, Label, ListFooter, ListLoadError, LoadingSpinner, MAX_FRACTION_DIGITS, MOD, Markdown, MediaUploadField, MediaUploadGrid, MetricBar, MilestoneTimeline, NativeSelect, NumericKeypad, PageHeader, Pagination, Paragraph, Radio_default, Result, SHIFT, SearchableSelect, Segmented, Select_default, SidebarActionButton, SidebarGroupLabel, SidebarNavItem, Skeleton, Sparkline, Stack, StatCard, Statistic, StatusBadge, StatusBadgeProvider, Switch_default, Tabs, Text, Textarea_default, TileButton_default, Title, Tooltip, TopNav, appendKey, applyThemePrefs, backspace, firstEnabledIndex, formatDate, inputClasses, isMac, isSeverityTone, lastEnabledIndex, matchTypeahead, mediaFileName, nextEnabledIndex, resolveTheme, severityOf, tabButtonId, tabPanelId, toISODate, useClickOutside, useTheme };
5442
- //# sourceMappingURL=chunk-D4WFGAUW.js.map
5443
- //# sourceMappingURL=chunk-D4WFGAUW.js.map
5706
+ export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N, Accordion, AuthScreen, Avatar, AvatarGroup, Banner, BarChart, Button_default, CMD_A, CMD_DOT, CMD_ENTER, CMD_K, CMD_S, CMD_SHIFT_Z, CMD_Z, Card, ChatTemplate, Checkbox_default, CheckoutTemplate, ColoredBadge, ContainerFillChart, CountBadge, DashboardTemplate, DataTable, DataTablePage, DatePicker_default, DateRangePicker, DescriptionList, Divider, DonutChart, Drawer, DropdownMenu, ENTER, EmailTemplate, EmptyState, ErrorBoundary, ErrorPage, FilePicker, FormField, FormLayoutPage, GROUP_COLORS, GalleryTemplate, GlobalSearch, Grid, HelpCenter, INPUT_BASE, INPUT_SIZES, IconButton_default, Inline, InputNumber_default, Input_default, Kanban, Label, ListFooter, ListLoadError, LoadingSpinner, MAX_FRACTION_DIGITS, MOD, Markdown, MediaUploadField, MediaUploadGrid, MetricBar, MilestoneTimeline, NativeSelect, NumericKeypad, PageHeader, Pagination, Paragraph, Radio_default, Result, SHIFT, SearchableSelect, Segmented, Select_default, SidebarActionButton, SidebarGroupLabel, SidebarNavItem, Skeleton, Sparkline, Stack, StatCard, Statistic, StatusBadge, StatusBadgeProvider, Switch_default, Tabs, Text, Textarea_default, TileButton_default, Title, Tooltip, TopNav, appendKey, applyThemePrefs, backspace, firstEnabledIndex, formatDate, inputClasses, isMac, isSeverityTone, lastEnabledIndex, matchTypeahead, mediaFileName, nextEnabledIndex, resolveTheme, severityOf, tabButtonId, tabPanelId, toISODate, useClickOutside, useTheme };
5707
+ //# sourceMappingURL=chunk-CTTHBTOX.js.map
5708
+ //# sourceMappingURL=chunk-CTTHBTOX.js.map