@jirawatpyk/aura-react 5.1.1 → 5.2.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,16 @@ 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.2 — the review's smaller items
190
+
191
+ - **DataTable**: the totals row is part of the grid (arrow keys and Ctrl+End reach it); server paging without `totalRows` shows "1–25 of many · Page 1" while pages come back full; a page that no longer exists (a filter left one page) is reported back through `onPageChange` / `onStateChange`; a pinned column that `hideBelow` hides no longer pushes the next pinned column before hydration.
192
+ - **Dates and times**: `parseDate` reads eight digits from a phone keypad (`18092569`, `20260918`), an era anywhere (`18 ก.ย. พ.ศ. 2569`; `ค.ศ.` keeps a year ≥ 2400 Gregorian), years 0–99 as written, and refuses 31 February instead of rolling it into March (so does `formatDate`). `parseTime` refuses `13pm` and `0am`. TimePicker says "That time isn't available" for a time `isTimeDisabled` refuses inside the range. The calendar popover is placed by its real size (touch cells, the range hint).
193
+ - **Popover** taller than the room above and below scrolls inside the viewport. **FileUpload** items with a `url` (files already stored) link to it and show image thumbnails.
194
+ - **Contrast**: the Avatar online dot has a `fg-positive` edge and the selected SegmentedControl option a `border-control` edge, so both marks reach 3:1 (210 pairs checked).
195
+ - **Tokens package**: `@jirawatpyk/aura-tokens/tailwind` has types (`import aura from …/tailwind` in a strict `tailwind.config.ts`); `require('@jirawatpyk/aura-tokens')` works. Tailwind v4 `dark:` follows `data-theme="system"` with no script and stays off inside a light island (`Surface`).
196
+ - **ESLint 9**: `import aura from '@jirawatpyk/aura-tokens/eslint-plugin'; export default [aura.configs.recommended];` The rule also catches `oklch()` / `color-mix()`, named colours in style values and Tailwind palette classes (`bg-red-500`, `text-white`), and no longer flags `#add-user` anchors. `lint-tokens.js` matches it and scans `.mjs`, `.cjs`, `.vue`, `.svelte`, `.astro`, `.html`.
197
+ - **`aura-theme` CLI** takes `--brand=#…`, says what's wrong in one line (exit 2) instead of a stack trace, and accepts a hex without `#`.
198
+
189
199
  ## 5.1 — from DxT Monitor
190
200
 
191
201
  - **StatusPill `tone="warning"`**: solid amber (amber-600 light, amber-500 dark, ink label) with `triangle-alert`, for states that need attention but still work — lighter than Blocked and darker than Ready, so it doesn't rely on hue. The words Warning, Problem, Degraded and At risk pick it by themselves. New tokens `--aura-status-warning-bg` / `-fg` (6.3:1 light, 9.3:1 dark). DataTable pill columns sort it between Ready and Blocked.
@@ -378,6 +378,12 @@ window.Aura = (() => {
378
378
  page: function(p, total) {
379
379
  return "Page " + p + " of " + total;
380
380
  },
381
+ rangeOpen: function(a, b) {
382
+ return a + "\u2013" + b + " of many";
383
+ },
384
+ pageOpen: function(p) {
385
+ return "Page " + p;
386
+ },
381
387
  prevPage: "Previous page",
382
388
  nextPage: "Next page",
383
389
  totals: "Totals",
@@ -391,6 +397,7 @@ window.Aura = (() => {
391
397
  timeOutOfRange: function(a, b) {
392
398
  return "Choose a time between " + a + " and " + b;
393
399
  },
400
+ timeUnavailable: "That time isn't available. Choose another.",
394
401
  dropFiles: "Drag files here or",
395
402
  browse: "Choose files",
396
403
  browseOne: "Choose a file",
@@ -492,6 +499,12 @@ window.Aura = (() => {
492
499
  page: function(p, total) {
493
500
  return "\u0E2B\u0E19\u0E49\u0E32 " + p + " / " + total;
494
501
  },
502
+ rangeOpen: function(a, b) {
503
+ return a + "\u2013" + b + " \u0E08\u0E32\u0E01\u0E2B\u0E25\u0E32\u0E22\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23";
504
+ },
505
+ pageOpen: function(p) {
506
+ return "\u0E2B\u0E19\u0E49\u0E32 " + p;
507
+ },
495
508
  prevPage: "\u0E2B\u0E19\u0E49\u0E32\u0E01\u0E48\u0E2D\u0E19\u0E2B\u0E19\u0E49\u0E32",
496
509
  nextPage: "\u0E2B\u0E19\u0E49\u0E32\u0E16\u0E31\u0E14\u0E44\u0E1B",
497
510
  totals: "\u0E23\u0E27\u0E21",
@@ -505,6 +518,7 @@ window.Aura = (() => {
505
518
  timeOutOfRange: function(a, b) {
506
519
  return "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E40\u0E27\u0E25\u0E32\u0E23\u0E30\u0E2B\u0E27\u0E48\u0E32\u0E07 " + a + "\u2013" + b + " \u0E19.";
507
520
  },
521
+ timeUnavailable: "\u0E40\u0E27\u0E25\u0E32\u0E19\u0E35\u0E49\u0E44\u0E21\u0E48\u0E27\u0E48\u0E32\u0E07 \u0E40\u0E25\u0E37\u0E2D\u0E01\u0E40\u0E27\u0E25\u0E32\u0E2D\u0E37\u0E48\u0E19",
508
522
  dropFiles: "\u0E25\u0E32\u0E01\u0E44\u0E1F\u0E25\u0E4C\u0E21\u0E32\u0E27\u0E32\u0E07 \u0E2B\u0E23\u0E37\u0E2D",
509
523
  browse: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E44\u0E1F\u0E25\u0E4C",
510
524
  browseOne: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E44\u0E1F\u0E25\u0E4C",
@@ -606,6 +620,12 @@ window.Aura = (() => {
606
620
  page: function(p, total) {
607
621
  return "Sida " + p + " av " + total;
608
622
  },
623
+ rangeOpen: function(a, b) {
624
+ return a + "\u2013" + b + " av m\xE5nga";
625
+ },
626
+ pageOpen: function(p) {
627
+ return "Sida " + p;
628
+ },
609
629
  prevPage: "F\xF6reg\xE5ende sida",
610
630
  nextPage: "N\xE4sta sida",
611
631
  totals: "Summa",
@@ -619,6 +639,7 @@ window.Aura = (() => {
619
639
  timeOutOfRange: function(a, b) {
620
640
  return "V\xE4lj en tid mellan " + a + " och " + b;
621
641
  },
642
+ timeUnavailable: "Den tiden \xE4r inte ledig. V\xE4lj en annan.",
622
643
  dropFiles: "Dra filer hit eller",
623
644
  browse: "V\xE4lj filer",
624
645
  browseOne: "V\xE4lj en fil",
@@ -1690,20 +1711,29 @@ window.Aura = (() => {
1690
1711
  return (n2 < 10 ? "0" : "") + n2;
1691
1712
  };
1692
1713
  function toISO(d) {
1693
- return d ? d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) : null;
1714
+ if (!d) return null;
1715
+ const y = String(d.getFullYear());
1716
+ return ("000" + y).slice(-Math.max(4, y.length)) + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate());
1717
+ }
1718
+ function makeDate(y, monthIndex2, day) {
1719
+ const d = new Date(2e3, 0, 1);
1720
+ d.setFullYear(y, monthIndex2, day);
1721
+ return d;
1694
1722
  }
1695
1723
  function fromISO(s) {
1696
1724
  if (!s) return null;
1697
1725
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
1698
- return m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;
1726
+ if (!m) return null;
1727
+ const d = makeDate(+m[1], +m[2] - 1, +m[3]);
1728
+ return d.getMonth() === +m[2] - 1 && d.getDate() === +m[3] ? d : null;
1699
1729
  }
1700
1730
  function addDays(d, n2) {
1701
- return new Date(d.getFullYear(), d.getMonth(), d.getDate() + n2);
1731
+ return makeDate(d.getFullYear(), d.getMonth(), d.getDate() + n2);
1702
1732
  }
1703
1733
  function addMonths(d, n2) {
1704
- const t = new Date(d.getFullYear(), d.getMonth() + n2, 1);
1705
- const last = new Date(t.getFullYear(), t.getMonth() + 1, 0).getDate();
1706
- return new Date(t.getFullYear(), t.getMonth(), Math.min(d.getDate(), last));
1734
+ const t = makeDate(d.getFullYear(), d.getMonth() + n2, 1);
1735
+ const last = makeDate(t.getFullYear(), t.getMonth() + 1, 0).getDate();
1736
+ return makeDate(t.getFullYear(), t.getMonth(), Math.min(d.getDate(), last));
1707
1737
  }
1708
1738
  function same(a, b) {
1709
1739
  return !!a && !!b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
@@ -1811,7 +1841,15 @@ window.Aura = (() => {
1811
1841
  return MONTHS[k] != null ? MONTHS[k] : -1;
1812
1842
  }
1813
1843
  function parseDate(text) {
1814
- const s = String(text || "").trim();
1844
+ const raw = String(text || "").trim();
1845
+ const gregorian = /ค\.ศ\.|\d\s*(AD|CE)\b|^\s*(AD|CE)\s/i.test(raw);
1846
+ let s = raw.replace(/พ\.ศ\.|ค\.ศ\./g, " ").replace(/(\d)\s*(BE|AD|CE)\b/gi, "$1 ").replace(/^\s*(BE|AD|CE)\s+/i, " ").replace(/\s+/g, " ").trim();
1847
+ const eight = /^(\d{2})(\d{2})(\d{4})$/.exec(s);
1848
+ if (eight) {
1849
+ const a = +eight[3] >= 1e3 ? parseDate(eight[1] + "/" + eight[2] + "/" + eight[3] + (gregorian ? " \u0E04.\u0E28." : "")) : null;
1850
+ if (a) return a;
1851
+ s = s.slice(0, 4) + "-" + s.slice(4, 6) + "-" + s.slice(6);
1852
+ }
1815
1853
  let m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(s), y, mo, d;
1816
1854
  if (m) {
1817
1855
  y = +m[1];
@@ -1821,13 +1859,13 @@ window.Aura = (() => {
1821
1859
  d = +m[1];
1822
1860
  mo = +m[2];
1823
1861
  y = +m[3];
1824
- } else if ((m = /^(\d{1,2})\s+(\S+)\s+(\d{4})$/.exec(s.replace(ERA, "").trim())) && monthIndex(m[2]) >= 0) {
1862
+ } else if ((m = /^(\d{1,2})\s+(\S+)\s+(\d{4})$/.exec(s)) && monthIndex(m[2]) >= 0) {
1825
1863
  d = +m[1];
1826
1864
  mo = monthIndex(m[2]) + 1;
1827
1865
  y = +m[3];
1828
1866
  } else return null;
1829
- if (y >= 2400) y -= 543;
1830
- const dt = new Date(y, mo - 1, d);
1867
+ if (y >= 2400 && !gregorian) y -= 543;
1868
+ const dt = makeDate(y, mo - 1, d);
1831
1869
  return dt.getMonth() === mo - 1 && dt.getDate() === d ? toISO(dt) : null;
1832
1870
  }
1833
1871
  function todayIn(timeZone) {
@@ -2118,16 +2156,16 @@ window.Aura = (() => {
2118
2156
  const pos = React17.useState(null), popRef = React17.useRef(null), mounted = useMounted();
2119
2157
  function place() {
2120
2158
  if (!boxRef.current) return;
2121
- const r = boxRef.current.getBoundingClientRect(), H = 380;
2122
- const up = window.innerHeight - r.bottom < H && r.top > H;
2123
- const left = Math.max(8, Math.min(r.left, window.innerWidth - 320 - 8));
2159
+ const el = popRef.current, r = boxRef.current.getBoundingClientRect(), W = el ? el.offsetWidth : 320, H = el ? el.offsetHeight : 380;
2160
+ const up = window.innerHeight - r.bottom < H + 12 && r.top > H + 12;
2161
+ const left = Math.max(8, Math.min(r.left, window.innerWidth - W - 8));
2124
2162
  pos[1](up ? { left, bottom: window.innerHeight - r.top + 4 } : { left, top: r.bottom + 4 });
2125
2163
  }
2126
2164
  useIsoLayoutEffect(
2127
2165
  function() {
2128
2166
  if (open) place();
2129
2167
  },
2130
- [open]
2168
+ [open, !!pos[0]]
2131
2169
  );
2132
2170
  React17.useEffect(
2133
2171
  function() {
@@ -3696,11 +3734,12 @@ window.Aura = (() => {
3696
3734
  if (lv && boxWidth[0] == null) hideTerms += " + " + w + "px * var(--aura-h" + lv + "-on, 1)";
3697
3735
  else fixedSum += w;
3698
3736
  });
3699
- let pinOffsets = {}, acc = 0;
3737
+ let pinOffsets = {}, acc = "0px";
3700
3738
  vis.forEach(function(c) {
3701
3739
  if (isPinned(c)) {
3702
3740
  pinOffsets[c.key] = acc;
3703
- acc += widthOf(c);
3741
+ const lv = hideLevel(c);
3742
+ acc += " + " + widthOf(c) + "px" + (lv && boxWidth[0] == null ? " * var(--aura-h" + lv + "-on, 1)" : "");
3704
3743
  }
3705
3744
  });
3706
3745
  const selW = selectable ? " + var(--aura-table-select-width)" : "";
@@ -3715,7 +3754,7 @@ window.Aura = (() => {
3715
3754
  s["--aura-cell-flex"] = !hasFlex && i === vis.length - 1 ? "1 0 auto" : "none";
3716
3755
  s["--aura-cell-w"] = w + "px";
3717
3756
  }
3718
- if (isPinned(c)) s["--aura-cell-left"] = "calc(var(--aura-space-6)" + selW + " + " + pinOffsets[c.key] + "px)";
3757
+ if (isPinned(c)) s["--aura-cell-left"] = "calc(var(--aura-space-6)" + selW + " + " + pinOffsets[c.key] + ")";
3719
3758
  return s;
3720
3759
  }
3721
3760
  const sortCol = sort && sort.key ? byKey[sort.key] : void 0;
@@ -3761,6 +3800,20 @@ window.Aura = (() => {
3761
3800
  const pageRows = pageSize && !manual ? view.slice(first, first + pageSize) : view;
3762
3801
  const canSortAny = !busy && (manual ? total > 1 : rows.length > 1);
3763
3802
  const shownTotal = manual && props.totalRows == null ? first + rows.length : total;
3803
+ const prevCount = React28.useRef(pageCount);
3804
+ React28.useEffect(
3805
+ function() {
3806
+ if (busy) return;
3807
+ const before = prevCount.current;
3808
+ prevCount.current = pageCount;
3809
+ const asked = pageState[0] || 1;
3810
+ if (asked === page || pageCount >= before || asked > before) return;
3811
+ if (props.getPageHref && !props.onPageChange && !props.onStateChange) return;
3812
+ pageState[1](page);
3813
+ emit(sort, page);
3814
+ },
3815
+ [pageState[0], page, busy, pageCount]
3816
+ );
3764
3817
  const linkPaging = !!props.getPageHref && !props.onPageChange && !oneCallback;
3765
3818
  const prevLink = React28.useRef(null), nextLink = React28.useRef(null), jumpLink = React28.useRef(null);
3766
3819
  const jumpState = React28.useState(null);
@@ -3829,9 +3882,10 @@ window.Aura = (() => {
3829
3882
  const visibleRows = Math.max(1, Math.floor(bodyH / ROW_H));
3830
3883
  const nCols = vis.length + (selectable ? 1 : 0);
3831
3884
  const nRows = loading ? 0 : pageRows.length;
3885
+ const hasTotal = !!(props.footer && rows.length && !loading), T = nRows + 1, lastR = nRows + (hasTotal ? 1 : 0);
3832
3886
  const active = activeState[0];
3833
- const ar = Math.min(active.r, nRows), ac = Math.min(active.c, nCols - 1);
3834
- const activeRendered = ar === 0 || ar - 1 >= start && ar - 1 < end;
3887
+ const ar = Math.min(active.r, lastR), ac = Math.min(active.c, nCols - 1);
3888
+ const activeRendered = ar === 0 || hasTotal && ar === T || ar - 1 >= start && ar - 1 < end;
3835
3889
  function colAt(ci) {
3836
3890
  return selectable ? ci === 0 ? null : vis[ci - 1] : vis[ci];
3837
3891
  }
@@ -3862,12 +3916,33 @@ window.Aura = (() => {
3862
3916
  }
3863
3917
  });
3864
3918
  function focusCell(r, c) {
3865
- r = Math.max(0, Math.min(r, nRows));
3919
+ r = Math.max(0, Math.min(r, lastR));
3866
3920
  c = Math.max(0, Math.min(c, nCols - 1));
3867
3921
  if (stacked && r === 0 && nRows > 0 && !(selectable && c === 0)) r = 1;
3922
+ if (gridRef.current && r === T && hasTotal) {
3923
+ const shown2 = function(cc) {
3924
+ const e = gridRef.current.querySelector('[data-rc="' + r + ":" + cc + '"]');
3925
+ return !!e && e.getClientRects().length > 0;
3926
+ };
3927
+ if (!shown2(c))
3928
+ for (let d = 1; d < nCols; d++) {
3929
+ if (c + d < nCols && shown2(c + d)) {
3930
+ c = c + d;
3931
+ break;
3932
+ }
3933
+ if (c - d >= 0 && shown2(c - d)) {
3934
+ c = c - d;
3935
+ break;
3936
+ }
3937
+ }
3938
+ }
3868
3939
  activeState[1]({ r, c });
3869
3940
  pending.current = { r, c };
3870
- if (virtual && r > 0) {
3941
+ if (virtual && hasTotal && r === T && !props.stickyFooter) {
3942
+ const sc = scrollRef.current;
3943
+ sc.scrollTop = sc.scrollHeight;
3944
+ setScrollTop(sc.scrollTop);
3945
+ } else if (virtual && r > 0 && r <= nRows) {
3871
3946
  const sc = scrollRef.current, top = (r - 1) * ROW_H;
3872
3947
  if (top < sc.scrollTop) sc.scrollTop = top;
3873
3948
  else if (top + ROW_H > sc.scrollTop + bodyH) sc.scrollTop = top + ROW_H - bodyH;
@@ -4110,7 +4185,7 @@ window.Aura = (() => {
4110
4185
  return;
4111
4186
  }
4112
4187
  const p = rc.split(":"), r = +p[0], c = +p[1], k = e.key, col = colAt(c);
4113
- const row = r > 0 ? pageRows[r - 1] : null;
4188
+ const row = r > 0 && r <= nRows ? pageRows[r - 1] : null, isTotal = hasTotal && r === T;
4114
4189
  let handled = true;
4115
4190
  if (r === 0 && col && e.altKey && (k === "ArrowLeft" || k === "ArrowRight") && canResize(col))
4116
4191
  setW(col, widthOf(col) + (k === "ArrowLeft" ? -1 : 1) * (e.shiftKey ? 48 : 16), true);
@@ -4121,21 +4196,23 @@ window.Aura = (() => {
4121
4196
  else if (k === "ArrowRight") focusCell(r, c + 1);
4122
4197
  else if (k === "ArrowLeft") focusCell(r, c - 1);
4123
4198
  else if (k === "ArrowDown") {
4124
- if (r < nRows) focusCell(r + 1, c);
4199
+ if (r < lastR) focusCell(r + 1, c);
4125
4200
  else if (pageSize && page < pageCount && goPage(page + 1, { r: 1, c })) activeState[1]({ r: 1, c });
4126
4201
  } else if (k === "ArrowUp") {
4127
4202
  if (r > 1 || r === 1 && !(pageSize && page > 1)) focusCell(r - 1, c);
4128
4203
  else if (r === 1 && goPage(page - 1, { r: pageSize, c })) activeState[1]({ r: pageSize, c });
4129
4204
  } else if (k === "Home") focusCell(e.ctrlKey ? 1 : r, 0);
4130
- else if (k === "End") focusCell(e.ctrlKey ? nRows : r, nCols - 1);
4205
+ else if (k === "End") focusCell(e.ctrlKey ? lastR : r, nCols - 1);
4131
4206
  else if (k === "PageDown") {
4132
4207
  if (pageSize) {
4133
4208
  if (goPage(page + 1, { r: 1, c })) activeState[1]({ r: 1, c });
4134
- } else focusCell(Math.min(nRows, r + visibleRows), c);
4209
+ } else focusCell(Math.min(lastR, r + visibleRows), c);
4135
4210
  } else if (k === "PageUp") {
4136
4211
  if (pageSize) {
4137
4212
  if (goPage(page - 1, { r: 1, c })) activeState[1]({ r: 1, c });
4138
4213
  } else focusCell(Math.max(1, r - visibleRows), c);
4214
+ } else if (isTotal && (k === " " || k === "Enter" || k === "F2")) {
4215
+ handled = false;
4139
4216
  } else if (k === "F2" && r > 0) {
4140
4217
  const inner = target.querySelector("button, a[href], input, select, textarea");
4141
4218
  if (inner) inner.focus();
@@ -4506,9 +4583,10 @@ window.Aura = (() => {
4506
4583
  }
4507
4584
  );
4508
4585
  }
4586
+ const openTotal = manual && props.totalRows == null && !!pageSize && rows.length >= pageSize;
4509
4587
  if (pageSize && (rows.length || busy || page > 1)) {
4510
4588
  const from = rows.length ? first + 1 : 0, to = manual ? first + rows.length : Math.min(first + pageSize, view.length);
4511
- foot = /* @__PURE__ */ React28.createElement("div", { className: "aura-table__foot" }, /* @__PURE__ */ React28.createElement("span", { "aria-live": "polite" }, busy ? t.loading : t.range(from, to, shownTotal)), /* @__PURE__ */ React28.createElement("span", { className: "aura-table__pager" }, /* @__PURE__ */ React28.createElement("span", null, t.page(page, pageCount)), pagerButton(-1), pagerButton(1)));
4589
+ foot = /* @__PURE__ */ React28.createElement("div", { className: "aura-table__foot" }, /* @__PURE__ */ React28.createElement("span", { "aria-live": "polite" }, busy ? t.loading : openTotal ? t.rangeOpen(from, to) : t.range(from, to, shownTotal)), /* @__PURE__ */ React28.createElement("span", { className: "aura-table__pager" }, /* @__PURE__ */ React28.createElement("span", null, openTotal ? t.pageOpen(page) : t.page(page, pageCount)), pagerButton(-1), pagerButton(1)));
4512
4590
  } else if (height && rows.length && !loading) {
4513
4591
  foot = /* @__PURE__ */ React28.createElement("div", { className: "aura-table__foot" }, /* @__PURE__ */ React28.createElement("span", null, t.rowCount(shownTotal)), selectable && selected.length ? /* @__PURE__ */ React28.createElement("span", null, t.selectedCount(selected.length)) : /* @__PURE__ */ React28.createElement("span", null));
4514
4592
  }
@@ -4522,15 +4600,32 @@ window.Aura = (() => {
4522
4600
  style: rowStyle
4523
4601
  },
4524
4602
  /* @__PURE__ */ React28.createElement("span", { className: "aura-table__gutter", "aria-hidden": true }),
4525
- selectable ? /* @__PURE__ */ React28.createElement("span", { className: cx("aura-table__sel", nPinned && "is-pinned"), "aria-hidden": true }) : null,
4603
+ selectable ? /* @__PURE__ */ React28.createElement(
4604
+ "span",
4605
+ {
4606
+ role: "gridcell",
4607
+ "aria-colindex": 1,
4608
+ className: cx("aura-table__sel", nPinned && "is-pinned"),
4609
+ tabIndex: tabFor(T, 0),
4610
+ "data-rc": T + ":0",
4611
+ onFocus: function() {
4612
+ activeState[1]({ r: T, c: 0 });
4613
+ }
4614
+ }
4615
+ ) : null,
4526
4616
  vis.map(function(c, j) {
4527
- const pin = isPinned(c);
4617
+ const pin = isPinned(c), ci = j + (selectable ? 1 : 0);
4528
4618
  return /* @__PURE__ */ React28.createElement(
4529
4619
  "span",
4530
4620
  {
4531
4621
  key: c.key,
4532
4622
  role: "gridcell",
4533
4623
  "aria-colindex": j + (selectable ? 2 : 1),
4624
+ tabIndex: tabFor(T, ci),
4625
+ "data-rc": T + ":" + ci,
4626
+ onFocus: function() {
4627
+ if (activeState[0].r !== T || activeState[0].c !== ci) activeState[1]({ r: T, c: ci });
4628
+ },
4534
4629
  className: cx(
4535
4630
  "aura-table__td",
4536
4631
  c.mono && "aura-table__mono",
@@ -4556,7 +4651,7 @@ window.Aura = (() => {
4556
4651
  )
4557
4652
  ) : null;
4558
4653
  const scrollStyle = {
4559
- scrollPaddingLeft: "calc(var(--aura-space-6)" + selW + " + " + acc + "px)",
4654
+ scrollPaddingLeft: "calc(var(--aura-space-6)" + selW + " + " + acc + ")",
4560
4655
  scrollPaddingTop: ROW_H + "px",
4561
4656
  scrollPaddingBottom: stickyTotal ? ROW_H + "px" : void 0
4562
4657
  };
@@ -5468,6 +5563,7 @@ window.Aura = (() => {
5468
5563
  const m = /^(\d{1,2})(?:[:.](\d{2}))?$/.exec(s) || /^(\d{1,2})(\d{2})$/.exec(s);
5469
5564
  if (!m) return null;
5470
5565
  let hh = +m[1], mm = m[2] ? +m[2] : 0;
5566
+ if ((am || pm) && (hh < 1 || hh > 12)) return null;
5471
5567
  if (pm && hh < 12) hh += 12;
5472
5568
  if (am && hh === 12) hh = 0;
5473
5569
  if (hh > 23 || mm > 59) return null;
@@ -5582,7 +5678,8 @@ window.Aura = (() => {
5582
5678
  return true;
5583
5679
  }
5584
5680
  if (blocked(v)) {
5585
- errState[1](t.timeOutOfRange(fromMin(lo), fromMin(hi)));
5681
+ const n2 = toMin(v);
5682
+ errState[1](n2 != null && n2 >= lo && n2 <= hi ? t.timeUnavailable : t.timeOutOfRange(fromMin(lo), fromMin(hi)));
5586
5683
  return false;
5587
5684
  }
5588
5685
  errState[1](null);
@@ -5841,6 +5938,7 @@ window.Aura = (() => {
5841
5938
  [items2, props.name]
5842
5939
  );
5843
5940
  function thumb(it) {
5941
+ if (!it.file && it.url && /^image\//.test(it.type || "")) return it.url;
5844
5942
  if (!it.file || !/^image\//.test(it.type || "") || typeof URL === "undefined" || !URL.createObjectURL) return null;
5845
5943
  if (!urls.current[it.id]) urls.current[it.id] = URL.createObjectURL(it.file);
5846
5944
  return urls.current[it.id];
@@ -5946,7 +6044,7 @@ window.Aura = (() => {
5946
6044
  ),
5947
6045
  items2.length ? /* @__PURE__ */ React46.createElement("ul", { className: "aura-upload__list", "aria-live": "polite" }, items2.map(function(it) {
5948
6046
  const src = thumb(it);
5949
- return /* @__PURE__ */ React46.createElement("li", { key: it.id, className: cx("aura-upload__item", it.error && "is-error") }, src ? /* @__PURE__ */ React46.createElement("img", { className: "aura-upload__thumb", src, alt: "" }) : /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__thumb is-icon", "aria-hidden": true }, /* @__PURE__ */ React46.createElement(Icon, { name: /^image\//.test(it.type || "") ? "image" : "file", size: "md" })), /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__meta" }, /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__name" }, it.name), /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__sub" }, it.error ? /* @__PURE__ */ React46.createElement(React46.Fragment, null, /* @__PURE__ */ React46.createElement(Icon, { name: "circle-alert", size: 12 }), it.error) : it.status === "uploading" ? t.uploading + (it.progress != null ? " " + Math.round(it.progress) + "%" : "") : it.status === "done" ? /* @__PURE__ */ React46.createElement(React46.Fragment, null, /* @__PURE__ */ React46.createElement(Icon, { name: "circle-check", size: 12 }), formatBytes(it.size)) : formatBytes(it.size)), it.status === "uploading" ? /* @__PURE__ */ React46.createElement(
6047
+ return /* @__PURE__ */ React46.createElement("li", { key: it.id, className: cx("aura-upload__item", it.error && "is-error") }, src ? /* @__PURE__ */ React46.createElement("img", { className: "aura-upload__thumb", src, alt: "" }) : /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__thumb is-icon", "aria-hidden": true }, /* @__PURE__ */ React46.createElement(Icon, { name: /^image\//.test(it.type || "") ? "image" : "file", size: "md" })), /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__meta" }, it.url && !it.error ? /* @__PURE__ */ React46.createElement("a", { className: "aura-upload__name", href: it.url, target: "_blank", rel: "noreferrer" }, it.name) : /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__name" }, it.name), /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__sub" }, it.error ? /* @__PURE__ */ React46.createElement(React46.Fragment, null, /* @__PURE__ */ React46.createElement(Icon, { name: "circle-alert", size: 12 }), it.error) : it.status === "uploading" ? t.uploading + (it.progress != null ? " " + Math.round(it.progress) + "%" : "") : it.status === "done" ? /* @__PURE__ */ React46.createElement(React46.Fragment, null, /* @__PURE__ */ React46.createElement(Icon, { name: "circle-check", size: 12 }), formatBytes(it.size)) : formatBytes(it.size)), it.status === "uploading" ? /* @__PURE__ */ React46.createElement(
5950
6048
  "span",
5951
6049
  {
5952
6050
  className: "aura-upload__bar",
@@ -6651,13 +6749,20 @@ window.Aura = (() => {
6651
6749
  var React55 = __toESM(require_react(), 1);
6652
6750
  var import_react_dom11 = __toESM(require_react_dom(), 1);
6653
6751
  function position(anchor, pop, placement) {
6654
- const r = anchor.getBoundingClientRect(), pw = pop.offsetWidth, ph = pop.offsetHeight, vw = window.innerWidth, vh = window.innerHeight, gap = 6;
6752
+ const r = anchor.getBoundingClientRect(), pw = pop.offsetWidth, ph = pop.scrollHeight, vw = window.innerWidth, vh = window.innerHeight, gap = 6;
6655
6753
  let side = (placement || "bottom-start").split("-")[0], align = (placement || "bottom-start").split("-")[1] || "start";
6656
6754
  if (side === "bottom" && r.bottom + gap + ph > vh - 8 && r.top - gap - ph > 8) side = "top";
6657
6755
  else if (side === "top" && r.top - gap - ph < 8 && r.bottom + gap + ph < vh - 8) side = "bottom";
6658
- const top = side === "top" ? r.top - gap - ph : r.bottom + gap;
6756
+ const below2 = vh - r.bottom - gap - 8, above = r.top - gap - 8;
6757
+ let maxHeight;
6758
+ if (ph > below2 && ph > above) {
6759
+ side = above > below2 ? "top" : "bottom";
6760
+ maxHeight = Math.max(120, side === "top" ? above : below2);
6761
+ }
6762
+ const h9 = maxHeight ? Math.min(ph, maxHeight) : ph;
6763
+ const top = side === "top" ? r.top - gap - h9 : r.bottom + gap;
6659
6764
  const left = align === "end" ? r.right - pw : align === "center" ? r.left + r.width / 2 - pw / 2 : r.left;
6660
- return { top: Math.max(8, top), left: Math.max(8, Math.min(left, vw - pw - 8)), side };
6765
+ return { top: Math.max(8, top), left: Math.max(8, Math.min(left, vw - pw - 8)), side, maxHeight };
6661
6766
  }
6662
6767
  var Popover = React55.forwardRef(function Popover2(props, ref) {
6663
6768
  const t = useStrings();
@@ -6737,7 +6842,11 @@ window.Aura = (() => {
6737
6842
  },
6738
6843
  className: cx("aura-popover", pos[0] && "is-" + pos[0].side, props.className),
6739
6844
  style: Object.assign(
6740
- { top: pos[0] ? pos[0].top : -9999, left: pos[0] ? pos[0].left : -9999 },
6845
+ {
6846
+ top: pos[0] ? pos[0].top : -9999,
6847
+ left: pos[0] ? pos[0].left : -9999,
6848
+ maxHeight: pos[0] && pos[0].maxHeight ? pos[0].maxHeight : void 0
6849
+ },
6741
6850
  props.width ? { width: props.width } : null
6742
6851
  ),
6743
6852
  onKeyDown: function(e) {