@datum-cloud/datum-ui 2.3.1 → 2.4.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.
@@ -333,4 +333,10 @@ export interface CreateStoreOptions<TData extends RowData> {
333
333
  readonly filterFns?: FilterFnMap;
334
334
  readonly isLoading?: boolean;
335
335
  readonly columnCount?: number;
336
+ /**
337
+ * Row identity, used to reconcile `rowSelection` across data changes.
338
+ * When omitted, TanStack falls back to array-index ids, which are
339
+ * meaningless across a data change — so selection is cleared instead.
340
+ */
341
+ readonly getRowId?: (row: TData) => string;
336
342
  }
@@ -0,0 +1,18 @@
1
+ import type { ReactNode } from 'react';
2
+ /**
3
+ * Height animation for a group body. Do not use Radix CollapsibleContent:
4
+ * it measures `--radix-collapsible-content-height` with getBoundingClientRect,
5
+ * and later groups inside `overflow-x-auto` often measure as 0px.
6
+ *
7
+ * Keep this node mounted. AnimatePresence exit from `height: auto` has to
8
+ * measure on the way out, which is the beat of delay after a click.
9
+ * `initial={false}` skips the enter animation for groups that start open.
10
+ *
11
+ * Overflow stays hidden. The header/body divider is a 1px rule inside this
12
+ * wrapper, not a row `border-t`, so it is not clipped.
13
+ */
14
+ export declare function GroupedCollapsibleBody({ open, reduceMotion, children, }: {
15
+ open: boolean;
16
+ reduceMotion: boolean;
17
+ children: ReactNode;
18
+ }): import("react").JSX.Element;
@@ -1,2 +1,2 @@
1
- import { _ as useNuqsAdapter, a as useDataTablePagination, b as DEFAULT_PAGE_SIZE, c as useDataTableSelection, d as resolvePath, f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, i as useDataTableLoading, l as useDataTableSorting, m as DataTableRowActions, n as useDataTableFilters, o as useDataTableRows, p as dataTableFeatures, r as useDataTableInlineContents, s as useDataTableSearch, t as DataTable, u as createDataTableStore, v as DEFAULT_DEBOUNCE_MS, x as DEFAULT_PAGE_SIZES, y as DEFAULT_LOADING_ROWS } from "../data-table-Du7_FLBi.mjs";
1
+ import { _ as useNuqsAdapter, a as useDataTablePagination, b as DEFAULT_PAGE_SIZE, c as useDataTableSelection, d as resolvePath, f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, i as useDataTableLoading, l as useDataTableSorting, m as DataTableRowActions, n as useDataTableFilters, o as useDataTableRows, p as dataTableFeatures, r as useDataTableInlineContents, s as useDataTableSearch, t as DataTable, u as createDataTableStore, v as DEFAULT_DEBOUNCE_MS, x as DEFAULT_PAGE_SIZES, y as DEFAULT_LOADING_ROWS } from "../data-table-ClfRpyHq.mjs";
2
2
  export { DEFAULT_DEBOUNCE_MS, DEFAULT_LOADING_ROWS, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZES, DataTable, DataTableColumnHeader, DataTableRowActions, createDataTableStore, createSelectionColumn, dataTableFeatures, resolvePath, rowMatchesSearch, useDataTableFilters, useDataTableInlineContents, useDataTableLoading, useDataTablePagination, useDataTableRows, useDataTableSearch, useDataTableSelection, useDataTableSorting, useNuqsAdapter };
@@ -313,6 +313,20 @@ function applyFilters(data, filters, search, registeredFilters, customFilterFns,
313
313
  }
314
314
  //#endregion
315
315
  //#region src/components/features/data-table/core/store.ts
316
+ /**
317
+ * Keep only the selection keys whose rows survive a data change.
318
+ * Dropping vanished keys stops a deleted row's selection from
319
+ * resurrecting if a row with the same id reappears later.
320
+ */
321
+ function pruneSelection(selection, rows, getRowId) {
322
+ const keys = Object.keys(selection);
323
+ if (keys.length === 0) return selection;
324
+ const surviving = /* @__PURE__ */ new Set();
325
+ for (const row of rows) surviving.add(getRowId(row));
326
+ const next = {};
327
+ for (const key of keys) if (selection[key] && surviving.has(key)) next[key] = true;
328
+ return next;
329
+ }
316
330
  function createDataTableStore(options) {
317
331
  let registeredFilters = /* @__PURE__ */ new Map();
318
332
  const listeners = /* @__PURE__ */ new Set();
@@ -362,13 +376,17 @@ function createDataTableStore(options) {
362
376
  setData: (data) => {
363
377
  const next = {
364
378
  ...state,
365
- data,
366
- pageIndex: 0,
367
- rowSelection: {}
379
+ data
368
380
  };
381
+ const filteredData = computeFilteredData(next);
382
+ const pageCount = Math.max(1, Math.ceil(filteredData.length / state.pageSize));
383
+ const pageIndex = Math.min(state.pageIndex, pageCount - 1);
384
+ const rowSelection = options.getRowId ? pruneSelection(state.rowSelection, filteredData, options.getRowId) : {};
369
385
  setState({
370
386
  ...next,
371
- filteredData: computeFilteredData(next)
387
+ filteredData,
388
+ pageIndex,
389
+ rowSelection
372
390
  });
373
391
  },
374
392
  setServerData: (data) => {
@@ -1168,6 +1186,7 @@ function useClientTable(store, options) {
1168
1186
  }) : updater;
1169
1187
  store.setPagination(next.pageIndex, next.pageSize);
1170
1188
  },
1189
+ autoResetPageIndex: false,
1171
1190
  getRowId,
1172
1191
  enableRowSelection: !!enableRowSelection
1173
1192
  });
@@ -1239,7 +1258,8 @@ function ClientProvider(props) {
1239
1258
  columnCount: columns.length,
1240
1259
  searchableColumns,
1241
1260
  searchFn,
1242
- filterFns
1261
+ filterFns,
1262
+ getRowId
1243
1263
  }), []);
1244
1264
  const isInitialRender = useRef(true);
1245
1265
  useEffect(() => {
@@ -1,2 +1,2 @@
1
- import { t as GroupedTable } from "../grouped-table-Dldsvwz6.mjs";
1
+ import { t as GroupedTable } from "../grouped-table-Bb9KoI1e.mjs";
2
2
  export { GroupedTable };
@@ -1,17 +1,56 @@
1
1
  import { t as cn } from "./utils-Bu32wN-x.mjs";
2
2
  import { t as Button } from "./button-A1qTjZ8U.mjs";
3
3
  import { t as Icon } from "./icon-wrapper-DKfJlJd0.mjs";
4
- import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./collapsible/index.mjs";
4
+ import { Collapsible, CollapsibleTrigger } from "./collapsible/index.mjs";
5
5
  import { t as useControllableState } from "./use-controllable-state-Dna7u3r9.mjs";
6
6
  import { Skeleton } from "./skeleton/index.mjs";
7
7
  import { TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table/index.mjs";
8
- import { f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, m as DataTableRowActions, p as dataTableFeatures } from "./data-table-Du7_FLBi.mjs";
8
+ import { f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, m as DataTableRowActions, p as dataTableFeatures } from "./data-table-ClfRpyHq.mjs";
9
9
  import { n as useDebouncedSearchInput } from "./use-debounced-search-input-yu_F0QBt.mjs";
10
10
  import { InputWithAddons } from "./input-with-addons/index.mjs";
11
11
  import { ChevronRight, Search, X } from "lucide-react";
12
12
  import { useCallback, useMemo, useState } from "react";
13
13
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
14
+ import { motion, useReducedMotion } from "motion/react";
14
15
  import { flexRender, useTable } from "@tanstack/react-table";
16
+ //#region src/components/features/grouped-table/components/grouped-collapsible-body.tsx
17
+ const OPEN_TRANSITION = {
18
+ duration: .18,
19
+ ease: [
20
+ .23,
21
+ 1,
22
+ .32,
23
+ 1
24
+ ]
25
+ };
26
+ const INSTANT = { duration: 0 };
27
+ /**
28
+ * Height animation for a group body. Do not use Radix CollapsibleContent:
29
+ * it measures `--radix-collapsible-content-height` with getBoundingClientRect,
30
+ * and later groups inside `overflow-x-auto` often measure as 0px.
31
+ *
32
+ * Keep this node mounted. AnimatePresence exit from `height: auto` has to
33
+ * measure on the way out, which is the beat of delay after a click.
34
+ * `initial={false}` skips the enter animation for groups that start open.
35
+ *
36
+ * Overflow stays hidden. The header/body divider is a 1px rule inside this
37
+ * wrapper, not a row `border-t`, so it is not clipped.
38
+ */
39
+ function GroupedCollapsibleBody({ open, reduceMotion, children }) {
40
+ return /* @__PURE__ */ jsxs(motion.div, {
41
+ "data-slot": "grouped-table-group-content",
42
+ initial: false,
43
+ animate: { height: open ? "auto" : 0 },
44
+ transition: reduceMotion ? INSTANT : OPEN_TRANSITION,
45
+ className: "overflow-hidden",
46
+ inert: !open,
47
+ children: [/* @__PURE__ */ jsx("div", {
48
+ "data-slot": "grouped-table-group-rule",
49
+ className: "bg-border h-px"
50
+ }), children]
51
+ });
52
+ }
53
+ //#endregion
15
54
  //#region src/components/features/grouped-table/components/grouped-skeleton.tsx
16
55
  function GroupedSkeleton({ columns = 4, groups = 2, rowsPerGroup = 3 }) {
17
56
  return /* @__PURE__ */ jsx("div", {
@@ -228,6 +267,7 @@ function GroupedTable(props) {
228
267
  expanded,
229
268
  onExpandedChange
230
269
  });
270
+ const reduceMotion = Boolean(useReducedMotion());
231
271
  const resolvedColumns = useMemo(() => composeColumns(columns, {
232
272
  enableRowSelection,
233
273
  enableSorting,
@@ -249,7 +289,11 @@ function GroupedTable(props) {
249
289
  state: {
250
290
  sorting,
251
291
  rowSelection,
252
- globalFilter: search
292
+ globalFilter: search,
293
+ pagination: {
294
+ pageIndex: 0,
295
+ pageSize: Math.max(flatData.length, 1)
296
+ }
253
297
  },
254
298
  onSortingChange: setSorting,
255
299
  onRowSelectionChange: setRowSelection,
@@ -265,7 +309,7 @@ function GroupedTable(props) {
265
309
  });
266
310
  const headerGroups = table.getHeaderGroups();
267
311
  const coreRows = table.getCoreRowModel().rows;
268
- const filteredRows = table.getRowModel().rows;
312
+ const filteredRows = table.getFilteredRowModel().rows;
269
313
  const buckets = useMemo(() => bucketRows(groups, coreRows, filteredRows), [
270
314
  groups,
271
315
  coreRows,
@@ -334,8 +378,9 @@ function GroupedTable(props) {
334
378
  children: slice.meta
335
379
  })
336
380
  ]
337
- }), /* @__PURE__ */ jsx(CollapsibleContent, {
338
- "data-slot": "grouped-table-group-content",
381
+ }), /* @__PURE__ */ jsx(GroupedCollapsibleBody, {
382
+ open,
383
+ reduceMotion,
339
384
  children: /* @__PURE__ */ jsxs("table", {
340
385
  className: cn("w-full table-fixed text-sm", tableClassName),
341
386
  "aria-label": typeof slice.title === "string" ? slice.title : void 0,
@@ -350,9 +395,9 @@ function GroupedTable(props) {
350
395
  }),
351
396
  /* @__PURE__ */ jsx(TableBody, {
352
397
  className: bodyClassName,
353
- children: slice.rows.map((row, rowIndex) => /* @__PURE__ */ jsx(TableRow, {
398
+ children: slice.rows.map((row) => /* @__PURE__ */ jsx(TableRow, {
354
399
  "data-state": row.getIsSelected() ? "selected" : void 0,
355
- className: cn(rowIndex === 0 && "border-t", resolveClassName(rowClassName, row)),
400
+ className: resolveClassName(rowClassName, row),
356
401
  children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, {
357
402
  className: resolveClassName(cellClassName, cell),
358
403
  children: flexRender(cell.column.columnDef.cell, cell.getContext())
package/dist/index.mjs CHANGED
@@ -53,7 +53,7 @@ import { i as ClientOnly, n as useTheme, r as ThemeScript, t as ThemeProvider }
53
53
  import { a as formatJson, c as isValidYaml, d as CodeEditor, i as CodeEditorTabs, l as jsonToYaml, n as jsonSchema, o as formatYaml, r as yamlSchema, s as isValidJson, t as createCodeEditorSchema, u as yamlToJson } from "./types-agDpVXBG.mjs";
54
54
  import { t as toast } from "./toast-BUPlDkN3.mjs";
55
55
  import { n as Toaster, t as useToast } from "./use-toast-Bpq0yJIT.mjs";
56
- import { _ as useNuqsAdapter, a as useDataTablePagination, b as DEFAULT_PAGE_SIZE, c as useDataTableSelection, d as resolvePath, f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, i as useDataTableLoading, l as useDataTableSorting, m as DataTableRowActions, n as useDataTableFilters, o as useDataTableRows, p as dataTableFeatures, r as useDataTableInlineContents, s as useDataTableSearch, t as DataTable, u as createDataTableStore, v as DEFAULT_DEBOUNCE_MS, x as DEFAULT_PAGE_SIZES, y as DEFAULT_LOADING_ROWS } from "./data-table-Du7_FLBi.mjs";
56
+ import { _ as useNuqsAdapter, a as useDataTablePagination, b as DEFAULT_PAGE_SIZE, c as useDataTableSelection, d as resolvePath, f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, i as useDataTableLoading, l as useDataTableSorting, m as DataTableRowActions, n as useDataTableFilters, o as useDataTableRows, p as dataTableFeatures, r as useDataTableInlineContents, s as useDataTableSearch, t as DataTable, u as createDataTableStore, v as DEFAULT_DEBOUNCE_MS, x as DEFAULT_PAGE_SIZES, y as DEFAULT_LOADING_ROWS } from "./data-table-ClfRpyHq.mjs";
57
57
  import { t as ActionRow } from "./action-row-DTvs91_3.mjs";
58
58
  import { n as useDebouncedSearchInput, t as DEFAULT_SEARCH_DEBOUNCE_MS } from "./use-debounced-search-input-yu_F0QBt.mjs";
59
59
  import { t as MultiSelect } from "./multi-select-PtdMChPF.mjs";
@@ -69,7 +69,7 @@ import { InputWithAddons } from "./input-with-addons/index.mjs";
69
69
  import { t as TimePicker } from "./time-picker-CFhaHTtI.mjs";
70
70
  import { t as Transfer } from "./transfer-CEq9AhL7.mjs";
71
71
  import { a as getResponsiveValue, c as GRID_COLUMNS, d as RESPONSIVE_MAP, i as getGutter, l as GRID_PREFIX, n as Row, o as registerMediaQuery, r as RowContext, s as GRID_BREAKPOINTS, t as Col, u as RESPONSIVE_ARRAY } from "./col-BYGWTB4j.mjs";
72
- import { t as GroupedTable } from "./grouped-table-Dldsvwz6.mjs";
72
+ import { t as GroupedTable } from "./grouped-table-Bb9KoI1e.mjs";
73
73
  import { t as InputNumber } from "./input-number-TXmW-X9o.mjs";
74
74
  import { a as logoStyles, i as LogoFlat, n as LogoStacked, r as LogoIcon, t as LogoText } from "./logo-text-Dpm5O6Kg.mjs";
75
75
  import { t as Logo } from "./logo-C3FxD7jb.mjs";
@@ -23,7 +23,7 @@ function TableHeader({ className, ...props }) {
23
23
  function TableBody({ className, ...props }) {
24
24
  return /* @__PURE__ */ jsx("tbody", {
25
25
  "data-slot": "table-body",
26
- className: cn("[&_tr:last-child]:border-0", className),
26
+ className: cn("[&_tr:last-child]:border-b-0", className),
27
27
  ...props
28
28
  });
29
29
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datum-cloud/datum-ui",
3
3
  "type": "module",
4
- "version": "2.3.1",
4
+ "version": "2.4.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "url": "https://github.com/datum-cloud/datum-ui"