@rovula/ui 0.1.39 → 0.1.40

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.
@@ -52,6 +52,15 @@ export interface DataTableProps<TData, TValue> extends DataTableEditingProps<TDa
52
52
  loading?: boolean;
53
53
  /** Label for the initial-loading state (default: "Loading…"). */
54
54
  loadingLabel?: string;
55
+ /** Icon rendered in the empty-state placeholder. Replaces the default `ClipboardList` icon. */
56
+ emptyIcon?: React.ReactNode;
57
+ /** Text rendered in the empty-state placeholder (default: "There is no information yet."). */
58
+ emptyText?: React.ReactNode;
59
+ /**
60
+ * Fully replaces the empty-state cell content. When provided, `emptyIcon` and
61
+ * `emptyText` are ignored.
62
+ */
63
+ renderEmpty?: () => React.ReactNode;
55
64
  /**
56
65
  * When true, DataTable will only render a vertical window of rows based on
57
66
  * the scroll position of its internal scroll container. This is a basic
@@ -197,4 +206,4 @@ export interface ColumnManagementOptions {
197
206
  /** Show "Show all" button. @default true */
198
207
  showAll?: boolean;
199
208
  }
200
- export declare function DataTable<TData, TValue>({ data, columns, manualSorting, onSorting, paginationMode, totalCount, pageIndex: controlledPageIndex, pageSize: controlledPageSize, onPaginationChange, pageSizeOptions, fetchMoreData, fetchMoreOffset, fetchingMore, fetchingMoreLabel, loading, loadingLabel, highlightRowId, scrollToHighlightOnMouseLeave, selectable, onRowSelectionChange, rowActions, reorderable, getRowId: getRowIdProp, onRowReorder, isRowReorderLocked, onRowClick, onCellClick, getSubRows, defaultExpanded, expanded: controlledExpanded, onExpandedChange, bordered, surface, striped, divided, rowClassName, cellClassName, headerCellClassName, headerClassName, headerRowClassName, sortIndicatorVisibility, tableLayout, columnManagement: columnManagementProp, resizable, columnMinSize, columnMaxSize, virtualized, virtualRowEstimate, className, enableEditing, editDisplayMode, editTrigger, onCellCommit, alwaysEditing, enableCellTabTraversal, editableColumnIds: editableColumnIdsProp, testId, }: DataTableProps<TData, TValue>): import("react/jsx-runtime").JSX.Element;
209
+ export declare function DataTable<TData, TValue>({ data, columns, manualSorting, onSorting, paginationMode, totalCount, pageIndex: controlledPageIndex, pageSize: controlledPageSize, onPaginationChange, pageSizeOptions, fetchMoreData, fetchMoreOffset, fetchingMore, fetchingMoreLabel, loading, loadingLabel, emptyIcon, emptyText, renderEmpty, highlightRowId, scrollToHighlightOnMouseLeave, selectable, onRowSelectionChange, rowActions, reorderable, getRowId: getRowIdProp, onRowReorder, isRowReorderLocked, onRowClick, onCellClick, getSubRows, defaultExpanded, expanded: controlledExpanded, onExpandedChange, bordered, surface, striped, divided, rowClassName, cellClassName, headerCellClassName, headerClassName, headerRowClassName, sortIndicatorVisibility, tableLayout, columnManagement: columnManagementProp, resizable, columnMinSize, columnMaxSize, virtualized, virtualRowEstimate, className, enableEditing, editDisplayMode, editTrigger, onCellCommit, alwaysEditing, enableCellTabTraversal, editableColumnIds: editableColumnIdsProp, testId, }: DataTableProps<TData, TValue>): import("react/jsx-runtime").JSX.Element;
@@ -48,6 +48,9 @@ declare const meta: {
48
48
  fetchingMoreLabel?: string | undefined;
49
49
  loading?: boolean | undefined;
50
50
  loadingLabel?: string | undefined;
51
+ emptyIcon?: React.ReactNode;
52
+ emptyText?: React.ReactNode;
53
+ renderEmpty?: (() => React.ReactNode) | undefined;
51
54
  virtualized?: boolean | undefined;
52
55
  virtualRowEstimate?: number | undefined;
53
56
  highlightRowId?: string | string[] | undefined;
@@ -165,6 +168,11 @@ export declare const PerformanceLargeDataset: StoryObj;
165
168
  export declare const WithVirtualizedRows: StoryObj;
166
169
  /** Checkbox selection — header checkbox selects all; row checkboxes select individually. */
167
170
  export declare const WithSelection: StoryObj;
171
+ /**
172
+ * Select rows then hit "Delete selected" — the header checkbox should clear
173
+ * automatically once the rows are gone (regression test for stale selection state).
174
+ */
175
+ export declare const WithSelectionAndDelete: StoryObj;
168
176
  /**
169
177
  * Row highlight + scroll — use `highlightRowId` to visually mark important rows
170
178
  * (uses the same token as selected rows) and automatically scroll them into view.
package/dist/index.d.ts CHANGED
@@ -958,6 +958,15 @@ interface DataTableProps<TData, TValue> extends DataTableEditingProps<TData> {
958
958
  loading?: boolean;
959
959
  /** Label for the initial-loading state (default: "Loading…"). */
960
960
  loadingLabel?: string;
961
+ /** Icon rendered in the empty-state placeholder. Replaces the default `ClipboardList` icon. */
962
+ emptyIcon?: React__default.ReactNode;
963
+ /** Text rendered in the empty-state placeholder (default: "There is no information yet."). */
964
+ emptyText?: React__default.ReactNode;
965
+ /**
966
+ * Fully replaces the empty-state cell content. When provided, `emptyIcon` and
967
+ * `emptyText` are ignored.
968
+ */
969
+ renderEmpty?: () => React__default.ReactNode;
961
970
  /**
962
971
  * When true, DataTable will only render a vertical window of rows based on
963
972
  * the scroll position of its internal scroll container. This is a basic
@@ -1103,7 +1112,7 @@ interface ColumnManagementOptions {
1103
1112
  /** Show "Show all" button. @default true */
1104
1113
  showAll?: boolean;
1105
1114
  }
1106
- declare function DataTable<TData, TValue>({ data, columns, manualSorting, onSorting, paginationMode, totalCount, pageIndex: controlledPageIndex, pageSize: controlledPageSize, onPaginationChange, pageSizeOptions, fetchMoreData, fetchMoreOffset, fetchingMore, fetchingMoreLabel, loading, loadingLabel, highlightRowId, scrollToHighlightOnMouseLeave, selectable, onRowSelectionChange, rowActions, reorderable, getRowId: getRowIdProp, onRowReorder, isRowReorderLocked, onRowClick, onCellClick, getSubRows, defaultExpanded, expanded: controlledExpanded, onExpandedChange, bordered, surface, striped, divided, rowClassName, cellClassName, headerCellClassName, headerClassName, headerRowClassName, sortIndicatorVisibility, tableLayout, columnManagement: columnManagementProp, resizable, columnMinSize, columnMaxSize, virtualized, virtualRowEstimate, className, enableEditing, editDisplayMode, editTrigger, onCellCommit, alwaysEditing, enableCellTabTraversal, editableColumnIds: editableColumnIdsProp, testId, }: DataTableProps<TData, TValue>): react_jsx_runtime.JSX.Element;
1115
+ declare function DataTable<TData, TValue>({ data, columns, manualSorting, onSorting, paginationMode, totalCount, pageIndex: controlledPageIndex, pageSize: controlledPageSize, onPaginationChange, pageSizeOptions, fetchMoreData, fetchMoreOffset, fetchingMore, fetchingMoreLabel, loading, loadingLabel, emptyIcon, emptyText, renderEmpty, highlightRowId, scrollToHighlightOnMouseLeave, selectable, onRowSelectionChange, rowActions, reorderable, getRowId: getRowIdProp, onRowReorder, isRowReorderLocked, onRowClick, onCellClick, getSubRows, defaultExpanded, expanded: controlledExpanded, onExpandedChange, bordered, surface, striped, divided, rowClassName, cellClassName, headerCellClassName, headerClassName, headerRowClassName, sortIndicatorVisibility, tableLayout, columnManagement: columnManagementProp, resizable, columnMinSize, columnMaxSize, virtualized, virtualRowEstimate, className, enableEditing, editDisplayMode, editTrigger, onCellCommit, alwaysEditing, enableCellTabTraversal, editableColumnIds: editableColumnIdsProp, testId, }: DataTableProps<TData, TValue>): react_jsx_runtime.JSX.Element;
1107
1116
 
1108
1117
  declare const Dialog: React$1.FC<DialogPrimitive.DialogProps>;
1109
1118
  declare const DialogTrigger: React$1.ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & React$1.RefAttributes<HTMLButtonElement>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rovula/ui",
3
- "version": "0.1.39",
3
+ "version": "0.1.40",
4
4
  "main": "dist/cjs/bundle.js",
5
5
  "module": "dist/esm/bundle.js",
6
6
  "types": "dist/index.d.ts",
@@ -814,6 +814,59 @@ export const WithSelection: StoryObj = {
814
814
  },
815
815
  };
816
816
 
817
+ /**
818
+ * Select rows then hit "Delete selected" — the header checkbox should clear
819
+ * automatically once the rows are gone (regression test for stale selection state).
820
+ */
821
+ export const WithSelectionAndDelete: StoryObj = {
822
+ render: () => {
823
+ const [rows, setRows] = React.useState<Project[]>(projectData);
824
+ const [selected, setSelected] = React.useState<Record<string, boolean>>({});
825
+
826
+ const selectedIds = Object.keys(selected).filter((k) => selected[k]);
827
+
828
+ const handleDelete = () => {
829
+ setRows((prev) => prev.filter((r) => !selected[r.id]));
830
+ };
831
+
832
+ return (
833
+ <div className="flex flex-col gap-3 w-full h-full">
834
+ <div className="flex items-center gap-3 px-1">
835
+ <p className="typography-small2 text-text-g-contrast-high flex-1">
836
+ Selected IDs: {selectedIds.join(", ") || "–"}
837
+ </p>
838
+ <Button
839
+ size="sm"
840
+ variant="solid"
841
+ color="error"
842
+ disabled={selectedIds.length === 0}
843
+ onClick={handleDelete}
844
+ startIcon={<Trash2 className="size-4" />}
845
+ >
846
+ Delete selected ({selectedIds.length})
847
+ </Button>
848
+ <Button
849
+ size="sm"
850
+ variant="outline"
851
+ onClick={() => setRows(projectData)}
852
+ >
853
+ Reset
854
+ </Button>
855
+ </div>
856
+ <DataTable
857
+ columns={projectColumns}
858
+ data={rows}
859
+ getRowId={(r) => r.id}
860
+ selectable
861
+ onRowSelectionChange={setSelected}
862
+ striped
863
+ divided
864
+ />
865
+ </div>
866
+ );
867
+ },
868
+ };
869
+
817
870
  /**
818
871
  * Row highlight + scroll — use `highlightRowId` to visually mark important rows
819
872
  * (uses the same token as selected rows) and automatically scroll them into view.
@@ -345,6 +345,15 @@ export interface DataTableProps<TData, TValue>
345
345
  loading?: boolean;
346
346
  /** Label for the initial-loading state (default: "Loading…"). */
347
347
  loadingLabel?: string;
348
+ /** Icon rendered in the empty-state placeholder. Replaces the default `ClipboardList` icon. */
349
+ emptyIcon?: React.ReactNode;
350
+ /** Text rendered in the empty-state placeholder (default: "There is no information yet."). */
351
+ emptyText?: React.ReactNode;
352
+ /**
353
+ * Fully replaces the empty-state cell content. When provided, `emptyIcon` and
354
+ * `emptyText` are ignored.
355
+ */
356
+ renderEmpty?: () => React.ReactNode;
348
357
 
349
358
  // --- Virtualization (experimental) ---
350
359
  /**
@@ -1055,6 +1064,9 @@ export function DataTable<TData, TValue>({
1055
1064
  fetchingMoreLabel = "Loading more…",
1056
1065
  loading = false,
1057
1066
  loadingLabel = "Loading…",
1067
+ emptyIcon,
1068
+ emptyText = "There is no information yet.",
1069
+ renderEmpty,
1058
1070
  highlightRowId,
1059
1071
  scrollToHighlightOnMouseLeave = false,
1060
1072
  selectable = false,
@@ -1380,6 +1392,23 @@ export function DataTable<TData, TValue>({
1380
1392
  onRowSelectionChange?.(rowSelection);
1381
1393
  }, [rowSelection, onRowSelectionChange]);
1382
1394
 
1395
+ // Prune stale selection keys when rows are removed (e.g. after deleting selected rows).
1396
+ useEffect(() => {
1397
+ if (!selectable) return;
1398
+ const currentIds = new Set(
1399
+ table.getRowModel().rows.map((r) => r.id),
1400
+ );
1401
+ setRowSelection((prev) => {
1402
+ const pruned = Object.fromEntries(
1403
+ Object.entries(prev).filter(([id]) => currentIds.has(id)),
1404
+ );
1405
+ // Avoid re-render if nothing changed.
1406
+ if (Object.keys(pruned).length === Object.keys(prev).length) return prev;
1407
+ return pruned;
1408
+ });
1409
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1410
+ }, [data, selectable]);
1411
+
1383
1412
  // Infinite scroll — listener on the wrapper div
1384
1413
  useEffect(() => {
1385
1414
  if (paginationMode !== "infinite") return;
@@ -2282,8 +2311,18 @@ export function DataTable<TData, TValue>({
2282
2311
  className="typography-body1 text-text-g-contrast-medium text-center h-full"
2283
2312
  >
2284
2313
  <div className="flex flex-1 h-full flex-col items-center justify-center gap-3 py-16">
2285
- <ClipboardList className="w-8 text-secondary-120" />
2286
- There is no information yet.
2314
+ {renderEmpty ? (
2315
+ renderEmpty()
2316
+ ) : (
2317
+ <>
2318
+ {emptyIcon !== undefined ? (
2319
+ emptyIcon
2320
+ ) : (
2321
+ <ClipboardList className="w-8 text-secondary-120" />
2322
+ )}
2323
+ {emptyText}
2324
+ </>
2325
+ )}
2287
2326
  </div>
2288
2327
  </TableCell>
2289
2328
  </TableRow>
@@ -325,6 +325,10 @@ module.exports = {
325
325
  "accordion-down": "accordion-down 0.2s ease-out",
326
326
  "accordion-up": "accordion-up 0.2s ease-out",
327
327
  },
328
+ boxShadow: {
329
+ card: "0 6px 12px 0 rgba(0, 0, 0, 0.08)",
330
+ modal: "0 12px 24px -4px rgba(0, 0, 0, 0.12)",
331
+ },
328
332
  },
329
333
  },
330
334
  presets: [require("./presets/colors"), require("tailwindcss-animate")],