@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
@@ -38,14 +38,37 @@ function useFormatDate() {
38
38
  [locale, calendar]
39
39
  );
40
40
  }
41
+ function allowedDate(iso, o) {
42
+ const t = o.min === "today" || o.max === "today" ? o.today && fromISO(o.today) ? o.today : todayIn(o.timeZone) : "";
43
+ const lo = o.min === "today" ? t : o.min, hi = o.max === "today" ? t : o.max;
44
+ if (lo && fromISO(lo) && iso < lo) return false;
45
+ if (hi && fromISO(hi) && iso > hi) return false;
46
+ return !(o.isDateDisabled && o.isDateDisabled(iso));
47
+ }
41
48
  const Calendar = React.forwardRef(function Calendar2(props, ref) {
42
49
  const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "en", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), tag = localeTag(locale, calendar);
43
50
  const weekStart = props.weekStartsOn == null ? locale === "sv" ? 1 : 0 : props.weekStartsOn;
44
- const todayISO = (props.today && fromISO(props.today) ? props.today : null) || todayIn(props.timeZone || ctx.timeZone);
51
+ const given = props.today && fromISO(props.today) ? props.today : null, zone = props.timeZone || ctx.timeZone;
52
+ const todayISO = given || todayIn(zone);
45
53
  const today = fromISO(todayISO);
54
+ const mounted = useMounted(), gridKey = given || mounted ? "client" : "server";
46
55
  const min = fromISO(props.min === "today" ? todayISO : props.min), max = fromISO(props.max === "today" ? todayISO : props.max);
47
56
  const start = fromISO(props.start), end = fromISO(props.end);
48
- const focusState = React.useState(fromISO(props.focus) || start || today);
57
+ function disabledAt(d) {
58
+ return !!(min && d < min || max && d > max || props.isDateDisabled && props.isDateDisabled(toISO(d)));
59
+ }
60
+ function select(iso) {
61
+ if (props.onSelect) props.onSelect(iso);
62
+ }
63
+ const focusState = React.useState(function() {
64
+ const d0 = fromISO(props.focus) || start || today;
65
+ if (!disabledAt(d0)) return d0;
66
+ for (let i = 1; i <= 366; i++) {
67
+ if (!disabledAt(addDays(d0, i))) return addDays(d0, i);
68
+ if (!disabledAt(addDays(d0, -i))) return addDays(d0, -i);
69
+ }
70
+ return d0;
71
+ });
49
72
  const focusDate = focusState[0], setFocus = focusState[1];
50
73
  const viewState = React.useState("days"), view = viewState[0], setView = viewState[1];
51
74
  const hoverState = React.useState(null);
@@ -53,7 +76,7 @@ const Calendar = React.forwardRef(function Calendar2(props, ref) {
53
76
  const th = locale === "th";
54
77
  const dt = dateText(locale);
55
78
  function disabled(d) {
56
- return !!(min && d < min || max && d > max || props.isDateDisabled && props.isDateDisabled(toISO(d)));
79
+ return disabledAt(d);
57
80
  }
58
81
  React.useEffect(function() {
59
82
  if (!moved.current) return;
@@ -61,11 +84,14 @@ const Calendar = React.forwardRef(function Calendar2(props, ref) {
61
84
  const el = gridRef.current && gridRef.current.querySelector('[data-date="' + toISO(focusDate) + '"]');
62
85
  if (el) el.focus();
63
86
  });
64
- React.useEffect(function() {
65
- if (props.autoFocus === false) return;
66
- const el = gridRef.current && gridRef.current.querySelector('[tabindex="0"]');
67
- if (el) el.focus();
68
- }, []);
87
+ React.useEffect(
88
+ function() {
89
+ if (props.autoFocus === false) return;
90
+ const el = gridRef.current && gridRef.current.querySelector('[tabindex="0"]');
91
+ if (el) el.focus();
92
+ },
93
+ [gridKey]
94
+ );
69
95
  function move(d) {
70
96
  moved.current = true;
71
97
  setFocus(d);
@@ -90,7 +116,7 @@ const Calendar = React.forwardRef(function Calendar2(props, ref) {
90
116
  else if (k === "PageDown") n = addMonths(d, e.shiftKey ? 12 : 1);
91
117
  else if (k === "Enter" || k === " ") {
92
118
  e.preventDefault();
93
- if (!disabled(d)) props.onSelect(toISO(d));
119
+ if (!disabled(d)) select(toISO(d));
94
120
  return;
95
121
  }
96
122
  if (n) {
@@ -113,7 +139,7 @@ const Calendar = React.forwardRef(function Calendar2(props, ref) {
113
139
  const yr = focusDate.getFullYear(), base = yr - yr % 12;
114
140
  const years = [];
115
141
  for (let y = base; y < base + 12; y++) years.push(y);
116
- return /* @__PURE__ */ React.createElement("div", { className: "aura-cal", ref: gridMerged }, /* @__PURE__ */ React.createElement("div", { className: "aura-cal__head" }, /* @__PURE__ */ React.createElement(
142
+ return /* @__PURE__ */ React.createElement("div", { className: "aura-cal", ref: gridMerged, key: gridKey }, /* @__PURE__ */ React.createElement("div", { className: "aura-cal__head" }, /* @__PURE__ */ React.createElement(
117
143
  IconButton,
118
144
  {
119
145
  icon: "chevron-left",
@@ -160,7 +186,7 @@ const Calendar = React.forwardRef(function Calendar2(props, ref) {
160
186
  );
161
187
  })));
162
188
  }
163
- return /* @__PURE__ */ React.createElement("div", { className: "aura-cal", ref: gridMerged }, /* @__PURE__ */ React.createElement("div", { className: "aura-cal__head" }, /* @__PURE__ */ React.createElement(
189
+ return /* @__PURE__ */ React.createElement("div", { className: "aura-cal", ref: gridMerged, key: gridKey }, /* @__PURE__ */ React.createElement("div", { className: "aura-cal__head" }, /* @__PURE__ */ React.createElement(
164
190
  IconButton,
165
191
  {
166
192
  icon: "chevron-left",
@@ -222,7 +248,7 @@ const Calendar = React.forwardRef(function Calendar2(props, ref) {
222
248
  type: "button",
223
249
  "data-date": iso,
224
250
  tabIndex: same(d, focusDate) ? 0 : -1,
225
- disabled: dis,
251
+ "aria-disabled": dis || void 0,
226
252
  "aria-label": fmt(tag, { weekday: "long", day: "numeric", month: "long", year: "numeric" }, d),
227
253
  suppressHydrationWarning: true,
228
254
  "aria-current": same(d, today) ? "date" : void 0,
@@ -235,7 +261,7 @@ const Calendar = React.forwardRef(function Calendar2(props, ref) {
235
261
  ),
236
262
  onClick: function() {
237
263
  setFocus(d);
238
- props.onSelect(iso);
264
+ if (!dis) select(iso);
239
265
  },
240
266
  onKeyDown: function(e) {
241
267
  onKey(e, d);
@@ -257,7 +283,7 @@ const Calendar = React.forwardRef(function Calendar2(props, ref) {
257
283
  disabled: disabled(today),
258
284
  onClick: function() {
259
285
  move(today);
260
- props.onSelect(toISO(today));
286
+ select(toISO(today));
261
287
  }
262
288
  },
263
289
  dt.today
@@ -268,16 +294,16 @@ function useCalendarPopover(boxRef) {
268
294
  const pos = React.useState(null), popRef = React.useRef(null), mounted = useMounted();
269
295
  function place() {
270
296
  if (!boxRef.current) return;
271
- const r = boxRef.current.getBoundingClientRect(), H = 380;
272
- const up = window.innerHeight - r.bottom < H && r.top > H;
273
- const left = Math.max(8, Math.min(r.left, window.innerWidth - 320 - 8));
297
+ const el = popRef.current, r = boxRef.current.getBoundingClientRect(), W = el ? el.offsetWidth : 320, H = el ? el.offsetHeight : 380;
298
+ const up = window.innerHeight - r.bottom < H + 12 && r.top > H + 12;
299
+ const left = Math.max(8, Math.min(r.left, window.innerWidth - W - 8));
274
300
  pos[1](up ? { left, bottom: window.innerHeight - r.top + 4 } : { left, top: r.bottom + 4 });
275
301
  }
276
302
  useIsoLayoutEffect(
277
303
  function() {
278
304
  if (open) place();
279
305
  },
280
- [open]
306
+ [open, !!pos[0]]
281
307
  );
282
308
  React.useEffect(
283
309
  function() {
@@ -391,13 +417,30 @@ const DatePicker = React.forwardRef(function DatePicker2(props, ref) {
391
417
  );
392
418
  const boxRef = React.useRef(null), inputRef = React.useRef(null), inputMerged = useMergedRef(ref, inputRef);
393
419
  const pop = useCalendarPopover(boxRef);
420
+ const limits = {
421
+ min: props.min,
422
+ max: props.max,
423
+ today: props.today,
424
+ timeZone: props.timeZone || ctx.timeZone,
425
+ isDateDisabled: props.isDateDisabled
426
+ };
427
+ const bad = React.useState(null);
428
+ React.useEffect(
429
+ function() {
430
+ bad[1](null);
431
+ },
432
+ [st[0]]
433
+ );
394
434
  function commit(text) {
435
+ bad[1](null);
395
436
  if (!text.trim()) {
396
437
  st[1](null);
397
438
  return;
398
439
  }
399
440
  const iso = parseDate(text);
400
- if (iso) st[1](iso);
441
+ if (!iso) return;
442
+ if (allowedDate(iso, limits)) st[1](iso);
443
+ else bad[1](dt.dateUnavailable);
401
444
  }
402
445
  function close() {
403
446
  pop.setOpen(false);
@@ -435,6 +478,7 @@ const DatePicker = React.forwardRef(function DatePicker2(props, ref) {
435
478
  start: st[0],
436
479
  focus: st[0],
437
480
  onSelect: function(iso) {
481
+ bad[1](null);
438
482
  st[1](iso);
439
483
  close();
440
484
  }
@@ -450,7 +494,7 @@ const DatePicker = React.forwardRef(function DatePicker2(props, ref) {
450
494
  dialogId,
451
495
  label: props.label,
452
496
  hint: props.hint,
453
- error: props.error,
497
+ error: bad[0] || props.error,
454
498
  required: props.required,
455
499
  optional: props.optional,
456
500
  disabled: props.disabled,
@@ -464,6 +508,7 @@ const DatePicker = React.forwardRef(function DatePicker2(props, ref) {
464
508
  hasValue: st[0] != null,
465
509
  clearable: props.clearable,
466
510
  onClear: function() {
511
+ bad[1](null);
467
512
  st[1](null);
468
513
  inputRef.current && inputRef.current.focus();
469
514
  },
@@ -490,20 +535,36 @@ const DateRangePicker = React.forwardRef(
490
535
  const draft = React.useState(null);
491
536
  const boxRef = React.useRef(null), inputRef = React.useRef(null), inputMerged = useMergedRef(ref, inputRef);
492
537
  const pop = useCalendarPopover(boxRef);
538
+ const limits = {
539
+ min: props.min,
540
+ max: props.max,
541
+ today: props.today,
542
+ timeZone: props.timeZone || ctx.timeZone,
543
+ isDateDisabled: props.isDateDisabled
544
+ };
493
545
  const o = { locale, calendar };
494
546
  function show(r) {
495
547
  if (!r.start) return "";
496
548
  if (!r.end) return formatDateBase(r.start, o) + " \u2013";
497
549
  return formatDateBase(r.start, o) + " \u2013 " + formatDateBase(r.end, o);
498
550
  }
551
+ const bad = React.useState(null);
552
+ React.useEffect(
553
+ function() {
554
+ bad[1](null);
555
+ },
556
+ [v.start, v.end]
557
+ );
499
558
  function commit(text) {
559
+ bad[1](null);
500
560
  const parts = String(text).split(/\s[–-]\s|\s*–\s*/);
501
561
  if (!text.trim()) {
502
562
  st[1]({ start: null, end: null });
503
563
  return;
504
564
  }
505
565
  const a = parseDate(parts[0]), b = parseDate(parts[1] || "");
506
- if (a && b) st[1](a <= b ? { start: a, end: b } : { start: b, end: a });
566
+ if (a && b && !(allowedDate(a, limits) && allowedDate(b, limits))) bad[1](dt.dateUnavailable);
567
+ else if (a && b) st[1](a <= b ? { start: a, end: b } : { start: b, end: a });
507
568
  }
508
569
  function close() {
509
570
  pop.setOpen(false);
@@ -516,6 +577,7 @@ const DateRangePicker = React.forwardRef(
516
577
  return;
517
578
  }
518
579
  const a = draft[0], b = iso;
580
+ bad[1](null);
519
581
  st[1](a <= b ? { start: a, end: b } : { start: b, end: a });
520
582
  close();
521
583
  }
@@ -566,7 +628,7 @@ const DateRangePicker = React.forwardRef(
566
628
  dialogId,
567
629
  label: props.label,
568
630
  hint: props.hint,
569
- error: props.error,
631
+ error: bad[0] || props.error,
570
632
  required: props.required,
571
633
  optional: props.optional,
572
634
  disabled: props.disabled,
@@ -580,6 +642,7 @@ const DateRangePicker = React.forwardRef(
580
642
  hasValue: v.start != null,
581
643
  clearable: props.clearable,
582
644
  onClear: function() {
645
+ bad[1](null);
583
646
  st[1]({ start: null, end: null });
584
647
  inputRef.current && inputRef.current.focus();
585
648
  },
@@ -6,6 +6,16 @@ const DropdownMenu = React.forwardRef(function DropdownMenu2(props, ref) {
6
6
  const st = React.useState(null), anchor = st[0], setAnchor = st[1];
7
7
  const wrap = React.useRef(null), wrapMerged = useMergedRef(ref, wrap);
8
8
  const child = React.Children.only(props.trigger);
9
+ const menuRef = React.useRef(null), toLast = React.useRef(false);
10
+ React.useEffect(
11
+ function() {
12
+ if (!anchor || !toLast.current || !menuRef.current) return;
13
+ toLast.current = false;
14
+ const list = menuRef.current.querySelectorAll('[role^="menuitem"]:not([disabled])');
15
+ if (list.length) list[list.length - 1].focus();
16
+ },
17
+ [anchor]
18
+ );
9
19
  function toggle(e) {
10
20
  if (child.props.onClick) child.props.onClick(e);
11
21
  const el = wrap.current && (wrap.current.querySelector('button, [role="button"], a') || wrap.current);
@@ -14,12 +24,14 @@ const DropdownMenu = React.forwardRef(function DropdownMenu2(props, ref) {
14
24
  function onKeyDown(e) {
15
25
  if ((e.key === "ArrowDown" || e.key === "ArrowUp") && !anchor) {
16
26
  e.preventDefault();
27
+ toLast.current = e.key === "ArrowUp";
17
28
  setAnchor(wrap.current.querySelector('button, [role="button"], a') || wrap.current);
18
29
  }
19
30
  }
20
31
  return /* @__PURE__ */ React.createElement("span", { ref: wrapMerged, className: "aura-dropdown", onKeyDown }, React.cloneElement(child, { onClick: toggle, "aria-haspopup": "menu", "aria-expanded": anchor ? true : false }), anchor ? /* @__PURE__ */ React.createElement(
21
32
  Menu,
22
33
  {
34
+ ref: menuRef,
23
35
  anchor,
24
36
  label: props.label,
25
37
  items: props.items,
@@ -46,7 +46,23 @@ const FileUpload = React.forwardRef(function FileUpload2(props, ref) {
46
46
  });
47
47
  };
48
48
  }, []);
49
+ React.useEffect(
50
+ function() {
51
+ const el = inputRef.current;
52
+ if (!el || !props.name || typeof DataTransfer === "undefined") return;
53
+ try {
54
+ const dt = new DataTransfer();
55
+ items.forEach(function(i) {
56
+ if (i.file && !i.error) dt.items.add(i.file);
57
+ });
58
+ el.files = dt.files;
59
+ } catch (e) {
60
+ }
61
+ },
62
+ [items, props.name]
63
+ );
49
64
  function thumb(it) {
65
+ if (!it.file && it.url && /^image\//.test(it.type || "")) return it.url;
50
66
  if (!it.file || !/^image\//.test(it.type || "") || typeof URL === "undefined" || !URL.createObjectURL) return null;
51
67
  if (!urls.current[it.id]) urls.current[it.id] = URL.createObjectURL(it.file);
52
68
  return urls.current[it.id];
@@ -152,7 +168,7 @@ const FileUpload = React.forwardRef(function FileUpload2(props, ref) {
152
168
  ),
153
169
  items.length ? /* @__PURE__ */ React.createElement("ul", { className: "aura-upload__list", "aria-live": "polite" }, items.map(function(it) {
154
170
  const src = thumb(it);
155
- return /* @__PURE__ */ React.createElement("li", { key: it.id, className: cx("aura-upload__item", it.error && "is-error") }, src ? /* @__PURE__ */ React.createElement("img", { className: "aura-upload__thumb", src, alt: "" }) : /* @__PURE__ */ React.createElement("span", { className: "aura-upload__thumb is-icon", "aria-hidden": true }, /* @__PURE__ */ React.createElement(Icon, { name: /^image\//.test(it.type || "") ? "image" : "file", size: "md" })), /* @__PURE__ */ React.createElement("span", { className: "aura-upload__meta" }, /* @__PURE__ */ React.createElement("span", { className: "aura-upload__name" }, it.name), /* @__PURE__ */ React.createElement("span", { className: "aura-upload__sub" }, it.error ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.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__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Icon, { name: "circle-check", size: 12 }), formatBytes(it.size)) : formatBytes(it.size)), it.status === "uploading" ? /* @__PURE__ */ React.createElement(
171
+ return /* @__PURE__ */ React.createElement("li", { key: it.id, className: cx("aura-upload__item", it.error && "is-error") }, src ? /* @__PURE__ */ React.createElement("img", { className: "aura-upload__thumb", src, alt: "" }) : /* @__PURE__ */ React.createElement("span", { className: "aura-upload__thumb is-icon", "aria-hidden": true }, /* @__PURE__ */ React.createElement(Icon, { name: /^image\//.test(it.type || "") ? "image" : "file", size: "md" })), /* @__PURE__ */ React.createElement("span", { className: "aura-upload__meta" }, it.url && !it.error ? /* @__PURE__ */ React.createElement("a", { className: "aura-upload__name", href: it.url, target: "_blank", rel: "noreferrer" }, it.name) : /* @__PURE__ */ React.createElement("span", { className: "aura-upload__name" }, it.name), /* @__PURE__ */ React.createElement("span", { className: "aura-upload__sub" }, it.error ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.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__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Icon, { name: "circle-check", size: 12 }), formatBytes(it.size)) : formatBytes(it.size)), it.status === "uploading" ? /* @__PURE__ */ React.createElement(
156
172
  "span",
157
173
  {
158
174
  className: "aura-upload__bar",
@@ -6,15 +6,30 @@ import { useStrings } from "./locale.js";
6
6
  function items(errors) {
7
7
  if (Array.isArray(errors)) return errors;
8
8
  const out = [];
9
- for (const k in errors || {}) {
10
- const e = errors[k];
11
- if (e && e.message) out.push({ field: k, message: e.message });
9
+ function walk(e, path, depth) {
10
+ if (!e || typeof e !== "object" || depth > 8) return;
11
+ const o = e;
12
+ if (o.message) out.push({ field: path, message: o.message });
13
+ for (const k in o) {
14
+ if (k === "ref" || k === "type" || k === "types" || k === "message") continue;
15
+ walk(o[k], path ? path + "." + k : k, depth + 1);
16
+ }
12
17
  }
18
+ walk(errors, "", 0);
13
19
  return out;
14
20
  }
15
21
  function fieldElement(field) {
16
22
  if (typeof document === "undefined") return null;
17
- return document.getElementById(field) || document.querySelector('[name="' + field.replace(/"/g, '\\"') + '"]');
23
+ const byId = document.getElementById(field);
24
+ if (byId) return byId;
25
+ const named = document.querySelector('[name="' + field.replace(/"/g, '\\"') + '"]');
26
+ if (named && named.getAttribute("type") === "hidden") {
27
+ const box = named.closest(".aura-field") || named.parentElement;
28
+ return box && box.querySelector(
29
+ 'input:not([type="hidden"]), [role="combobox"], select, textarea, button:not([tabindex="-1"])'
30
+ ) || named;
31
+ }
32
+ return named;
18
33
  }
19
34
  const FormErrorSummary = React.forwardRef(
20
35
  function FormErrorSummary2(props, ref) {
package/dist/esm/Menu.js CHANGED
@@ -15,11 +15,17 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
15
15
  function() {
16
16
  const a = props.anchor, m = own.current;
17
17
  if (!a || !m) return;
18
- const r = a.getBoundingClientRect(), mh = m.offsetHeight, mw = m.offsetWidth;
19
- let top = r.bottom + 4;
20
- if (top + mh > window.innerHeight - 8 && r.top - mh - 4 > 8) top = r.top - mh - 4;
18
+ const r = a.getBoundingClientRect(), mh = m.scrollHeight, mw = m.offsetWidth, below = window.innerHeight - r.bottom - 12, above = r.top - 12;
19
+ let top = r.bottom + 4, maxHeight;
20
+ if (mh > below) {
21
+ if (mh <= above) top = r.top - mh - 4;
22
+ else if (above > below) {
23
+ maxHeight = above;
24
+ top = r.top - above - 4;
25
+ } else maxHeight = below;
26
+ }
21
27
  const left = Math.max(8, Math.min(r.right - mw, window.innerWidth - mw - 8));
22
- setPos({ top, left });
28
+ setPos({ top, left, maxHeight });
23
29
  },
24
30
  [props.anchor, mounted]
25
31
  );
@@ -63,6 +69,9 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
63
69
  } else if (e.key === "End") {
64
70
  e.preventDefault();
65
71
  list[list.length - 1].focus();
72
+ } else if (e.key === " " && document.activeElement && document.activeElement.tagName === "A") {
73
+ e.preventDefault();
74
+ document.activeElement.click();
66
75
  } else if (e.key === "Escape") {
67
76
  e.preventDefault();
68
77
  props.onClose(true);
@@ -103,9 +112,9 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
103
112
  tabIndex: -1,
104
113
  role: "menuitem",
105
114
  className: cls,
106
- onClick: function() {
115
+ onClick: function(e) {
107
116
  if (it.onSelect) it.onSelect();
108
- props.onClose(false);
117
+ props.onClose(e.detail === 0);
109
118
  }
110
119
  },
111
120
  body
@@ -136,7 +145,11 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
136
145
  "aria-label": props.label,
137
146
  className: "aura-menu",
138
147
  onKeyDown,
139
- style: { top: pos ? pos.top : -9999, left: pos ? pos.left : -9999 }
148
+ style: {
149
+ top: pos ? pos.top : -9999,
150
+ left: pos ? pos.left : -9999,
151
+ maxHeight: pos && pos.maxHeight ? pos.maxHeight : void 0
152
+ }
140
153
  },
141
154
  groupItems(items).map(function(block, bi) {
142
155
  const rendered = block.items.map(function(x) {
@@ -10,7 +10,9 @@ function decimalsOf(n) {
10
10
  return i < 0 ? 0 : s.length - i - 1;
11
11
  }
12
12
  function parse(text) {
13
- const s = text.replace(/[,\s ]/g, "");
13
+ let t = text.trim();
14
+ if (t.indexOf(".") < 0 && /^-?\d+,\d+$/.test(t.replace(/[\s ]/g, "")) && !/,\d{3}$/.test(t)) t = t.replace(",", ".");
15
+ const s = t.replace(/[,\s ]/g, "");
14
16
  if (!/^-?(\d+\.?\d*|\.\d+)$/.test(s)) return NaN;
15
17
  return Number(s);
16
18
  }
@@ -2,7 +2,7 @@
2
2
  import * as React from "react";
3
3
  import { Icon } from "./Icon.js";
4
4
  import { IconButton } from "./IconButton.js";
5
- import { cx, useMaybeControlled } from "./internal.js";
5
+ import { cx, plainClick, useMaybeControlled } from "./internal.js";
6
6
  import { useLinkComponent, useStrings } from "./locale.js";
7
7
  function pageList(page, count, sib) {
8
8
  let out = [], lo = Math.max(2, page - sib), hi = Math.min(count - 1, page + sib);
@@ -47,7 +47,7 @@ const Pagination = React.forwardRef(function Pagination2(props, ref) {
47
47
  {
48
48
  href: link(p),
49
49
  onClick: function(e) {
50
- if (props.onChange) {
50
+ if (props.onChange && plainClick(e)) {
51
51
  e.preventDefault();
52
52
  go(p);
53
53
  }
@@ -89,7 +89,7 @@ const Pagination = React.forwardRef(function Pagination2(props, ref) {
89
89
  "aria-label": label,
90
90
  title: label,
91
91
  onClick: function(e) {
92
- if (props.onChange) {
92
+ if (props.onChange && plainClick(e)) {
93
93
  e.preventDefault();
94
94
  go(p);
95
95
  }
@@ -14,13 +14,20 @@ import {
14
14
  import { useDensity, useStrings } from "./locale.js";
15
15
  import { createPortal } from "react-dom";
16
16
  function position(anchor, pop, placement) {
17
- const r = anchor.getBoundingClientRect(), pw = pop.offsetWidth, ph = pop.offsetHeight, vw = window.innerWidth, vh = window.innerHeight, gap = 6;
17
+ const r = anchor.getBoundingClientRect(), pw = pop.offsetWidth, ph = pop.scrollHeight, vw = window.innerWidth, vh = window.innerHeight, gap = 6;
18
18
  let side = (placement || "bottom-start").split("-")[0], align = (placement || "bottom-start").split("-")[1] || "start";
19
19
  if (side === "bottom" && r.bottom + gap + ph > vh - 8 && r.top - gap - ph > 8) side = "top";
20
20
  else if (side === "top" && r.top - gap - ph < 8 && r.bottom + gap + ph < vh - 8) side = "bottom";
21
- const top = side === "top" ? r.top - gap - ph : r.bottom + gap;
21
+ const below = vh - r.bottom - gap - 8, above = r.top - gap - 8;
22
+ let maxHeight;
23
+ if (ph > below && ph > above) {
24
+ side = above > below ? "top" : "bottom";
25
+ maxHeight = Math.max(120, side === "top" ? above : below);
26
+ }
27
+ const h = maxHeight ? Math.min(ph, maxHeight) : ph;
28
+ const top = side === "top" ? r.top - gap - h : r.bottom + gap;
22
29
  const left = align === "end" ? r.right - pw : align === "center" ? r.left + r.width / 2 - pw / 2 : r.left;
23
- return { top: Math.max(8, top), left: Math.max(8, Math.min(left, vw - pw - 8)), side };
30
+ return { top: Math.max(8, top), left: Math.max(8, Math.min(left, vw - pw - 8)), side, maxHeight };
24
31
  }
25
32
  const Popover = React.forwardRef(function Popover2(props, ref) {
26
33
  const t = useStrings();
@@ -30,6 +37,7 @@ const Popover = React.forwardRef(function Popover2(props, ref) {
30
37
  const open = !!st[0];
31
38
  const wrap = React.useRef(null), pop = React.useRef(null), popMerged = useMergedRef(ref, pop);
32
39
  const pos = React.useState(null), mounted = useMounted();
40
+ const inTree = React.useRef(false);
33
41
  function trigger() {
34
42
  return wrap.current && (wrap.current.querySelector('button, [role="button"], a, input') || wrap.current.firstElementChild);
35
43
  }
@@ -63,13 +71,19 @@ const Popover = React.forwardRef(function Popover2(props, ref) {
63
71
  const f = pop.current.querySelector("[data-autofocus]") || pop.current.querySelector(FOCUSABLE);
64
72
  (f || pop.current).focus();
65
73
  }
74
+ let timer;
66
75
  function outside(e) {
67
76
  if (pop.current && pop.current.contains(e.target)) return;
68
77
  if (wrap.current && wrap.current.contains(e.target)) return;
69
- close(false);
78
+ inTree.current = false;
79
+ clearTimeout(timer);
80
+ timer = setTimeout(function() {
81
+ if (!inTree.current) close(false);
82
+ }, 0);
70
83
  }
71
84
  document.addEventListener("pointerdown", outside, true);
72
85
  return function() {
86
+ clearTimeout(timer);
73
87
  document.removeEventListener("pointerdown", outside, true);
74
88
  };
75
89
  },
@@ -88,9 +102,16 @@ const Popover = React.forwardRef(function Popover2(props, ref) {
88
102
  "aria-label": props.title ? void 0 : props.label,
89
103
  "aria-labelledby": props.title ? id + "-title" : void 0,
90
104
  tabIndex: -1,
105
+ onPointerDownCapture: function() {
106
+ inTree.current = true;
107
+ },
91
108
  className: cx("aura-popover", pos[0] && "is-" + pos[0].side, props.className),
92
109
  style: Object.assign(
93
- { top: pos[0] ? pos[0].top : -9999, left: pos[0] ? pos[0].left : -9999 },
110
+ {
111
+ top: pos[0] ? pos[0].top : -9999,
112
+ left: pos[0] ? pos[0].left : -9999,
113
+ maxHeight: pos[0] && pos[0].maxHeight ? pos[0].maxHeight : void 0
114
+ },
94
115
  props.width ? { width: props.width } : null
95
116
  ),
96
117
  onKeyDown: function(e) {
@@ -11,10 +11,22 @@ const RadioGroup = React.forwardRef(function RadioGroup2(props, ref) {
11
11
  props.onChange
12
12
  );
13
13
  const name = props.name || id;
14
+ const setRef = React.useCallback(
15
+ function(el) {
16
+ if (el)
17
+ el.focus = function(opts) {
18
+ const r = el.querySelector('input[type="radio"]:checked') || el.querySelector('input[type="radio"]:not(:disabled)');
19
+ if (r) r.focus(opts);
20
+ };
21
+ if (typeof ref === "function") ref(el);
22
+ else if (ref) ref.current = el;
23
+ },
24
+ [ref]
25
+ );
14
26
  return /* @__PURE__ */ React.createElement(
15
27
  "fieldset",
16
28
  {
17
- ref,
29
+ ref: setRef,
18
30
  className: cx("aura-field aura-radio-group", props.error && "is-invalid", props.className),
19
31
  "aria-describedby": describedBy(id, props),
20
32
  "aria-invalid": props.error ? true : void 0,
@@ -31,6 +43,7 @@ const RadioGroup = React.forwardRef(function RadioGroup2(props, ref) {
31
43
  name,
32
44
  value: v.value,
33
45
  disabled: v.disabled,
46
+ required: props.required || void 0,
34
47
  checked: st[0] === v.value,
35
48
  onChange: function() {
36
49
  st[1](v.value);
@@ -3,7 +3,7 @@ import * as React from "react";
3
3
  import { createPortal } from "react-dom";
4
4
  import { Icon } from "./Icon.js";
5
5
  import { IconButton } from "./IconButton.js";
6
- import { cx, uid, useMaybeControlled } from "./internal.js";
6
+ import { cx, plainClick, uid, useMaybeControlled } from "./internal.js";
7
7
  import { useLinkComponent, useStrings } from "./locale.js";
8
8
  function contains(it, id) {
9
9
  return !!id && !!it.children && it.children.some(function(c) {
@@ -154,6 +154,7 @@ const SideNav = React.forwardRef(function SideNav2(props, ref) {
154
154
  style: depth ? { ["--aura-nav-depth"]: depth } : void 0,
155
155
  onClick: function(e) {
156
156
  if (!it.href) e.preventDefault();
157
+ else if (!plainClick(e)) return;
157
158
  st[1](it.id);
158
159
  }
159
160
  };
@@ -34,7 +34,7 @@ const Switch = React.forwardRef(function Switch2(props, ref) {
34
34
  },
35
35
  /* @__PURE__ */ React.createElement("span", { className: "aura-switch__thumb" })
36
36
  ),
37
- props.label ? /* @__PURE__ */ React.createElement("span", { className: "aura-choice__text" }, /* @__PURE__ */ React.createElement("label", { className: "aura-choice__label", id: id + "-label", htmlFor: id }, props.label), props.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-choice__desc", id: id + "-desc" }, props.description) : null) : null
37
+ props.label ? /* @__PURE__ */ React.createElement("span", { className: "aura-choice__text" }, /* @__PURE__ */ React.createElement("label", { className: "aura-choice__label", id: id + "-label", htmlFor: id }, props.label), props.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-choice__desc", id: id + "-desc" }, props.description) : null) : props.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-sr-only", id: id + "-desc" }, props.description) : null
38
38
  );
39
39
  });
40
40
  export {
package/dist/esm/Table.js CHANGED
@@ -26,8 +26,9 @@ const Table = React.forwardRef(function Table2(props, ref) {
26
26
  className: "aura-tbl-wrap",
27
27
  "data-density": density,
28
28
  tabIndex: scrolls ? 0 : void 0,
29
- role: scrolls ? "region" : void 0,
30
- "aria-labelledby": scrolls && caption != null ? capId : void 0
29
+ role: scrolls && (caption != null || rest["aria-label"]) ? "region" : void 0,
30
+ "aria-labelledby": scrolls && caption != null ? capId : void 0,
31
+ "aria-label": scrolls && caption == null ? rest["aria-label"] : void 0
31
32
  },
32
33
  /* @__PURE__ */ React.createElement("table", { ref, className: cx("aura-tbl", className), ...rest }, caption != null ? /* @__PURE__ */ React.createElement("caption", { id: capId, className: cx("aura-tbl__caption", captionHidden && "aura-sr-only") }, caption) : null, children)
33
34
  );
package/dist/esm/Tabs.js CHANGED
@@ -5,19 +5,23 @@ import { cx, uid, useMaybeControlled } from "./internal.js";
5
5
  import { useLinkComponent } from "./locale.js";
6
6
  const Tabs = React.forwardRef(function Tabs2(props, ref) {
7
7
  const items = props.tabs || [], base = uid();
8
+ const asLinks = items.length > 0 && items.every(function(t) {
9
+ return !!t.href;
10
+ });
11
+ const firstOn = items.filter(function(t) {
12
+ return !t.disabled;
13
+ })[0];
8
14
  const st = useMaybeControlled(
9
15
  props.value,
10
- props.defaultValue || items[0] && items[0].id,
16
+ props.defaultValue || (asLinks ? void 0 : firstOn && firstOn.id),
11
17
  props.onChange
12
18
  );
19
+ const cur = asLinks && "value" in props ? props.value : st[0];
13
20
  const refs = React.useRef({});
14
21
  const current = items.filter(function(t) {
15
- return t.id === st[0];
16
- })[0] || items[0];
22
+ return t.id === cur;
23
+ })[0] || firstOn || items[0];
17
24
  const Link = useLinkComponent(props.linkComponent);
18
- const asLinks = items.length > 0 && items.every(function(t) {
19
- return !!t.href;
20
- });
21
25
  if (asLinks)
22
26
  return /* @__PURE__ */ React.createElement(
23
27
  "nav",
@@ -27,7 +31,7 @@ const Tabs = React.forwardRef(function Tabs2(props, ref) {
27
31
  className: cx("aura-tabs aura-tabs--links", props.className)
28
32
  },
29
33
  /* @__PURE__ */ React.createElement("div", { className: "aura-tabs__list" }, items.map(function(t) {
30
- const on = t.id === st[0];
34
+ const on = t.id === cur;
31
35
  const inner = [
32
36
  t.icon ? /* @__PURE__ */ React.createElement(Icon, { key: "i", name: t.icon }) : null,
33
37
  t.label,
@@ -28,8 +28,8 @@ const TextField = React.forwardRef(function TextField2(props, ref) {
28
28
  className: "aura-input__control",
29
29
  placeholder: props.placeholder,
30
30
  required: props.required,
31
- "aria-invalid": props.error ? true : void 0,
32
- "aria-describedby": describedBy(id, props)
31
+ "aria-invalid": props.error ? true : rest["aria-invalid"],
32
+ "aria-describedby": [describedBy(id, props), rest["aria-describedby"]].filter(Boolean).join(" ") || void 0
33
33
  }
34
34
  ), props.suffix ? /* @__PURE__ */ React.createElement("span", { className: "aura-input__suffix" }, props.suffix) : null)
35
35
  );