@asteby/metacore-runtime-react 39.0.0 → 39.1.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/CHANGELOG.md +6 -0
- package/dist/dynamic-table.d.ts +8 -1
- package/dist/dynamic-table.d.ts.map +1 -1
- package/dist/dynamic-table.js +59 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/table-virtualization.d.ts +16 -0
- package/dist/table-virtualization.d.ts.map +1 -0
- package/dist/table-virtualization.js +23 -0
- package/package.json +2 -1
- package/src/__tests__/table-virtualization.test.ts +25 -0
- package/src/dynamic-table.tsx +163 -9
- package/src/index.ts +1 -0
- package/src/table-virtualization.ts +27 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 39.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 6549688: DynamicTable virtualizes desktop rows and mobile cards once the visible row model reaches 40+ rows (override with `virtualizeRows`). Cuts DOM cost for infinite scroll and large page sizes.
|
|
8
|
+
|
|
3
9
|
## 39.0.0
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/dynamic-table.d.ts
CHANGED
|
@@ -83,6 +83,13 @@ export interface DynamicTableProps {
|
|
|
83
83
|
* mode exactly as before.
|
|
84
84
|
*/
|
|
85
85
|
infiniteScroll?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Row virtualization. When the visible row model has at least N rows,
|
|
88
|
+
* only viewport rows are mounted (helps infinite scroll and large page
|
|
89
|
+
* sizes). `true` / omitted → default threshold (40). `false` → always
|
|
90
|
+
* render every row. A positive number overrides the threshold.
|
|
91
|
+
*/
|
|
92
|
+
virtualizeRows?: boolean | number;
|
|
86
93
|
}
|
|
87
|
-
export declare function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync, hideImport, hideExport, hiddenColumns, allowedActionKeys, onAction, onRowClick, refreshTrigger, defaultFilters, extraColumns, getDynamicColumns, timeZone, currency, pagination: paginationMode, infiniteScroll: infiniteScrollProp, }: DynamicTableProps): import("react").JSX.Element;
|
|
94
|
+
export declare function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync, hideImport, hideExport, hiddenColumns, allowedActionKeys, onAction, onRowClick, refreshTrigger, defaultFilters, extraColumns, getDynamicColumns, timeZone, currency, pagination: paginationMode, infiniteScroll: infiniteScrollProp, virtualizeRows, }: DynamicTableProps): import("react").JSX.Element;
|
|
88
95
|
//# sourceMappingURL=dynamic-table.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AAgC9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAuGnF,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC/B,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,UAAU,CAAA;IACjC;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;CACpC;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,gBAAgB,EAChB,aAAoB,EACpB,UAAU,EACV,UAAU,EACV,aAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACR,UAAU,EACV,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,EAC5C,QAAQ,EACR,QAAQ,EACR,UAAU,EAAE,cAAc,EAC1B,cAAc,EAAE,kBAA0B,EAC1C,cAAc,GACjB,EAAE,iBAAiB,+BAy/CnB"}
|
package/dist/dynamic-table.js
CHANGED
|
@@ -14,6 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
14
14
|
import { useEffect, useState, useMemo, useCallback, useRef } from 'react';
|
|
15
15
|
import { useTranslation } from 'react-i18next';
|
|
16
16
|
import { format } from 'date-fns';
|
|
17
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
17
18
|
import { flexRender, getCoreRowModel, getFacetedRowModel, getFacetedUniqueValues, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, } from '@tanstack/react-table';
|
|
18
19
|
import { cn } from '@asteby/metacore-ui/lib';
|
|
19
20
|
import { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow, Button, Skeleton, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@asteby/metacore-ui/primitives';
|
|
@@ -27,6 +28,7 @@ import { defaultGetDynamicColumns, DATE_CELL_TYPES, aggregateOf, formatAggregate
|
|
|
27
28
|
import { useFacetLoaders, isLongTextColumn } from './use-facet-loaders';
|
|
28
29
|
import { translateOptionLabels } from './filter-chips';
|
|
29
30
|
import { dedupeById, useInfiniteScrollSentinel } from './use-infinite-scroll';
|
|
31
|
+
import { DYNAMIC_TABLE_CARD_ESTIMATE_PX, DYNAMIC_TABLE_ROW_ESTIMATE_PX, resolveVirtualizeThreshold, } from './table-virtualization';
|
|
30
32
|
import { OptionsContext } from './options-context';
|
|
31
33
|
import { getSearchableColumnKeys } from './column-visibility';
|
|
32
34
|
import { useDebouncedValue } from './use-debounced-value';
|
|
@@ -105,7 +107,7 @@ function writeStoredPageSize(model, size) {
|
|
|
105
107
|
// quota / private mode — persistence is a nicety, never fatal
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
|
-
export function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync = true, hideImport, hideExport, hiddenColumns = [], allowedActionKeys, onAction, onRowClick, refreshTrigger, defaultFilters, extraColumns = [], getDynamicColumns = defaultGetDynamicColumns, timeZone, currency, pagination: paginationMode, infiniteScroll: infiniteScrollProp = false, }) {
|
|
110
|
+
export function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync = true, hideImport, hideExport, hiddenColumns = [], allowedActionKeys, onAction, onRowClick, refreshTrigger, defaultFilters, extraColumns = [], getDynamicColumns = defaultGetDynamicColumns, timeZone, currency, pagination: paginationMode, infiniteScroll: infiniteScrollProp = false, virtualizeRows, }) {
|
|
109
111
|
// The explicit `pagination` prop wins; the legacy `infiniteScroll` boolean
|
|
110
112
|
// still selects the mode when `pagination` is absent (back-compat).
|
|
111
113
|
const infiniteScroll = paginationMode ? paginationMode === 'infinite' : infiniteScrollProp;
|
|
@@ -1171,6 +1173,24 @@ export function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync
|
|
|
1171
1173
|
getFacetedRowModel: getFacetedRowModel(),
|
|
1172
1174
|
getFacetedUniqueValues: getFacetedUniqueValues(),
|
|
1173
1175
|
});
|
|
1176
|
+
const tableRows = table.getRowModel().rows;
|
|
1177
|
+
const virtualizeThreshold = resolveVirtualizeThreshold(virtualizeRows);
|
|
1178
|
+
const shouldVirtualize = virtualizeThreshold !== false && tableRows.length >= virtualizeThreshold;
|
|
1179
|
+
const colSpan = Math.max(columns.length, 1);
|
|
1180
|
+
const desktopVirtualizer = useVirtualizer({
|
|
1181
|
+
count: shouldVirtualize ? tableRows.length : 0,
|
|
1182
|
+
getScrollElement: () => infDesktopRoot.current,
|
|
1183
|
+
estimateSize: () => DYNAMIC_TABLE_ROW_ESTIMATE_PX,
|
|
1184
|
+
overscan: 8,
|
|
1185
|
+
enabled: shouldVirtualize,
|
|
1186
|
+
});
|
|
1187
|
+
const mobileVirtualizer = useVirtualizer({
|
|
1188
|
+
count: shouldVirtualize ? tableRows.length : 0,
|
|
1189
|
+
getScrollElement: () => infMobileRoot.current,
|
|
1190
|
+
estimateSize: () => DYNAMIC_TABLE_CARD_ESTIMATE_PX,
|
|
1191
|
+
overscan: 6,
|
|
1192
|
+
enabled: shouldVirtualize,
|
|
1193
|
+
});
|
|
1174
1194
|
const TableSkeleton = () => (_jsx(_Fragment, { children: Array.from({ length: 5 }).map((_, i) => (_jsxs(TableRow, { className: "hover:bg-transparent", children: [_jsx(TableCell, { className: "py-3 w-10", children: _jsx(Skeleton, { className: "h-4 w-4" }) }), _jsx(TableCell, { className: "py-3", children: _jsx(Skeleton, { className: "h-4 w-[70%]" }) }), _jsx(TableCell, { className: "py-3", children: _jsx(Skeleton, { className: "h-4 w-[50%]" }) }), _jsx(TableCell, { className: "py-3", children: _jsx(Skeleton, { className: "h-4 w-[60%]" }) }), _jsx(TableCell, { className: "py-3 w-16", children: _jsx(Skeleton, { className: "h-6 w-6" }) })] }, `skeleton-${i}`))) }));
|
|
1175
1195
|
if (loading) {
|
|
1176
1196
|
return (_jsxs("div", { className: 'flex flex-col h-full min-h-0 w-full', children: [_jsx("div", { className: 'pb-4 shrink-0', children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Skeleton, { className: "h-9 w-[280px]" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Skeleton, { className: "h-9 w-9" }), _jsx(Skeleton, { className: "h-9 w-[70px]" })] })] }) }), _jsx("div", { className: 'flex-1 min-h-0 overflow-auto border rounded-md bg-card', children: _jsxs(Table, { noWrapper: true, className: "min-w-max w-full", children: [_jsx(TableHeader, { className: 'sticky top-0 z-10', children: _jsxs(TableRow, { className: 'border-b-0 hover:bg-transparent', children: [_jsx(TableHead, { className: 'bg-card border-b h-10 w-10', children: _jsx(Skeleton, { className: "h-4 w-4" }) }), _jsx(TableHead, { className: 'bg-card border-b h-10', children: _jsx(Skeleton, { className: "h-4 w-16" }) }), _jsx(TableHead, { className: 'bg-card border-b h-10', children: _jsx(Skeleton, { className: "h-4 w-14" }) }), _jsx(TableHead, { className: 'bg-card border-b h-10', children: _jsx(Skeleton, { className: "h-4 w-20" }) }), _jsx(TableHead, { className: 'bg-card border-b h-10 w-16', children: _jsx(Skeleton, { className: "h-4 w-12" }) })] }) }), _jsx(TableBody, { children: _jsx(TableSkeleton, {}) })] }) })] }));
|
|
@@ -1181,11 +1201,31 @@ export function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync
|
|
|
1181
1201
|
return (_jsxs(OptionsContext.Provider, { value: { optionsMap }, children: [_jsxs("div", { className: 'flex flex-col h-full min-h-0 w-full', children: [_jsx("div", { className: 'pb-4 shrink-0', children: _jsx(DataTableToolbar, { table: table, searchPlaceholder: metadata.searchPlaceholder || 'Buscar...', filters: filters, activeFilters: dynamicFilters, onDynamicFilterChange: handleDynamicFilterChange, dateFilter: { value: dateRange, onChange: setDateRange, placeholder: 'Filtrar por fecha' }, perPageOptions: metadata.perPageOptions, onRefresh: handleRefresh, isLoading: loadingData, selectedCount: Object.keys(rowSelection).length, onBulkDelete: () => setShowBulkDeleteConfirm(true), extraActions: _jsxs(_Fragment, { children: [exportEnabled && (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8", onClick: () => setExportOpen(true), children: [_jsx(Download, { className: "h-4 w-4 mr-1" }), " Exportar"] })), importEnabled && (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8", onClick: () => setImportOpen(true), children: [_jsx(Upload, { className: "h-4 w-4 mr-1" }), " Importar"] }))] }) }) }), _jsxs("div", { ref: infDesktopRoot, className: 'hidden sm:block flex-1 min-h-0 overflow-auto border rounded-md bg-card', children: [_jsxs(Table, { noWrapper: true, className: cn('min-w-max w-full', aggregateColumns.length > 0 && Object.keys(footerTotals).length > 0 && 'h-full'), children: [_jsx(TableHeader, { className: 'sticky top-0 z-10', children: table.getHeaderGroups().map((headerGroup) => (_jsx(TableRow, { className: 'border-b-0 hover:bg-transparent', children: headerGroup.headers.map((header) => {
|
|
1182
1202
|
const isActionsColumn = header.id === 'actions';
|
|
1183
1203
|
return (_jsx(TableHead, { colSpan: header.colSpan, style: header.column.columnDef.size ? { width: header.column.columnDef.size } : undefined, className: cn('bg-card border-b h-10', isActionsColumn && 'sticky right-0 z-20 bg-card shadow-[-2px_0_5px_-2px_rgba(0,0,0,0.1)]'), children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext()) }, header.id));
|
|
1184
|
-
}) }, headerGroup.id))) }), _jsx(TableBody, { children: loadingData && data.length === 0 ? (_jsx(TableSkeleton, {})) :
|
|
1204
|
+
}) }, headerGroup.id))) }), _jsx(TableBody, { children: loadingData && data.length === 0 ? (_jsx(TableSkeleton, {})) : tableRows.length ? (_jsxs(_Fragment, { children: [shouldVirtualize ? (_jsxs(_Fragment, { children: [desktopVirtualizer.getVirtualItems().length > 0 && (_jsx(TableRow, { className: 'border-0 hover:bg-transparent', "aria-hidden": true, children: _jsx(TableCell, { colSpan: colSpan, className: 'p-0', style: {
|
|
1205
|
+
height: desktopVirtualizer.getVirtualItems()[0]?.start ?? 0,
|
|
1206
|
+
} }) })), desktopVirtualizer.getVirtualItems().map((virtualRow) => {
|
|
1207
|
+
const row = tableRows[virtualRow.index];
|
|
1208
|
+
if (!row)
|
|
1209
|
+
return null;
|
|
1210
|
+
return (_jsx(TableRow, { "data-index": virtualRow.index, ref: desktopVirtualizer.measureElement, "data-state": row.getIsSelected() && 'selected', className: cn(onRowClick && 'cursor-pointer'), onClick: onRowClick ? () => onRowClick(row.original) : undefined, children: row.getVisibleCells().map((cell) => {
|
|
1211
|
+
const isActionsColumn = cell.column.id === 'actions';
|
|
1212
|
+
const isSelectColumn = cell.column.id === 'select';
|
|
1213
|
+
return (_jsx(TableCell, { style: cell.column.columnDef.size ? { width: cell.column.columnDef.size } : undefined, className: cn('py-2', isActionsColumn && 'sticky right-0 bg-card shadow-[-2px_0_5px_-2px_rgba(0,0,0,0.1)]'), onClick: (isActionsColumn || isSelectColumn) ? (e) => e.stopPropagation() : undefined, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id));
|
|
1214
|
+
}) }, row.id));
|
|
1215
|
+
}), (() => {
|
|
1216
|
+
const items = desktopVirtualizer.getVirtualItems();
|
|
1217
|
+
const last = items[items.length - 1];
|
|
1218
|
+
const pad = last != null
|
|
1219
|
+
? desktopVirtualizer.getTotalSize() - last.end
|
|
1220
|
+
: 0;
|
|
1221
|
+
if (pad <= 0)
|
|
1222
|
+
return null;
|
|
1223
|
+
return (_jsx(TableRow, { className: 'border-0 hover:bg-transparent', "aria-hidden": true, children: _jsx(TableCell, { colSpan: colSpan, className: 'p-0', style: { height: pad } }) }));
|
|
1224
|
+
})()] })) : (tableRows.map((row) => (_jsx(TableRow, { "data-state": row.getIsSelected() && 'selected', className: cn(onRowClick && 'cursor-pointer'), onClick: onRowClick ? () => onRowClick(row.original) : undefined, children: row.getVisibleCells().map((cell) => {
|
|
1185
1225
|
const isActionsColumn = cell.column.id === 'actions';
|
|
1186
1226
|
const isSelectColumn = cell.column.id === 'select';
|
|
1187
1227
|
return (_jsx(TableCell, { style: cell.column.columnDef.size ? { width: cell.column.columnDef.size } : undefined, className: cn('py-2', isActionsColumn && 'sticky right-0 bg-card shadow-[-2px_0_5px_-2px_rgba(0,0,0,0.1)]'), onClick: (isActionsColumn || isSelectColumn) ? (e) => e.stopPropagation() : undefined, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id));
|
|
1188
|
-
}) }, row.id))), aggregateColumns.length > 0 && Object.keys(footerTotals).length > 0 && (_jsx(TableRow, { className: 'border-0 hover:bg-transparent', children: _jsx(TableCell, { colSpan: columns.length, className: 'h-full p-0' }) }))] })) : (_jsx(TableRow, { className: 'border-b-0 hover:bg-transparent', children: _jsx(TableCell, { colSpan: columns.length, className: 'h-full p-0', children: _jsxs("div", { className: "flex h-full py-12 flex-col items-center justify-center gap-2 text-muted-foreground", children: [_jsx("div", { className: "flex h-20 w-20 items-center justify-center rounded-full bg-muted/50", children: _jsx(Inbox, { className: "h-10 w-10" }) }), _jsxs("div", { className: "flex flex-col items-center gap-1", children: [_jsx("h3", { className: "text-lg font-semibold text-foreground", children: "No se encontraron resultados" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "No hay datos para mostrar en este momento." })] })] }) }) })) }), aggregateColumns.length > 0 && Object.keys(footerTotals).length > 0 && (_jsx(TableFooter, { className: "bg-transparent", children: _jsx(TableRow, { className: "hover:bg-transparent", children: table.getVisibleLeafColumns().map((leaf, idx) => {
|
|
1228
|
+
}) }, row.id)))), aggregateColumns.length > 0 && Object.keys(footerTotals).length > 0 && (_jsx(TableRow, { className: 'border-0 hover:bg-transparent', children: _jsx(TableCell, { colSpan: columns.length, className: 'h-full p-0' }) }))] })) : (_jsx(TableRow, { className: 'border-b-0 hover:bg-transparent', children: _jsx(TableCell, { colSpan: columns.length, className: 'h-full p-0', children: _jsxs("div", { className: "flex h-full py-12 flex-col items-center justify-center gap-2 text-muted-foreground", children: [_jsx("div", { className: "flex h-20 w-20 items-center justify-center rounded-full bg-muted/50", children: _jsx(Inbox, { className: "h-10 w-10" }) }), _jsxs("div", { className: "flex flex-col items-center gap-1", children: [_jsx("h3", { className: "text-lg font-semibold text-foreground", children: "No se encontraron resultados" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "No hay datos para mostrar en este momento." })] })] }) }) })) }), aggregateColumns.length > 0 && Object.keys(footerTotals).length > 0 && (_jsx(TableFooter, { className: "bg-transparent", children: _jsx(TableRow, { className: "hover:bg-transparent", children: table.getVisibleLeafColumns().map((leaf, idx) => {
|
|
1189
1229
|
const col = (metadata?.columns ?? []).find((c) => c.key === leaf.id);
|
|
1190
1230
|
const isFirst = idx === 0;
|
|
1191
1231
|
const stickyBase = 'sticky bottom-0 z-10 border-t bg-background py-2 font-semibold';
|
|
@@ -1195,7 +1235,21 @@ export function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync
|
|
|
1195
1235
|
}
|
|
1196
1236
|
// First non-aggregate column carries the "Total" label.
|
|
1197
1237
|
return (_jsx(TableCell, { className: stickyBase, children: isFirst ? t('common.total', 'Total') : '' }, leaf.id));
|
|
1198
|
-
}) }) }))] }), infiniteScroll && (_jsxs(_Fragment, { children: [loadingMore && (_jsx("div", { className: 'p-2', children: _jsx(Skeleton, { className: 'h-8 w-full', "data-testid": 'table-loading-more' }) })), _jsx("div", { ref: infDesktopSentinel, className: 'h-1 w-full', "aria-hidden": true })] }))] }), _jsxs("div", { ref: infMobileRoot, className: 'flex flex-1 min-h-0 flex-col gap-2 overflow-y-auto sm:hidden', children: [loadingData && data.length === 0 ? (Array.from({ length: 5 }).map((_, i) => (_jsxs("div", { className: 'rounded-lg border bg-card p-3', children: [_jsx(Skeleton, { className: 'h-4 w-24' }), _jsx(Skeleton, { className: 'mt-2 h-4 w-40' }), _jsx(Skeleton, { className: 'mt-2 h-4 w-32' })] }, i)))) :
|
|
1238
|
+
}) }) }))] }), infiniteScroll && (_jsxs(_Fragment, { children: [loadingMore && (_jsx("div", { className: 'p-2', children: _jsx(Skeleton, { className: 'h-8 w-full', "data-testid": 'table-loading-more' }) })), _jsx("div", { ref: infDesktopSentinel, className: 'h-1 w-full', "aria-hidden": true })] }))] }), _jsxs("div", { ref: infMobileRoot, className: 'flex flex-1 min-h-0 flex-col gap-2 overflow-y-auto sm:hidden', children: [loadingData && data.length === 0 ? (Array.from({ length: 5 }).map((_, i) => (_jsxs("div", { className: 'rounded-lg border bg-card p-3', children: [_jsx(Skeleton, { className: 'h-4 w-24' }), _jsx(Skeleton, { className: 'mt-2 h-4 w-40' }), _jsx(Skeleton, { className: 'mt-2 h-4 w-32' })] }, i)))) : tableRows.length ? (shouldVirtualize ? (_jsx("div", { className: 'relative w-full', style: { height: mobileVirtualizer.getTotalSize() }, children: mobileVirtualizer.getVirtualItems().map((virtualRow) => {
|
|
1239
|
+
const row = tableRows[virtualRow.index];
|
|
1240
|
+
if (!row)
|
|
1241
|
+
return null;
|
|
1242
|
+
const cells = row.getVisibleCells();
|
|
1243
|
+
const actionsCell = cells.find((c) => c.column.id === 'actions');
|
|
1244
|
+
const dataCells = cells.filter((c) => c.column.id !== 'actions' && c.column.id !== 'select');
|
|
1245
|
+
return (_jsxs("div", { "data-index": virtualRow.index, ref: mobileVirtualizer.measureElement, "data-state": row.getIsSelected() && 'selected', className: cn('absolute left-0 right-0 flex flex-col gap-1.5 rounded-lg border bg-card p-3 data-[state=selected]:border-primary/40', onRowClick && 'cursor-pointer'), style: { transform: `translateY(${virtualRow.start}px)` }, onClick: onRowClick ? () => onRowClick(row.original) : undefined, children: [dataCells.map((cell) => {
|
|
1246
|
+
const cellMeta = cell.column.columnDef.meta;
|
|
1247
|
+
const header = cell.column.columnDef.header;
|
|
1248
|
+
const label = cellMeta?.label ??
|
|
1249
|
+
(typeof header === 'string' ? header : cell.column.id);
|
|
1250
|
+
return (_jsxs("div", { className: 'flex items-start justify-between gap-3 text-sm', children: [_jsx("span", { className: 'shrink-0 text-muted-foreground', children: label }), _jsx("span", { className: 'min-w-0 overflow-hidden break-words text-right font-medium [&_[data-slot=badge]]:max-w-full [&_[data-slot=badge]]:whitespace-normal [&_[data-slot=badge]]:text-start', children: flexRender(cell.column.columnDef.cell, cell.getContext()) })] }, cell.id));
|
|
1251
|
+
}), actionsCell && (_jsx("div", { className: 'flex justify-end border-t pt-2', onClick: onRowClick ? (e) => e.stopPropagation() : undefined, children: flexRender(actionsCell.column.columnDef.cell, actionsCell.getContext()) }))] }, row.id));
|
|
1252
|
+
}) })) : (tableRows.map((row) => {
|
|
1199
1253
|
const cells = row.getVisibleCells();
|
|
1200
1254
|
const actionsCell = cells.find((c) => c.column.id === 'actions');
|
|
1201
1255
|
const dataCells = cells.filter((c) => c.column.id !== 'actions' && c.column.id !== 'select');
|
|
@@ -1211,7 +1265,7 @@ export function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync
|
|
|
1211
1265
|
(typeof header === 'string' ? header : cell.column.id);
|
|
1212
1266
|
return (_jsxs("div", { className: 'flex items-start justify-between gap-3 text-sm', children: [_jsx("span", { className: 'shrink-0 text-muted-foreground', children: label }), _jsx("span", { className: 'min-w-0 overflow-hidden break-words text-right font-medium [&_[data-slot=badge]]:max-w-full [&_[data-slot=badge]]:whitespace-normal [&_[data-slot=badge]]:text-start', children: flexRender(cell.column.columnDef.cell, cell.getContext()) })] }, cell.id));
|
|
1213
1267
|
}), actionsCell && (_jsx("div", { className: 'flex justify-end border-t pt-2', onClick: onRowClick ? (e) => e.stopPropagation() : undefined, children: flexRender(actionsCell.column.columnDef.cell, actionsCell.getContext()) }))] }, row.id));
|
|
1214
|
-
})) : (_jsxs("div", { className: 'flex flex-col items-center justify-center gap-2 rounded-lg border bg-card py-12 text-muted-foreground', children: [_jsx("div", { className: 'flex h-16 w-16 items-center justify-center rounded-full bg-muted/50', children: _jsx(Inbox, { className: 'h-8 w-8' }) }), _jsx("h3", { className: 'text-base font-semibold text-foreground', children: "No se encontraron resultados" }), _jsx("p", { className: 'text-sm text-muted-foreground', children: "No hay datos para mostrar en este momento." })] })), infiniteScroll && (_jsxs(_Fragment, { children: [loadingMore && (_jsx(Skeleton, { className: 'h-16 w-full shrink-0', "data-testid": 'table-loading-more-mobile' })), _jsx("div", { ref: infMobileSentinel, className: 'h-1 w-full shrink-0', "aria-hidden": true })] }))] }), _jsx("div", { className: 'shrink-0 pt-4', children: infiniteScroll ? (data.length > 0 && (_jsx("p", { className: 'text-center text-xs text-muted-foreground tabular-nums', children: t('common.showingCount', {
|
|
1268
|
+
}))) : (_jsxs("div", { className: 'flex flex-col items-center justify-center gap-2 rounded-lg border bg-card py-12 text-muted-foreground', children: [_jsx("div", { className: 'flex h-16 w-16 items-center justify-center rounded-full bg-muted/50', children: _jsx(Inbox, { className: 'h-8 w-8' }) }), _jsx("h3", { className: 'text-base font-semibold text-foreground', children: "No se encontraron resultados" }), _jsx("p", { className: 'text-sm text-muted-foreground', children: "No hay datos para mostrar en este momento." })] })), infiniteScroll && (_jsxs(_Fragment, { children: [loadingMore && (_jsx(Skeleton, { className: 'h-16 w-full shrink-0', "data-testid": 'table-loading-more-mobile' })), _jsx("div", { ref: infMobileSentinel, className: 'h-1 w-full shrink-0', "aria-hidden": true })] }))] }), _jsx("div", { className: 'shrink-0 pt-4', children: infiniteScroll ? (data.length > 0 && (_jsx("p", { className: 'text-center text-xs text-muted-foreground tabular-nums', children: t('common.showingCount', {
|
|
1215
1269
|
defaultValue: '{{count}} de {{total}}',
|
|
1216
1270
|
count: data.length,
|
|
1217
1271
|
total: rowCount,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from './types';
|
|
2
|
+
export * from './table-virtualization';
|
|
2
3
|
export { LicenseGate, LicenseExpiryBanner, LicenseStatusBadge, isLicenseOperable, isLicenseBlocking, isPresetEntitled, isTrialExpired, type LicenseGateProps, type LicenseExpiryBannerProps, type LicenseStatusBadgeProps, type LicenseState, type LicenseStatus, type LicenseBranding, } from './license';
|
|
3
4
|
export * from './options-context';
|
|
4
5
|
export { extractServerError, extractFieldErrors, localizeFieldIssue, localizeFieldErrorMap, toastServerError, toastServerSuccess, type ExtractedError, type Translate, type FieldIssue, } from './server-error';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// duplicate-symbol conflict; consumers who want the canonical SDK type
|
|
5
5
|
// should import from `@asteby/metacore-sdk` directly.
|
|
6
6
|
export * from './types';
|
|
7
|
+
export * from './table-virtualization';
|
|
7
8
|
export { LicenseGate, LicenseExpiryBanner, LicenseStatusBadge, isLicenseOperable, isLicenseBlocking, isPresetEntitled, isTrialExpired, } from './license';
|
|
8
9
|
export * from './options-context';
|
|
9
10
|
export { extractServerError, extractFieldErrors, localizeFieldIssue, localizeFieldErrorMap, toastServerError, toastServerSuccess, } from './server-error';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Row virtualization defaults for DynamicTable.
|
|
3
|
+
*
|
|
4
|
+
* When the visible row model grows past the threshold (common in
|
|
5
|
+
* `pagination="infinite"` as pages accumulate, or when the user picks a large
|
|
6
|
+
* page size), we only mount the rows in the viewport via
|
|
7
|
+
* `@tanstack/react-virtual`. Below the threshold we keep the simple map so
|
|
8
|
+
* short tables stay trivial to debug.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DYNAMIC_TABLE_VIRTUALIZE_THRESHOLD = 40;
|
|
11
|
+
/** Estimated desktop table row height (py-2 + content). */
|
|
12
|
+
export declare const DYNAMIC_TABLE_ROW_ESTIMATE_PX = 44;
|
|
13
|
+
/** Estimated mobile card height (padding + a few label/value rows). */
|
|
14
|
+
export declare const DYNAMIC_TABLE_CARD_ESTIMATE_PX = 120;
|
|
15
|
+
export declare function resolveVirtualizeThreshold(virtualizeRows?: boolean | number): number | false;
|
|
16
|
+
//# sourceMappingURL=table-virtualization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-virtualization.d.ts","sourceRoot":"","sources":["../src/table-virtualization.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,kCAAkC,KAAK,CAAA;AAEpD,2DAA2D;AAC3D,eAAO,MAAM,6BAA6B,KAAK,CAAA;AAE/C,uEAAuE;AACvE,eAAO,MAAM,8BAA8B,MAAM,CAAA;AAEjD,wBAAgB,0BAA0B,CACxC,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,GAChC,MAAM,GAAG,KAAK,CAOhB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Row virtualization defaults for DynamicTable.
|
|
3
|
+
*
|
|
4
|
+
* When the visible row model grows past the threshold (common in
|
|
5
|
+
* `pagination="infinite"` as pages accumulate, or when the user picks a large
|
|
6
|
+
* page size), we only mount the rows in the viewport via
|
|
7
|
+
* `@tanstack/react-virtual`. Below the threshold we keep the simple map so
|
|
8
|
+
* short tables stay trivial to debug.
|
|
9
|
+
*/
|
|
10
|
+
export const DYNAMIC_TABLE_VIRTUALIZE_THRESHOLD = 40;
|
|
11
|
+
/** Estimated desktop table row height (py-2 + content). */
|
|
12
|
+
export const DYNAMIC_TABLE_ROW_ESTIMATE_PX = 44;
|
|
13
|
+
/** Estimated mobile card height (padding + a few label/value rows). */
|
|
14
|
+
export const DYNAMIC_TABLE_CARD_ESTIMATE_PX = 120;
|
|
15
|
+
export function resolveVirtualizeThreshold(virtualizeRows) {
|
|
16
|
+
if (virtualizeRows === false)
|
|
17
|
+
return false;
|
|
18
|
+
if (typeof virtualizeRows === 'number') {
|
|
19
|
+
return virtualizeRows > 0 ? virtualizeRows : false;
|
|
20
|
+
}
|
|
21
|
+
// true or undefined → default threshold
|
|
22
|
+
return DYNAMIC_TABLE_VIRTUALIZE_THRESHOLD;
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/metacore-runtime-react",
|
|
3
|
-
"version": "39.
|
|
3
|
+
"version": "39.1.0",
|
|
4
4
|
"description": "React runtime for metacore hosts — renders addon contributions dynamically",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"@dnd-kit/sortable": "^10.0.0",
|
|
22
22
|
"@dnd-kit/utilities": "^3.2.2",
|
|
23
23
|
"@module-federation/runtime": "^2.5.0",
|
|
24
|
+
"@tanstack/react-virtual": "^3.14.13",
|
|
24
25
|
"recharts": "^2.15.0",
|
|
25
26
|
"zod": "^4.3.0"
|
|
26
27
|
},
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
DYNAMIC_TABLE_VIRTUALIZE_THRESHOLD,
|
|
4
|
+
resolveVirtualizeThreshold,
|
|
5
|
+
} from '../table-virtualization'
|
|
6
|
+
|
|
7
|
+
describe('resolveVirtualizeThreshold', () => {
|
|
8
|
+
it('defaults to the shared threshold', () => {
|
|
9
|
+
expect(resolveVirtualizeThreshold()).toBe(DYNAMIC_TABLE_VIRTUALIZE_THRESHOLD)
|
|
10
|
+
expect(resolveVirtualizeThreshold(true)).toBe(DYNAMIC_TABLE_VIRTUALIZE_THRESHOLD)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('can be disabled', () => {
|
|
14
|
+
expect(resolveVirtualizeThreshold(false)).toBe(false)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('accepts a custom positive threshold', () => {
|
|
18
|
+
expect(resolveVirtualizeThreshold(80)).toBe(80)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('treats non-positive numbers as disabled', () => {
|
|
22
|
+
expect(resolveVirtualizeThreshold(0)).toBe(false)
|
|
23
|
+
expect(resolveVirtualizeThreshold(-1)).toBe(false)
|
|
24
|
+
})
|
|
25
|
+
})
|
package/src/dynamic-table.tsx
CHANGED
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
// * `@/components/dynamic/dynamic-columns` → host-injected via the
|
|
11
11
|
// `getDynamicColumns` prop (hosts retain ownership because the rendered
|
|
12
12
|
// column cells are tightly coupled to their design system).
|
|
13
|
-
import { useEffect, useState, useMemo, useCallback, useRef } from 'react'
|
|
13
|
+
import { useEffect, useState, useMemo, useCallback, useRef, type MouseEvent } from 'react'
|
|
14
14
|
import { useTranslation } from 'react-i18next'
|
|
15
15
|
import { format } from 'date-fns'
|
|
16
16
|
import type { DateRange } from 'react-day-picker'
|
|
17
|
+
import { useVirtualizer } from '@tanstack/react-virtual'
|
|
17
18
|
import {
|
|
18
19
|
type SortingState,
|
|
19
20
|
type VisibilityState,
|
|
@@ -69,6 +70,11 @@ import { defaultGetDynamicColumns, DATE_CELL_TYPES, aggregateOf, formatAggregate
|
|
|
69
70
|
import { useFacetLoaders, isLongTextColumn } from './use-facet-loaders'
|
|
70
71
|
import { translateOptionLabels } from './filter-chips'
|
|
71
72
|
import { dedupeById, useInfiniteScrollSentinel } from './use-infinite-scroll'
|
|
73
|
+
import {
|
|
74
|
+
DYNAMIC_TABLE_CARD_ESTIMATE_PX,
|
|
75
|
+
DYNAMIC_TABLE_ROW_ESTIMATE_PX,
|
|
76
|
+
resolveVirtualizeThreshold,
|
|
77
|
+
} from './table-virtualization'
|
|
72
78
|
import { OptionsContext } from './options-context'
|
|
73
79
|
import type { TableMetadata, ApiResponse, ColumnDefinition } from './types'
|
|
74
80
|
import { getSearchableColumnKeys } from './column-visibility'
|
|
@@ -245,6 +251,13 @@ export interface DynamicTableProps {
|
|
|
245
251
|
* mode exactly as before.
|
|
246
252
|
*/
|
|
247
253
|
infiniteScroll?: boolean
|
|
254
|
+
/**
|
|
255
|
+
* Row virtualization. When the visible row model has at least N rows,
|
|
256
|
+
* only viewport rows are mounted (helps infinite scroll and large page
|
|
257
|
+
* sizes). `true` / omitted → default threshold (40). `false` → always
|
|
258
|
+
* render every row. A positive number overrides the threshold.
|
|
259
|
+
*/
|
|
260
|
+
virtualizeRows?: boolean | number
|
|
248
261
|
}
|
|
249
262
|
|
|
250
263
|
export function DynamicTable({
|
|
@@ -266,6 +279,7 @@ export function DynamicTable({
|
|
|
266
279
|
currency,
|
|
267
280
|
pagination: paginationMode,
|
|
268
281
|
infiniteScroll: infiniteScrollProp = false,
|
|
282
|
+
virtualizeRows,
|
|
269
283
|
}: DynamicTableProps) {
|
|
270
284
|
// The explicit `pagination` prop wins; the legacy `infiniteScroll` boolean
|
|
271
285
|
// still selects the mode when `pagination` is absent (back-compat).
|
|
@@ -1300,6 +1314,27 @@ export function DynamicTable({
|
|
|
1300
1314
|
getFacetedUniqueValues: getFacetedUniqueValues(),
|
|
1301
1315
|
})
|
|
1302
1316
|
|
|
1317
|
+
const tableRows = table.getRowModel().rows
|
|
1318
|
+
const virtualizeThreshold = resolveVirtualizeThreshold(virtualizeRows)
|
|
1319
|
+
const shouldVirtualize =
|
|
1320
|
+
virtualizeThreshold !== false && tableRows.length >= virtualizeThreshold
|
|
1321
|
+
const colSpan = Math.max(columns.length, 1)
|
|
1322
|
+
|
|
1323
|
+
const desktopVirtualizer = useVirtualizer({
|
|
1324
|
+
count: shouldVirtualize ? tableRows.length : 0,
|
|
1325
|
+
getScrollElement: () => infDesktopRoot.current,
|
|
1326
|
+
estimateSize: () => DYNAMIC_TABLE_ROW_ESTIMATE_PX,
|
|
1327
|
+
overscan: 8,
|
|
1328
|
+
enabled: shouldVirtualize,
|
|
1329
|
+
})
|
|
1330
|
+
const mobileVirtualizer = useVirtualizer({
|
|
1331
|
+
count: shouldVirtualize ? tableRows.length : 0,
|
|
1332
|
+
getScrollElement: () => infMobileRoot.current,
|
|
1333
|
+
estimateSize: () => DYNAMIC_TABLE_CARD_ESTIMATE_PX,
|
|
1334
|
+
overscan: 6,
|
|
1335
|
+
enabled: shouldVirtualize,
|
|
1336
|
+
})
|
|
1337
|
+
|
|
1303
1338
|
const TableSkeleton = () => (
|
|
1304
1339
|
<>
|
|
1305
1340
|
{Array.from({ length: 5 }).map((_, i) => (
|
|
@@ -1407,9 +1442,70 @@ export function DynamicTable({
|
|
|
1407
1442
|
<TableBody>
|
|
1408
1443
|
{loadingData && data.length === 0 ? (
|
|
1409
1444
|
<TableSkeleton />
|
|
1410
|
-
) :
|
|
1445
|
+
) : tableRows.length ? (
|
|
1411
1446
|
<>
|
|
1412
|
-
{
|
|
1447
|
+
{shouldVirtualize ? (
|
|
1448
|
+
<>
|
|
1449
|
+
{desktopVirtualizer.getVirtualItems().length > 0 && (
|
|
1450
|
+
<TableRow
|
|
1451
|
+
className='border-0 hover:bg-transparent'
|
|
1452
|
+
aria-hidden
|
|
1453
|
+
>
|
|
1454
|
+
<TableCell
|
|
1455
|
+
colSpan={colSpan}
|
|
1456
|
+
className='p-0'
|
|
1457
|
+
style={{
|
|
1458
|
+
height: desktopVirtualizer.getVirtualItems()[0]?.start ?? 0,
|
|
1459
|
+
}}
|
|
1460
|
+
/>
|
|
1461
|
+
</TableRow>
|
|
1462
|
+
)}
|
|
1463
|
+
{desktopVirtualizer.getVirtualItems().map((virtualRow) => {
|
|
1464
|
+
const row = tableRows[virtualRow.index]
|
|
1465
|
+
if (!row) return null
|
|
1466
|
+
return (
|
|
1467
|
+
<TableRow
|
|
1468
|
+
key={row.id}
|
|
1469
|
+
data-index={virtualRow.index}
|
|
1470
|
+
ref={desktopVirtualizer.measureElement}
|
|
1471
|
+
data-state={row.getIsSelected() && 'selected'}
|
|
1472
|
+
className={cn(onRowClick && 'cursor-pointer')}
|
|
1473
|
+
onClick={onRowClick ? () => onRowClick(row.original) : undefined}
|
|
1474
|
+
>
|
|
1475
|
+
{row.getVisibleCells().map((cell: Cell<any, unknown>) => {
|
|
1476
|
+
const isActionsColumn = cell.column.id === 'actions'
|
|
1477
|
+
const isSelectColumn = cell.column.id === 'select'
|
|
1478
|
+
return (
|
|
1479
|
+
<TableCell
|
|
1480
|
+
key={cell.id}
|
|
1481
|
+
style={cell.column.columnDef.size ? { width: cell.column.columnDef.size } : undefined}
|
|
1482
|
+
className={cn('py-2', isActionsColumn && 'sticky right-0 bg-card shadow-[-2px_0_5px_-2px_rgba(0,0,0,0.1)]')}
|
|
1483
|
+
onClick={(isActionsColumn || isSelectColumn) ? (e: MouseEvent) => e.stopPropagation() : undefined}
|
|
1484
|
+
>
|
|
1485
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
1486
|
+
</TableCell>
|
|
1487
|
+
)
|
|
1488
|
+
})}
|
|
1489
|
+
</TableRow>
|
|
1490
|
+
)
|
|
1491
|
+
})}
|
|
1492
|
+
{(() => {
|
|
1493
|
+
const items = desktopVirtualizer.getVirtualItems()
|
|
1494
|
+
const last = items[items.length - 1]
|
|
1495
|
+
const pad =
|
|
1496
|
+
last != null
|
|
1497
|
+
? desktopVirtualizer.getTotalSize() - last.end
|
|
1498
|
+
: 0
|
|
1499
|
+
if (pad <= 0) return null
|
|
1500
|
+
return (
|
|
1501
|
+
<TableRow className='border-0 hover:bg-transparent' aria-hidden>
|
|
1502
|
+
<TableCell colSpan={colSpan} className='p-0' style={{ height: pad }} />
|
|
1503
|
+
</TableRow>
|
|
1504
|
+
)
|
|
1505
|
+
})()}
|
|
1506
|
+
</>
|
|
1507
|
+
) : (
|
|
1508
|
+
tableRows.map((row: Row<any>) => (
|
|
1413
1509
|
<TableRow
|
|
1414
1510
|
key={row.id}
|
|
1415
1511
|
data-state={row.getIsSelected() && 'selected'}
|
|
@@ -1424,14 +1520,15 @@ export function DynamicTable({
|
|
|
1424
1520
|
key={cell.id}
|
|
1425
1521
|
style={cell.column.columnDef.size ? { width: cell.column.columnDef.size } : undefined}
|
|
1426
1522
|
className={cn('py-2', isActionsColumn && 'sticky right-0 bg-card shadow-[-2px_0_5px_-2px_rgba(0,0,0,0.1)]')}
|
|
1427
|
-
onClick={(isActionsColumn || isSelectColumn) ? (e:
|
|
1523
|
+
onClick={(isActionsColumn || isSelectColumn) ? (e: MouseEvent) => e.stopPropagation() : undefined}
|
|
1428
1524
|
>
|
|
1429
1525
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
1430
1526
|
</TableCell>
|
|
1431
1527
|
)
|
|
1432
1528
|
})}
|
|
1433
1529
|
</TableRow>
|
|
1434
|
-
|
|
1530
|
+
))
|
|
1531
|
+
)}
|
|
1435
1532
|
{/* Spacer row: absorbs the table's leftover height (table is
|
|
1436
1533
|
h-full when a footer shows) so the totals footer is pinned to
|
|
1437
1534
|
the bottom of the box even with only a few rows. */}
|
|
@@ -1526,8 +1623,64 @@ export function DynamicTable({
|
|
|
1526
1623
|
<Skeleton className='mt-2 h-4 w-32' />
|
|
1527
1624
|
</div>
|
|
1528
1625
|
))
|
|
1529
|
-
) :
|
|
1530
|
-
|
|
1626
|
+
) : tableRows.length ? (
|
|
1627
|
+
shouldVirtualize ? (
|
|
1628
|
+
<div
|
|
1629
|
+
className='relative w-full'
|
|
1630
|
+
style={{ height: mobileVirtualizer.getTotalSize() }}
|
|
1631
|
+
>
|
|
1632
|
+
{mobileVirtualizer.getVirtualItems().map((virtualRow) => {
|
|
1633
|
+
const row = tableRows[virtualRow.index]
|
|
1634
|
+
if (!row) return null
|
|
1635
|
+
const cells = row.getVisibleCells()
|
|
1636
|
+
const actionsCell = cells.find((c: Cell<any, unknown>) => c.column.id === 'actions')
|
|
1637
|
+
const dataCells = cells.filter(
|
|
1638
|
+
(c: Cell<any, unknown>) => c.column.id !== 'actions' && c.column.id !== 'select',
|
|
1639
|
+
)
|
|
1640
|
+
return (
|
|
1641
|
+
<div
|
|
1642
|
+
key={row.id}
|
|
1643
|
+
data-index={virtualRow.index}
|
|
1644
|
+
ref={mobileVirtualizer.measureElement}
|
|
1645
|
+
data-state={row.getIsSelected() && 'selected'}
|
|
1646
|
+
className={cn(
|
|
1647
|
+
'absolute left-0 right-0 flex flex-col gap-1.5 rounded-lg border bg-card p-3 data-[state=selected]:border-primary/40',
|
|
1648
|
+
onRowClick && 'cursor-pointer',
|
|
1649
|
+
)}
|
|
1650
|
+
style={{ transform: `translateY(${virtualRow.start}px)` }}
|
|
1651
|
+
onClick={onRowClick ? () => onRowClick(row.original) : undefined}
|
|
1652
|
+
>
|
|
1653
|
+
{dataCells.map((cell: Cell<any, unknown>) => {
|
|
1654
|
+
const cellMeta = cell.column.columnDef.meta as
|
|
1655
|
+
| { label?: string }
|
|
1656
|
+
| undefined
|
|
1657
|
+
const header = cell.column.columnDef.header
|
|
1658
|
+
const label =
|
|
1659
|
+
cellMeta?.label ??
|
|
1660
|
+
(typeof header === 'string' ? header : cell.column.id)
|
|
1661
|
+
return (
|
|
1662
|
+
<div key={cell.id} className='flex items-start justify-between gap-3 text-sm'>
|
|
1663
|
+
<span className='shrink-0 text-muted-foreground'>{label}</span>
|
|
1664
|
+
<span className='min-w-0 overflow-hidden break-words text-right font-medium [&_[data-slot=badge]]:max-w-full [&_[data-slot=badge]]:whitespace-normal [&_[data-slot=badge]]:text-start'>
|
|
1665
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
1666
|
+
</span>
|
|
1667
|
+
</div>
|
|
1668
|
+
)
|
|
1669
|
+
})}
|
|
1670
|
+
{actionsCell && (
|
|
1671
|
+
<div
|
|
1672
|
+
className='flex justify-end border-t pt-2'
|
|
1673
|
+
onClick={onRowClick ? (e: MouseEvent) => e.stopPropagation() : undefined}
|
|
1674
|
+
>
|
|
1675
|
+
{flexRender(actionsCell.column.columnDef.cell, actionsCell.getContext())}
|
|
1676
|
+
</div>
|
|
1677
|
+
)}
|
|
1678
|
+
</div>
|
|
1679
|
+
)
|
|
1680
|
+
})}
|
|
1681
|
+
</div>
|
|
1682
|
+
) : (
|
|
1683
|
+
tableRows.map((row: Row<any>) => {
|
|
1531
1684
|
const cells = row.getVisibleCells()
|
|
1532
1685
|
const actionsCell = cells.find((c: Cell<any, unknown>) => c.column.id === 'actions')
|
|
1533
1686
|
const dataCells = cells.filter(
|
|
@@ -1569,7 +1722,7 @@ export function DynamicTable({
|
|
|
1569
1722
|
{actionsCell && (
|
|
1570
1723
|
<div
|
|
1571
1724
|
className='flex justify-end border-t pt-2'
|
|
1572
|
-
onClick={onRowClick ? (e:
|
|
1725
|
+
onClick={onRowClick ? (e: MouseEvent) => e.stopPropagation() : undefined}
|
|
1573
1726
|
>
|
|
1574
1727
|
{flexRender(actionsCell.column.columnDef.cell, actionsCell.getContext())}
|
|
1575
1728
|
</div>
|
|
@@ -1577,6 +1730,7 @@ export function DynamicTable({
|
|
|
1577
1730
|
</div>
|
|
1578
1731
|
)
|
|
1579
1732
|
})
|
|
1733
|
+
)
|
|
1580
1734
|
) : (
|
|
1581
1735
|
<div className='flex flex-col items-center justify-center gap-2 rounded-lg border bg-card py-12 text-muted-foreground'>
|
|
1582
1736
|
<div className='flex h-16 w-16 items-center justify-center rounded-full bg-muted/50'>
|
|
@@ -1636,7 +1790,7 @@ export function DynamicTable({
|
|
|
1636
1790
|
{!isBulkDeleting && (
|
|
1637
1791
|
<AlertDialogFooter>
|
|
1638
1792
|
<AlertDialogCancel>{t('common.cancel')}</AlertDialogCancel>
|
|
1639
|
-
<AlertDialogAction onClick={(e:
|
|
1793
|
+
<AlertDialogAction onClick={(e: MouseEvent) => { e.preventDefault(); confirmBulkDelete() }} className="bg-red-600 hover:bg-red-700">Eliminar todos</AlertDialogAction>
|
|
1640
1794
|
</AlertDialogFooter>
|
|
1641
1795
|
)}
|
|
1642
1796
|
</AlertDialogContent>
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Row virtualization defaults for DynamicTable.
|
|
3
|
+
*
|
|
4
|
+
* When the visible row model grows past the threshold (common in
|
|
5
|
+
* `pagination="infinite"` as pages accumulate, or when the user picks a large
|
|
6
|
+
* page size), we only mount the rows in the viewport via
|
|
7
|
+
* `@tanstack/react-virtual`. Below the threshold we keep the simple map so
|
|
8
|
+
* short tables stay trivial to debug.
|
|
9
|
+
*/
|
|
10
|
+
export const DYNAMIC_TABLE_VIRTUALIZE_THRESHOLD = 40
|
|
11
|
+
|
|
12
|
+
/** Estimated desktop table row height (py-2 + content). */
|
|
13
|
+
export const DYNAMIC_TABLE_ROW_ESTIMATE_PX = 44
|
|
14
|
+
|
|
15
|
+
/** Estimated mobile card height (padding + a few label/value rows). */
|
|
16
|
+
export const DYNAMIC_TABLE_CARD_ESTIMATE_PX = 120
|
|
17
|
+
|
|
18
|
+
export function resolveVirtualizeThreshold(
|
|
19
|
+
virtualizeRows?: boolean | number,
|
|
20
|
+
): number | false {
|
|
21
|
+
if (virtualizeRows === false) return false
|
|
22
|
+
if (typeof virtualizeRows === 'number') {
|
|
23
|
+
return virtualizeRows > 0 ? virtualizeRows : false
|
|
24
|
+
}
|
|
25
|
+
// true or undefined → default threshold
|
|
26
|
+
return DYNAMIC_TABLE_VIRTUALIZE_THRESHOLD
|
|
27
|
+
}
|