@tensor_1/react 2.49.1 → 2.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -131,6 +131,7 @@ __export(src_exports, {
131
131
  DiffViewer: () => DiffViewer,
132
132
  DisplayPreferences: () => DisplayPreferences,
133
133
  Drawer: () => Drawer,
134
+ ENUM_FILTER_OPERATORS: () => ENUM_FILTER_OPERATORS,
134
135
  EmptyState: () => EmptyState,
135
136
  EntityPicker: () => EntityPicker,
136
137
  ErrorState: () => ErrorState,
@@ -325,6 +326,7 @@ __export(src_exports, {
325
326
  edgeLabelSize: () => edgeLabelSize,
326
327
  emptyFilterValue: () => emptyFilterValue,
327
328
  encodeQr: () => encodeQr,
329
+ enumColumnFilter: () => enumColumnFilter,
328
330
  enumerateDateKeys: () => enumerateDateKeys,
329
331
  enumerateDateSpan: () => enumerateDateSpan,
330
332
  fileKind: () => fileKind,
@@ -633,8 +635,12 @@ function toggleSort(keys, field, additive = false) {
633
635
  const existing = keys.find((k) => k.field === field);
634
636
  const dir = nextSortDir(existing?.dir);
635
637
  if (!additive) return dir === "none" ? [] : [{ field, dir }];
636
- const rest = keys.filter((k) => k.field !== field);
637
- return dir === "none" ? rest : [...rest, { field, dir }];
638
+ if (dir === "none") return keys.filter((k) => k.field !== field);
639
+ if (!existing) return [...keys, { field, dir }];
640
+ return keys.map((k) => k.field === field ? { field, dir } : k);
641
+ }
642
+ function enumColumnFilter(field, value) {
643
+ return { field, kind: "enum", op: value.operator === "is-none-of" ? "isNoneOf" : "isAnyOf", value: [...value.values] };
638
644
  }
639
645
  function asNum(v) {
640
646
  return typeof v === "number" ? v : Number(v);
@@ -4007,6 +4013,7 @@ function SearchBar({
4007
4013
  inputDescribedBy,
4008
4014
  labels: labelsProp,
4009
4015
  name,
4016
+ maxLength,
4010
4017
  inputId,
4011
4018
  inputRef,
4012
4019
  autoFocus,
@@ -4065,6 +4072,7 @@ function SearchBar({
4065
4072
  id: fieldId,
4066
4073
  type: "search",
4067
4074
  name,
4075
+ maxLength,
4068
4076
  className: "uix-input",
4069
4077
  value,
4070
4078
  placeholder,
@@ -10899,7 +10907,7 @@ var TableWrap = (0, import_react69.forwardRef)(function TableWrap2({ announcemen
10899
10907
  ] });
10900
10908
  });
10901
10909
  function Table({
10902
- density = "standard",
10910
+ density,
10903
10911
  zebra = true,
10904
10912
  pinnedCol,
10905
10913
  pinnedActions,
@@ -10921,7 +10929,7 @@ function Table({
10921
10929
  fixed && "uix-table--fixed",
10922
10930
  className
10923
10931
  ),
10924
- "data-density": density !== "standard" ? density : void 0,
10932
+ "data-density": density,
10925
10933
  "data-uix-rowpeek": rowPeek || void 0,
10926
10934
  "data-uix-table-v2": v2 || void 0,
10927
10935
  ...props,
@@ -10929,17 +10937,25 @@ function Table({
10929
10937
  }
10930
10938
  );
10931
10939
  }
10932
- function Th({ sortable, sortDirection, sortOrder, onSort, className, children, onClick, ...props }) {
10940
+ function Th({ sortable, sortDirection, sortOrder, sortOrderLabel, onSort, className, children, onClick, ...props }) {
10941
+ const ordinal = sortOrder != null && sortOrder > 0 && sortOrderLabel ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { className: "uix-visually-hidden", children: `, ${sortOrderLabel}` }) : null;
10933
10942
  return /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
10934
10943
  "th",
10935
10944
  {
10936
10945
  className,
10937
10946
  "data-sort": sortable || void 0,
10938
10947
  "data-sort-order": sortOrder != null && sortOrder > 0 ? sortOrder : void 0,
10948
+ "data-sort-labelled": ordinal != null || void 0,
10939
10949
  "aria-sort": sortDirection && sortDirection !== "none" ? sortDirection : void 0,
10940
10950
  onClick: sortable && onSort ? void 0 : onClick,
10941
10951
  ...props,
10942
- children: sortable ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("button", { type: "button", className: "uix-th__sortbtn", onClick: onSort, children }) : children
10952
+ children: sortable ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("button", { type: "button", className: "uix-th__sortbtn", onClick: onSort, children: [
10953
+ children,
10954
+ ordinal
10955
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(import_jsx_runtime76.Fragment, { children: [
10956
+ children,
10957
+ ordinal
10958
+ ] })
10943
10959
  }
10944
10960
  );
10945
10961
  }
@@ -14059,6 +14075,7 @@ var DEFAULT_FILTER_SUMMARY_LABELS = {
14059
14075
  until: "until {to}",
14060
14076
  more: "{first} +{count}",
14061
14077
  preset: "{label}",
14078
+ noneOf: "not {values}",
14062
14079
  on: "{date}",
14063
14080
  before: "before {date}",
14064
14081
  after: "after {date}"
@@ -14066,6 +14083,7 @@ var DEFAULT_FILTER_SUMMARY_LABELS = {
14066
14083
  var TEXT_FILTER_OPERATORS = ["contains", "not-contains", "is", "is-not", "starts-with"];
14067
14084
  var NUMBER_FILTER_OPERATORS = ["eq", "neq", "lt", "lte", "gt", "gte", "between"];
14068
14085
  var DATE_FILTER_OPERATORS = ["on", "before", "after", "between"];
14086
+ var ENUM_FILTER_OPERATORS = ["is-any-of", "is-none-of"];
14069
14087
  function emptyFilterValue(kind) {
14070
14088
  switch (kind) {
14071
14089
  case "enum":
@@ -14147,7 +14165,7 @@ async function resolveFilterLabels(field, value) {
14147
14165
  return { kind: "reference", values: value.values.map((v) => resolved.has(v.value) ? { value: v.value, label: resolved.get(v.value) } : v) };
14148
14166
  }
14149
14167
  function matchFilterPreset(field, value) {
14150
- if (value?.kind !== "enum" || !field.presets?.length) return void 0;
14168
+ if (value?.kind !== "enum" || value.operator === "is-none-of" || !field.presets?.length) return void 0;
14151
14169
  const chosen = new Set(value.values);
14152
14170
  return field.presets.find((preset) => {
14153
14171
  const wanted = new Set(preset.values);
@@ -14170,7 +14188,10 @@ function summarizeFilter(field, input, options = {}) {
14170
14188
  const preset = matchFilterPreset(field, value);
14171
14189
  if (preset) return fill3(labels.preset ?? DEFAULT_FILTER_SUMMARY_LABELS.preset, { label: preset.label, field: field.label });
14172
14190
  const byValue = new Map((field.options ?? []).map((o) => [o.value, o.label]));
14173
- return `${field.label}: ${list(value.values.map((v) => byValue.get(v) ?? v))}`;
14191
+ const named = list(value.values.map((v) => byValue.get(v) ?? v));
14192
+ const condition = value.operator ?? (field.enumOperators?.length === 1 ? field.enumOperators[0] : "is-any-of");
14193
+ if (condition === "is-none-of") return `${field.label}: ${fill3(labels.noneOf ?? DEFAULT_FILTER_SUMMARY_LABELS.noneOf, { values: named, field: field.label })}`;
14194
+ return `${field.label}: ${named}`;
14174
14195
  }
14175
14196
  case "reference":
14176
14197
  return `${field.label}: ${list(value.values.map((v) => v.label))}`;
@@ -14251,6 +14272,7 @@ var DEFAULT_FILTER_EDITOR_LABELS = {
14251
14272
  addTagHint: "Enter or a comma adds it.",
14252
14273
  date: "Date",
14253
14274
  dateOperators: { on: "on", before: "before", after: "after", between: "between" },
14275
+ enumOperators: { "is-any-of": "is any of", "is-none-of": "is none of" },
14254
14276
  operators: {
14255
14277
  contains: "contains",
14256
14278
  "not-contains": "does not contain",
@@ -14273,7 +14295,8 @@ function FilterEditor({ field, value: valueProp, onValueChange, onApply, onClear
14273
14295
  ...provided,
14274
14296
  ...labelOverrides,
14275
14297
  operators: { ...DEFAULT_FILTER_EDITOR_LABELS.operators, ...provided?.operators, ...labelOverrides?.operators },
14276
- dateOperators: { ...DEFAULT_FILTER_EDITOR_LABELS.dateOperators, ...provided?.dateOperators, ...labelOverrides?.dateOperators }
14298
+ dateOperators: { ...DEFAULT_FILTER_EDITOR_LABELS.dateOperators, ...provided?.dateOperators, ...labelOverrides?.dateOperators },
14299
+ enumOperators: { ...DEFAULT_FILTER_EDITOR_LABELS.enumOperators, ...provided?.enumOperators, ...labelOverrides?.enumOperators }
14277
14300
  };
14278
14301
  const id = (0, import_react88.useId)();
14279
14302
  let value = valueProp?.kind === field.kind ? valueProp : emptyFilterValue(field.kind);
@@ -14376,16 +14399,30 @@ function EnumBody({ field, value, onChange, labels, threshold }) {
14376
14399
  return q ? options.filter((o) => foldForSearch(o.label).text.includes(q)) : options;
14377
14400
  }, [options, query]);
14378
14401
  const selected = new Set(value.values);
14379
- const toggle = (v, on) => onChange({ kind: "enum", values: on ? [...value.values, v] : value.values.filter((x) => x !== v) });
14402
+ const ops = (field.enumOperators ?? []).filter((op) => ENUM_FILTER_OPERATORS.includes(op));
14403
+ const condition = value.operator ?? (ops.length === 1 ? ops[0] : void 0);
14404
+ const withValues = (values) => condition && condition !== "is-any-of" ? { kind: "enum", values, operator: condition } : { kind: "enum", values };
14405
+ const toggle = (v, on) => onChange(withValues(on ? [...value.values, v] : value.values.filter((x) => x !== v)));
14380
14406
  const presets = field.presets ?? [];
14381
14407
  const active = matchFilterPreset(field, value);
14408
+ const words2 = labels.enumOperators ?? {};
14382
14409
  return /* @__PURE__ */ (0, import_jsx_runtime99.jsxs)(import_jsx_runtime99.Fragment, { children: [
14383
- presets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime99.jsx)("div", { className: "uix-chip-group uix-filter-editor__presets", role: "group", "aria-label": labels.presets ?? DEFAULT_FILTER_EDITOR_LABELS.presets, children: presets.map((p) => /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(Chip, { pressed: p === active, onPressedChange: () => onChange({ kind: "enum", values: [...p.values] }), children: p.label }, p.id)) }),
14410
+ ops.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime99.jsx)("div", { className: "uix-filter-editor__row", children: /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(
14411
+ Select,
14412
+ {
14413
+ size: "sm",
14414
+ "aria-label": labels.operator,
14415
+ value: value.operator ?? "is-any-of",
14416
+ options: ops.map((op) => ({ value: op, label: words2[op] ?? DEFAULT_FILTER_EDITOR_LABELS.enumOperators[op] })),
14417
+ onValueChange: (op) => onChange(op === "is-none-of" ? { kind: "enum", values: value.values, operator: "is-none-of" } : { kind: "enum", values: value.values })
14418
+ }
14419
+ ) }),
14420
+ presets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime99.jsx)("div", { className: "uix-chip-group uix-filter-editor__presets", role: "group", "aria-label": labels.presets ?? DEFAULT_FILTER_EDITOR_LABELS.presets, children: presets.map((p) => /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(Chip, { pressed: p === active, onPressedChange: () => onChange(withValues([...p.values])), children: p.label }, p.id)) }),
14384
14421
  options.length > threshold && /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(Input, { type: "search", size: "sm", "aria-label": labels.search, placeholder: labels.search, value: query, onChange: (e) => setQuery(e.currentTarget.value), autoFocus: true }),
14385
14422
  /* @__PURE__ */ (0, import_jsx_runtime99.jsxs)("div", { className: "uix-filter-editor__bulk", children: [
14386
14423
  /* @__PURE__ */ (0, import_jsx_runtime99.jsx)("span", { className: "uix-filter-editor__count", "aria-live": "polite", children: fillLabel(labels.selected, { count: value.values.length }) }),
14387
- /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(Button, { type: "button", variant: "link", size: "sm", onClick: () => onChange({ kind: "enum", values: [.../* @__PURE__ */ new Set([...value.values, ...shown.map((o) => o.value)])] }), children: labels.selectAll }),
14388
- /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(Button, { type: "button", variant: "link", size: "sm", onClick: () => onChange({ kind: "enum", values: [] }), children: labels.selectNone })
14424
+ /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(Button, { type: "button", variant: "link", size: "sm", onClick: () => onChange(withValues([.../* @__PURE__ */ new Set([...value.values, ...shown.map((o) => o.value)])])), children: labels.selectAll }),
14425
+ /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(Button, { type: "button", variant: "link", size: "sm", onClick: () => onChange(withValues([])), children: labels.selectNone })
14389
14426
  ] }),
14390
14427
  /* @__PURE__ */ (0, import_jsx_runtime99.jsx)("div", { className: "uix-filter-editor__list", children: shown.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime99.jsx)("p", { className: "uix-filter-editor__note", children: labels.noMatches }) : shown.map((o) => /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(Checkbox, { label: o.label, checked: selected.has(o.value), onChange: (e) => toggle(o.value, e.currentTarget.checked) }, o.value)) })
14391
14428
  ] });
@@ -14543,25 +14580,60 @@ var DEFAULT_DATA_WORKSPACE_LABELS = {
14543
14580
  filteredEmpty: "Nothing matches",
14544
14581
  filteredEmptyDetail: "Change the search or clear a filter.",
14545
14582
  error: "The list could not be loaded",
14546
- retry: "Try again"
14583
+ retry: "Try again",
14584
+ sortPriority: "Sort priority {n}",
14585
+ groupCount: "{count} rows",
14586
+ groupCountOne: "{count} row",
14587
+ collapseGroup: "Collapse group {name}",
14588
+ expandGroup: "Expand group {name}",
14589
+ selectGroup: "Select group {name}"
14547
14590
  };
14591
+ var useIsomorphicLayoutEffect22 = typeof document !== "undefined" ? import_react89.useLayoutEffect : import_react89.useEffect;
14548
14592
  var ArrowUpRight = () => /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("svg", { className: "uix-id-cell__arrow", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("path", { d: "M7 17 17 7M8 7h9v9" }) });
14549
14593
  var PlusIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("path", { d: "M12 5v14M5 12h14" }) });
14550
14594
  var MaximizeIcon = ({ restore }) => /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("path", { d: restore ? "M4 14h6v6M20 10h-6V4M14 10l7-7M3 21l7-7" : "M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7" }) });
14595
+ var ChevronIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("path", { d: "M6 4l4 4-4 4" }) });
14596
+ var LADDER_CARDS = 768;
14597
+ var LADDER_LOW = 900;
14598
+ var LADDER_MID = 600;
14599
+ var ladderFor = (mobile, width) => {
14600
+ if (!mobile || !(width > 0)) return null;
14601
+ if (mobile === "cards" && width < LADDER_CARDS) return "cards";
14602
+ if (width < LADDER_MID) return "priority-mid";
14603
+ if (width < LADDER_LOW) return "priority";
14604
+ return null;
14605
+ };
14606
+ var hiddenBy = (ladder, priority) => ladder === "priority" && priority === "low" || ladder === "priority-mid" && (priority === "low" || priority === "mid");
14607
+ var ROW_HEIGHT = { compact: 44, standard: 56, comfortable: 68 };
14608
+ var CONTROLS = 'a, button, input, select, textarea, label, summary, dialog, [role="button"], [role="menuitem"], [contenteditable="true"], [popover], td.uix-table__selcell, td.uix-table__actions';
14609
+ var RESERVED_COLS = /* @__PURE__ */ new Set(["select", "id", "actions", "expand"]);
14610
+ var textOf2 = (node) => typeof node === "string" ? node : void 0;
14611
+ var columnLabel = (column) => column.label ?? textOf2(column.header);
14612
+ var ladderAttrs = (on, column, body) => on ? {
14613
+ "data-col": RESERVED_COLS.has(column.id) ? void 0 : column.id,
14614
+ "data-priority": column.priority && column.priority !== "high" ? column.priority : void 0,
14615
+ "data-label": body ? columnLabel(column) : void 0
14616
+ } : {};
14551
14617
  function DataWorkspace({
14552
14618
  label,
14553
14619
  rows,
14554
14620
  getRowId,
14621
+ rowLabel,
14555
14622
  columns,
14556
14623
  idColumn,
14557
14624
  sort,
14558
14625
  onSortChange,
14559
14626
  selection,
14560
14627
  onSelectionChange,
14628
+ maxSelection,
14561
14629
  bulkActions,
14562
14630
  rowActions,
14563
14631
  getRowProps,
14564
14632
  density,
14633
+ zebra,
14634
+ freezeFirstColumn = false,
14635
+ fixed,
14636
+ mobile,
14565
14637
  search,
14566
14638
  filters,
14567
14639
  savedViews,
@@ -14569,13 +14641,32 @@ function DataWorkspace({
14569
14641
  viewChanged = false,
14570
14642
  onSaveView,
14571
14643
  onSaveViewAs,
14644
+ saveViewAs = "changed",
14572
14645
  maximized = false,
14573
14646
  onMaximizedChange,
14574
14647
  toolbarEnd,
14648
+ beforeBody,
14575
14649
  pagination,
14576
14650
  status = "ready",
14577
14651
  refreshing = false,
14578
14652
  onRetry,
14653
+ loadingRows = 6,
14654
+ empty,
14655
+ emptyAction,
14656
+ filteredEmpty,
14657
+ filteredEmptyAction,
14658
+ error,
14659
+ stateOverride,
14660
+ groups,
14661
+ collapsedGroups,
14662
+ onCollapsedGroupsChange,
14663
+ virtualize,
14664
+ rowNavigation,
14665
+ onRowActivate,
14666
+ activateOnClick = true,
14667
+ activeRowId,
14668
+ onActiveRowChange,
14669
+ multiSort: multiSort2 = false,
14579
14670
  labels: labelOverrides,
14580
14671
  className,
14581
14672
  ...props
@@ -14609,68 +14700,128 @@ function DataWorkspace({
14609
14700
  const root = rootRef.current;
14610
14701
  const target = event.target;
14611
14702
  const owner = target?.closest?.('[popover], [role="dialog"], [role="menu"], [role="listbox"], [aria-expanded="true"]');
14612
- if (owner && root?.contains(owner)) return;
14703
+ if (owner && root?.contains(owner) && (owner.getAttribute("aria-expanded") !== "true" || stillExpanded(owner))) return;
14613
14704
  if (root && openInside(root)) return;
14614
14705
  onMaximizedChange(false);
14615
14706
  };
14616
14707
  window.addEventListener("keydown", onKey);
14617
14708
  return () => window.removeEventListener("keydown", onKey);
14618
14709
  }, [maximized, onMaximizedChange]);
14710
+ const [ladder, setLadder] = (0, import_react89.useState)(null);
14711
+ useIsomorphicLayoutEffect22(() => {
14712
+ const root = rootRef.current;
14713
+ if (!mobile || !root) {
14714
+ setLadder(null);
14715
+ return;
14716
+ }
14717
+ const read = () => {
14718
+ const style = getComputedStyle(root);
14719
+ const padding = (parseFloat(style.paddingLeft) || 0) + (parseFloat(style.paddingRight) || 0);
14720
+ setLadder(ladderFor(mobile, root.getBoundingClientRect().width - padding));
14721
+ };
14722
+ read();
14723
+ if (typeof ResizeObserver === "undefined") return;
14724
+ const observer = new ResizeObserver(read);
14725
+ observer.observe(root);
14726
+ return () => observer.disconnect();
14727
+ }, [mobile]);
14728
+ const cards = ladder === "cards";
14619
14729
  const filtered = (filters ? filters.fields.some((field) => !isFilterEmpty(filters.values[field.id])) : false) || (search?.value.trim() ?? "") !== "";
14620
- const [settled, setSettled] = (0, import_react89.useState)({ rows, filtered });
14621
- if (!refreshing && rows !== settled.rows) setSettled({ rows, filtered });
14622
- const view = rows.length > 0 ? { rows, filtered } : refreshing || rows === settled.rows ? settled : { rows, filtered };
14623
- const shownRows = view.rows;
14730
+ const source = groups ?? rows;
14731
+ const flat = (0, import_react89.useMemo)(() => groups ? groups.flatMap((group2) => group2.rows) : rows, [groups, rows]);
14732
+ const current = { source, flat, groups, filtered };
14733
+ const [settled, setSettled] = (0, import_react89.useState)(current);
14734
+ if (!refreshing && source !== settled.source) setSettled(current);
14735
+ const view = flat.length > 0 ? current : refreshing || source === settled.source ? settled : current;
14736
+ const shownRows = view.flat;
14737
+ const shownGroups = view.groups;
14738
+ const [ownCollapsed, setOwnCollapsed] = (0, import_react89.useState)(() => /* @__PURE__ */ new Set());
14739
+ const collapsed = collapsedGroups ?? ownCollapsed;
14740
+ const toggleGroupOpen = (groupId) => {
14741
+ const next = new Set(collapsed);
14742
+ if (next.has(groupId)) next.delete(groupId);
14743
+ else next.add(groupId);
14744
+ if (!collapsedGroups) setOwnCollapsed(next);
14745
+ onCollapsedGroupsChange?.(next);
14746
+ };
14624
14747
  const ids = (0, import_react89.useMemo)(() => shownRows.map(getRowId), [shownRows, getRowId]);
14625
- const selected = selection ?? /* @__PURE__ */ new Set();
14748
+ const visibleIds = (0, import_react89.useMemo)(() => {
14749
+ if (!shownGroups) return ids;
14750
+ const out = [];
14751
+ for (const group2 of shownGroups) if (!collapsed.has(group2.id)) for (const row of group2.rows) out.push(getRowId(row));
14752
+ return out;
14753
+ }, [shownGroups, collapsed, ids, getRowId]);
14754
+ const selected = selection ?? EMPTY_SET;
14626
14755
  const selectable = onSelectionChange != null;
14756
+ const cap = maxSelection ?? Infinity;
14627
14757
  const onPage = ids.filter((rowId) => selected.has(rowId)).length;
14758
+ const apply = (next, rowIds, on) => {
14759
+ for (const rowId of rowIds) {
14760
+ if (!on) next.delete(rowId);
14761
+ else if (next.has(rowId) || next.size < cap) next.add(rowId);
14762
+ }
14763
+ return next;
14764
+ };
14628
14765
  const toggleRow = (rowId, on, event) => {
14629
14766
  if (!onSelectionChange) return;
14630
14767
  const next = new Set(selected);
14631
- const from = anchor.current ? ids.indexOf(anchor.current) : -1;
14632
- const to = ids.indexOf(rowId);
14768
+ const from = anchor.current ? visibleIds.indexOf(anchor.current) : -1;
14769
+ const to = visibleIds.indexOf(rowId);
14633
14770
  if (event?.shiftKey && from >= 0 && to >= 0) {
14634
- for (let i = Math.min(from, to); i <= Math.max(from, to); i += 1) {
14635
- if (on) next.add(ids[i]);
14636
- else next.delete(ids[i]);
14637
- }
14771
+ apply(next, visibleIds.slice(Math.min(from, to), Math.max(from, to) + 1), on);
14638
14772
  } else {
14639
- if (on) next.add(rowId);
14640
- else next.delete(rowId);
14773
+ apply(next, [rowId], on);
14641
14774
  anchor.current = rowId;
14642
14775
  }
14643
14776
  onSelectionChange(next);
14644
14777
  };
14645
- const toggleAll = (on) => {
14778
+ const toggleMany = (rowIds, on) => {
14646
14779
  if (!onSelectionChange) return;
14647
- const next = new Set(selected);
14648
- for (const rowId of ids) {
14649
- if (on) next.add(rowId);
14650
- else next.delete(rowId);
14651
- }
14652
- onSelectionChange(next);
14653
- };
14654
- const actionsByRow = (0, import_react89.useMemo)(() => rowActions ? shownRows.map((row) => rowActions(row)) : [], [shownRows, rowActions]);
14780
+ onSelectionChange(apply(new Set(selected), rowIds, on));
14781
+ };
14782
+ const atCap = selected.size >= cap;
14783
+ const actionsByRow = (0, import_react89.useMemo)(() => {
14784
+ if (!rowActions) return [];
14785
+ const context = { ladder, compact: ladder === "priority-mid" };
14786
+ return shownRows.map((row) => rowActions(row, context));
14787
+ }, [shownRows, rowActions, ladder]);
14655
14788
  const hasActions = actionsByRow.some((node) => node != null && node !== false);
14656
14789
  const activeFilters = filters ? filters.fields.filter((field) => !isFilterEmpty(filters.values[field.id])) : [];
14657
14790
  const searching = (search?.value.trim() ?? "") !== "";
14658
14791
  const clearAll = () => {
14792
+ if (filters?.onClearAll) {
14793
+ filters.onClearAll({ search: searching });
14794
+ return;
14795
+ }
14659
14796
  for (const field of activeFilters) filters?.onChange(field.id, void 0);
14660
14797
  if (searching) search?.onChange("");
14661
14798
  };
14662
- const sortFor = (column) => sort?.columnId === column.id ? sort.direction : "none";
14663
- const cycleSort = (column) => {
14664
- const current = sortFor(column);
14665
- onSortChange?.(current === "none" ? { columnId: column.id, direction: "ascending" } : current === "ascending" ? { columnId: column.id, direction: "descending" } : null);
14799
+ const multi = multiSort2 || Array.isArray(sort);
14800
+ const keys = Array.isArray(sort) ? sort : sort ? [sort] : [];
14801
+ const sortFor = (column) => keys.find((key) => key.columnId === column.id)?.direction ?? "none";
14802
+ const cycleSort = (column, additive) => {
14803
+ if (!onSortChange) return;
14804
+ const current2 = sortFor(column);
14805
+ const turned = current2 === "none" ? "ascending" : current2 === "ascending" ? "descending" : null;
14806
+ if (!multi) {
14807
+ onSortChange(turned ? { columnId: column.id, direction: turned } : null);
14808
+ return;
14809
+ }
14810
+ const next = toggleSort(keys.map((key) => ({ field: key.columnId, dir: key.direction })), column.id, additive).map((key) => ({ columnId: key.field, direction: key.dir === "descending" ? "descending" : "ascending" }));
14811
+ onSortChange(next);
14666
14812
  };
14667
- const toolbar = /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)("div", { className: "uix-toolbar uix-data-workspace__toolbar", role: "toolbar", "aria-label": label, children: [
14668
- savedViews && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(PopoverButton, { id: `${id}-views`, label: savedViews.current, title: typeof savedViews.current === "string" ? fillLabel(labels.savedView, { name: savedViews.current }) : void 0, variant: "secondary", children: (close) => /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(SavedViewMenu, { ...savedViews.menu, onSelect: (viewId) => {
14669
- savedViews.menu.onSelect(viewId);
14670
- close();
14671
- } }) }),
14813
+ const showSaveAs = onSaveViewAs != null && (viewChanged || saveViewAs === "always");
14814
+ const hasToolbar = savedViews != null || viewChanged && onSaveView != null || showSaveAs || filters != null || search != null || viewMenu != null || onMaximizedChange != null || toolbarEnd != null && toolbarEnd !== false;
14815
+ const toolbar = hasToolbar && /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)("div", { className: "uix-toolbar uix-data-workspace__toolbar", role: "toolbar", "aria-label": label, children: [
14816
+ savedViews && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(PopoverButton, { id: `${id}-views`, label: savedViews.current, title: typeof savedViews.current === "string" ? fillLabel(labels.savedView, { name: savedViews.current }) : void 0, variant: "secondary", children: (close) => /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)(import_jsx_runtime100.Fragment, { children: [
14817
+ savedViews.note != null && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("div", { className: "uix-data-workspace__views-note", children: savedViews.note }),
14818
+ /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(SavedViewMenu, { ...savedViews.menu, onSelect: (viewId) => {
14819
+ savedViews.menu.onSelect(viewId);
14820
+ close();
14821
+ } })
14822
+ ] }) }),
14672
14823
  viewChanged && onSaveView && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Button, { size: "sm", variant: "secondary", onClick: onSaveView, children: labels.saveView }),
14673
- viewChanged && onSaveViewAs && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Button, { size: "sm", variant: "ghost", onClick: onSaveViewAs, children: labels.saveViewAs }),
14824
+ showSaveAs && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Button, { size: "sm", variant: "ghost", onClick: onSaveViewAs, children: labels.saveViewAs }),
14674
14825
  filters && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(FilterChips, { filters, labels, baseId: id }),
14675
14826
  /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("span", { className: "uix-toolbar__spacer" }),
14676
14827
  search && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
@@ -14682,67 +14833,78 @@ function DataWorkspace({
14682
14833
  placeholder: search.placeholder ?? labels.searchPlaceholder,
14683
14834
  value: search.value,
14684
14835
  onValueChange: search.onChange,
14685
- inputRef: searchRef
14836
+ inputRef: searchRef,
14837
+ onSubmit: search.onSubmit,
14838
+ onClear: search.onClear,
14839
+ maxLength: search.maxLength
14686
14840
  }
14687
14841
  ),
14688
14842
  viewMenu && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(PopoverButton, { id: `${id}-view`, label: labels.view, variant: "ghost", children: () => /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(ViewMenu, { ...viewMenu }) }),
14689
14843
  onMaximizedChange && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Button, { size: "sm", variant: "ghost", icon: true, "aria-label": maximized ? labels.restore : labels.maximize, "aria-pressed": maximized, onClick: () => onMaximizedChange(!maximized), children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(MaximizeIcon, { restore: maximized }) }),
14690
14844
  toolbarEnd
14691
14845
  ] });
14846
+ const busy = (node) => /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(BusyRegion, { busy: refreshing, className: "uix-data-workspace__state", children: node });
14692
14847
  let body;
14693
- if (status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(LoadingState, { rows: 6, label: labels.loading, className: "uix-data-workspace__loading" });
14694
- else if (status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(ErrorState, { title: labels.error, action: onRetry && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Button, { size: "sm", onClick: onRetry, children: labels.retry }) });
14848
+ if (stateOverride != null) body = busy(stateOverride);
14849
+ else if (status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(LoadingState, { rows: loadingRows, label: labels.loading, className: "uix-data-workspace__loading" });
14850
+ else if (status === "error") body = error != null ? busy(error) : /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(ErrorState, { title: labels.error, action: onRetry && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Button, { size: "sm", onClick: onRetry, children: labels.retry }) });
14695
14851
  else if (shownRows.length === 0) {
14696
- body = /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(BusyRegion, { busy: refreshing, className: "uix-data-workspace__state", children: view.filtered ? /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(FilteredEmptyState, { title: labels.filteredEmpty, action: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Button, { size: "sm", onClick: clearAll, children: labels.clearFilters }), children: labels.filteredEmptyDetail }) : /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(EmptyState, { title: labels.empty, children: labels.emptyDetail }) });
14852
+ const clear = /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Button, { size: "sm", onClick: clearAll, children: labels.clearFilters });
14853
+ body = busy(view.filtered ? filteredEmpty ?? /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
14854
+ FilteredEmptyState,
14855
+ {
14856
+ title: labels.filteredEmpty,
14857
+ action: filteredEmptyAction != null ? /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)("div", { className: "uix-data-workspace__state-actions", children: [
14858
+ clear,
14859
+ filteredEmptyAction
14860
+ ] }) : clear,
14861
+ children: labels.filteredEmptyDetail
14862
+ }
14863
+ ) : empty ?? /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(EmptyState, { title: labels.empty, action: emptyAction, children: labels.emptyDetail }));
14697
14864
  } else {
14698
- body = /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(TableWrap, { refreshing, className: "uix-data-workspace__table", children: /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)(Table, { density, pinnedCol: idColumn != null, pinnedActions: hasActions, className: cx(selectable && idColumn && "uix-data-workspace__grid--pin-id"), "aria-label": label, children: [
14699
- /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)("tr", { children: [
14700
- selectable && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("th", { className: "uix-table__selcol", scope: "col", children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Checkbox, { "aria-label": labels.selectAll, checked: onPage > 0 && onPage === ids.length, indeterminate: onPage > 0 && onPage < ids.length, onChange: (event) => toggleAll(event.currentTarget.checked) }) }),
14701
- idColumn && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Th, { scope: "col", children: idColumn.header }),
14702
- columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
14703
- Th,
14704
- {
14705
- scope: "col",
14706
- className: column.align === "end" ? "uix-col--num" : void 0,
14707
- style: column.width ? { width: column.width } : void 0,
14708
- sortable: column.sortable && onSortChange != null,
14709
- sortDirection: sortFor(column),
14710
- onSort: () => cycleSort(column),
14711
- children: column.header
14712
- },
14713
- column.id
14714
- )),
14715
- hasActions && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("th", { scope: "col", className: "uix-table__actions", children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("span", { className: "uix-visually-hidden", children: labels.rowActions }) })
14716
- ] }) }),
14717
- /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("tbody", { children: shownRows.map((row, index) => {
14718
- const rowId = ids[index];
14719
- const text = idColumn?.text(row) ?? rowId;
14720
- const isSelected = selected.has(rowId);
14721
- return /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)(Tr, { selected: isSelected, ...getRowProps?.(row), children: [
14722
- selectable && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("td", { className: "uix-table__selcell", children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
14723
- Checkbox,
14724
- {
14725
- "aria-label": fillLabel(labels.selectRow, { label: text }),
14726
- checked: isSelected,
14727
- onClick: (event) => toggleRow(rowId, event.currentTarget.checked, event),
14728
- onChange: () => {
14729
- }
14730
- }
14731
- ) }),
14732
- idColumn && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Td, { className: "uix-id-cell", children: /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)("span", { className: "uix-data-workspace__id", children: [
14733
- idColumn.onPeek ? /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("button", { type: "button", className: "uix-id-cell__btn", "aria-haspopup": "dialog", onClick: () => idColumn.onPeek?.(row), children: text }) : /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("span", { className: "uix-cell-strong", children: text }),
14734
- idColumn.href && renderUixLink(idColumn.renderLink, {
14735
- href: idColumn.href(row),
14736
- className: "uix-data-workspace__open",
14737
- "aria-label": fillLabel(labels.open, { label: text }),
14738
- children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(ArrowUpRight, {})
14739
- })
14740
- ] }) }),
14741
- columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Td, { className: column.align === "end" ? "uix-col--num" : void 0, children: column.cell(row) }, column.id)),
14742
- hasActions && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(RowActions, { children: actionsByRow[index] })
14743
- ] }, rowId);
14744
- }) })
14745
- ] }) });
14865
+ body = /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
14866
+ WorkspaceGrid,
14867
+ {
14868
+ label,
14869
+ rows: shownRows,
14870
+ ids,
14871
+ groups: shownGroups,
14872
+ collapsed,
14873
+ onToggleGroup: toggleGroupOpen,
14874
+ getRowId,
14875
+ rowLabel,
14876
+ columns,
14877
+ idColumn,
14878
+ getRowProps,
14879
+ density,
14880
+ zebra,
14881
+ freezeFirstColumn,
14882
+ fixed: fixed ?? columns.some((column) => column.className != null || column.minWidth != null),
14883
+ ladderOn: mobile != null,
14884
+ ladder,
14885
+ refreshing,
14886
+ selectable,
14887
+ selected,
14888
+ onPage,
14889
+ atCap,
14890
+ toggleRow,
14891
+ toggleMany,
14892
+ actionsByRow,
14893
+ hasActions,
14894
+ keys,
14895
+ sortable: onSortChange != null,
14896
+ multi,
14897
+ sortFor,
14898
+ cycleSort,
14899
+ virtualize: pagination || shownGroups ? void 0 : virtualize,
14900
+ rowNavigation: rowNavigation ?? onRowActivate != null,
14901
+ onRowActivate,
14902
+ activateOnClick,
14903
+ activeRowId,
14904
+ onActiveRowChange,
14905
+ labels
14906
+ }
14907
+ );
14746
14908
  }
14747
14909
  const selectedIds = [...selected];
14748
14910
  return /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)(
@@ -14755,13 +14917,627 @@ function DataWorkspace({
14755
14917
  children: [
14756
14918
  toolbar,
14757
14919
  bulkActions && selectedIds.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(BulkBar, { count: selectedIds.length, label: labels.bulkActions, selectedLabel: (n) => fillLabel(labels.selected, { count: n }), children: bulkActions(selectedIds) }),
14920
+ beforeBody != null && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("div", { className: "uix-data-workspace__before", children: beforeBody }),
14758
14921
  body,
14759
- pagination && status === "ready" && shownRows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Footer, { pagination, labels, rowCount: shownRows.length })
14922
+ pagination && status === "ready" && stateOverride == null && shownRows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Footer, { pagination, labels, rowCount: shownRows.length })
14760
14923
  ]
14761
14924
  }
14762
14925
  );
14763
14926
  }
14927
+ var EMPTY_SET = /* @__PURE__ */ new Set();
14928
+ function WorkspaceGrid(props) {
14929
+ return props.virtualize ? /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(WindowedGrid, { ...props }) : /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(WorkspaceTable, { ...props, windowing: null, virtual: null });
14930
+ }
14931
+ function WindowedGrid(props) {
14932
+ const windowing = props.virtualize === true || !props.virtualize ? {} : props.virtualize;
14933
+ const threshold = windowing.threshold ?? 100;
14934
+ const virtual = useVirtualRows(props.rows, {
14935
+ rowHeight: windowing.estimatedRowHeight ?? ROW_HEIGHT[props.density ?? "standard"],
14936
+ overscan: windowing.overscan ?? 8,
14937
+ // windowed at `threshold` rows or more (the hook windows above its own threshold)
14938
+ threshold: Math.max(0, threshold - 1),
14939
+ estimatedViewportHeight: windowing.maxHeight ?? 640,
14940
+ measureRows: "tbody > tr[data-uix-row-id]",
14941
+ // cards are not windowed: a card's height is its content's
14942
+ enabled: props.rows.length >= threshold && props.ladder !== "cards"
14943
+ });
14944
+ return /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(WorkspaceTable, { ...props, windowing, virtual });
14945
+ }
14946
+ var columnFloor = (width, minWidth, className) => {
14947
+ if (minWidth) return minWidth;
14948
+ if (width && !width.trim().endsWith("%")) return width;
14949
+ const classes = className?.split(/\s+/) ?? [];
14950
+ for (const cls of classes) {
14951
+ const tier = /^uix-col--w-(xs|sm|md|lg|xl)$/.exec(cls)?.[1];
14952
+ if (tier) return `var(--uix-col-w-${tier})`;
14953
+ }
14954
+ if (classes.includes("uix-col--flex") || classes.includes("uix-col--primary")) return "var(--uix-col-title-min)";
14955
+ return "var(--uix-col-w-sm)";
14956
+ };
14957
+ var isFlex = (className) => /(^|\s)uix-col--(flex|primary)(\s|$)/.test(className ?? "");
14958
+ var colClasses = (className) => className?.split(/\s+/).filter((cls) => /^uix-col--(w-(xs|sm|md|lg|xl)|flex|primary)$/.test(cls)).join(" ") || void 0;
14959
+ function WorkspaceTable({
14960
+ label,
14961
+ rows,
14962
+ ids,
14963
+ groups,
14964
+ collapsed,
14965
+ onToggleGroup,
14966
+ getRowId,
14967
+ rowLabel,
14968
+ columns,
14969
+ idColumn,
14970
+ getRowProps,
14971
+ density,
14972
+ zebra,
14973
+ freezeFirstColumn,
14974
+ fixed,
14975
+ ladderOn,
14976
+ ladder,
14977
+ refreshing,
14978
+ selectable,
14979
+ selected,
14980
+ onPage,
14981
+ atCap,
14982
+ toggleRow,
14983
+ toggleMany,
14984
+ actionsByRow,
14985
+ hasActions,
14986
+ keys,
14987
+ sortable,
14988
+ multi,
14989
+ sortFor,
14990
+ cycleSort,
14991
+ rowNavigation,
14992
+ onRowActivate,
14993
+ activateOnClick,
14994
+ activeRowId,
14995
+ onActiveRowChange,
14996
+ labels,
14997
+ windowing,
14998
+ virtual
14999
+ }) {
15000
+ const cards = ladder === "cards";
15001
+ const wrapRef = (0, import_react89.useRef)(null);
15002
+ const baseId = (0, import_react89.useId)();
15003
+ const maxHeight = windowing?.maxHeight ?? 640;
15004
+ const long = windowing != null && !cards && rows.length >= (windowing.threshold ?? 100);
15005
+ const containerRef = virtual?.containerRef;
15006
+ const setWrap = (0, import_react89.useCallback)((node) => {
15007
+ wrapRef.current = node;
15008
+ if (containerRef) containerRef.current = node;
15009
+ }, [containerRef]);
15010
+ const windowed = virtual?.virtualized ?? false;
15011
+ const start = virtual && windowed ? virtual.startIndex : 0;
15012
+ const mountedRows = virtual && windowed ? virtual.rows : rows;
15013
+ const nav = rowNavigation;
15014
+ const clickActivates = onRowActivate != null && activateOnClick;
15015
+ const [ownActive, setOwnActive] = (0, import_react89.useState)(null);
15016
+ const active = activeRowId !== void 0 ? activeRowId : ownActive;
15017
+ const navIds = (0, import_react89.useMemo)(() => {
15018
+ if (!groups) return ids;
15019
+ const out = [];
15020
+ for (const group2 of groups) if (!collapsed.has(group2.id)) for (const row of group2.rows) out.push(getRowId(row));
15021
+ return out;
15022
+ }, [groups, collapsed, ids, getRowId]);
15023
+ const mountedIds = (0, import_react89.useMemo)(() => new Set(windowed ? ids.slice(start, start + mountedRows.length) : navIds), [windowed, ids, start, mountedRows.length, navIds]);
15024
+ const tabStop = active != null && mountedIds.has(active) ? active : navIds.find((rowId) => mountedIds.has(rowId)) ?? null;
15025
+ const pendingFocus = (0, import_react89.useRef)(null);
15026
+ const focusedRow = (0, import_react89.useRef)(null);
15027
+ const rowElement = (rowId) => {
15028
+ for (const el of Array.from(wrapRef.current?.querySelectorAll("tbody > tr[data-uix-row-id]") ?? [])) {
15029
+ if (el.getAttribute("data-uix-row-id") === rowId) return el;
15030
+ }
15031
+ return null;
15032
+ };
15033
+ const reveal = (el) => {
15034
+ el.scrollIntoView?.({ block: "nearest", inline: "nearest" });
15035
+ const wrap = wrapRef.current;
15036
+ const head = wrap?.querySelector("thead");
15037
+ if (wrap && head && windowed) {
15038
+ const under = head.getBoundingClientRect().bottom - el.getBoundingClientRect().top;
15039
+ if (under > 0) wrap.scrollTop -= under;
15040
+ }
15041
+ };
15042
+ const setActive = (rowId) => {
15043
+ if (rowId === active) return;
15044
+ if (activeRowId === void 0) setOwnActive(rowId);
15045
+ onActiveRowChange?.(rowId);
15046
+ };
15047
+ const focusRow = (rowId, { take = true } = {}) => {
15048
+ if (!navIds.includes(rowId)) return;
15049
+ setActive(rowId);
15050
+ if (!take) return;
15051
+ const el = rowElement(rowId);
15052
+ if (el) {
15053
+ el.focus({ preventScroll: true });
15054
+ reveal(el);
15055
+ return;
15056
+ }
15057
+ const index = ids.indexOf(rowId);
15058
+ if (index >= 0 && wrapRef.current && virtual && windowed) {
15059
+ pendingFocus.current = rowId;
15060
+ wrapRef.current.scrollTop = index * virtual.rowHeight;
15061
+ }
15062
+ };
15063
+ useIsomorphicLayoutEffect22(() => {
15064
+ if (!nav) return;
15065
+ const pending = pendingFocus.current;
15066
+ if (pending != null) {
15067
+ if (!ids.includes(pending)) {
15068
+ pendingFocus.current = null;
15069
+ return;
15070
+ }
15071
+ const el2 = rowElement(pending);
15072
+ if (el2) {
15073
+ pendingFocus.current = null;
15074
+ el2.focus({ preventScroll: true });
15075
+ reveal(el2);
15076
+ }
15077
+ return;
15078
+ }
15079
+ const had = focusedRow.current;
15080
+ if (had == null) return;
15081
+ const wrap = wrapRef.current;
15082
+ const focused = typeof document !== "undefined" ? document.activeElement : null;
15083
+ const el = rowElement(had);
15084
+ if (el && wrap && focused === wrap) {
15085
+ el.focus({ preventScroll: true });
15086
+ return;
15087
+ }
15088
+ if (el || !(focused == null || focused === document.body)) return;
15089
+ if (windowed && ids.includes(had) && wrap) {
15090
+ wrap.focus({ preventScroll: true });
15091
+ } else if (tabStop != null) {
15092
+ rowElement(tabStop)?.focus({ preventScroll: true });
15093
+ }
15094
+ });
15095
+ const lastActiveProp = (0, import_react89.useRef)(activeRowId);
15096
+ (0, import_react89.useEffect)(() => {
15097
+ if (!nav || activeRowId === lastActiveProp.current) return;
15098
+ lastActiveProp.current = activeRowId;
15099
+ if (activeRowId == null) return;
15100
+ const focused = document.activeElement;
15101
+ const el = rowElement(activeRowId);
15102
+ if (el && el.contains(focused)) return;
15103
+ const mayTake = focused == null || focused === document.body || (wrapRef.current?.contains(focused) ?? false);
15104
+ focusRow(activeRowId, { take: mayTake });
15105
+ });
15106
+ const latest = {
15107
+ toggle: (rowId, on, event) => toggleRow(rowId, on, event),
15108
+ key: (rowId, event) => {
15109
+ if (event.target !== event.currentTarget || event.altKey || event.ctrlKey || event.metaKey) return;
15110
+ const at2 = navIds.indexOf(rowId);
15111
+ const to = event.key === "ArrowDown" ? at2 + 1 : event.key === "ArrowUp" ? at2 - 1 : event.key === "Home" ? 0 : event.key === "End" ? navIds.length - 1 : null;
15112
+ if (to != null) {
15113
+ event.preventDefault();
15114
+ const target = navIds[Math.max(0, Math.min(navIds.length - 1, to))];
15115
+ if (target != null) focusRow(target);
15116
+ } else if (event.key === "Enter") {
15117
+ event.preventDefault();
15118
+ const row = rows[ids.indexOf(rowId)];
15119
+ if (onRowActivate) {
15120
+ if (row !== void 0) onRowActivate(row);
15121
+ return;
15122
+ }
15123
+ const el = event.currentTarget;
15124
+ (el.querySelector(".uix-id-cell__btn") ?? el.querySelector("a[href]"))?.click();
15125
+ } else if (event.key === " ") {
15126
+ event.preventDefault();
15127
+ }
15128
+ },
15129
+ click: (rowId, event) => {
15130
+ if (!clickActivates) return;
15131
+ if (!event.currentTarget.contains(event.target)) return;
15132
+ const control = event.target.closest?.(CONTROLS);
15133
+ if (control && control !== event.currentTarget && event.currentTarget.contains(control)) return;
15134
+ if (typeof window !== "undefined" && (window.getSelection?.()?.toString() ?? "") !== "") return;
15135
+ const row = rows[ids.indexOf(rowId)];
15136
+ if (row !== void 0) onRowActivate?.(row);
15137
+ },
15138
+ focus: (rowId) => {
15139
+ focusedRow.current = rowId;
15140
+ setActive(rowId);
15141
+ },
15142
+ blur: (rowId, event) => {
15143
+ const next = event.relatedTarget;
15144
+ if (next && wrapRef.current?.contains(next)) return;
15145
+ if (next) {
15146
+ focusedRow.current = null;
15147
+ return;
15148
+ }
15149
+ const el = event.currentTarget;
15150
+ setTimeout(() => {
15151
+ if (el.isConnected && focusedRow.current === rowId) focusedRow.current = null;
15152
+ }, 0);
15153
+ }
15154
+ };
15155
+ const latestRef = (0, import_react89.useRef)(latest);
15156
+ latestRef.current = latest;
15157
+ const handlers2 = (0, import_react89.useMemo)(() => ({
15158
+ toggle: (...args) => latestRef.current.toggle(...args),
15159
+ key: (...args) => latestRef.current.key(...args),
15160
+ click: (...args) => latestRef.current.click(...args),
15161
+ focus: (...args) => latestRef.current.focus(...args),
15162
+ blur: (...args) => latestRef.current.blur(...args)
15163
+ }), []);
15164
+ const groupAnchor = (0, import_react89.useRef)(null);
15165
+ const toggleGroup = (groupId, header) => {
15166
+ groupAnchor.current = header ? { id: groupId, top: header.getBoundingClientRect().top } : null;
15167
+ onToggleGroup(groupId);
15168
+ };
15169
+ useIsomorphicLayoutEffect22(() => {
15170
+ const was = groupAnchor.current;
15171
+ if (!was) return;
15172
+ groupAnchor.current = null;
15173
+ const header = wrapRef.current?.querySelector(`tr[data-group-id="${cssEscape(was.id)}"]`);
15174
+ if (!header) return;
15175
+ const moved = header.getBoundingClientRect().top - was.top;
15176
+ if (Math.abs(moved) < 1) return;
15177
+ const wrap = wrapRef.current;
15178
+ if (wrap && wrap.scrollHeight > wrap.clientHeight + 1 && getComputedStyle(wrap).overflowY !== "visible") wrap.scrollTop += moved;
15179
+ else window.scrollBy?.(0, moved);
15180
+ });
15181
+ const shiftKey = (0, import_react89.useRef)(false);
15182
+ const additive = (0, import_react89.useRef)(false);
15183
+ const frozen = !cards && ladder !== "priority-mid" && (idColumn != null || freezeFirstColumn);
15184
+ const shown = columns.filter((column) => !hiddenBy(ladder, column.priority));
15185
+ const colCount = (selectable ? 1 : 0) + (idColumn ? 1 : 0) + shown.length + (hasActions ? 1 : 0);
15186
+ const selectRowLabel = labels.selectRow;
15187
+ const openLabel = labels.open;
15188
+ const fixedOn = fixed && !cards;
15189
+ const [actionsTooWide, setActionsTooWide] = (0, import_react89.useState)(false);
15190
+ const [actionsWidth, setActionsWidth] = (0, import_react89.useState)(null);
15191
+ const measureActions = !cards && hasActions;
15192
+ const measureConfig = (0, import_react89.useRef)({ measureActions, fixedOn, hasActions });
15193
+ measureConfig.current = { measureActions, fixedOn, hasActions };
15194
+ const measureActionsNow = (0, import_react89.useRef)(() => {
15195
+ });
15196
+ measureActionsNow.current = () => {
15197
+ const wrap = wrapRef.current;
15198
+ const { measureActions: on, fixedOn: fixedNow, hasActions: any } = measureConfig.current;
15199
+ if (!wrap) return;
15200
+ const cells = Array.from(wrap.querySelectorAll("tbody td.uix-table__actions"));
15201
+ if (!on) setActionsTooWide(false);
15202
+ else {
15203
+ let widest = 0;
15204
+ for (const td of cells) if (typeof td.getBoundingClientRect === "function") widest = Math.max(widest, td.getBoundingClientRect().width, td.scrollWidth);
15205
+ if (wrap.clientWidth > 0 && widest > 0) setActionsTooWide(widest > wrap.clientWidth / 4);
15206
+ }
15207
+ if (!fixedNow || !any || typeof document.createRange !== "function" || typeof getComputedStyle !== "function") return;
15208
+ if (typeof document.createRange().getBoundingClientRect !== "function") return;
15209
+ let content = 0;
15210
+ for (const td of cells) {
15211
+ const range = document.createRange();
15212
+ range.selectNodeContents(td);
15213
+ const style = getComputedStyle(td);
15214
+ content = Math.max(content, range.getBoundingClientRect().width + parseFloat(style.paddingLeft || "0") + parseFloat(style.paddingRight || "0"));
15215
+ }
15216
+ const next = content > 0 ? Math.ceil(content) : null;
15217
+ setActionsWidth((current) => current === next ? current : next);
15218
+ };
15219
+ useIsomorphicLayoutEffect22(() => {
15220
+ const wrap = wrapRef.current;
15221
+ if (!wrap) return;
15222
+ let frame = 0;
15223
+ const schedule = () => {
15224
+ if (frame || typeof requestAnimationFrame !== "function") return;
15225
+ frame = requestAnimationFrame(() => {
15226
+ frame = 0;
15227
+ measureActionsNow.current();
15228
+ });
15229
+ };
15230
+ const resize = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(schedule);
15231
+ const collect = () => {
15232
+ if (!resize) return;
15233
+ resize.disconnect();
15234
+ resize.observe(wrap);
15235
+ const table = wrap.querySelector("table");
15236
+ if (table) resize.observe(table);
15237
+ for (const td of Array.from(wrap.querySelectorAll("tbody td.uix-table__actions"))) {
15238
+ resize.observe(td);
15239
+ for (const child of Array.from(td.children)) resize.observe(child);
15240
+ }
15241
+ };
15242
+ collect();
15243
+ const mutation = typeof MutationObserver === "undefined" ? null : new MutationObserver(() => {
15244
+ collect();
15245
+ schedule();
15246
+ });
15247
+ mutation?.observe(wrap, { childList: true, subtree: true });
15248
+ return () => {
15249
+ resize?.disconnect();
15250
+ mutation?.disconnect();
15251
+ if (frame && typeof cancelAnimationFrame === "function") cancelAnimationFrame(frame);
15252
+ };
15253
+ }, []);
15254
+ const rowSet = ids.join("\0");
15255
+ useIsomorphicLayoutEffect22(() => {
15256
+ measureActionsNow.current();
15257
+ }, [measureActions, fixedOn, ladder, rowSet]);
15258
+ const actionsCol = actionsWidth != null ? `${actionsWidth}px` : "5.25rem";
15259
+ const floors = fixedOn ? [
15260
+ ...selectable ? ["var(--uix-selcol-w)"] : [],
15261
+ ...idColumn ? [columnFloor(idColumn.width, idColumn.minWidth, void 0)] : [],
15262
+ ...shown.map((column) => columnFloor(column.width, column.minWidth, column.className)),
15263
+ ...hasActions ? [actionsCol] : []
15264
+ ] : [];
15265
+ const tableStyle = fixedOn && floors.length ? { minWidth: floors.length === 1 ? floors[0] : `calc(${floors.join(" + ")})` } : void 0;
15266
+ const sortProps = (column, columnSortable) => {
15267
+ const order = keys.findIndex((key) => key.columnId === column.id);
15268
+ const canSort = columnSortable && sortable;
15269
+ const ordinal = keys.length > 1 && order >= 0 ? order + 1 : void 0;
15270
+ return {
15271
+ sortable: canSort,
15272
+ sortDirection: sortFor(column),
15273
+ sortOrder: ordinal,
15274
+ sortOrderLabel: ordinal != null ? fillLabel(labels.sortPriority, { n: ordinal }) : void 0,
15275
+ onSort: () => {
15276
+ cycleSort(column, multi && additive.current);
15277
+ additive.current = false;
15278
+ },
15279
+ ...canSort && multi ? {
15280
+ onKeyDownCapture: (event) => {
15281
+ if (event.key === "Enter" || event.key === " ") shiftKey.current = event.shiftKey;
15282
+ },
15283
+ // A click made by Enter or Space has no pointer (detail 0) and may not carry Shift itself.
15284
+ onClickCapture: (event) => {
15285
+ additive.current = event.shiftKey || event.detail === 0 && shiftKey.current;
15286
+ shiftKey.current = false;
15287
+ }
15288
+ } : {}
15289
+ };
15290
+ };
15291
+ const idSortColumn = idColumn ? { id: idColumn.id ?? "id", header: idColumn.header, cell: () => null } : null;
15292
+ const renderRow = (row, index) => {
15293
+ const rowId = ids[index];
15294
+ const isSelected = selected.has(rowId);
15295
+ return /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
15296
+ WorkspaceRow,
15297
+ {
15298
+ row,
15299
+ rowId,
15300
+ rowLabel,
15301
+ columns,
15302
+ idColumn,
15303
+ getRowProps,
15304
+ selectable,
15305
+ selected: isSelected,
15306
+ selectDisabled: !isSelected && atCap,
15307
+ hasActions,
15308
+ actions: actionsByRow[index],
15309
+ ladderOn,
15310
+ selectRowLabel,
15311
+ openLabel,
15312
+ marked: nav || windowing != null,
15313
+ ariaRowIndex: windowed ? index + 2 : void 0,
15314
+ nav,
15315
+ tabStop: tabStop === rowId,
15316
+ handlers: handlers2
15317
+ },
15318
+ rowId
15319
+ );
15320
+ };
15321
+ let tbody;
15322
+ if (groups) {
15323
+ let offset = 0;
15324
+ tbody = groups.map((group2, groupIndex) => {
15325
+ const first = offset;
15326
+ offset += group2.rows.length;
15327
+ const open = !collapsed.has(group2.id);
15328
+ const name = group2.name ?? textOf2(group2.label) ?? group2.id;
15329
+ const groupIds = ids.slice(first, first + group2.rows.length);
15330
+ const on = groupIds.filter((rowId) => selected.has(rowId)).length;
15331
+ const count2 = group2.count ?? group2.rows.length;
15332
+ const nameId = `${baseId}-g${groupIndex}-name`;
15333
+ const countId = `${baseId}-g${groupIndex}-count`;
15334
+ const firstAgg = shown.findIndex((column) => group2.aggregates?.[column.id] != null);
15335
+ const lead = (idColumn ? 1 : 0) + (firstAgg < 0 ? shown.length + (hasActions ? 1 : 0) : firstAgg);
15336
+ const leadAgg = lead === 0 && firstAgg === 0 ? group2.aggregates?.[shown[0].id] : null;
15337
+ return /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)("tbody", { className: "uix-data-workspace__group", "data-collapsed": open ? void 0 : "", children: [
15338
+ /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)("tr", { className: "uix-data-workspace__group-head", "data-group-id": group2.id, children: [
15339
+ selectable && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("td", { className: "uix-table__selcell", "data-col": ladderOn ? "select" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
15340
+ Checkbox,
15341
+ {
15342
+ "aria-label": fillLabel(labels.selectGroup, { name }),
15343
+ disabled: group2.rows.length === 0 || atCap && on === 0,
15344
+ checked: on > 0 && on === groupIds.length,
15345
+ indeterminate: on > 0 && on < groupIds.length,
15346
+ onChange: () => toggleMany(groupIds, on < groupIds.length && !atCap)
15347
+ }
15348
+ ) }),
15349
+ /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("th", { scope: "rowgroup", colSpan: Math.max(1, lead), className: "uix-data-workspace__group-label", "aria-labelledby": `${nameId} ${countId}`, children: /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)("span", { className: "uix-data-workspace__group-title", children: [
15350
+ /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
15351
+ "button",
15352
+ {
15353
+ type: "button",
15354
+ className: "uix-data-workspace__group-toggle",
15355
+ "aria-expanded": open,
15356
+ "aria-label": fillLabel(open ? labels.collapseGroup : labels.expandGroup, { name }),
15357
+ onClick: (event) => toggleGroup(group2.id, event.currentTarget.closest("tr")),
15358
+ children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(ChevronIcon2, {})
15359
+ }
15360
+ ),
15361
+ /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("span", { className: "uix-data-workspace__group-name", id: nameId, children: group2.label }),
15362
+ /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("span", { className: "uix-data-workspace__group-count", id: countId, children: fillLabel(count2 === 1 ? labels.groupCountOne : labels.groupCount, { count: count2 }) }),
15363
+ leadAgg
15364
+ ] }) }),
15365
+ firstAgg >= 0 && shown.slice(lead === 0 ? 1 : firstAgg).map((column) => /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("td", { className: cx("uix-data-workspace__group-agg", column.align === "end" && "uix-col--num", column.className), children: group2.aggregates?.[column.id] }, column.id)),
15366
+ firstAgg >= 0 && hasActions && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("td", { className: "uix-table__actions" })
15367
+ ] }),
15368
+ open && group2.rows.map((row, at2) => renderRow(row, first + at2))
15369
+ ] }, group2.id);
15370
+ });
15371
+ } else {
15372
+ const spacer = (height, key) => /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("tr", { "aria-hidden": "true", className: "uix-data-workspace__spacer", children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("td", { colSpan: colCount, style: { height } }) }, key);
15373
+ tbody = /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)("tbody", { children: [
15374
+ virtual && windowed && start > 0 && spacer(virtual.padTop, "pad-top"),
15375
+ windowed && start > 0 && start % 2 === 0 && spacer(0, "pad-zebra"),
15376
+ mountedRows.map((row, at2) => renderRow(row, start + at2)),
15377
+ virtual && windowed && virtual.padBottom > 0 && spacer(virtual.padBottom, "pad-bottom")
15378
+ ] });
15379
+ }
15380
+ const wrapStyle = long ? { "--uix-table-max-height": `${maxHeight}px` } : void 0;
15381
+ return /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
15382
+ TableWrap,
15383
+ {
15384
+ ref: setWrap,
15385
+ refreshing,
15386
+ style: wrapStyle,
15387
+ tabIndex: windowing != null && nav ? -1 : void 0,
15388
+ className: cx("uix-data-workspace__table", long && "uix-table-wrap--scroll", cards && "uix-data-workspace__table--cards"),
15389
+ children: /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)(
15390
+ Table,
15391
+ {
15392
+ density,
15393
+ zebra,
15394
+ pinnedCol: frozen,
15395
+ pinnedActions: hasActions && !cards && !actionsTooWide,
15396
+ fixed: fixedOn || void 0,
15397
+ style: tableStyle,
15398
+ className: cx(
15399
+ selectable && frozen && "uix-data-workspace__grid--pin-id",
15400
+ ladder && `uix-table--${ladder}`,
15401
+ groups && zebra !== false && "uix-data-workspace__grid--grouped",
15402
+ nav && "uix-data-workspace__grid--rows",
15403
+ clickActivates && "uix-data-workspace__grid--click"
15404
+ ),
15405
+ "aria-label": label,
15406
+ "aria-rowcount": windowed ? ids.length + 1 : void 0,
15407
+ children: [
15408
+ fixedOn && /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)("colgroup", { children: [
15409
+ selectable && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("col", { style: { width: "var(--uix-selcol-w)" } }),
15410
+ idColumn && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("col", { style: idColumn.width ? { width: idColumn.width } : void 0 }),
15411
+ shown.map((column) => {
15412
+ const width = column.width ?? (isFlex(column.className) ? void 0 : columnFloor(void 0, column.minWidth, column.className));
15413
+ return /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("col", { className: colClasses(column.className), style: width ? { width } : void 0 }, column.id);
15414
+ }),
15415
+ hasActions && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("col", { style: { width: actionsCol } })
15416
+ ] }),
15417
+ /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)("tr", { "aria-rowindex": windowed ? 1 : void 0, children: [
15418
+ selectable && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("th", { className: "uix-table__selcol", scope: "col", "data-col": ladderOn ? "select" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
15419
+ Checkbox,
15420
+ {
15421
+ "aria-label": labels.selectAll,
15422
+ checked: onPage > 0 && onPage === ids.length,
15423
+ indeterminate: onPage > 0 && onPage < ids.length,
15424
+ disabled: atCap && onPage === 0,
15425
+ onChange: () => toggleMany(ids, onPage < ids.length && !atCap)
15426
+ }
15427
+ ) }),
15428
+ idColumn && (idColumn.sortable && idSortColumn ? /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Th, { scope: "col", "data-col": ladderOn ? "id" : void 0, ...sortProps(idSortColumn, true), children: idColumn.header }) : /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Th, { scope: "col", "data-col": ladderOn ? "id" : void 0, children: idColumn.header })),
15429
+ columns.map((column) => {
15430
+ const style = !fixedOn && (column.width || column.minWidth) ? { width: column.width, minWidth: column.minWidth } : void 0;
15431
+ return /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
15432
+ Th,
15433
+ {
15434
+ scope: "col",
15435
+ className: cx(column.align === "end" && "uix-col--num", column.className) || void 0,
15436
+ style,
15437
+ ...sortProps(column, column.sortable === true),
15438
+ ...ladderAttrs(ladderOn, column, false),
15439
+ children: column.header
15440
+ },
15441
+ column.id
15442
+ );
15443
+ }),
15444
+ hasActions && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("th", { scope: "col", className: "uix-table__actions", "data-col": ladderOn ? "actions" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("span", { className: "uix-visually-hidden", children: labels.rowActions }) })
15445
+ ] }) }),
15446
+ tbody
15447
+ ]
15448
+ }
15449
+ )
15450
+ }
15451
+ );
15452
+ }
15453
+ function WorkspaceRowInner({
15454
+ row,
15455
+ rowId,
15456
+ rowLabel,
15457
+ columns,
15458
+ idColumn,
15459
+ getRowProps,
15460
+ selectable,
15461
+ selected,
15462
+ selectDisabled,
15463
+ hasActions,
15464
+ actions,
15465
+ ladderOn,
15466
+ selectRowLabel,
15467
+ openLabel,
15468
+ marked,
15469
+ ariaRowIndex,
15470
+ nav,
15471
+ tabStop,
15472
+ handlers: handlers2
15473
+ }) {
15474
+ const text = idColumn?.text(row) ?? rowLabel?.(row) ?? rowId;
15475
+ const extra = getRowProps?.(row);
15476
+ const navProps = nav ? {
15477
+ tabIndex: tabStop ? 0 : -1,
15478
+ onKeyDown: (event) => {
15479
+ extra?.onKeyDown?.(event);
15480
+ if (!event.defaultPrevented) handlers2.key(rowId, event);
15481
+ },
15482
+ onClick: (event) => {
15483
+ extra?.onClick?.(event);
15484
+ if (!event.defaultPrevented) handlers2.click(rowId, event);
15485
+ },
15486
+ onFocus: (event) => {
15487
+ extra?.onFocus?.(event);
15488
+ handlers2.focus(rowId);
15489
+ },
15490
+ onBlur: (event) => {
15491
+ extra?.onBlur?.(event);
15492
+ handlers2.blur(rowId, event);
15493
+ }
15494
+ } : {};
15495
+ return /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)(Tr, { selected, "data-uix-row-id": marked ? rowId : void 0, "aria-rowindex": ariaRowIndex, ...extra, ...navProps, children: [
15496
+ selectable && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("td", { className: "uix-table__selcell", "data-col": ladderOn ? "select" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
15497
+ Checkbox,
15498
+ {
15499
+ "aria-label": fillLabel(selectRowLabel, { label: text }),
15500
+ checked: selected,
15501
+ disabled: selectDisabled,
15502
+ onClick: (event) => handlers2.toggle(rowId, event.currentTarget.checked, event),
15503
+ onChange: () => {
15504
+ }
15505
+ }
15506
+ ) }),
15507
+ idColumn && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Td, { className: "uix-id-cell", "data-col": ladderOn ? "id" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(IdCell, { row, text, idColumn, openLabel }) }),
15508
+ columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Td, { className: cx(column.align === "end" && "uix-col--num", column.className) || void 0, ...ladderAttrs(ladderOn, column, true), children: column.cell(row) }, column.id)),
15509
+ hasActions && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(RowActions, { "data-col": ladderOn ? "actions" : void 0, children: actions })
15510
+ ] });
15511
+ }
15512
+ var WorkspaceRow = (0, import_react89.memo)(WorkspaceRowInner);
15513
+ function IdCell({ row, text, idColumn, openLabel }) {
15514
+ const rich = idColumn.render ? /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("span", { className: "uix-data-workspace__id-text", children: idColumn.render(row) }) : null;
15515
+ const label = /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("span", { className: "uix-data-workspace__id-label", children: text });
15516
+ const arrow = idColumn.href && renderUixLink(idColumn.renderLink, {
15517
+ href: idColumn.href(row),
15518
+ className: "uix-data-workspace__open",
15519
+ "aria-label": fillLabel(openLabel, { label: text }),
15520
+ children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(ArrowUpRight, {})
15521
+ });
15522
+ if (!idColumn.onPeek && idColumn.href && idColumn.linkId) {
15523
+ return /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("span", { className: "uix-data-workspace__id", children: renderUixLink(idColumn.renderLink, {
15524
+ href: idColumn.href(row),
15525
+ className: "uix-id-cell__btn uix-data-workspace__id-link",
15526
+ children: /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)(import_jsx_runtime100.Fragment, { children: [
15527
+ rich ?? label,
15528
+ /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(ArrowUpRight, {})
15529
+ ] })
15530
+ }) });
15531
+ }
15532
+ return /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)("span", { className: "uix-data-workspace__id", children: [
15533
+ idColumn.onPeek ? /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("button", { type: "button", className: "uix-id-cell__btn", "aria-haspopup": "dialog", onClick: () => idColumn.onPeek?.(row), children: rich ?? label }) : rich ?? /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("span", { className: "uix-cell-strong uix-data-workspace__id-label", children: text }),
15534
+ arrow
15535
+ ] });
15536
+ }
15537
+ var cssEscape = (value) => typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value.replace(/["\\]/g, "\\$&");
14764
15538
  function Footer({ pagination, labels, rowCount }) {
15539
+ const provided = useUixLabels().pagination;
15540
+ const pagerLabels = provided || pagination.labels ? { ...provided, ...pagination.labels } : void 0;
14765
15541
  const sizes = pagination.pageSizes;
14766
15542
  const size = pagination.mode === "offset" ? pagination.pageSize : pagination.pageSize;
14767
15543
  const rangeText = pagination.mode === "offset" ? fillLabel(labels.range, {
@@ -14776,7 +15552,7 @@ function Footer({ pagination, labels, rowCount }) {
14776
15552
  /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Select, { size: "sm", value: String(size), onValueChange: (v) => pagination.onPageSizeChange?.(Number(v)), options: sizes.map((s) => ({ value: String(s), label: String(s) })) })
14777
15553
  ] }),
14778
15554
  /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("span", { className: "uix-toolbar__spacer" }),
14779
- pagination.mode === "offset" ? /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Pagination, { mode: "offset", page: pagination.page, pageCount: Math.max(1, Math.ceil(pagination.total / pagination.pageSize)), onChange: pagination.onPageChange }) : /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Pagination, { mode: "cursor", hasPrevious: pagination.hasPrevious, hasNext: pagination.hasNext, onFirst: pagination.onFirst, onPrevious: pagination.onPrevious, onNext: pagination.onNext, summary: pagination.summary })
15555
+ pagination.mode === "offset" ? /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Pagination, { mode: "offset", page: pagination.page, pageCount: Math.max(1, Math.ceil(pagination.total / pagination.pageSize)), onChange: pagination.onPageChange, labels: pagerLabels }) : /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Pagination, { mode: "cursor", hasPrevious: pagination.hasPrevious, hasNext: pagination.hasNext, onFirst: pagination.onFirst, onPrevious: pagination.onPrevious, onNext: pagination.onNext, summary: pagination.summary, labels: pagerLabels })
14780
15556
  ] });
14781
15557
  }
14782
15558
  function PopoverButton({ id, label, title, variant, children }) {
@@ -14801,14 +15577,27 @@ function PopoverButton({ id, label, title, variant, children }) {
14801
15577
  }
14802
15578
  function ChipSummary({ field, value, options }) {
14803
15579
  const resolved = useResolvedFilterLabels(field, value);
14804
- return /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(import_jsx_runtime100.Fragment, { children: summarizeFilter(resolved.field, resolved.value, options) });
15580
+ const provided = useUixLabels().filterSummary;
15581
+ const merged = provided ? { ...options, labels: { ...provided, ...options?.labels, operators: { ...provided.operators, ...options?.labels?.operators } } } : options;
15582
+ return /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(import_jsx_runtime100.Fragment, { children: summarizeFilter(resolved.field, resolved.value, merged) });
14805
15583
  }
14806
15584
  function openInside(root) {
14807
15585
  try {
14808
15586
  if (root.querySelector(":popover-open")) return true;
14809
15587
  } catch {
14810
15588
  }
14811
- return root.querySelector('dialog[open], [aria-expanded="true"]') != null;
15589
+ if (root.querySelector("dialog[open]")) return true;
15590
+ return Array.from(root.querySelectorAll('[aria-expanded="true"]')).some(stillExpanded);
15591
+ }
15592
+ function stillExpanded(el) {
15593
+ const controls = el.getAttribute("aria-controls");
15594
+ const target = controls ? document.getElementById(controls) : null;
15595
+ if (!target?.hasAttribute("popover")) return true;
15596
+ try {
15597
+ return target.matches(":popover-open");
15598
+ } catch {
15599
+ return true;
15600
+ }
14812
15601
  }
14813
15602
  function FilterChips({ filters, labels, baseId }) {
14814
15603
  const [editing, setEditing] = (0, import_react89.useState)(null);
@@ -14851,6 +15640,10 @@ function FilterChips({ filters, labels, baseId }) {
14851
15640
  setFocusChip(null);
14852
15641
  }
14853
15642
  });
15643
+ const clearSet = () => {
15644
+ if (filters.onClearAll) filters.onClearAll({ search: false });
15645
+ else for (const f of set) filters.onChange(f.id, void 0);
15646
+ };
14854
15647
  return /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)(import_jsx_runtime100.Fragment, { children: [
14855
15648
  set.map((field) => /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("span", { ref: (el) => {
14856
15649
  if (el) anchors.current.set(field.id, el);
@@ -14866,9 +15659,7 @@ function FilterChips({ filters, labels, baseId }) {
14866
15659
  }
14867
15660
  ) }, field.id)),
14868
15661
  unset.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)("span", { ref: addRef, children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Menu, { trigger: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Chip, { variant: "add", icon: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(PlusIcon2, {}), children: labels.addFilter }), children: unset.map((field) => /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(MenuItem, { onSelect: () => edit(field, addRef.current), children: field.label }, field.id)) }) }),
14869
- set.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Button, { size: "sm", variant: "link", onClick: () => {
14870
- for (const f of set) filters.onChange(f.id, void 0);
14871
- }, children: labels.clearFilters }),
15662
+ set.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Button, { size: "sm", variant: "link", onClick: clearSet, children: labels.clearFilters }),
14872
15663
  /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Popover, { id: popId, anchor: editing?.anchor ?? null, role: "dialog", "aria-label": editing?.field.label, children: editing && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(FilterEditor, { field: editing.field, value: draft, onValueChange: setDraft, onApply: (v) => finish(v), onClear: () => finish(void 0) }) })
14873
15664
  ] });
14874
15665
  }
@@ -15049,7 +15840,7 @@ function groupCommandItems(items) {
15049
15840
  // src/components/CommandPalette.tsx
15050
15841
  var import_jsx_runtime105 = require("react/jsx-runtime");
15051
15842
  var CmdkContext = (0, import_react91.createContext)(null);
15052
- var useIsomorphicLayoutEffect22 = typeof document !== "undefined" ? import_react91.useLayoutEffect : import_react91.useEffect;
15843
+ var useIsomorphicLayoutEffect23 = typeof document !== "undefined" ? import_react91.useLayoutEffect : import_react91.useEffect;
15053
15844
  function CommandPalette(props) {
15054
15845
  if (props.items) return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(CommandPaletteItems, { ...props, items: props.items });
15055
15846
  return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(CommandPaletteChildren, { ...props });
@@ -15084,7 +15875,7 @@ function CommandPaletteChildren({
15084
15875
  resultsTextRef.current = resultsText;
15085
15876
  const listId = (0, import_react91.useId)();
15086
15877
  const rootRef = (0, import_react91.useRef)(null);
15087
- useIsomorphicLayoutEffect22(() => {
15878
+ useIsomorphicLayoutEffect23(() => {
15088
15879
  const overlay = rootRef.current?.closest("dialog, [popover]");
15089
15880
  if (!overlay || overlay.hasAttribute("data-uix-instant")) return;
15090
15881
  overlay.setAttribute("data-uix-instant", "");
@@ -15596,7 +16387,7 @@ function treeNav(flat, currentId, key) {
15596
16387
 
15597
16388
  // src/components/Tree.tsx
15598
16389
  var import_jsx_runtime108 = require("react/jsx-runtime");
15599
- var ChevronIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("svg", { viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("path", { d: "M2 4l4 4 4-4" }) });
16390
+ var ChevronIcon3 = () => /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("svg", { viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("path", { d: "M2 4l4 4 4-4" }) });
15600
16391
  var DEFAULT_TREE_LABELS = {
15601
16392
  loading: "Loading\u2026",
15602
16393
  error: "Could not load.",
@@ -15650,7 +16441,7 @@ function TreeNode({ node, expanded, selected, selectable, tabbableId, onToggle,
15650
16441
  onToggle(node.id);
15651
16442
  onFocusNode(node.id);
15652
16443
  },
15653
- children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(ChevronIcon2, {})
16444
+ children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(ChevronIcon3, {})
15654
16445
  }
15655
16446
  )
15656
16447
  ) : /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("span", { className: "uix-tree__toggle" }),
@@ -16050,7 +16841,7 @@ function VirtualTreeView({
16050
16841
  onToggle(f.node.id);
16051
16842
  setFocusedId(f.node.id);
16052
16843
  },
16053
- children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(ChevronIcon2, {})
16844
+ children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(ChevronIcon3, {})
16054
16845
  }
16055
16846
  )
16056
16847
  ) : /* @__PURE__ */ (0, import_jsx_runtime108.jsx)("span", { className: "uix-tree__toggle" }),
@@ -16113,7 +16904,7 @@ var idRef = (chartId, nodeId, part) => {
16113
16904
  return `${chartId}-${nodeId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 40)}-${hash.toString(36)}-${part}`;
16114
16905
  };
16115
16906
  var isExpandable = (node, known, lazy) => known ? known.length > 0 : lazy && (node.reportCount ?? 0) > 0;
16116
- var ChevronIcon3 = () => /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("svg", { viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("path", { d: "M2 4l4 4 4-4" }) });
16907
+ var ChevronIcon4 = () => /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("svg", { viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("path", { d: "M2 4l4 4 4-4" }) });
16117
16908
  var PlusIcon3 = () => /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("path", { d: "M8 3v10M3 8h10" }) });
16118
16909
  var MinusIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("path", { d: "M3 8h10" }) });
16119
16910
  var FitIcon = () => /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)("path", { d: "M2 6V2h4M14 6V2h-4M2 10v4h4M14 10v4h-4" }) });
@@ -16208,7 +16999,7 @@ function OrgChartItem({ node, level, size, pos, ctx }) {
16208
16999
  ctx.onToggle(node.id);
16209
17000
  },
16210
17001
  children: [
16211
- /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(ChevronIcon3, {}),
17002
+ /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(ChevronIcon4, {}),
16212
17003
  count2 > 0 ? count2 : null
16213
17004
  ]
16214
17005
  }
@@ -19691,7 +20482,7 @@ var DEFAULT_UNITS = { row: 36, pad: 16, head: 40 };
19691
20482
  var ESTIMATED_VIEWPORT = 720;
19692
20483
  var OVERSCAN_ROWS = 4;
19693
20484
  var BAR_MIN_FALLBACK = 1.5 * 16;
19694
- var useIsomorphicLayoutEffect23 = typeof document !== "undefined" ? import_react102.useLayoutEffect : import_react102.useEffect;
20485
+ var useIsomorphicLayoutEffect24 = typeof document !== "undefined" ? import_react102.useLayoutEffect : import_react102.useEffect;
19695
20486
  var sameExtent = (a, b) => a.subRows === b.subRows && a.lanes === b.lanes && a.heads === b.heads;
19696
20487
  var IN_LANE = { scope: "lane", labelTops: ["0%"] };
19697
20488
  var vars3 = (values) => values;
@@ -19801,7 +20592,7 @@ function SchedulingTimeline({
19801
20592
  // eslint-disable-line react-hooks/exhaustive-deps
19802
20593
  );
19803
20594
  const tickCount = ticks.length;
19804
- useIsomorphicLayoutEffect23(() => {
20595
+ useIsomorphicLayoutEffect24(() => {
19805
20596
  if (!layerTrack) return void 0;
19806
20597
  const measure3 = () => {
19807
20598
  const width = layerTrack.getBoundingClientRect().width;
@@ -19955,7 +20746,7 @@ function SchedulingTimeline({
19955
20746
  list.sort((a, b) => a - b);
19956
20747
  return { mounted: list, windowStart: window_.start };
19957
20748
  }, [virtual, rows, offsets, view, overscan, pinned, focusedRow, anchorRow]);
19958
- useIsomorphicLayoutEffect23(() => {
20749
+ useIsomorphicLayoutEffect24(() => {
19959
20750
  const anchor = anchorRef.current;
19960
20751
  if (anchorKey === null || !anchor || anchor.key !== anchorKey) return;
19961
20752
  anchorRef.current = null;
@@ -21413,7 +22204,7 @@ function EquivalentRelationshipList({
21413
22204
  // src/components/RelationshipGraphLayered.tsx
21414
22205
  var import_jsx_runtime120 = require("react/jsx-runtime");
21415
22206
  var EMPTY_IDS2 = /* @__PURE__ */ new Set();
21416
- var useIsomorphicLayoutEffect24 = typeof document !== "undefined" ? import_react105.useLayoutEffect : import_react105.useEffect;
22207
+ var useIsomorphicLayoutEffect25 = typeof document !== "undefined" ? import_react105.useLayoutEffect : import_react105.useEffect;
21417
22208
  var MIN_ZOOM2 = 0.4;
21418
22209
  var MAX_ZOOM2 = 1.5;
21419
22210
  var READABLE_ZOOM = 0.75;
@@ -21873,7 +22664,7 @@ function LayeredRelationshipGraph({
21873
22664
  }) })
21874
22665
  ] });
21875
22666
  }, [layout, activeIds, highlightedEdgeIds, conflictedEdgeIds, dimmedNodeIds, labelPlacement, labels]);
21876
- useIsomorphicLayoutEffect24(() => {
22667
+ useIsomorphicLayoutEffect25(() => {
21877
22668
  const svg2 = edgesRef.current;
21878
22669
  if (!svg2) return;
21879
22670
  for (const group2 of Array.from(svg2.querySelectorAll(".uix-relationship-graph__edge-label"))) {
@@ -23968,6 +24759,7 @@ function TextDiff({
23968
24759
  DiffViewer,
23969
24760
  DisplayPreferences,
23970
24761
  Drawer,
24762
+ ENUM_FILTER_OPERATORS,
23971
24763
  EmptyState,
23972
24764
  EntityPicker,
23973
24765
  ErrorState,
@@ -24162,6 +24954,7 @@ function TextDiff({
24162
24954
  edgeLabelSize,
24163
24955
  emptyFilterValue,
24164
24956
  encodeQr,
24957
+ enumColumnFilter,
24165
24958
  enumerateDateKeys,
24166
24959
  enumerateDateSpan,
24167
24960
  fileKind,