@ai-matrx/design-system 0.20.6 → 0.20.8

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.
@@ -8,9 +8,9 @@ import {
8
8
  useCallback as useCallback6,
9
9
  useEffect as useEffect13,
10
10
  useId as useId2,
11
- useLayoutEffect,
11
+ useLayoutEffect as useLayoutEffect2,
12
12
  useMemo as useMemo7,
13
- useRef as useRef10,
13
+ useRef as useRef11,
14
14
  useState as useState19
15
15
  } from "react";
16
16
  import {
@@ -183,6 +183,46 @@ var Button = React.forwardRef(
183
183
  );
184
184
  Button.displayName = "Button";
185
185
 
186
+ // src/data-table/use-column-reorder-animation.ts
187
+ import { useLayoutEffect, useRef } from "react";
188
+ function useColumnReorderAnimation(tableRef, order, dragging) {
189
+ const previous = useRef({ positions: /* @__PURE__ */ new Map(), dragging: false });
190
+ const animations = useRef([]);
191
+ useLayoutEffect(() => {
192
+ const table = tableRef.current;
193
+ if (!table) return;
194
+ const headers = Array.from(table.querySelectorAll("th[data-matrx-table-column-id]"));
195
+ const positions = new Map(headers.map((header) => [header.dataset.matrxTableColumnId, header.offsetLeft]));
196
+ const prefersReducedMotion = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
197
+ const shouldAnimate = (dragging || previous.current.dragging) && !prefersReducedMotion;
198
+ const offsets = /* @__PURE__ */ new Map();
199
+ for (const header of headers) {
200
+ const id = header.dataset.matrxTableColumnId;
201
+ const oldLeft = previous.current.positions.get(id);
202
+ if (oldLeft === void 0) continue;
203
+ const transform = getComputedStyle(header).transform;
204
+ const remaining = transform && transform !== "none" ? new DOMMatrixReadOnly(transform).m41 : 0;
205
+ offsets.set(id, oldLeft - header.offsetLeft + remaining);
206
+ }
207
+ animations.current.forEach((animation) => animation.cancel());
208
+ animations.current = [];
209
+ if (shouldAnimate) {
210
+ for (const cell of Array.from(table.querySelectorAll("[data-matrx-table-column-id]"))) {
211
+ const offset = offsets.get(cell.dataset.matrxTableColumnId);
212
+ if (!offset || typeof cell.animate !== "function") continue;
213
+ animations.current.push(cell.animate(
214
+ [{ transform: `translateX(${offset}px)` }, { transform: "translateX(0)" }],
215
+ { duration: 180, easing: "cubic-bezier(0.2, 0.8, 0.2, 1)" }
216
+ ));
217
+ }
218
+ }
219
+ previous.current = { positions, dragging };
220
+ }, [tableRef, order, dragging]);
221
+ useLayoutEffect(() => () => {
222
+ animations.current.forEach((animation) => animation.cancel());
223
+ }, []);
224
+ }
225
+
186
226
  // src/data-table/MatrxDataTable.tsx
187
227
  import { formatCount as formatCount3 } from "@ai-matrx/kit/format";
188
228
 
@@ -928,7 +968,7 @@ function TableTitleRow({
928
968
  }
929
969
 
930
970
  // src/data-table/TableSearch.tsx
931
- import { useRef, useState } from "react";
971
+ import { useRef as useRef2, useState } from "react";
932
972
 
933
973
  // src/tooltip.tsx
934
974
  import * as TooltipPrimitive from "@radix-ui/react-tooltip";
@@ -960,7 +1000,7 @@ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
960
1000
  import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
961
1001
  function TableSearch({ value, onChange, scope, onScopeChange, scopes, placeholder = "Search\u2026" }) {
962
1002
  const [expanded, setExpanded] = useState(false);
963
- const inputRef = useRef(null);
1003
+ const inputRef = useRef2(null);
964
1004
  return /* @__PURE__ */ jsx13("div", { "data-matrx-table-search": true, className: "h-11 min-w-0 w-full sm:max-w-xs", children: /* @__PURE__ */ jsxs8(
965
1005
  "div",
966
1006
  {
@@ -1006,7 +1046,7 @@ function TableSearch({ value, onChange, scope, onScopeChange, scopes, placeholde
1006
1046
  }
1007
1047
 
1008
1048
  // src/data-table/useScrollPagination.ts
1009
- import { useEffect, useRef as useRef2, useState as useState2 } from "react";
1049
+ import { useEffect, useRef as useRef3, useState as useState2 } from "react";
1010
1050
  var DEFAULT_THRESHOLD_PX = 96;
1011
1051
  var DEFAULT_INTENT_TIMEOUT_MS = 1200;
1012
1052
  function resolveContainer(container) {
@@ -1078,7 +1118,7 @@ function useScrollPagination({
1078
1118
  enabled = true
1079
1119
  }) {
1080
1120
  const [requestError, setRequestError] = useState2(null);
1081
- const latestRef = useRef2({
1121
+ const latestRef = useRef3({
1082
1122
  queryKey,
1083
1123
  hasNextPage,
1084
1124
  isFetchingNextPage,
@@ -1100,14 +1140,14 @@ function useScrollPagination({
1100
1140
  thresholdPx,
1101
1141
  enabled
1102
1142
  };
1103
- const intentRef = useRef2(null);
1104
- const inputSequenceRef = useRef2(0);
1105
- const consumedSequenceRef = useRef2(0);
1106
- const requestGenerationRef = useRef2(0);
1107
- const inFlightGenerationRef = useRef2(null);
1108
- const requestErrorRef = useRef2(null);
1109
- const mountedRef = useRef2(false);
1110
- const observedTopRef = useRef2(/* @__PURE__ */ new WeakMap());
1143
+ const intentRef = useRef3(null);
1144
+ const inputSequenceRef = useRef3(0);
1145
+ const consumedSequenceRef = useRef3(0);
1146
+ const requestGenerationRef = useRef3(0);
1147
+ const inFlightGenerationRef = useRef3(null);
1148
+ const requestErrorRef = useRef3(null);
1149
+ const mountedRef = useRef3(false);
1150
+ const observedTopRef = useRef3(/* @__PURE__ */ new WeakMap());
1111
1151
  const effectiveError = error ?? requestError;
1112
1152
  useEffect(() => {
1113
1153
  mountedRef.current = true;
@@ -1244,7 +1284,7 @@ function useScrollPagination({
1244
1284
  }
1245
1285
 
1246
1286
  // src/data-table/useProgressiveTableRows.ts
1247
- import { useEffect as useEffect2, useMemo, useRef as useRef3, useState as useState3 } from "react";
1287
+ import { useEffect as useEffect2, useMemo, useRef as useRef4, useState as useState3 } from "react";
1248
1288
  function safePageSize(pageSize) {
1249
1289
  return Number.isFinite(pageSize) && pageSize > 0 ? Math.floor(pageSize) : 0;
1250
1290
  }
@@ -1267,9 +1307,9 @@ function useProgressiveTableRows({
1267
1307
  const pageSize = safePageSize(rawPageSize);
1268
1308
  const hasExplicitRequestedPage = rawRequestedPage !== void 0;
1269
1309
  const requestedPage = safeRequestedPage(rawRequestedPage);
1270
- const initialRequestedPageRef = useRef3(requestedPage);
1271
- const latestRequestedPageRef = useRef3(requestedPage);
1272
- const optimisticAdvancePageRef = useRef3(null);
1310
+ const initialRequestedPageRef = useRef4(requestedPage);
1311
+ const latestRequestedPageRef = useRef4(requestedPage);
1312
+ const optimisticAdvancePageRef = useRef4(null);
1273
1313
  latestRequestedPageRef.current = requestedPage;
1274
1314
  const progressionKey = `${queryKey}\0${pageSize}`;
1275
1315
  const initialShownCount = startCount(pageSize, initialRequestedPageRef.current);
@@ -4819,6 +4859,9 @@ function compareValues(a, b) {
4819
4859
  sensitivity: "base"
4820
4860
  });
4821
4861
  }
4862
+ function selectCellValues(value) {
4863
+ return (Array.isArray(value) ? value : [value]).map(stringifyCellValue).filter((item) => item !== "");
4864
+ }
4822
4865
  function passesColumnFilter(value, filter) {
4823
4866
  const matches = (() => {
4824
4867
  switch (filter.kind) {
@@ -4835,9 +4878,9 @@ function passesColumnFilter(value, filter) {
4835
4878
  case "select": {
4836
4879
  const selected = filter.values ?? (filter.value && filter.value !== "__all__" ? [filter.value] : []);
4837
4880
  if (selected.length === 0) return true;
4838
- const s = stringifyCellValue(value);
4881
+ const values = selectCellValues(value);
4839
4882
  return selected.some(
4840
- (v) => v === SELECT_EMPTY_VALUE ? s === "" : v === SELECT_NOT_EMPTY_VALUE ? s !== "" : s === v
4883
+ (v) => v === SELECT_EMPTY_VALUE ? values.length === 0 : v === SELECT_NOT_EMPTY_VALUE ? values.length > 0 : values.includes(v)
4841
4884
  );
4842
4885
  }
4843
4886
  case "boolean": {
@@ -5177,7 +5220,7 @@ function useClippedContentGuard(ref, options) {
5177
5220
  }
5178
5221
 
5179
5222
  // src/data-table/url-state.ts
5180
- import { useCallback as useCallback4, useEffect as useEffect7, useMemo as useMemo4, useRef as useRef7, useState as useState11 } from "react";
5223
+ import { useCallback as useCallback4, useEffect as useEffect7, useMemo as useMemo4, useRef as useRef8, useState as useState11 } from "react";
5181
5224
  import {
5182
5225
  commitUrlParams,
5183
5226
  useUrlSearchParams
@@ -5257,7 +5300,7 @@ function useTableUrlState(options) {
5257
5300
  [defaultPageSize, defaultSortDirection, defaultSortId, key]
5258
5301
  );
5259
5302
  const state = readState(searchParams);
5260
- const lastTextWriteAt = useRef7(0);
5303
+ const lastTextWriteAt = useRef8(0);
5261
5304
  const [debouncedSearch, setDebouncedSearch] = useState11(state.search);
5262
5305
  useEffect7(() => {
5263
5306
  if (state.search === debouncedSearch) return;
@@ -6158,7 +6201,7 @@ function parseOptionalNumber(raw) {
6158
6201
  import {
6159
6202
  useCallback as useCallback5,
6160
6203
  useEffect as useEffect9,
6161
- useRef as useRef8,
6204
+ useRef as useRef9,
6162
6205
  useState as useState13
6163
6206
  } from "react";
6164
6207
 
@@ -6202,11 +6245,11 @@ function ColumnResizeHandle({
6202
6245
  onResizeCancel,
6203
6246
  onResizeCommit
6204
6247
  }) {
6205
- const handleRef = useRef8(null);
6206
- const activeResizeRef = useRef8(null);
6207
- const previewRafRef = useRef8(null);
6208
- const queuedWidthRef = useRef8(null);
6209
- const keyboardWidthRef = useRef8(null);
6248
+ const handleRef = useRef9(null);
6249
+ const activeResizeRef = useRef9(null);
6250
+ const previewRafRef = useRef9(null);
6251
+ const queuedWidthRef = useRef9(null);
6252
+ const keyboardWidthRef = useRef9(null);
6210
6253
  const [announcedWidth, setAnnouncedWidth] = useState13(null);
6211
6254
  const bounds = resolveColumnResizeBounds(minWidth, maxWidth);
6212
6255
  const cssVar = columnResizeCssVar(columnId2);
@@ -6663,7 +6706,7 @@ function parseTableViewSnapshot(raw) {
6663
6706
  // src/data-table/MatrxUuidCell.tsx
6664
6707
  import {
6665
6708
  useEffect as useEffect11,
6666
- useRef as useRef9,
6709
+ useRef as useRef10,
6667
6710
  useState as useState15
6668
6711
  } from "react";
6669
6712
  import { jsx as jsx32, jsxs as jsxs22 } from "react/jsx-runtime";
@@ -6708,7 +6751,7 @@ function MatrxUuidCell({
6708
6751
  const [copied, setCopied] = useState15(false);
6709
6752
  const [opening, setOpening] = useState15(false);
6710
6753
  const [peekOpen, setPeekOpen] = useState15(false);
6711
- const timer = useRef9(null);
6754
+ const timer = useRef10(null);
6712
6755
  useEffect11(
6713
6756
  () => () => {
6714
6757
  if (timer.current) clearTimeout(timer.current);
@@ -8071,7 +8114,7 @@ function resolveFilterKind(col, sampleRows) {
8071
8114
  if (values.every((v) => typeof v === "number" && Number.isFinite(v))) {
8072
8115
  return "number";
8073
8116
  }
8074
- const strings = values.map(stringifyCellValue);
8117
+ const strings = values.flatMap(selectCellValues);
8075
8118
  const unique = new Set(strings);
8076
8119
  if (unique.size >= 1 && unique.size <= SELECT_CARDINALITY_CAP && unique.size < strings.length * 0.6) {
8077
8120
  return "select";
@@ -8083,15 +8126,16 @@ function collectSelectOptions(col, rows) {
8083
8126
  const options = [];
8084
8127
  let hasEmpty = false;
8085
8128
  for (const row of rows) {
8086
- const raw = getCellValue(row, col);
8087
- const value = raw == null ? "" : stringifyCellValue(raw);
8088
- if (!value) {
8129
+ const values = selectCellValues(getCellValue(row, col));
8130
+ if (values.length === 0) {
8089
8131
  hasEmpty = true;
8090
8132
  continue;
8091
8133
  }
8092
- if (col.filterOptions?.length || seen.has(value)) continue;
8093
- seen.add(value);
8094
- options.push({ value, label: value });
8134
+ for (const value of values) {
8135
+ if (col.filterOptions?.length || seen.has(value)) continue;
8136
+ seen.add(value);
8137
+ options.push({ value, label: value });
8138
+ }
8095
8139
  }
8096
8140
  const base = col.filterOptions?.length ? [...col.filterOptions] : options.sort((a, b) => a.label.localeCompare(b.label));
8097
8141
  if (hasEmpty) {
@@ -8343,7 +8387,7 @@ function MatrxDataTableCore({
8343
8387
  }) {
8344
8388
  const appendQuery = query?.mode === "controlled-append" ? query : null;
8345
8389
  const appendQueryKey = appendQuery?.pagination.queryKey ?? null;
8346
- const committedAppendQueryKey = useRef10(appendQueryKey);
8390
+ const committedAppendQueryKey = useRef11(appendQueryKey);
8347
8391
  const appendSourceChanged = appendQuery !== null && committedAppendQueryKey.current !== appendQueryKey;
8348
8392
  const data = appendQuery ? appendQuery.pagination.rows : suppliedData;
8349
8393
  const isLoading = appendQuery ? appendQuery.pagination.loading : suppliedLoading;
@@ -8357,7 +8401,7 @@ function MatrxDataTableCore({
8357
8401
  const [savedViewError, setSavedViewError] = useState19(null);
8358
8402
  const [toolbarActionError, setToolbarActionError] = useState19(null);
8359
8403
  const [toolbarActionPending, setToolbarActionPending] = useState19({ refresh: false, add: false });
8360
- const toolbarActionPendingRef = useRef10({ refresh: false, add: false });
8404
+ const toolbarActionPendingRef = useRef11({ refresh: false, add: false });
8361
8405
  const CHECKBOX_TAP_AREA = "h-4 w-4 touch-manipulation";
8362
8406
  const mobileCardsClass = mobileCardsBreakpoint === "lg" ? "lg:hidden" : "sm:hidden";
8363
8407
  const tableWithCardsClass = mobileCardsBreakpoint === "lg" ? "max-lg:hidden" : "max-sm:hidden";
@@ -8495,7 +8539,7 @@ function MatrxDataTableCore({
8495
8539
  const [windowRowSnapshot, setWindowRowSnapshot] = useState19(null);
8496
8540
  const editScopeKey = appendQueryKey === null ? "\0local" : `append:${appendQueryKey}`;
8497
8541
  const [editsByScope, setEditsByScope] = useState19({});
8498
- const editRevisionRef = useRef10({});
8542
+ const editRevisionRef = useRef11({});
8499
8543
  const [savingByScope, setSavingByScope] = useState19({});
8500
8544
  const [confirmForeignDraftDiscard, setConfirmForeignDraftDiscard] = useState19(false);
8501
8545
  const edits = editsByScope[editScopeKey]?.values ?? {};
@@ -8582,11 +8626,11 @@ function MatrxDataTableCore({
8582
8626
  onWindowRowIdChange,
8583
8627
  selectionConfig
8584
8628
  ]);
8585
- const scrollRef = useRef10(null);
8586
- const tableRef = useRef10(null);
8587
- const cardsScrollRef = useRef10(null);
8588
- const previousAppendKeyForScrollReset = useRef10(appendQueryKey);
8589
- useLayoutEffect(() => {
8629
+ const scrollRef = useRef11(null);
8630
+ const tableRef = useRef11(null);
8631
+ const cardsScrollRef = useRef11(null);
8632
+ const previousAppendKeyForScrollReset = useRef11(appendQueryKey);
8633
+ useLayoutEffect2(() => {
8590
8634
  const previous = previousAppendKeyForScrollReset.current;
8591
8635
  previousAppendKeyForScrollReset.current = appendQueryKey;
8592
8636
  if (previous === null || appendQueryKey === null || previous === appendQueryKey) {
@@ -8611,7 +8655,7 @@ function MatrxDataTableCore({
8611
8655
  ...appendScrollPolicy?.mode === "scroll" && appendScrollPolicy.thresholdPx !== void 0 ? { thresholdPx: appendScrollPolicy.thresholdPx } : {},
8612
8656
  ...appendScrollPolicy?.mode === "scroll" && appendScrollPolicy.intentTimeoutMs !== void 0 ? { intentTimeoutMs: appendScrollPolicy.intentTimeoutMs } : {}
8613
8657
  });
8614
- const rootRef = useRef10(null);
8658
+ const rootRef = useRef11(null);
8615
8659
  useClippedContentGuard(rootRef, {
8616
8660
  label: typeof copy === "object" && copy.listLabel ? `Table: ${copy.listLabel}` : "MatrxDataTable"
8617
8661
  });
@@ -8654,11 +8698,11 @@ function MatrxDataTableCore({
8654
8698
  () => normalizeColumnWidthsForColumns(columnState?.widths, columns)
8655
8699
  );
8656
8700
  const [fixedTableGeometry, setFixedTableGeometry] = useState19(null);
8657
- const knownColumnIds = useRef10(new Set(columns.map(columnId)));
8658
- const draggedColumnId = useRef10(null);
8659
- const columnDragActive = useRef10(false);
8660
- const columnDragRaf = useRef10(null);
8661
- const columnDragScrollDirection = useRef10(0);
8701
+ const knownColumnIds = useRef11(new Set(columns.map(columnId)));
8702
+ const draggedColumnId = useRef11(null);
8703
+ const columnDragActive = useRef11(false);
8704
+ const columnDragRaf = useRef11(null);
8705
+ const columnDragScrollDirection = useRef11(0);
8662
8706
  const [columnDragPreview, setColumnDragPreview] = useState19(null);
8663
8707
  const stopColumnDragScroll = useCallback6(() => {
8664
8708
  if (columnDragRaf.current !== null) cancelAnimationFrame(columnDragRaf.current);
@@ -8713,7 +8757,7 @@ function MatrxDataTableCore({
8713
8757
  () => normalizeColumnWidthsForColumns(columnState?.widths ?? columnWidths, columns),
8714
8758
  [columns, columnState?.widths, columnWidths]
8715
8759
  );
8716
- const resizeGestureSnapshotRef = useRef10(null);
8760
+ const resizeGestureSnapshotRef = useRef11(null);
8717
8761
  const measureVisibleColumnWidths = useCallback6(() => {
8718
8762
  const measured = {};
8719
8763
  const headers = tableRef.current ? Array.from(tableRef.current.querySelectorAll(
@@ -8805,7 +8849,7 @@ function MatrxDataTableCore({
8805
8849
  setColumnWidths((current) => normalizeColumnWidthsForColumns(current, columns));
8806
8850
  knownColumnIds.current = new Set(ids);
8807
8851
  }, [columns, columnState]);
8808
- useLayoutEffect(() => {
8852
+ useLayoutEffect2(() => {
8809
8853
  for (const id of columnIds) {
8810
8854
  tableRef.current?.style.removeProperty(columnResizeCssVar(id));
8811
8855
  }
@@ -8814,6 +8858,7 @@ function MatrxDataTableCore({
8814
8858
  () => projectColumnDragOrder(effectiveColumnOrder, columnDragPreview),
8815
8859
  [columnDragPreview, effectiveColumnOrder]
8816
8860
  );
8861
+ useColumnReorderAnimation(tableRef, previewColumnOrder.join("\0"), columnDragPreview !== null);
8817
8862
  const visibleColumns = useMemo7(() => {
8818
8863
  const byId = new Map(columns.map((column) => [columnId(column), column]));
8819
8864
  return previewColumnOrder.map((id) => byId.get(id)).filter(
@@ -8821,14 +8866,14 @@ function MatrxDataTableCore({
8821
8866
  );
8822
8867
  }, [columns, effectiveHiddenColumnIds, previewColumnOrder]);
8823
8868
  const usesMeasuredFixedLayout = visibleColumns.length > 0 && visibleColumns.every((column) => effectiveColumnWidths[columnId(column)] !== void 0);
8824
- useLayoutEffect(() => {
8869
+ useLayoutEffect2(() => {
8825
8870
  if (!usesMeasuredFixedLayout) {
8826
8871
  if (fixedTableGeometry !== null) setFixedTableGeometry(null);
8827
8872
  return;
8828
8873
  }
8829
8874
  if (fixedTableGeometry === null) setFixedTableGeometry(measureFixedTableGeometry());
8830
8875
  }, [fixedTableGeometry, measureFixedTableGeometry, usesMeasuredFixedLayout]);
8831
- useLayoutEffect(() => {
8876
+ useLayoutEffect2(() => {
8832
8877
  if (!usesMeasuredFixedLayout) return;
8833
8878
  const table = tableRef.current;
8834
8879
  if (!table) return;
@@ -8969,7 +9014,7 @@ function MatrxDataTableCore({
8969
9014
  intelligentSearchEnabled,
8970
9015
  searchScope
8971
9016
  ]);
8972
- const lastNotifiedViewRows = useRef10(null);
9017
+ const lastNotifiedViewRows = useRef11(null);
8973
9018
  useEffect13(() => {
8974
9019
  if (!onViewChange) {
8975
9020
  lastNotifiedViewRows.current = null;
@@ -9383,7 +9428,7 @@ function MatrxDataTableCore({
9383
9428
  () => data.filter((row) => selectedIdSet.has(getRowId(row))),
9384
9429
  [data, selectedIdSet, getRowId]
9385
9430
  );
9386
- const lastToggledIndex = useRef10(null);
9431
+ const lastToggledIndex = useRef11(null);
9387
9432
  const setSelectedIds = (next) => selection?.onSelectedIdsChange([...next]);
9388
9433
  const toggleRowSelected = (index, shiftKey) => {
9389
9434
  if (!selection) return;
@@ -10387,6 +10432,7 @@ function MatrxDataTableCore({
10387
10432
  return /* @__PURE__ */ jsx39(
10388
10433
  "td",
10389
10434
  {
10435
+ "data-matrx-table-column-id": columnId(col),
10390
10436
  "data-matrx-column-drag-shadow": isColumnDragShadow ? "true" : void 0,
10391
10437
  className: cn(
10392
10438
  "py-1.5 align-middle lg:py-0.5",