@ai-matrx/design-system 0.21.10 → 0.21.12

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.21.12
4
+
5
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
6
+ `git diff npm/design-system/v0.21.11..npm/design-system/v0.21.12 -- apps/shared/design-system`).
7
+ No source changes intended and no consumer action required.
8
+
9
+ ## 0.21.11
10
+
11
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
12
+ `git diff npm/design-system/v0.21.10..npm/design-system/v0.21.11 -- apps/shared/design-system`).
13
+ No source changes intended and no consumer action required.
14
+
3
15
  ## 0.21.10
4
16
 
5
17
  Automatic changed-only republish (docs/metadata drift since the last tag — see
@@ -1,5 +1,5 @@
1
1
  import { AgentPayloadInput } from './copy-types.cjs';
2
- import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../types-BAJiOaQL.cjs';
2
+ import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../types-9Ii-b9EJ.cjs';
3
3
  import '../content-transfer.cjs';
4
4
  import 'react';
5
5
  import '@ai-matrx/kit/content-transfer';
@@ -1,5 +1,5 @@
1
1
  import { AgentPayloadInput } from './copy-types.js';
2
- import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../types-Da781-Wq.js';
2
+ import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../types-wy1EEaeG.js';
3
3
  import '../content-transfer.js';
4
4
  import 'react';
5
5
  import '@ai-matrx/kit/content-transfer';
@@ -1,4 +1,4 @@
1
- import { M as MatrxColumnDef, C as ColumnFiltersState, S as SortState, L as LayeredFilterRule, T as TableSearchMatchMode, a as ColumnFilterValue } from '../types-BAJiOaQL.cjs';
1
+ import { M as MatrxColumnDef, C as ColumnFiltersState, S as SortState, L as LayeredFilterRule, T as TableSearchMatchMode, a as ColumnFilterValue } from '../types-9Ii-b9EJ.cjs';
2
2
  import '../content-transfer.cjs';
3
3
  import 'react';
4
4
  import '@ai-matrx/kit/content-transfer';
@@ -1,4 +1,4 @@
1
- import { M as MatrxColumnDef, C as ColumnFiltersState, S as SortState, L as LayeredFilterRule, T as TableSearchMatchMode, a as ColumnFilterValue } from '../types-Da781-Wq.js';
1
+ import { M as MatrxColumnDef, C as ColumnFiltersState, S as SortState, L as LayeredFilterRule, T as TableSearchMatchMode, a as ColumnFilterValue } from '../types-wy1EEaeG.js';
2
2
  import '../content-transfer.js';
3
3
  import 'react';
4
4
  import '@ai-matrx/kit/content-transfer';
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { ComponentType, ReactNode, MouseEventHandler } from 'react';
3
- import { b as TableSavedViewsProps, c as MatrxDataTableRecordControls, d as MatrxDataTableDensity } from '../types-BAJiOaQL.cjs';
3
+ import { b as TableSavedViewsProps, c as MatrxDataTableRecordControls, d as MatrxDataTableDensity } from '../types-9Ii-b9EJ.cjs';
4
4
  import { CopyControlProps } from './copy-types.cjs';
5
5
  export { AgentPayloadInput, AiCustomSource, AiVariant, CopyExportConfig } from './copy-types.cjs';
6
6
  import '../content-transfer.cjs';
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { ComponentType, ReactNode, MouseEventHandler } from 'react';
3
- import { b as TableSavedViewsProps, c as MatrxDataTableRecordControls, d as MatrxDataTableDensity } from '../types-Da781-Wq.js';
3
+ import { b as TableSavedViewsProps, c as MatrxDataTableRecordControls, d as MatrxDataTableDensity } from '../types-wy1EEaeG.js';
4
4
  import { CopyControlProps } from './copy-types.js';
5
5
  export { AgentPayloadInput, AiCustomSource, AiVariant, CopyExportConfig } from './copy-types.js';
6
6
  import '../content-transfer.js';
@@ -5899,6 +5899,7 @@ function ColumnHeaderCell({
5899
5899
  onFilterChange,
5900
5900
  selectOptions = [],
5901
5901
  selectSingle = false,
5902
+ selectAllowCustom = false,
5902
5903
  align = "left",
5903
5904
  className,
5904
5905
  compact = false,
@@ -5934,9 +5935,13 @@ function ColumnHeaderCell({
5934
5935
  const filterActive = isColumnFilterActive(filterValue);
5935
5936
  const deferredFilter = filterApplyLabel !== void 0 || filterShowCancel || filterFooterHint !== void 0;
5936
5937
  const [filterOpen, setFilterOpen] = (0, import_react13.useState)(false);
5938
+ const [pendingNegation, setPendingNegation] = (0, import_react13.useState)(false);
5937
5939
  const [draftFilter, setDraftFilter] = (0, import_react13.useState)(filterValue);
5938
5940
  (0, import_react13.useEffect)(() => {
5939
- if (!filterOpen) setDraftFilter(filterValue);
5941
+ if (!filterOpen) {
5942
+ setDraftFilter(filterValue);
5943
+ setPendingNegation(filterValue?.negated ?? false);
5944
+ }
5940
5945
  }, [filterOpen, filterValue]);
5941
5946
  const applyFilter = () => {
5942
5947
  onFilterChange(draftFilter);
@@ -5948,6 +5953,7 @@ function ColumnHeaderCell({
5948
5953
  };
5949
5954
  const visibleFilter = deferredFilter ? draftFilter : filterValue;
5950
5955
  const setNegated = (negated) => {
5956
+ setPendingNegation(negated);
5951
5957
  if (!visibleFilter) return;
5952
5958
  const next = { ...visibleFilter, ...negated ? { negated: true } : {} };
5953
5959
  if (!negated) delete next.negated;
@@ -6024,12 +6030,13 @@ function ColumnHeaderCell({
6024
6030
  kind: filterKind,
6025
6031
  value: visibleFilter,
6026
6032
  onChange: (next) => {
6027
- const preservingNegation = next && visibleFilter?.negated ? { ...next, negated: true } : next;
6033
+ const preservingNegation = next && (visibleFilter?.negated ?? pendingNegation) ? { ...next, negated: true } : next;
6028
6034
  if (deferredFilter) setDraftFilter(preservingNegation);
6029
6035
  else onFilterChange(preservingNegation);
6030
6036
  },
6031
6037
  selectOptions,
6032
6038
  selectSingle,
6039
+ selectAllowCustom,
6033
6040
  ...markerLabels ? {
6034
6041
  booleanLabels: {
6035
6042
  all: "All",
@@ -6040,18 +6047,18 @@ function ColumnHeaderCell({
6040
6047
  allowNullModes
6041
6048
  }
6042
6049
  ) : null,
6043
- filterKind && visibleFilter && allowNegation ? /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "mt-2 border-t border-border pt-2", children: [
6050
+ filterKind && allowNegation ? /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "mt-2 border-t border-border pt-2", children: [
6044
6051
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { className: "mb-1 text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: "Match mode" }),
6045
6052
  /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "grid grid-cols-2 gap-1 rounded-md border p-0.5", children: [
6046
6053
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
6047
6054
  "button",
6048
6055
  {
6049
6056
  type: "button",
6050
- "aria-pressed": !visibleFilter.negated,
6057
+ "aria-pressed": !(visibleFilter?.negated ?? pendingNegation),
6051
6058
  onClick: () => setNegated(false),
6052
6059
  className: cn(
6053
6060
  "rounded px-2 py-1.5 text-xs transition-colors",
6054
- !visibleFilter.negated ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"
6061
+ !(visibleFilter?.negated ?? pendingNegation) ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"
6055
6062
  ),
6056
6063
  children: "Include"
6057
6064
  }
@@ -6060,11 +6067,11 @@ function ColumnHeaderCell({
6060
6067
  "button",
6061
6068
  {
6062
6069
  type: "button",
6063
- "aria-pressed": visibleFilter.negated === true,
6070
+ "aria-pressed": visibleFilter?.negated ?? pendingNegation,
6064
6071
  onClick: () => setNegated(true),
6065
6072
  className: cn(
6066
6073
  "rounded px-2 py-1.5 text-xs transition-colors",
6067
- visibleFilter.negated ? "bg-destructive text-destructive-foreground" : "text-muted-foreground hover:bg-muted"
6074
+ visibleFilter?.negated ?? pendingNegation ? "bg-destructive text-destructive-foreground" : "text-muted-foreground hover:bg-muted"
6068
6075
  ),
6069
6076
  children: "Exclude"
6070
6077
  }
@@ -6252,6 +6259,7 @@ function FilterBody({
6252
6259
  onChange,
6253
6260
  selectOptions,
6254
6261
  selectSingle = false,
6262
+ selectAllowCustom = false,
6255
6263
  allowNullModes = false,
6256
6264
  booleanLabels
6257
6265
  }) {
@@ -6315,15 +6323,29 @@ function FilterBody({
6315
6323
  }
6316
6324
  if (kind === "select") {
6317
6325
  const selected = value?.kind === "select" ? value.values ?? (value.value && value.value !== "__all__" ? [value.value] : []) : [];
6318
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
6319
- SearchableSelectFilter,
6320
- {
6321
- selected,
6322
- options: selectOptions,
6323
- onChange,
6324
- single: selectSingle
6325
- }
6326
- );
6326
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "space-y-2", children: [
6327
+ allowNullModes && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex flex-wrap gap-1", "aria-label": "Blank checks", children: [["empty", "(empty)"], ["not_empty", "(not empty)"], ["null", "(null)"], ["not_null", "(not null)"]].map(([mode, label]) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
6328
+ "button",
6329
+ {
6330
+ type: "button",
6331
+ "aria-pressed": value?.kind === "text" && value.mode === mode,
6332
+ className: "rounded border px-1.5 py-0.5 text-[10px] hover:bg-muted",
6333
+ onClick: () => onChange({ kind: "text", value: "", mode }),
6334
+ children: label
6335
+ },
6336
+ mode
6337
+ )) }),
6338
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
6339
+ SearchableSelectFilter,
6340
+ {
6341
+ selected,
6342
+ options: selectOptions,
6343
+ onChange,
6344
+ single: selectSingle,
6345
+ allowCustom: selectAllowCustom
6346
+ }
6347
+ )
6348
+ ] });
6327
6349
  }
6328
6350
  if (kind === "boolean") {
6329
6351
  const current = value?.kind === "boolean" ? value.value : "__all__";
@@ -6355,7 +6377,8 @@ function SearchableSelectFilter({
6355
6377
  selected,
6356
6378
  options,
6357
6379
  onChange,
6358
- single = false
6380
+ single = false,
6381
+ allowCustom = false
6359
6382
  }) {
6360
6383
  const [query, setQuery] = (0, import_react13.useState)("");
6361
6384
  const filtered = (0, import_react13.useMemo)(() => {
@@ -6405,6 +6428,11 @@ function SearchableSelectFilter({
6405
6428
  /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(CommandList, { className: "max-h-48", children: [
6406
6429
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(CommandEmpty, { className: "py-3 text-xs text-muted-foreground", children: "No matches" }),
6407
6430
  /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(CommandGroup, { children: [
6431
+ allowCustom && query.trim() && !options.some((option) => option.value === query.trim()) && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(CommandItem, { value: `custom:${query.trim()}`, onSelect: () => toggle(query.trim()), className: "text-xs", children: [
6432
+ "Use \u201C",
6433
+ query.trim(),
6434
+ "\u201D"
6435
+ ] }),
6408
6436
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
6409
6437
  CommandItem,
6410
6438
  {
@@ -8588,6 +8616,10 @@ function TableToolbarAction({
8588
8616
  function hasSameRowReferences(previous, next) {
8589
8617
  return previous.length === next.length && previous.every((row, index) => row === next[index]);
8590
8618
  }
8619
+ var VERTICAL_SCROLL_GEOMETRY_TOLERANCE_PX = 2;
8620
+ function hasUsableVerticalScroll(element) {
8621
+ return element.scrollHeight - element.clientHeight > VERTICAL_SCROLL_GEOMETRY_TOLERANCE_PX;
8622
+ }
8591
8623
  function TableRowCopyControl({
8592
8624
  copy,
8593
8625
  row,
@@ -8753,6 +8785,7 @@ function MatrxDataTableCore({
8753
8785
  hidePagination = false,
8754
8786
  filterUi,
8755
8787
  detail,
8788
+ expandedDetail,
8756
8789
  window: windowConfig,
8757
8790
  copy,
8758
8791
  contextMenu,
@@ -9102,7 +9135,7 @@ function MatrxDataTableCore({
9102
9135
  if (!scroll || !table || !header || !stickyHeader) return;
9103
9136
  let frame = 0;
9104
9137
  const update = () => {
9105
- const hasInternalVerticalScroll = scroll.scrollHeight > scroll.clientHeight + 1;
9138
+ const hasInternalVerticalScroll = hasUsableVerticalScroll(scroll);
9106
9139
  const nextDocumentSticky = !hasInternalVerticalScroll;
9107
9140
  if (documentStickyHeaderRef.current !== nextDocumentSticky) {
9108
9141
  documentStickyHeaderRef.current = nextDocumentSticky;
@@ -9122,7 +9155,7 @@ function MatrxDataTableCore({
9122
9155
  for (let ancestor = scroll.parentElement; ancestor; ancestor = ancestor.parentElement) {
9123
9156
  const ancestorStyle = window.getComputedStyle(ancestor);
9124
9157
  const overflowY = ancestorStyle.overflowY;
9125
- if (/^(auto|scroll|overlay)$/.test(overflowY) && ancestor.scrollHeight > ancestor.clientHeight + 1) {
9158
+ if (/^(auto|scroll|overlay)$/.test(overflowY) && hasUsableVerticalScroll(ancestor)) {
9126
9159
  const scrollPaddingTop = Number.parseFloat(ancestorStyle.scrollPaddingTop) || 0;
9127
9160
  scrollTargetTop = ancestor.getBoundingClientRect().top + scrollPaddingTop;
9128
9161
  break;
@@ -9620,7 +9653,20 @@ function MatrxDataTableCore({
9620
9653
  setWindowRowId(null);
9621
9654
  setWindowRowSnapshot(null);
9622
9655
  };
9656
+ const canExpandRow = (row) => Boolean(expandedDetail && (expandedDetail.canExpand?.(row) ?? true));
9657
+ const isRowExpanded = (row) => canExpandRow(row) && expandedDetail?.expandedId === getRowId(row);
9658
+ const toggleExpanded = (row) => {
9659
+ if (!expandedDetail || !canExpandRow(row)) return;
9660
+ const id = getRowId(row);
9661
+ expandedDetail.onExpandedIdChange(
9662
+ expandedDetail.expandedId === id ? null : id
9663
+ );
9664
+ };
9623
9665
  const openRow = (row) => {
9666
+ if (canExpandRow(row)) {
9667
+ toggleExpanded(row);
9668
+ return;
9669
+ }
9624
9670
  if (opensWindowOnRowClick) {
9625
9671
  openWindow(row);
9626
9672
  return;
@@ -9724,7 +9770,11 @@ function MatrxDataTableCore({
9724
9770
  } : {},
9725
9771
  ...editEnabled && visibleColumns.some(
9726
9772
  (column) => column.editable && (column.editableIf?.(row) ?? true)
9727
- ) ? { beginEdit: () => beginRowEdit(row) } : {}
9773
+ ) ? { beginEdit: () => beginRowEdit(row) } : {},
9774
+ ...canExpandRow(row) ? {
9775
+ isExpanded: isRowExpanded(row),
9776
+ toggleExpanded: () => toggleExpanded(row)
9777
+ } : {}
9728
9778
  };
9729
9779
  };
9730
9780
  const hierarchyRows = hierarchy?.rows ?? data;
@@ -10630,6 +10680,7 @@ function MatrxDataTableCore({
10630
10680
  ] }) }) : paginated.map((row, index) => {
10631
10681
  const id = getRowId(row);
10632
10682
  const displayRow = applyRowEdits(row, edits[id]);
10683
+ const controls = buildRecordControls(displayRow);
10633
10684
  const selectable = selection?.isRowSelectable?.(row) ?? Boolean(selection);
10634
10685
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { "data-row-id": id, className: "contents", children: mobileCards(displayRow, index, {
10635
10686
  renderCell: (columnKey) => {
@@ -10664,10 +10715,15 @@ function MatrxDataTableCore({
10664
10715
  ...copy === void 0 ? { defaultColumns: declaredVisibleCopyColumns } : {}
10665
10716
  }
10666
10717
  ) : null,
10667
- renderRowActions(displayRow, buildRecordControls(displayRow))
10718
+ renderRowActions(displayRow, controls)
10668
10719
  ]
10669
10720
  }
10670
- ) : null
10721
+ ) : null,
10722
+ ...canExpandRow(row) ? {
10723
+ isExpanded: isRowExpanded(row),
10724
+ toggleExpanded: () => toggleExpanded(row)
10725
+ } : {},
10726
+ expandedDetail: expandedDetail && isRowExpanded(row) ? expandedDetail.render(displayRow, controls) : null
10671
10727
  }) }, id);
10672
10728
  }),
10673
10729
  appendQuery !== null && isFetching && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
@@ -10857,6 +10913,7 @@ function MatrxDataTableCore({
10857
10913
  onFilterChange: (next) => setColumnFilter(id, next),
10858
10914
  selectOptions: meta?.options,
10859
10915
  selectSingle: col.filterSingle,
10916
+ selectAllowCustom: col.filterAllowCustom,
10860
10917
  filterApplyLabel: filterUi?.applyLabel,
10861
10918
  filterShowCancel: filterUi?.showCancel,
10862
10919
  filterFooterHint: filterUi?.footerHint,
@@ -10934,6 +10991,7 @@ function MatrxDataTableCore({
10934
10991
  const isSelected = selectedId === id;
10935
10992
  const rowEdits = edits[id];
10936
10993
  const displayRow = applyRowEdits(row, rowEdits);
10994
+ const controls = buildRecordControls(displayRow);
10937
10995
  const isChecked = selectedIdSet.has(id);
10938
10996
  const rowNode = /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
10939
10997
  "tr",
@@ -10962,7 +11020,7 @@ function MatrxDataTableCore({
10962
11020
  // horizontally. The translucent zebra/hover tints remain
10963
11021
  // desktop-only so densely layered mobile content stays crisp.
10964
11022
  "border-b border-border/60 bg-card transition-colors",
10965
- (detailEnabled || Boolean(onRowOpen)) && "cursor-pointer sm:hover:bg-muted/50",
11023
+ (detailEnabled || Boolean(onRowOpen) || canExpandRow(row)) && "cursor-pointer sm:hover:bg-muted/50",
10966
11024
  isSelected && "bg-muted",
10967
11025
  isChecked && "bg-primary/5",
10968
11026
  highlightedIndex === index && "bg-muted ring-1 ring-inset ring-primary/60",
@@ -11078,7 +11136,7 @@ function MatrxDataTableCore({
11078
11136
  ...copy === void 0 ? { defaultColumns: declaredVisibleCopyColumns } : {}
11079
11137
  }
11080
11138
  ) : null,
11081
- renderRowActions(displayRow, buildRecordControls(displayRow)),
11139
+ renderRowActions(displayRow, controls),
11082
11140
  windowEnabled ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(Tooltip, { children: [
11083
11141
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
11084
11142
  Button,
@@ -11109,15 +11167,36 @@ function MatrxDataTableCore({
11109
11167
  id
11110
11168
  );
11111
11169
  const wrappedRow = rowWrapper ? rowWrapper(row, rowNode) : rowNode;
11112
- return hierarchy ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
11113
- HierarchyDroppableRow,
11170
+ const inlineDetail = expandedDetail && isRowExpanded(row) ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
11171
+ "tr",
11114
11172
  {
11115
- id,
11116
- disabled: !canDropOn(row),
11117
- children: wrappedRow
11173
+ "data-matrx-table-expanded-detail": id,
11174
+ className: cn("bg-muted/20", expandedDetail.className),
11175
+ children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
11176
+ "td",
11177
+ {
11178
+ colSpan: visibleColumns.length + leadingCols + (showActionsCol ? 1 : 0),
11179
+ className: "p-4",
11180
+ children: expandedDetail.render(displayRow, controls)
11181
+ }
11182
+ )
11118
11183
  },
11119
- id
11120
- ) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react20.Fragment, { children: wrappedRow }, id);
11184
+ `${id}:expanded-detail`
11185
+ ) : null;
11186
+ return hierarchy ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_react20.Fragment, { children: [
11187
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
11188
+ HierarchyDroppableRow,
11189
+ {
11190
+ id,
11191
+ disabled: !canDropOn(row),
11192
+ children: wrappedRow
11193
+ }
11194
+ ),
11195
+ inlineDetail
11196
+ ] }, id) : /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_react20.Fragment, { children: [
11197
+ wrappedRow,
11198
+ inlineDetail
11199
+ ] }, id);
11121
11200
  }),
11122
11201
  appendQuery !== null && isFetching && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("tr", { "data-matrx-table-append-loading-row": true, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
11123
11202
  "td",