@jirawatpyk/aura-react 5.10.1 → 5.12.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.
@@ -50,7 +50,7 @@ const Checkbox = React.forwardRef(function Checkbox2(props, ref) {
50
50
  checked: on,
51
51
  tabIndex: props.tabIndex,
52
52
  disabled: props.disabled,
53
- "aria-describedby": descId,
53
+ "aria-describedby": cx(props["aria-describedby"], descId) || void 0,
54
54
  "aria-label": labelled ? void 0 : props.label,
55
55
  onChange: function(e) {
56
56
  st[1](e.target.checked);
@@ -2,7 +2,7 @@
2
2
  import * as React from "react";
3
3
  import { createPortal } from "react-dom";
4
4
  import { useStrings, useLinkComponent } from "./locale.js";
5
- import { cx, useMaybeControlled, compare, useIsoLayoutEffect, useMergedRef } from "./internal.js";
5
+ import { cx, devWarnOnce, useMaybeControlled, compare, useIsoLayoutEffect, useMergedRef } from "./internal.js";
6
6
  import { Icon } from "./Icon.js";
7
7
  import { IconButton } from "./IconButton.js";
8
8
  import { Checkbox } from "./Checkbox.js";
@@ -416,6 +416,12 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
416
416
  return rowHref ? /* @__PURE__ */ React.createElement(Link, { href: rowHref(r), className: "aura-table__row-link" }, content) : content;
417
417
  }
418
418
  const height = props.height || 0;
419
+ const autoRows = props.rowHeight === "auto" && !height;
420
+ if (props.rowHeight === "auto" && height)
421
+ devWarnOnce(
422
+ "table-row-height-auto",
423
+ 'DataTable `rowHeight="auto"` is ignored with `height`: virtualized rows are one fixed height. Drop `height` (and paginate) to let rows grow.'
424
+ );
419
425
  const virtual = !!height && !loading && pageRows.length > 0 && !stacked;
420
426
  const stickyTotal = !!(props.footer && props.stickyFooter && rows.length && !loading);
421
427
  const bodyH = Math.max(ROW_H, height - ROW_H - (stickyTotal ? ROW_H : 0));
@@ -940,6 +946,7 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
940
946
  }
941
947
  function rowCells(r, i, k, isSel) {
942
948
  const ri = i + 1;
949
+ const selLabel = selectable && canSelect(r) ? props.rowSelectLabel && props.rowSelectLabel(r) || t.selectRow(k) : void 0;
943
950
  const cells = [/* @__PURE__ */ React.createElement("span", { key: "__gl", className: "aura-table__gutter", "aria-hidden": true })];
944
951
  if (selectable)
945
952
  cells.push(
@@ -952,7 +959,7 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
952
959
  className: cx("aura-table__sel", nPinned && "is-pinned"),
953
960
  tabIndex: tabFor(ri, 0),
954
961
  "data-rc": ri + ":0",
955
- "aria-label": canSelect(r) ? t.selectRow(k) : props.rowSelectDisabledLabel ? props.rowSelectDisabledLabel(r) : void 0,
962
+ "aria-label": canSelect(r) ? selLabel : props.rowSelectDisabledLabel ? props.rowSelectDisabledLabel(r) : void 0,
956
963
  onFocus: function(e) {
957
964
  if (e.target === e.currentTarget) activeState[1]({ r: ri, c: 0 });
958
965
  }
@@ -963,7 +970,7 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
963
970
  hideLabel: true,
964
971
  checked: isSel,
965
972
  tabIndex: -1,
966
- label: t.selectRow(k),
973
+ label: selLabel,
967
974
  onChange: function(on) {
968
975
  toggle(k, on);
969
976
  }
@@ -984,6 +991,7 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
984
991
  "data-rc": ri + ":" + ci,
985
992
  className: cx(
986
993
  "aura-table__td",
994
+ autoRows && "aura-table__td--auto",
987
995
  c.mono && "aura-table__mono",
988
996
  c.align === "end" && "is-end",
989
997
  pin && "is-pinned",
@@ -995,7 +1003,7 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
995
1003
  if (activeState[0].r !== ri || activeState[0].c !== ci) activeState[1]({ r: ri, c: ci });
996
1004
  }
997
1005
  },
998
- j === 0 ? rowLinkWrap(r, cellContent(c, r)) : cellContent(c, r)
1006
+ autoRows ? /* @__PURE__ */ React.createElement("span", { className: "aura-table__cell" }, j === 0 ? rowLinkWrap(r, cellContent(c, r)) : cellContent(c, r)) : j === 0 ? rowLinkWrap(r, cellContent(c, r)) : cellContent(c, r)
999
1007
  )
1000
1008
  );
1001
1009
  });
@@ -1074,6 +1082,7 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
1074
1082
  "aria-selected": selectable && canSelect(r) ? isSel : void 0,
1075
1083
  className: cx(
1076
1084
  "aura-table__row",
1085
+ autoRows && "aura-table__row--auto",
1077
1086
  isSel && "is-selected",
1078
1087
  (props.onRowActivate || rowHref) && "is-actionable"
1079
1088
  ),
@@ -55,7 +55,7 @@ const FilterBar = React.forwardRef(function FilterBar2(props, ref) {
55
55
  ref,
56
56
  role: "region",
57
57
  "aria-label": props.label || t.filters,
58
- className: cx("aura-filterbar", props.className)
58
+ className: cx("aura-filterbar", props.searchGrow && "aura-filterbar--grow", props.className)
59
59
  },
60
60
  /* @__PURE__ */ React.createElement("div", { className: "aura-filterbar__row" }, hasSearch ? /* @__PURE__ */ React.createElement("div", { className: "aura-input has-icon aura-filterbar__search" }, /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconSearch, null), className: "aura-input__icon" }), /* @__PURE__ */ React.createElement(
61
61
  "input",
@@ -0,0 +1,17 @@
1
+ 'use client';
2
+ import * as React from "react";
3
+ import { Select, FILTER_KEY } from "./Select.js";
4
+ import { omit } from "./internal.js";
5
+ const FilterSelect = React.forwardRef(function FilterSelect2(props, ref) {
6
+ const onChange = props.onChange;
7
+ const pass = Object.assign(omit(props, ["label", "allLabel", "onChange"]), {
8
+ [FILTER_KEY]: { name: props.label, allLabel: props.allLabel },
9
+ onChange: onChange ? function(e) {
10
+ onChange(e.target.value);
11
+ } : void 0
12
+ });
13
+ return /* @__PURE__ */ React.createElement(Select, { ...pass, ref });
14
+ });
15
+ export {
16
+ FilterSelect
17
+ };
@@ -7,6 +7,7 @@ import { useDensity } from "./locale.js";
7
7
  import { cx, devWarnOnce, omit, uid, useIsoLayoutEffect, useMergedRef, useMounted } from "./internal.js";
8
8
  import { IconCheck, IconChevronDown } from "./icons.js";
9
9
  const h = React.createElement;
10
+ const FILTER_KEY = "__auraFilter";
10
11
  const FORM_KEYS = ["name", "form", "value", "defaultValue", "disabled", "autoComplete", "onInput", "onInvalid"];
11
12
  const HANDLED_KEYS = FORM_KEYS.concat([
12
13
  "onChange",
@@ -40,20 +41,49 @@ function readItems(el) {
40
41
  };
41
42
  });
42
43
  }
43
- function initialLabel(props) {
44
+ function textOf(n) {
45
+ if (n == null || typeof n === "boolean") return "";
46
+ if (typeof n === "string" || typeof n === "number") return String(n);
47
+ if (Array.isArray(n)) return n.map(textOf).join("");
48
+ return React.isValidElement(n) ? textOf(n.props.children) : "";
49
+ }
50
+ function childOptions(children, off) {
51
+ const out = [];
52
+ React.Children.forEach(children, function(c) {
53
+ if (!React.isValidElement(c)) return;
54
+ const p = c.props;
55
+ if (c.type === "optgroup" || c.type === React.Fragment)
56
+ out.push.apply(out, childOptions(p.children, off || c.type === "optgroup" && !!p.disabled));
57
+ else if (c.type === "option") {
58
+ const label = textOf(p.children);
59
+ out.push({ value: p.value != null ? String(p.value) : label, label, disabled: off || !!p.disabled });
60
+ }
61
+ });
62
+ return out;
63
+ }
64
+ function initialLabel(props, filter) {
44
65
  const opts = (props.options || []).map(function(o) {
45
66
  return typeof o === "object" ? o : { value: o, label: o };
46
- });
47
- const v = props.value !== void 0 && props.value !== null ? String(props.value) : props.defaultValue !== void 0 && props.defaultValue !== null ? String(props.defaultValue) : props.placeholder ? "" : opts[0] ? opts[0].value : "";
67
+ }).concat(childOptions(props.children));
68
+ const first = opts.filter(function(o) {
69
+ return !o.disabled;
70
+ })[0];
71
+ const v = props.value !== void 0 && props.value !== null ? String(props.value) : props.defaultValue !== void 0 && props.defaultValue !== null ? String(props.defaultValue) : props.placeholder ? "" : first ? first.value : "";
48
72
  const hit = opts.filter(function(o) {
49
73
  return o.value === v;
50
74
  })[0];
51
- if (hit) return { label: hit.label, empty: false };
52
- return { label: props.placeholder || "", empty: true };
75
+ if (hit)
76
+ return {
77
+ label: filter && filter.allLabel && opts[0] && hit.value === opts[0].value ? filter.allLabel : hit.label,
78
+ full: hit.label,
79
+ empty: false
80
+ };
81
+ return { label: props.placeholder || "", full: props.placeholder || "", empty: true };
53
82
  }
54
83
  const Select = React.forwardRef(function Select2(props, ref) {
55
84
  const auto = uid(), id = props.id || auto;
56
- const rest = omit(props, FIELD_KEYS.concat(["children"]));
85
+ const filter = props[FILTER_KEY];
86
+ const rest = omit(props, FIELD_KEYS.concat(["children", FILTER_KEY]));
57
87
  const opts = (props.options || []).map(function(o) {
58
88
  const v = typeof o === "object" ? o : { value: o, label: o };
59
89
  return /* @__PURE__ */ React.createElement("option", { key: v.value, value: v.value, disabled: v.disabled }, v.label);
@@ -68,7 +98,7 @@ const Select = React.forwardRef(function Select2(props, ref) {
68
98
  const live = !native && mounted;
69
99
  const density = useDensity();
70
100
  React.useEffect(function() {
71
- if (props.label || props["aria-label"] || props["aria-labelledby"] || props.title) return;
101
+ if (filter || props.label || props["aria-label"] || props["aria-labelledby"] || props.title) return;
72
102
  if (document.querySelector('label[for="' + id.replace(/["\\]/g, "\\$&") + '"]')) return;
73
103
  devWarnOnce(
74
104
  "select-name",
@@ -77,20 +107,24 @@ const Select = React.forwardRef(function Select2(props, ref) {
77
107
  }, []);
78
108
  const selRef = React.useRef(null), trigRef = React.useRef(null), listRef = React.useRef(null);
79
109
  const shown = React.useState(function() {
80
- return initialLabel(props);
110
+ return initialLabel(props, filter);
81
111
  });
82
112
  const openState = React.useState(false), open = openState[0];
83
113
  const activeState = React.useState(-1), active = activeState[0];
84
114
  const items = React.useState([]);
85
115
  const pos = React.useState(null);
86
116
  const typed = React.useRef({ text: "", at: 0 });
117
+ const allLabel = React.useRef(void 0);
118
+ allLabel.current = filter ? filter.allLabel : void 0;
87
119
  const sync = React.useCallback(function() {
88
120
  const el = selRef.current;
89
121
  if (!el) return;
90
122
  const o = el.selectedIndex >= 0 ? el.options[el.selectedIndex] : null;
91
- const next = o ? { label: o.textContent || "", empty: o.value === "" } : { label: "", empty: true };
123
+ const text = o ? o.textContent || "" : "";
124
+ const short = allLabel.current && el.selectedIndex === 0 ? allLabel.current : text;
125
+ const next = o ? { label: short, full: text, empty: o.value === "" } : { label: "", full: "", empty: true };
92
126
  shown[1](function(cur) {
93
- return cur.label === next.label && cur.empty === next.empty ? cur : next;
127
+ return cur.label === next.label && cur.full === next.full && cur.empty === next.empty ? cur : next;
94
128
  });
95
129
  }, []);
96
130
  const hook = React.useCallback(
@@ -281,12 +315,16 @@ const Select = React.forwardRef(function Select2(props, ref) {
281
315
  const t = trigRef.current && trigRef.current.parentElement;
282
316
  if (!t) return;
283
317
  const r = t.getBoundingClientRect(), below = window.innerHeight - r.bottom - 8, above = r.top - 8, want = listRef.current ? listRef.current.scrollHeight : 320, up = below < Math.min(want, 200) && above > below;
318
+ const lw = listRef.current ? Math.max(r.width, listRef.current.offsetWidth) : r.width;
319
+ const rtl = getComputedStyle(t).direction === "rtl";
320
+ const left = lw <= r.width ? r.left : Math.max(8, Math.min(rtl ? r.right - lw : r.left, window.innerWidth - lw - 8));
284
321
  pos[1]({
285
- left: r.left,
322
+ left,
286
323
  width: r.width,
287
324
  top: up ? void 0 : r.bottom + 4,
288
325
  bottom: up ? window.innerHeight - r.top + 4 : void 0,
289
- maxHeight: Math.min(320, (up ? above : below) - 4)
326
+ maxHeight: Math.min(320, (up ? above : below) - 4),
327
+ rtl
290
328
  });
291
329
  }
292
330
  place();
@@ -319,7 +357,7 @@ const Select = React.forwardRef(function Select2(props, ref) {
319
357
  [open, active, pos[0] != null]
320
358
  );
321
359
  const described = [describedBy(id, props), rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
322
- const field = (child) => /* @__PURE__ */ React.createElement(
360
+ const field = (child) => filter ? child : /* @__PURE__ */ React.createElement(
323
361
  Field,
324
362
  {
325
363
  id,
@@ -334,6 +372,21 @@ const Select = React.forwardRef(function Select2(props, ref) {
334
372
  },
335
373
  child
336
374
  );
375
+ const nameId = id + "-name";
376
+ const face = filter ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { id: nameId, className: "aura-filterselect__name", "aria-hidden": true }, filter.name), /* @__PURE__ */ React.createElement("span", { className: "aura-filterselect__value", "aria-hidden": shown[0].label !== shown[0].full || void 0 }, shown[0].label || " "), shown[0].label !== shown[0].full ? /* @__PURE__ */ React.createElement("span", { className: "aura-sr-only" }, shown[0].full) : null, /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconChevronDown, null), className: "aura-filterselect__chevron" })) : null;
377
+ if (!live && filter)
378
+ return /* @__PURE__ */ React.createElement("div", { className: cx("aura-filterselect", props.disabled && "is-disabled", props.className) }, /* @__PURE__ */ React.createElement("span", { className: "aura-filterselect__face", "aria-hidden": true, style: props.style }, face), h(
379
+ "select",
380
+ Object.assign(extra, omit(rest, ["style"]), {
381
+ ref: selMerged,
382
+ id,
383
+ className: "aura-filterselect__native",
384
+ "aria-label": rest["aria-label"] || (rest["aria-labelledby"] ? void 0 : filter.name),
385
+ "aria-describedby": described
386
+ }),
387
+ opts,
388
+ props.children
389
+ ));
337
390
  if (!live)
338
391
  return field(
339
392
  /* @__PURE__ */ React.createElement("div", { className: cx("aura-input aura-select", props.icon && "has-icon") }, props.icon ? /* @__PURE__ */ React.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null, h(
@@ -354,7 +407,7 @@ const Select = React.forwardRef(function Select2(props, ref) {
354
407
  const optId = function(i) {
355
408
  return id + "-opt-" + i;
356
409
  };
357
- const labelledBy = rest["aria-labelledby"] || (props.label ? labelId : void 0);
410
+ const labelledBy = rest["aria-labelledby"] || (filter && !rest["aria-label"] ? nameId : props.label ? labelId : void 0);
358
411
  const selIndex = selRef.current ? selRef.current.selectedIndex : -1;
359
412
  const option = function(it) {
360
413
  const sel = selIndex === it.index;
@@ -400,6 +453,7 @@ const Select = React.forwardRef(function Select2(props, ref) {
400
453
  {
401
454
  ref: listRef,
402
455
  "data-density": density,
456
+ dir: pos[0] ? pos[0].rtl ? "rtl" : "ltr" : void 0,
403
457
  className: "aura-combo__popover aura-select__popover",
404
458
  style: pos[0] ? {
405
459
  left: pos[0].left,
@@ -432,10 +486,17 @@ const Select = React.forwardRef(function Select2(props, ref) {
432
486
  /* @__PURE__ */ React.createElement(
433
487
  "div",
434
488
  {
435
- className: cx("aura-input aura-select aura-select--custom", props.icon && "has-icon", open && "is-open"),
489
+ className: cx(
490
+ filter ? "aura-filterselect" : "aura-input",
491
+ "aura-select aura-select--custom",
492
+ props.icon && "has-icon",
493
+ open && "is-open",
494
+ filter && props.disabled && "is-disabled",
495
+ filter && props.className
496
+ ),
436
497
  "data-invalid": props.error ? "" : void 0
437
498
  },
438
- props.icon ? /* @__PURE__ */ React.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null,
499
+ props.icon && !filter ? /* @__PURE__ */ React.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null,
439
500
  h(
440
501
  "select",
441
502
  Object.assign(extra, pick(rest, FORM_KEYS), {
@@ -469,7 +530,7 @@ const Select = React.forwardRef(function Select2(props, ref) {
469
530
  type: "button",
470
531
  role: "combobox",
471
532
  form: id + "-no-form",
472
- className: "aura-input__control aura-select__trigger",
533
+ className: filter ? "aura-select__trigger aura-filterselect__face" : "aura-input__control aura-select__trigger",
473
534
  disabled: props.disabled,
474
535
  tabIndex: rest.tabIndex,
475
536
  "aria-haspopup": "listbox",
@@ -478,7 +539,7 @@ const Select = React.forwardRef(function Select2(props, ref) {
478
539
  "aria-activedescendant": open && active >= 0 ? optId(active) : void 0,
479
540
  id,
480
541
  "aria-label": rest["aria-label"],
481
- "aria-labelledby": rest["aria-labelledby"],
542
+ "aria-labelledby": rest["aria-labelledby"] || (filter && !rest["aria-label"] ? nameId : void 0),
482
543
  "aria-required": props.required || void 0,
483
544
  "aria-invalid": props.error ? true : void 0,
484
545
  "aria-describedby": described,
@@ -501,13 +562,14 @@ const Select = React.forwardRef(function Select2(props, ref) {
501
562
  if (selRef.current) selRef.current.dispatchEvent(new FocusEvent("focusout", { bubbles: true }));
502
563
  }
503
564
  },
504
- /* @__PURE__ */ React.createElement("span", { id: id + "-value", className: cx("aura-select__value", shown[0].empty && "is-placeholder") }, shown[0].label || " ")
565
+ face || /* @__PURE__ */ React.createElement("span", { id: id + "-value", className: cx("aura-select__value", shown[0].empty && "is-placeholder") }, shown[0].label || " ")
505
566
  ),
506
- /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconChevronDown, null), className: "aura-select__chevron" }),
567
+ filter ? null : /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconChevronDown, null), className: "aura-select__chevron" }),
507
568
  popup
508
569
  )
509
570
  );
510
571
  });
511
572
  export {
573
+ FILTER_KEY,
512
574
  Select
513
575
  };
package/dist/esm/index.js CHANGED
@@ -10,6 +10,7 @@ import { Field } from "./Field.js";
10
10
  import { TextField } from "./TextField.js";
11
11
  import { Textarea } from "./Textarea.js";
12
12
  import { Select } from "./Select.js";
13
+ import { FilterSelect } from "./FilterSelect.js";
13
14
  import { RadioGroup } from "./RadioGroup.js";
14
15
  import { Switch } from "./Switch.js";
15
16
  import { Combobox, defaultFilter } from "./Combobox.js";
@@ -87,6 +88,7 @@ export {
87
88
  Field,
88
89
  FileUpload,
89
90
  FilterBar,
91
+ FilterSelect,
90
92
  FormErrorSummary,
91
93
  Grid,
92
94
  ICONS,
package/dist/index.d.cts CHANGED
@@ -129,6 +129,9 @@ export interface DataTableProps<Row extends Record<string, any> = Record<string,
129
129
  isRowSelectable?: RowCallback<boolean, Row> | undefined;
130
130
  /** Accessible name for a rejected row's empty cell, e.g. `(r) => 'Not awaiting review'` (5.6). */
131
131
  rowSelectDisabledLabel?: RowCallback<string, Row> | undefined;
132
+ /** Accessible name for a row's checkbox (and its cell), e.g. `(r) => 'Select ' + r.company` (5.11). Default: the
133
+ * `selectRow` string with the row key ("Select M-102"), also used when this returns an empty string. */
134
+ rowSelectLabel?: RowCallback<string, Row> | undefined;
132
135
  /** Controlled selection (row keys); pair with onSelectionChange. */
133
136
  selected?: Array<string | number> | undefined;
134
137
  /** Initial selection when uncontrolled. */
@@ -175,6 +178,10 @@ export interface DataTableProps<Row extends Record<string, any> = Record<string,
175
178
  onRowActivate?: RowCallback<void, Row> | undefined;
176
179
  /** Fixed height in px: sticky header, and only the rows in view are rendered. */
177
180
  height?: number | undefined;
181
+ /** `'auto'` (5.11): rows grow to fit content that wraps (two badges, a long name); cells stay vertically centred and
182
+ * rows keep at least the density's row height. Needs every row in the DOM, so it is ignored with `height`
183
+ * (virtualized rows are one fixed height). Default: one fixed-height line per row, long text ends in an ellipsis. */
184
+ rowHeight?: "auto" | undefined;
178
185
  /** Column menu on every header, drag-to-reorder, and the show/hide columns button. */
179
186
  columnControls?: boolean | undefined;
180
187
  /** Set false to keep column controls but turn off reordering. */
@@ -256,7 +263,7 @@ export interface CheckboxProps extends Omit<React$1.InputHTMLAttributes<HTMLInpu
256
263
  hideLabel?: boolean | undefined;
257
264
  /** The visible label beside the box. */
258
265
  children?: React$1.ReactNode | undefined;
259
- /** Second line under the visible label (needs an id to be linked). */
266
+ /** Second line under the visible label, linked by `aria-describedby` after any id you pass there (5.11). */
260
267
  description?: React$1.ReactNode | undefined;
261
268
  onChange?: ((checked: boolean) => void) | undefined;
262
269
  disabled?: boolean | undefined;
@@ -394,6 +401,9 @@ export interface FilterBarProps {
394
401
  actions?: React$1.ReactNode | undefined;
395
402
  /** Accessible name of the region. Default "Filters". */
396
403
  label?: string | undefined;
404
+ /** The search takes all the room the controls leave in its row, instead of stopping at 360px with the count and
405
+ * actions pushed to the end (5.12). */
406
+ searchGrow?: boolean | undefined;
397
407
  className?: string | undefined;
398
408
  }
399
409
  /** One command in the palette. */
@@ -460,6 +470,20 @@ export interface SelectProps extends Omit<FieldProps, "label">, Omit<React$1.Sel
460
470
  placeholder?: string | undefined;
461
471
  icon?: IconInput | undefined;
462
472
  }
473
+ /** A compact filter for FilterBar (5.12): "Status All ▾", as wide as its words. */
474
+ export interface FilterSelectProps extends Omit<React$1.SelectHTMLAttributes<HTMLSelectElement>, "onChange" | "value" | "defaultValue" | "multiple" | "size" | "placeholder" | "required" | "children"> {
475
+ /** The filter's name: shown first on the face ("Status") and its accessible name. */
476
+ label: string;
477
+ /** The choices; the first is the "all" choice. Or pass `<option>` children. */
478
+ options?: SelectOption[] | undefined;
479
+ children?: React$1.ReactNode | undefined;
480
+ value?: string | undefined;
481
+ defaultValue?: string | undefined;
482
+ onChange?: ((value: string) => void) | undefined;
483
+ /** A shorter word the face shows while the first option is chosen ("All" for "All statuses"). The list and screen
484
+ * readers keep the option's own text. */
485
+ allLabel?: string | undefined;
486
+ }
463
487
  export type ChoiceOption = string | {
464
488
  value: string;
465
489
  label: string;
@@ -1331,6 +1355,9 @@ export declare const TextField: React$1.ForwardRefExoticComponent<TextFieldProps
1331
1355
  export declare const Textarea: React$1.ForwardRefExoticComponent<TextareaProps & React$1.RefAttributes<HTMLTextAreaElement>>;
1332
1356
  /** A select field: a button that opens an AURA list (5.3), over a real <select> that keeps forms, refs and events. */
1333
1357
  export declare const Select: React$1.ForwardRefExoticComponent<SelectProps & React$1.RefAttributes<HTMLSelectElement>>;
1358
+ /** A compact filter for FilterBar (5.12): a small button reading "Status All ▾" that opens Select's list. The name
1359
+ * is the accessible name, the chosen option its value; forms, refs and the keyboard work as in Select. */
1360
+ export declare const FilterSelect: React$1.ForwardRefExoticComponent<FilterSelectProps & React$1.RefAttributes<HTMLSelectElement>>;
1334
1361
  export declare const RadioGroup: React$1.ForwardRefExoticComponent<RadioGroupProps & React$1.RefAttributes<HTMLFieldSetElement>>;
1335
1362
  export declare const Switch: React$1.ForwardRefExoticComponent<SwitchProps & React$1.RefAttributes<HTMLButtonElement>>;
1336
1363
  /** One value, or with `multiple` any number (chips). Two call signatures so value/onChange are typed for each. */
package/dist/index.d.ts CHANGED
@@ -129,6 +129,9 @@ export interface DataTableProps<Row extends Record<string, any> = Record<string,
129
129
  isRowSelectable?: RowCallback<boolean, Row> | undefined;
130
130
  /** Accessible name for a rejected row's empty cell, e.g. `(r) => 'Not awaiting review'` (5.6). */
131
131
  rowSelectDisabledLabel?: RowCallback<string, Row> | undefined;
132
+ /** Accessible name for a row's checkbox (and its cell), e.g. `(r) => 'Select ' + r.company` (5.11). Default: the
133
+ * `selectRow` string with the row key ("Select M-102"), also used when this returns an empty string. */
134
+ rowSelectLabel?: RowCallback<string, Row> | undefined;
132
135
  /** Controlled selection (row keys); pair with onSelectionChange. */
133
136
  selected?: Array<string | number> | undefined;
134
137
  /** Initial selection when uncontrolled. */
@@ -175,6 +178,10 @@ export interface DataTableProps<Row extends Record<string, any> = Record<string,
175
178
  onRowActivate?: RowCallback<void, Row> | undefined;
176
179
  /** Fixed height in px: sticky header, and only the rows in view are rendered. */
177
180
  height?: number | undefined;
181
+ /** `'auto'` (5.11): rows grow to fit content that wraps (two badges, a long name); cells stay vertically centred and
182
+ * rows keep at least the density's row height. Needs every row in the DOM, so it is ignored with `height`
183
+ * (virtualized rows are one fixed height). Default: one fixed-height line per row, long text ends in an ellipsis. */
184
+ rowHeight?: "auto" | undefined;
178
185
  /** Column menu on every header, drag-to-reorder, and the show/hide columns button. */
179
186
  columnControls?: boolean | undefined;
180
187
  /** Set false to keep column controls but turn off reordering. */
@@ -256,7 +263,7 @@ export interface CheckboxProps extends Omit<React$1.InputHTMLAttributes<HTMLInpu
256
263
  hideLabel?: boolean | undefined;
257
264
  /** The visible label beside the box. */
258
265
  children?: React$1.ReactNode | undefined;
259
- /** Second line under the visible label (needs an id to be linked). */
266
+ /** Second line under the visible label, linked by `aria-describedby` after any id you pass there (5.11). */
260
267
  description?: React$1.ReactNode | undefined;
261
268
  onChange?: ((checked: boolean) => void) | undefined;
262
269
  disabled?: boolean | undefined;
@@ -394,6 +401,9 @@ export interface FilterBarProps {
394
401
  actions?: React$1.ReactNode | undefined;
395
402
  /** Accessible name of the region. Default "Filters". */
396
403
  label?: string | undefined;
404
+ /** The search takes all the room the controls leave in its row, instead of stopping at 360px with the count and
405
+ * actions pushed to the end (5.12). */
406
+ searchGrow?: boolean | undefined;
397
407
  className?: string | undefined;
398
408
  }
399
409
  /** One command in the palette. */
@@ -460,6 +470,20 @@ export interface SelectProps extends Omit<FieldProps, "label">, Omit<React$1.Sel
460
470
  placeholder?: string | undefined;
461
471
  icon?: IconInput | undefined;
462
472
  }
473
+ /** A compact filter for FilterBar (5.12): "Status All ▾", as wide as its words. */
474
+ export interface FilterSelectProps extends Omit<React$1.SelectHTMLAttributes<HTMLSelectElement>, "onChange" | "value" | "defaultValue" | "multiple" | "size" | "placeholder" | "required" | "children"> {
475
+ /** The filter's name: shown first on the face ("Status") and its accessible name. */
476
+ label: string;
477
+ /** The choices; the first is the "all" choice. Or pass `<option>` children. */
478
+ options?: SelectOption[] | undefined;
479
+ children?: React$1.ReactNode | undefined;
480
+ value?: string | undefined;
481
+ defaultValue?: string | undefined;
482
+ onChange?: ((value: string) => void) | undefined;
483
+ /** A shorter word the face shows while the first option is chosen ("All" for "All statuses"). The list and screen
484
+ * readers keep the option's own text. */
485
+ allLabel?: string | undefined;
486
+ }
463
487
  export type ChoiceOption = string | {
464
488
  value: string;
465
489
  label: string;
@@ -1331,6 +1355,9 @@ export declare const TextField: React$1.ForwardRefExoticComponent<TextFieldProps
1331
1355
  export declare const Textarea: React$1.ForwardRefExoticComponent<TextareaProps & React$1.RefAttributes<HTMLTextAreaElement>>;
1332
1356
  /** A select field: a button that opens an AURA list (5.3), over a real <select> that keeps forms, refs and events. */
1333
1357
  export declare const Select: React$1.ForwardRefExoticComponent<SelectProps & React$1.RefAttributes<HTMLSelectElement>>;
1358
+ /** A compact filter for FilterBar (5.12): a small button reading "Status All ▾" that opens Select's list. The name
1359
+ * is the accessible name, the chosen option its value; forms, refs and the keyboard work as in Select. */
1360
+ export declare const FilterSelect: React$1.ForwardRefExoticComponent<FilterSelectProps & React$1.RefAttributes<HTMLSelectElement>>;
1334
1361
  export declare const RadioGroup: React$1.ForwardRefExoticComponent<RadioGroupProps & React$1.RefAttributes<HTMLFieldSetElement>>;
1335
1362
  export declare const Switch: React$1.ForwardRefExoticComponent<SwitchProps & React$1.RefAttributes<HTMLButtonElement>>;
1336
1363
  /** One value, or with `multiple` any number (chips). Two call signatures so value/onChange are typed for each. */
package/dist/styles.css CHANGED
@@ -130,7 +130,21 @@ a.aura-btn { text-decoration: none; }
130
130
  box-sizing: border-box; height: 100%; line-height: calc(var(--aura-table-row-height) - 1px);
131
131
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-right: var(--aura-space-4);
132
132
  }
133
- .aura-table__td .aura-pill, .aura-table__td .aura-dropdown, .aura-table__td > .aura-icon-btn, .aura-table__td > .aura-btn { vertical-align: middle; }
133
+ /* 5.11 rowHeight="auto" (never virtualized): a row grows to fit what wraps in it; each cell centres its content.
134
+ * The padding only shows once content is taller than the row: a small Button or IconButton (32px), a pill or one line
135
+ * still fits the density's row height (7.5px above and below at 48px, 3.5px compact). */
136
+ .aura-table__row--auto { height: auto; min-height: var(--aura-table-row-height); }
137
+ .aura-table__td--auto {
138
+ --aura-cell-pad-y: clamp(0px, calc((var(--aura-table-row-height) - 1px - var(--aura-button-height-sm)) / 2), var(--aura-space-2));
139
+ display: flex; align-items: center; height: auto; line-height: 20px; white-space: normal;
140
+ padding-top: var(--aura-cell-pad-y); padding-bottom: var(--aura-cell-pad-y);
141
+ }
142
+ .aura-table__cell { flex: 1 1 auto; min-width: 0; overflow-wrap: break-word; }
143
+ /* Badges and pills side by side keep apart when they wrap onto a second line. */
144
+ .aura-table__cell > .aura-badge, .aura-table__cell > .aura-pill { vertical-align: middle; margin-top: 2px; margin-bottom: 2px; }
145
+ .aura-table__cell > .aura-badge:not(:last-child), .aura-table__cell > .aura-pill:not(:last-child) { margin-inline-end: var(--aura-space-1); }
146
+ .aura-table__td .aura-pill, .aura-table__td .aura-dropdown, .aura-table__td > .aura-icon-btn, .aura-table__td > .aura-btn,
147
+ .aura-table__cell > .aura-icon-btn, .aura-table__cell > .aura-btn { vertical-align: middle; }
134
148
  .aura-table__th { position: relative; box-sizing: border-box; display: flex; align-items: center; padding-right: var(--aura-space-2); }
135
149
  /* Column sizes come from custom properties set per cell (4.20), so the card layout can override them. */
136
150
  .aura-table__th, .aura-table__td { flex: var(--aura-cell-flex, none); width: var(--aura-cell-w, auto); min-width: var(--aura-cell-min, 0); }
@@ -153,6 +167,10 @@ a.aura-btn { text-decoration: none; }
153
167
 
154
168
  /* Selection column */
155
169
  .aura-table__sel { width: var(--aura-table-select-width); flex: none; display: flex; align-items: center; background: inherit; }
170
+ /* 5.11: the selection box's invisible input reaches 24×24 (WCAG 2.5.8) around the 16px box it draws. Its left edge
171
+ * overlaps the sticky 24px gutter, so it sits one layer up — unless the column has scrolled under that gutter. */
172
+ .aura-table__sel .aura-check__input { inset: calc((var(--aura-icon-sm) - 24px) / 2); z-index: 2; }
173
+ .aura-table.is-scrolled-x .aura-table__sel:not(.is-pinned) .aura-check__input { z-index: auto; }
156
174
  .aura-table__sel.is-pinned { position: sticky; left: var(--aura-space-6); z-index: 2; }
157
175
 
158
176
  /* Pinned columns */
@@ -1345,6 +1363,37 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1345
1363
  .aura-filterbar__search { flex-basis: 100%; max-width: none; }
1346
1364
  .aura-filterbar__spacer { display: none; }
1347
1365
  }
1366
+ /* 5.12 searchGrow: the search fills what the controls leave. */
1367
+ .aura-filterbar--grow .aura-filterbar__search { max-width: none; }
1368
+ .aura-filterbar--grow .aura-filterbar__search ~ .aura-filterbar__spacer { display: none; }
1369
+ /* Compact filters sit closer together than full fields. */
1370
+ .aura-filterbar__controls:has(> .aura-filterselect) { gap: var(--aura-space-2); }
1371
+
1372
+ /* 5.12 FilterSelect: a compact face — "Status All ▾" — as wide as its words, over Select's machinery and list. */
1373
+ .aura-filterselect { position: relative; display: inline-flex; width: fit-content; max-width: 100%; vertical-align: middle; font-family: var(--font-sans); }
1374
+ .aura-filterselect__face {
1375
+ box-sizing: border-box; display: inline-flex; align-items: center; gap: var(--aura-space-1); width: auto; max-width: 100%;
1376
+ min-height: var(--aura-input-height); padding-block: 0; padding-inline: var(--aura-space-2) var(--aura-space-1);
1377
+ background: var(--aura-bg-input); color: var(--aura-fg-primary);
1378
+ border: 1px solid var(--aura-border-control); border-radius: var(--aura-radius-md);
1379
+ font: inherit; font-size: 13px; line-height: 18px; text-align: start; cursor: pointer;
1380
+ transition: border-color var(--aura-duration-fast) var(--aura-ease);
1381
+ }
1382
+ .aura-filterselect__face:hover { border-color: var(--aura-fg-secondary); }
1383
+ .aura-filterselect__name { flex: none; white-space: nowrap; color: var(--aura-fg-secondary); }
1384
+ .aura-filterselect__value { min-width: 0; font-weight: 500; overflow-wrap: anywhere; }
1385
+ .aura-filterselect__chevron { flex: none; width: 14px; height: 14px; color: var(--aura-fg-secondary); }
1386
+ .aura-filterselect.is-open .aura-filterselect__chevron { transform: rotate(180deg); }
1387
+ .aura-filterselect__face:focus-visible, .aura-filterselect:has(.aura-filterselect__native:focus-visible) .aura-filterselect__face {
1388
+ outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 1px; border-color: var(--aura-border-strong);
1389
+ }
1390
+ .aura-filterselect__face:focus { outline-style: none; }
1391
+ .aura-filterselect__face:focus-visible { outline-style: solid; }
1392
+ /* Before hydration: the real <select>, invisible, over the face (16px so iOS doesn't zoom when it opens). */
1393
+ .aura-filterselect__native { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; opacity: 0; font-size: 16px; cursor: pointer; appearance: none; }
1394
+ .aura-filterselect.is-disabled .aura-filterselect__face { background: var(--aura-bg-input-disabled); color: var(--aura-fg-secondary); border-color: var(--aura-border-default); cursor: not-allowed; }
1395
+ .aura-filterselect.is-disabled .aura-filterselect__native { cursor: not-allowed; }
1396
+ @media (forced-colors: active) { .aura-filterselect__face { border-color: ButtonText; } }
1348
1397
 
1349
1398
  /* Command palette: a search field over a grouped list, above everything else (it can open from a Dialog) */
1350
1399
  .aura-dialog-layer.aura-command-layer { z-index: var(--aura-z-menu); align-items: flex-start; padding: 12vh var(--aura-space-4) var(--aura-space-4); }
@@ -1580,6 +1629,9 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1580
1629
  padding: 0; line-height: normal;
1581
1630
  }
1582
1631
  .aura-table__td[data-hide], .aura-table__head > .aura-table__th[data-hide] { display: block; }
1632
+ /* 5.11: rowHeight="auto" leaves cards as they are. */
1633
+ .aura-table__td--auto { display: block; white-space: nowrap; }
1634
+ .aura-table__td--auto .aura-table__cell > .aura-badge, .aura-table__td--auto .aura-table__cell > .aura-pill { margin: 0; }
1583
1635
  .aura-table__spacer { display: none; }
1584
1636
  .aura-table__td[data-label]::before {
1585
1637
  content: attr(data-label); content: attr(data-label) / '';