@jirawatpyk/aura-react 5.1.0 → 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.
Files changed (48) hide show
  1. package/README.md +10 -0
  2. package/dist/aura.bundle.js +562 -183
  3. package/dist/cjs/index.cjs +562 -183
  4. package/dist/esm/AppShell.js +10 -2
  5. package/dist/esm/Avatar.js +4 -3
  6. package/dist/esm/BottomNav.js +2 -1
  7. package/dist/esm/Checkbox.js +3 -2
  8. package/dist/esm/Combobox.js +19 -19
  9. package/dist/esm/Command.js +1 -0
  10. package/dist/esm/DataTable.js +116 -29
  11. package/dist/esm/DatePicker.js +85 -22
  12. package/dist/esm/DropdownMenu.js +12 -0
  13. package/dist/esm/FileUpload.js +17 -1
  14. package/dist/esm/FormErrorSummary.js +19 -4
  15. package/dist/esm/Menu.js +20 -7
  16. package/dist/esm/NumberField.js +3 -1
  17. package/dist/esm/Pagination.js +3 -3
  18. package/dist/esm/Popover.js +26 -5
  19. package/dist/esm/RadioGroup.js +14 -1
  20. package/dist/esm/SideNav.js +2 -1
  21. package/dist/esm/Switch.js +1 -1
  22. package/dist/esm/Table.js +3 -2
  23. package/dist/esm/Tabs.js +11 -7
  24. package/dist/esm/TextField.js +2 -2
  25. package/dist/esm/Textarea.js +2 -2
  26. package/dist/esm/ThemeStyle.js +13 -3
  27. package/dist/esm/TimePicker.js +2 -1
  28. package/dist/esm/Toaster.js +1 -0
  29. package/dist/esm/Tooltip.js +54 -29
  30. package/dist/esm/colorScheme.js +7 -3
  31. package/dist/esm/colorSchemeScript.js +3 -3
  32. package/dist/esm/dates.js +34 -13
  33. package/dist/esm/internal.js +18 -7
  34. package/dist/esm/locale.js +6 -5
  35. package/dist/esm/strings.js +21 -0
  36. package/dist/esm/text.js +1 -0
  37. package/dist/esm/theme.js +3 -1
  38. package/dist/esm/useModal.js +31 -8
  39. package/dist/index.d.cts +1513 -0
  40. package/dist/index.d.ts +6 -1
  41. package/dist/server/index.cjs +51 -10
  42. package/dist/server/index.d.cts +182 -0
  43. package/dist/server/index.d.ts +6 -0
  44. package/dist/server/index.js +51 -10
  45. package/dist/styles.css +26 -5
  46. package/dist/styles.layer.css +26 -5
  47. package/package.json +19 -8
  48. package/scripts/aura-theme.mjs +31 -6
@@ -17,16 +17,24 @@ const AppShell = React.forwardRef(function AppShell2(props, ref) {
17
17
  [wide]
18
18
  );
19
19
  const navEl = props.nav && React.isValidElement(props.nav) ? props.nav : null;
20
+ const shared = !!navEl && navEl.props.value === void 0 && (navEl.props.defaultValue !== void 0 || navEl.props.sections !== void 0 || navEl.props.items !== void 0);
21
+ const navState = React.useState(navEl ? navEl.props.defaultValue : void 0);
22
+ function onNav(id) {
23
+ if (shared) navState[1](id);
24
+ if (navEl && navEl.props.onChange) navEl.props.onChange(id);
25
+ }
26
+ const deskNav = navEl && shared ? React.cloneElement(navEl, { value: navState[0], onChange: onNav }) : props.nav;
20
27
  const drawerNav = navEl ? React.cloneElement(navEl, {
28
+ value: shared ? navState[0] : navEl.props.value,
21
29
  onChange: function(id) {
22
- if (navEl.props.onChange) navEl.props.onChange(id);
30
+ onNav(id);
23
31
  setOpen(false);
24
32
  },
25
33
  className: cx(navEl.props.className, "is-in-drawer"),
26
34
  collapsed: false,
27
35
  collapsible: false
28
36
  }) : props.nav;
29
- return /* @__PURE__ */ React.createElement("div", { ref, className: cx("aura-shell", props.bottomNav && "aura-shell--bottomnav", props.className) }, props.nav ? /* @__PURE__ */ React.createElement("div", { className: "aura-shell__nav" }, props.nav) : null, props.nav ? /* @__PURE__ */ React.createElement(
37
+ return /* @__PURE__ */ React.createElement("div", { ref, className: cx("aura-shell", props.bottomNav && "aura-shell--bottomnav", props.className) }, props.nav ? /* @__PURE__ */ React.createElement("div", { className: "aura-shell__nav" }, deskNav) : null, props.nav ? /* @__PURE__ */ React.createElement(
30
38
  Drawer,
31
39
  {
32
40
  open,
@@ -7,7 +7,7 @@ function initials(name) {
7
7
  return ((parts[0] || "?")[0] + (parts.length > 1 ? parts[parts.length - 1][0] : parts[0][1] || "")).toUpperCase();
8
8
  }
9
9
  const Avatar = React.forwardRef(function Avatar2(props, ref) {
10
- const size = props.size || "md", errState = React.useState(false);
10
+ const size = props.size || "md", errState = React.useState(null);
11
11
  let hash = 0;
12
12
  String(props.name || "").split("").forEach(function(ch) {
13
13
  hash = hash * 31 + ch.charCodeAt(0) >>> 0;
@@ -21,13 +21,14 @@ const Avatar = React.forwardRef(function Avatar2(props, ref) {
21
21
  role: "img",
22
22
  "aria-label": props.name + (props.status ? ", " + props.status : "")
23
23
  },
24
- props.src && !errState[0] ? /* @__PURE__ */ React.createElement(
24
+ props.src && errState[0] !== props.src ? /* @__PURE__ */ React.createElement(
25
25
  "img",
26
26
  {
27
+ key: props.src,
27
28
  src: props.src,
28
29
  alt: "",
29
30
  onError: function() {
30
- errState[1](true);
31
+ errState[1](props.src || null);
31
32
  }
32
33
  }
33
34
  ) : /* @__PURE__ */ React.createElement("span", { "aria-hidden": true }, initials(props.name)),
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import * as React from "react";
3
3
  import { Icon } from "./Icon.js";
4
- import { cx, useMaybeControlled } from "./internal.js";
4
+ import { cx, plainClick, useMaybeControlled } from "./internal.js";
5
5
  import { useLinkComponent, useStrings } from "./locale.js";
6
6
  const BottomNav = React.forwardRef(function BottomNav2(props, ref) {
7
7
  const t = useStrings();
@@ -28,6 +28,7 @@ const BottomNav = React.forwardRef(function BottomNav2(props, ref) {
28
28
  "aria-current": on ? "page" : void 0,
29
29
  onClick: function(e) {
30
30
  if (!it.href) e.preventDefault();
31
+ else if (!plainClick(e)) return;
31
32
  st[1](it.id);
32
33
  }
33
34
  };
@@ -1,11 +1,12 @@
1
1
  'use client';
2
2
  import * as React from "react";
3
- import { cx, devWarnOnce, omit, useMergedRef, useMaybeControlled } from "./internal.js";
3
+ import { cx, devWarnOnce, omit, uid, useMergedRef, useMaybeControlled } from "./internal.js";
4
4
  import { Icon } from "./Icon.js";
5
5
  const Checkbox = React.forwardRef(function Checkbox2(props, ref) {
6
6
  const own = React.useRef(null), merged = useMergedRef(ref, own);
7
7
  const st = useMaybeControlled(props.checked, !!props.defaultChecked, props.onChange);
8
8
  const on = !!st[0];
9
+ const auto = uid();
9
10
  React.useEffect(function() {
10
11
  if (own.current) own.current.indeterminate = !!props.indeterminate;
11
12
  });
@@ -29,7 +30,7 @@ const Checkbox = React.forwardRef(function Checkbox2(props, ref) {
29
30
  "checkbox-label",
30
31
  "Checkbox `label` is only the accessible name: it is not shown. In 6.0 it will be shown beside the box, like Switch and TextField. For visible text now pass it as children; for a bare box (a table row) add `hideLabel`."
31
32
  );
32
- const descId = props.description && props.id ? props.id + "-desc" : void 0;
33
+ const descId = props.description ? (props.id || auto) + "-desc" : void 0;
33
34
  return /* @__PURE__ */ React.createElement(
34
35
  "label",
35
36
  {
@@ -82,7 +82,7 @@ const Combobox = React.forwardRef(
82
82
  function() {
83
83
  if (open) place();
84
84
  },
85
- [open, shown.length]
85
+ [open, shown.length, values.length]
86
86
  );
87
87
  React.useEffect(
88
88
  function() {
@@ -114,6 +114,7 @@ const Combobox = React.forwardRef(
114
114
  },
115
115
  [activeIdx, open]
116
116
  );
117
+ const live = props.loading ? [] : shown;
117
118
  function openList() {
118
119
  if (!open && !props.disabled && !props.readOnly) {
119
120
  setOpen(true);
@@ -149,7 +150,7 @@ const Combobox = React.forwardRef(
149
150
  if (k === "ArrowDown") {
150
151
  e.preventDefault();
151
152
  if (!open) openList();
152
- else setActive(Math.min(activeIdx + 1, shown.length - 1));
153
+ else setActive(Math.max(0, Math.min(activeIdx + 1, live.length - 1)));
153
154
  } else if (k === "ArrowUp") {
154
155
  e.preventDefault();
155
156
  if (!open) openList();
@@ -159,21 +160,22 @@ const Combobox = React.forwardRef(
159
160
  setActive(0);
160
161
  } else if (k === "End" && open) {
161
162
  e.preventDefault();
162
- setActive(shown.length - 1);
163
+ setActive(Math.max(0, live.length - 1));
163
164
  } else if (k === "Enter") {
164
- if (open && shown[activeIdx]) {
165
+ if (e.nativeEvent.isComposing || e.keyCode === 229) return;
166
+ if (open && live[activeIdx]) {
165
167
  e.preventDefault();
166
- choose(shown[activeIdx]);
168
+ choose(live[activeIdx]);
167
169
  }
168
170
  } else if (k === "Escape") {
169
171
  if (open) {
170
172
  e.preventDefault();
171
173
  e.stopPropagation();
172
174
  close();
173
- } else if (!multi && props.clearable !== false && value != null && query == null) {
175
+ } else if (!multi && !props.readOnly && props.clearable !== false && value != null && query == null) {
174
176
  setValue(null);
175
177
  }
176
- } else if (k === "Backspace" && multi && !text && values.length) {
178
+ } else if (k === "Backspace" && multi && !props.readOnly && !text && values.length) {
177
179
  setValues(values.slice(0, -1));
178
180
  } else if (k === "Tab") {
179
181
  if (open) close();
@@ -185,7 +187,7 @@ const Combobox = React.forwardRef(
185
187
  return id + "-opt-" + i;
186
188
  };
187
189
  const list = open && mounted && posState[0] ? createPortal(
188
- /* @__PURE__ */ React.createElement("div", { ref: listRef, className: "aura-combo__popover", style: posState[0] }, /* @__PURE__ */ React.createElement(
190
+ /* @__PURE__ */ React.createElement("div", { ref: listRef, className: "aura-combo__popover", style: posState[0] }, live.length ? /* @__PURE__ */ React.createElement(
189
191
  "ul",
190
192
  {
191
193
  id: listId,
@@ -194,7 +196,7 @@ const Combobox = React.forwardRef(
194
196
  "aria-multiselectable": multi || void 0,
195
197
  className: "aura-combo__list"
196
198
  },
197
- props.loading ? /* @__PURE__ */ React.createElement("li", { className: "aura-combo__note", role: "presentation" }, /* @__PURE__ */ React.createElement(Icon, { name: "loader-circle", className: "aura-spin" }), props.loadingText || t.searching) : !shown.length ? /* @__PURE__ */ React.createElement("li", { className: "aura-combo__note", role: "presentation" }, props.emptyText || t.noMatches) : shown.map(function(o, i) {
199
+ live.map(function(o, i) {
198
200
  const isSel = multi ? isPicked(o.value) : !!selected && o.value === selected.value;
199
201
  const blocked = o.disabled || multi && full && !isSel;
200
202
  return /* @__PURE__ */ React.createElement(
@@ -226,9 +228,8 @@ const Combobox = React.forwardRef(
226
228
  /* @__PURE__ */ React.createElement("span", { className: "aura-combo__text" }, /* @__PURE__ */ React.createElement("span", { className: "aura-combo__label" }, o.label), o.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-combo__desc" }, o.description) : null),
227
229
  isSel ? /* @__PURE__ */ React.createElement(Icon, { name: "check", className: "aura-combo__check" }) : null
228
230
  );
229
- }),
230
- more && !props.loading ? /* @__PURE__ */ React.createElement("li", { className: "aura-combo__note", role: "presentation" }, t.keepTyping((props.options || []).length)) : null
231
- )),
231
+ })
232
+ ) : null, props.loading || !live.length || more ? /* @__PURE__ */ React.createElement("div", { className: "aura-combo__list", role: "status" }, props.loading ? /* @__PURE__ */ React.createElement("div", { className: "aura-combo__note" }, /* @__PURE__ */ React.createElement(Icon, { name: "loader-circle", className: "aura-spin" }), props.loadingText || t.searching) : !live.length ? /* @__PURE__ */ React.createElement("div", { className: "aura-combo__note" }, props.emptyText || t.noMatches) : /* @__PURE__ */ React.createElement("div", { className: "aura-combo__note" }, t.keepTyping((props.options || []).length))) : null),
232
233
  document.body
233
234
  ) : null;
234
235
  return /* @__PURE__ */ React.createElement(
@@ -267,9 +268,9 @@ const Combobox = React.forwardRef(
267
268
  ));
268
269
  })) : null, multi ? /* @__PURE__ */ React.createElement("span", { id: summaryId, className: "aura-sr-only" }, picked.length ? t.selectedCount(picked.length) + ": " + picked.map(function(o) {
269
270
  return o.label;
270
- }).join(", ") : "") : null, multi && props.name ? values.map(function(v) {
271
+ }).join(", ") : "") : null, props.name ? multi ? values.map(function(v) {
271
272
  return /* @__PURE__ */ React.createElement("input", { key: v, type: "hidden", name: props.name, value: v });
272
- }) : null, /* @__PURE__ */ React.createElement(
273
+ }) : /* @__PURE__ */ React.createElement("input", { type: "hidden", name: props.name, value: value == null ? "" : value }) : null, /* @__PURE__ */ React.createElement(
273
274
  "input",
274
275
  {
275
276
  ref: inputMerged,
@@ -278,17 +279,16 @@ const Combobox = React.forwardRef(
278
279
  role: "combobox",
279
280
  className: "aura-input__control",
280
281
  autoComplete: "off",
281
- "aria-expanded": open,
282
- "aria-controls": listId,
282
+ "aria-expanded": open && live.length > 0,
283
+ "aria-controls": open && live.length ? listId : void 0,
283
284
  "aria-autocomplete": "list",
284
- "aria-activedescendant": open && shown[activeIdx] ? optId(activeIdx) : void 0,
285
+ "aria-activedescendant": open && live[activeIdx] ? optId(activeIdx) : void 0,
285
286
  "aria-invalid": props.error ? true : void 0,
286
287
  "aria-describedby": [props.error ? id + "-error" : props.hint ? id + "-hint" : "", multi && picked.length ? summaryId : ""].filter(Boolean).join(" ") || void 0,
287
288
  placeholder: multi && values.length ? void 0 : props.placeholder,
288
289
  disabled: props.disabled,
289
290
  readOnly: props.readOnly,
290
291
  required: props.required && (!multi || !values.length),
291
- name: multi ? void 0 : props.name,
292
292
  value: text,
293
293
  onChange: function(e) {
294
294
  setQuery(e.target.value);
@@ -306,7 +306,7 @@ const Combobox = React.forwardRef(
306
306
  }, 0);
307
307
  }
308
308
  }
309
- ), props.clearable !== false && values.length && !props.disabled ? /* @__PURE__ */ React.createElement(
309
+ ), props.clearable !== false && values.length && !props.disabled && !props.readOnly ? /* @__PURE__ */ React.createElement(
310
310
  "button",
311
311
  {
312
312
  type: "button",
@@ -120,6 +120,7 @@ function Command(props) {
120
120
  e.preventDefault();
121
121
  move("last");
122
122
  } else if (e.key === "Enter") {
123
+ if (e.nativeEvent.isComposing || e.keyCode === 229) return;
123
124
  e.preventDefault();
124
125
  if (active >= 0) run(flat[active]);
125
126
  }
@@ -35,7 +35,7 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
35
35
  byKey[c.key] = c;
36
36
  });
37
37
  const rows = props.rows || [];
38
- const rowKey = props.rowKey || columns[0].key;
38
+ const rowKey = props.rowKey || (columns[0] ? columns[0].key : "id");
39
39
  const manual = !!props.manual;
40
40
  const Link = useLinkComponent(props.linkComponent);
41
41
  const busy = !!props.loading;
@@ -243,11 +243,12 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
243
243
  if (lv && boxWidth[0] == null) hideTerms += " + " + w + "px * var(--aura-h" + lv + "-on, 1)";
244
244
  else fixedSum += w;
245
245
  });
246
- let pinOffsets = {}, acc = 0;
246
+ let pinOffsets = {}, acc = "0px";
247
247
  vis.forEach(function(c) {
248
248
  if (isPinned(c)) {
249
249
  pinOffsets[c.key] = acc;
250
- acc += widthOf(c);
250
+ const lv = hideLevel(c);
251
+ acc += " + " + widthOf(c) + "px" + (lv && boxWidth[0] == null ? " * var(--aura-h" + lv + "-on, 1)" : "");
251
252
  }
252
253
  });
253
254
  const selW = selectable ? " + var(--aura-table-select-width)" : "";
@@ -262,9 +263,10 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
262
263
  s["--aura-cell-flex"] = !hasFlex && i === vis.length - 1 ? "1 0 auto" : "none";
263
264
  s["--aura-cell-w"] = w + "px";
264
265
  }
265
- if (isPinned(c)) s["--aura-cell-left"] = "calc(var(--aura-space-6)" + selW + " + " + pinOffsets[c.key] + "px)";
266
+ if (isPinned(c)) s["--aura-cell-left"] = "calc(var(--aura-space-6)" + selW + " + " + pinOffsets[c.key] + ")";
266
267
  return s;
267
268
  }
269
+ const sortCol = sort && sort.key ? byKey[sort.key] : void 0;
268
270
  const view = React.useMemo(
269
271
  function() {
270
272
  if (manual || !sort || !sort.key || !byKey[sort.key]) return rows;
@@ -283,7 +285,16 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
283
285
  return p[0];
284
286
  });
285
287
  },
286
- [rows, manual, sort && sort.key, sort && sort.dir]
288
+ [
289
+ rows,
290
+ manual,
291
+ sort && sort.key,
292
+ sort && sort.dir,
293
+ !!sortCol,
294
+ sortCol && sortCol.sortValue,
295
+ sortCol && sortCol.pill,
296
+ sortCol && sortCol.tones
297
+ ]
287
298
  );
288
299
  function nextSort(key) {
289
300
  if (!sort || sort.key !== key) return { key, dir: "asc" };
@@ -292,14 +303,37 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
292
303
  }
293
304
  const pageSize = props.pageSize || 0;
294
305
  const total = manual ? props.totalRows != null ? props.totalRows : (Math.max(1, pageState[0] || 1) - 1) * pageSize + rows.length + (pageSize && rows.length >= pageSize ? 1 : 0) : view.length;
295
- const pageCount = pageSize ? Math.max(1, Math.ceil(total / pageSize)) : 1;
306
+ const pageCount = pageSize ? Math.max(1, Math.ceil(total / pageSize), manual && props.totalRows == null ? pageState[0] || 1 : 1) : 1;
296
307
  const page = Math.min(Math.max(1, pageState[0] || 1), pageCount);
297
308
  const first = pageSize ? (page - 1) * pageSize : 0;
298
309
  const pageRows = pageSize && !manual ? view.slice(first, first + pageSize) : view;
299
310
  const canSortAny = !busy && (manual ? total > 1 : rows.length > 1);
300
311
  const shownTotal = manual && props.totalRows == null ? first + rows.length : total;
312
+ const prevCount = React.useRef(pageCount);
313
+ React.useEffect(
314
+ function() {
315
+ if (busy) return;
316
+ const before = prevCount.current;
317
+ prevCount.current = pageCount;
318
+ const asked = pageState[0] || 1;
319
+ if (asked === page || pageCount >= before || asked > before) return;
320
+ if (props.getPageHref && !props.onPageChange && !props.onStateChange) return;
321
+ pageState[1](page);
322
+ emit(sort, page);
323
+ },
324
+ [pageState[0], page, busy, pageCount]
325
+ );
301
326
  const linkPaging = !!props.getPageHref && !props.onPageChange && !oneCallback;
302
- const prevLink = React.useRef(null), nextLink = React.useRef(null);
327
+ const prevLink = React.useRef(null), nextLink = React.useRef(null), jumpLink = React.useRef(null);
328
+ const jumpState = React.useState(null);
329
+ React.useEffect(
330
+ function() {
331
+ if (jumpState[0] == null) return;
332
+ if (jumpLink.current) jumpLink.current.click();
333
+ jumpState[1](null);
334
+ },
335
+ [jumpState[0]]
336
+ );
303
337
  function emit(s, p) {
304
338
  if (props.onStateChange) props.onStateChange({ sort: s, page: p });
305
339
  }
@@ -310,6 +344,7 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
310
344
  if (linkPaging) {
311
345
  const a = next === page - 1 ? prevLink.current : next === page + 1 ? nextLink.current : null;
312
346
  if (a) a.click();
347
+ else jumpState[1](next);
313
348
  return false;
314
349
  }
315
350
  if (focus) pending.current = focus;
@@ -346,7 +381,8 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
346
381
  }
347
382
  const height = props.height || 0;
348
383
  const virtual = !!height && !loading && pageRows.length > 0 && !stacked;
349
- const bodyH = Math.max(ROW_H, height - ROW_H);
384
+ const stickyTotal = !!(props.footer && props.stickyFooter && rows.length && !loading);
385
+ const bodyH = Math.max(ROW_H, height - ROW_H - (stickyTotal ? ROW_H : 0));
350
386
  let start = 0, end = pageRows.length;
351
387
  if (virtual) {
352
388
  start = Math.max(0, Math.floor(scrollTop / ROW_H) - OVERSCAN);
@@ -355,9 +391,10 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
355
391
  const visibleRows = Math.max(1, Math.floor(bodyH / ROW_H));
356
392
  const nCols = vis.length + (selectable ? 1 : 0);
357
393
  const nRows = loading ? 0 : pageRows.length;
394
+ const hasTotal = !!(props.footer && rows.length && !loading), T = nRows + 1, lastR = nRows + (hasTotal ? 1 : 0);
358
395
  const active = activeState[0];
359
- const ar = Math.min(active.r, nRows), ac = Math.min(active.c, nCols - 1);
360
- const activeRendered = ar === 0 || ar - 1 >= start && ar - 1 < end;
396
+ const ar = Math.min(active.r, lastR), ac = Math.min(active.c, nCols - 1);
397
+ const activeRendered = ar === 0 || hasTotal && ar === T || ar - 1 >= start && ar - 1 < end;
361
398
  function colAt(ci) {
362
399
  return selectable ? ci === 0 ? null : vis[ci - 1] : vis[ci];
363
400
  }
@@ -376,6 +413,11 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
376
413
  });
377
414
  c = at >= 0 ? at + (selectable ? 1 : 0) : Math.min(p.c || 0, nCols - 1);
378
415
  }
416
+ const ae = document.activeElement;
417
+ if (ae && ae !== document.body && !gridRef.current.contains(ae)) {
418
+ pending.current = null;
419
+ return;
420
+ }
379
421
  const el = gridRef.current.querySelector('[data-rc="' + p.r + ":" + c + '"]');
380
422
  if (el) {
381
423
  pending.current = null;
@@ -383,12 +425,33 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
383
425
  }
384
426
  });
385
427
  function focusCell(r, c) {
386
- r = Math.max(0, Math.min(r, nRows));
428
+ r = Math.max(0, Math.min(r, lastR));
387
429
  c = Math.max(0, Math.min(c, nCols - 1));
388
430
  if (stacked && r === 0 && nRows > 0 && !(selectable && c === 0)) r = 1;
431
+ if (gridRef.current && r === T && hasTotal) {
432
+ const shown2 = function(cc) {
433
+ const e = gridRef.current.querySelector('[data-rc="' + r + ":" + cc + '"]');
434
+ return !!e && e.getClientRects().length > 0;
435
+ };
436
+ if (!shown2(c))
437
+ for (let d = 1; d < nCols; d++) {
438
+ if (c + d < nCols && shown2(c + d)) {
439
+ c = c + d;
440
+ break;
441
+ }
442
+ if (c - d >= 0 && shown2(c - d)) {
443
+ c = c - d;
444
+ break;
445
+ }
446
+ }
447
+ }
389
448
  activeState[1]({ r, c });
390
449
  pending.current = { r, c };
391
- if (virtual && r > 0) {
450
+ if (virtual && hasTotal && r === T && !props.stickyFooter) {
451
+ const sc = scrollRef.current;
452
+ sc.scrollTop = sc.scrollHeight;
453
+ setScrollTop(sc.scrollTop);
454
+ } else if (virtual && r > 0 && r <= nRows) {
392
455
  const sc = scrollRef.current, top = (r - 1) * ROW_H;
393
456
  if (top < sc.scrollTop) sc.scrollTop = top;
394
457
  else if (top + ROW_H > sc.scrollTop + bodyH) sc.scrollTop = top + ROW_H - bodyH;
@@ -405,26 +468,28 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
405
468
  });
406
469
  const selSet = {};
407
470
  selected.forEach(function(k) {
408
- selSet[k] = true;
471
+ selSet[String(k)] = true;
409
472
  });
410
473
  const nSel = pageKeys.filter(function(k) {
411
- return selSet[k];
474
+ return selSet[String(k)];
412
475
  }).length;
413
476
  const all = nSel > 0 && nSel === pageKeys.length;
414
477
  function toggle(k, on) {
415
478
  setSelected(
416
479
  on ? selected.concat([k]) : selected.filter(function(x) {
417
- return x !== k;
480
+ return String(x) !== String(k);
418
481
  })
419
482
  );
420
483
  }
421
484
  function toggleAll() {
422
485
  setSelected(
423
486
  all ? selected.filter(function(k) {
424
- return pageKeys.indexOf(k) < 0;
487
+ return !pageKeys.some(function(p) {
488
+ return String(p) === String(k);
489
+ });
425
490
  }) : selected.concat(
426
491
  pageKeys.filter(function(k) {
427
- return !selSet[k];
492
+ return !selSet[String(k)];
428
493
  })
429
494
  )
430
495
  );
@@ -629,7 +694,7 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
629
694
  return;
630
695
  }
631
696
  const p = rc.split(":"), r = +p[0], c = +p[1], k = e.key, col = colAt(c);
632
- const row = r > 0 ? pageRows[r - 1] : null;
697
+ const row = r > 0 && r <= nRows ? pageRows[r - 1] : null, isTotal = hasTotal && r === T;
633
698
  let handled = true;
634
699
  if (r === 0 && col && e.altKey && (k === "ArrowLeft" || k === "ArrowRight") && canResize(col))
635
700
  setW(col, widthOf(col) + (k === "ArrowLeft" ? -1 : 1) * (e.shiftKey ? 48 : 16), true);
@@ -640,21 +705,23 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
640
705
  else if (k === "ArrowRight") focusCell(r, c + 1);
641
706
  else if (k === "ArrowLeft") focusCell(r, c - 1);
642
707
  else if (k === "ArrowDown") {
643
- if (r < nRows) focusCell(r + 1, c);
708
+ if (r < lastR) focusCell(r + 1, c);
644
709
  else if (pageSize && page < pageCount && goPage(page + 1, { r: 1, c })) activeState[1]({ r: 1, c });
645
710
  } else if (k === "ArrowUp") {
646
711
  if (r > 1 || r === 1 && !(pageSize && page > 1)) focusCell(r - 1, c);
647
712
  else if (r === 1 && goPage(page - 1, { r: pageSize, c })) activeState[1]({ r: pageSize, c });
648
713
  } else if (k === "Home") focusCell(e.ctrlKey ? 1 : r, 0);
649
- else if (k === "End") focusCell(e.ctrlKey ? nRows : r, nCols - 1);
714
+ else if (k === "End") focusCell(e.ctrlKey ? lastR : r, nCols - 1);
650
715
  else if (k === "PageDown") {
651
716
  if (pageSize) {
652
717
  if (goPage(page + 1, { r: 1, c })) activeState[1]({ r: 1, c });
653
- } else focusCell(Math.min(nRows, r + visibleRows), c);
718
+ } else focusCell(Math.min(lastR, r + visibleRows), c);
654
719
  } else if (k === "PageUp") {
655
720
  if (pageSize) {
656
721
  if (goPage(page - 1, { r: 1, c })) activeState[1]({ r: 1, c });
657
722
  } else focusCell(Math.max(1, r - visibleRows), c);
723
+ } else if (isTotal && (k === " " || k === "Enter" || k === "F2")) {
724
+ handled = false;
658
725
  } else if (k === "F2" && r > 0) {
659
726
  const inner = target.querySelector("button, a[href], input, select, textarea");
660
727
  if (inner) inner.focus();
@@ -1025,9 +1092,10 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
1025
1092
  }
1026
1093
  );
1027
1094
  }
1028
- if (pageSize && (rows.length || busy)) {
1095
+ const openTotal = manual && props.totalRows == null && !!pageSize && rows.length >= pageSize;
1096
+ if (pageSize && (rows.length || busy || page > 1)) {
1029
1097
  const from = rows.length ? first + 1 : 0, to = manual ? first + rows.length : Math.min(first + pageSize, view.length);
1030
- foot = /* @__PURE__ */ React.createElement("div", { className: "aura-table__foot" }, /* @__PURE__ */ React.createElement("span", { "aria-live": "polite" }, busy ? t.loading : t.range(from, to, shownTotal)), /* @__PURE__ */ React.createElement("span", { className: "aura-table__pager" }, /* @__PURE__ */ React.createElement("span", null, t.page(page, pageCount)), pagerButton(-1), pagerButton(1)));
1098
+ foot = /* @__PURE__ */ React.createElement("div", { className: "aura-table__foot" }, /* @__PURE__ */ React.createElement("span", { "aria-live": "polite" }, busy ? t.loading : openTotal ? t.rangeOpen(from, to) : t.range(from, to, shownTotal)), /* @__PURE__ */ React.createElement("span", { className: "aura-table__pager" }, /* @__PURE__ */ React.createElement("span", null, openTotal ? t.pageOpen(page) : t.page(page, pageCount)), pagerButton(-1), pagerButton(1)));
1031
1099
  } else if (height && rows.length && !loading) {
1032
1100
  foot = /* @__PURE__ */ React.createElement("div", { className: "aura-table__foot" }, /* @__PURE__ */ React.createElement("span", null, t.rowCount(shownTotal)), selectable && selected.length ? /* @__PURE__ */ React.createElement("span", null, t.selectedCount(selected.length)) : /* @__PURE__ */ React.createElement("span", null));
1033
1101
  }
@@ -1041,15 +1109,32 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
1041
1109
  style: rowStyle
1042
1110
  },
1043
1111
  /* @__PURE__ */ React.createElement("span", { className: "aura-table__gutter", "aria-hidden": true }),
1044
- selectable ? /* @__PURE__ */ React.createElement("span", { className: cx("aura-table__sel", nPinned && "is-pinned"), "aria-hidden": true }) : null,
1112
+ selectable ? /* @__PURE__ */ React.createElement(
1113
+ "span",
1114
+ {
1115
+ role: "gridcell",
1116
+ "aria-colindex": 1,
1117
+ className: cx("aura-table__sel", nPinned && "is-pinned"),
1118
+ tabIndex: tabFor(T, 0),
1119
+ "data-rc": T + ":0",
1120
+ onFocus: function() {
1121
+ activeState[1]({ r: T, c: 0 });
1122
+ }
1123
+ }
1124
+ ) : null,
1045
1125
  vis.map(function(c, j) {
1046
- const pin = isPinned(c);
1126
+ const pin = isPinned(c), ci = j + (selectable ? 1 : 0);
1047
1127
  return /* @__PURE__ */ React.createElement(
1048
1128
  "span",
1049
1129
  {
1050
1130
  key: c.key,
1051
1131
  role: "gridcell",
1052
1132
  "aria-colindex": j + (selectable ? 2 : 1),
1133
+ tabIndex: tabFor(T, ci),
1134
+ "data-rc": T + ":" + ci,
1135
+ onFocus: function() {
1136
+ if (activeState[0].r !== T || activeState[0].c !== ci) activeState[1]({ r: T, c: ci });
1137
+ },
1053
1138
  className: cx(
1054
1139
  "aura-table__td",
1055
1140
  c.mono && "aura-table__mono",
@@ -1075,8 +1160,9 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
1075
1160
  )
1076
1161
  ) : null;
1077
1162
  const scrollStyle = {
1078
- scrollPaddingLeft: "calc(var(--aura-space-6)" + selW + " + " + acc + "px)",
1079
- scrollPaddingTop: ROW_H + "px"
1163
+ scrollPaddingLeft: "calc(var(--aura-space-6)" + selW + " + " + acc + ")",
1164
+ scrollPaddingTop: ROW_H + "px",
1165
+ scrollPaddingBottom: stickyTotal ? ROW_H + "px" : void 0
1080
1166
  };
1081
1167
  if (height) scrollStyle["--aura-table-h"] = height + "px";
1082
1168
  const gridEl = /* @__PURE__ */ React.createElement(
@@ -1110,7 +1196,7 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
1110
1196
  role: "grid",
1111
1197
  "aria-label": props.label,
1112
1198
  "aria-busy": busy || void 0,
1113
- "aria-rowcount": loading ? -1 : shownTotal + 1 + (totalRow ? 1 : 0),
1199
+ "aria-rowcount": loading || manual && props.totalRows == null && pageSize && rows.length >= pageSize ? -1 : shownTotal + 1 + (totalRow ? 1 : 0),
1114
1200
  "aria-colcount": nCols,
1115
1201
  "aria-multiselectable": selectable || void 0,
1116
1202
  onKeyDown: onGridKey,
@@ -1158,7 +1244,8 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
1158
1244
  items: menu.kind === "picker" ? pickerItems() : byKey[menu.key] ? columnMenuItems(byKey[menu.key]) : []
1159
1245
  }
1160
1246
  ) : null,
1161
- tipEl
1247
+ tipEl,
1248
+ jumpState[0] != null && props.getPageHref ? /* @__PURE__ */ React.createElement(Link, { ref: jumpLink, href: props.getPageHref(jumpState[0]), hidden: true, tabIndex: -1, "aria-hidden": true }) : null
1162
1249
  );
1163
1250
  if (!levels.length) return gridEl;
1164
1251
  let out = gridEl;