@pallasoft/ps-table-components 1.25.0 → 1.27.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/dist/components/ps-data-table.d.ts +10 -1
- package/dist/components/ps-data-table.js +93 -17
- package/dist/components/ps-select-all-matching-banner.d.ts +10 -0
- package/dist/components/ps-select-all-matching-banner.js +10 -0
- package/dist/components/ps-table-pagination.d.ts +3 -1
- package/dist/components/ps-table-pagination.js +11 -3
- package/dist/components/ps-table-reorder-frame.d.ts +2 -1
- package/dist/components/ps-table-reorder-frame.js +8 -4
- package/dist/constants/ps-server-rows-config.d.ts +8 -0
- package/dist/constants/ps-server-rows-config.js +11 -0
- package/dist/constants/ps-table-drop-classes.js +2 -2
- package/dist/constants/ps-table-pagination-position.d.ts +6 -0
- package/dist/constants/ps-table-pagination-position.js +8 -0
- package/dist/esm/components/ps-data-table.js +94 -18
- package/dist/esm/components/ps-select-all-matching-banner.js +6 -0
- package/dist/esm/components/ps-table-pagination.js +11 -3
- package/dist/esm/components/ps-table-reorder-frame.js +8 -4
- package/dist/esm/constants/ps-server-rows-config.js +8 -0
- package/dist/esm/constants/ps-table-drop-classes.js +2 -2
- package/dist/esm/constants/ps-table-pagination-position.js +5 -0
- package/dist/esm/hooks/use-ps-server-rows.js +209 -0
- package/dist/esm/hooks/use-ps-table-reorder.js +8 -1
- package/dist/esm/index.js +12 -0
- package/dist/esm/interfaces/ps-server-entry.js +1 -0
- package/dist/esm/interfaces/ps-server-page.js +1 -0
- package/dist/esm/interfaces/ps-server-rows-input.js +1 -0
- package/dist/esm/interfaces/ps-server-rows-result.js +1 -0
- package/dist/esm/interfaces/ps-table-cursor-paging.js +1 -0
- package/dist/esm/interfaces/ps-table-select-all-matching.js +1 -0
- package/dist/esm/interfaces/table-drag-size.js +1 -0
- package/dist/esm/services/ps-server-rows-service.js +36 -0
- package/dist/esm/services/ps-table-reorder-service.js +12 -0
- package/dist/hooks/use-ps-server-rows.d.ts +3 -0
- package/dist/hooks/use-ps-server-rows.js +213 -0
- package/dist/hooks/use-ps-table-reorder.d.ts +2 -0
- package/dist/hooks/use-ps-table-reorder.js +8 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/interfaces/ps-server-entry.d.ts +6 -0
- package/dist/interfaces/ps-server-entry.js +2 -0
- package/dist/interfaces/ps-server-page.d.ts +7 -0
- package/dist/interfaces/ps-server-page.js +2 -0
- package/dist/interfaces/ps-server-rows-input.d.ts +13 -0
- package/dist/interfaces/ps-server-rows-input.js +2 -0
- package/dist/interfaces/ps-server-rows-result.d.ts +18 -0
- package/dist/interfaces/ps-server-rows-result.js +2 -0
- package/dist/interfaces/ps-table-cursor-paging.d.ts +12 -0
- package/dist/interfaces/ps-table-cursor-paging.js +2 -0
- package/dist/interfaces/ps-table-select-all-matching.d.ts +7 -0
- package/dist/interfaces/ps-table-select-all-matching.js +2 -0
- package/dist/interfaces/table-drag-size.d.ts +5 -0
- package/dist/interfaces/table-drag-size.js +2 -0
- package/dist/interfaces/table-reorder-item.d.ts +1 -0
- package/dist/interfaces/table-reorder.d.ts +1 -0
- package/dist/services/ps-server-rows-service.d.ts +11 -0
- package/dist/services/ps-server-rows-service.js +40 -0
- package/dist/services/ps-table-reorder-service.d.ts +2 -0
- package/dist/services/ps-table-reorder-service.js +12 -0
- package/package.json +2 -2
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Fragment, Suspense, lazy, useCallback, useDeferredValue, useEffect, useMemo, useRef, useState, } from 'react';
|
|
2
|
+
import { Fragment, Suspense, lazy, useCallback, useDeferredValue, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
3
3
|
import { PsDragGrip, PsSkeletonTable } from '@pallasoft/ps-core-components';
|
|
4
4
|
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
5
5
|
import { PsCheckbox } from '@pallasoft/ps-input-components';
|
|
6
6
|
import { PsCollapse } from '@pallasoft/ps-motion-components';
|
|
7
7
|
import { useVirtualizer, useWindowVirtualizer } from '@tanstack/react-virtual';
|
|
8
8
|
import { PsTableControlClasses } from '../constants/ps-table-control-classes';
|
|
9
|
+
import { PsTablePaginationPosition } from '../constants/ps-table-pagination-position';
|
|
9
10
|
import { PsTableHideBelowClasses } from '../constants/table-breakpoint';
|
|
10
11
|
import { PsTableConfig } from '../constants/table-config';
|
|
11
12
|
import { usePsIncrementalCount } from '../hooks/use-ps-incremental-count';
|
|
@@ -19,6 +20,7 @@ import { PsTablePinService } from '../services/ps-table-pin-service';
|
|
|
19
20
|
import { PsTableQueryService } from '../services/ps-table-query-service';
|
|
20
21
|
import { PsTableReorderService } from '../services/ps-table-reorder-service';
|
|
21
22
|
import { PsTableScrollService } from '../services/ps-table-scroll-service';
|
|
23
|
+
import { PsSelectAllMatchingBanner } from './ps-select-all-matching-banner';
|
|
22
24
|
import { PsTableBulkBar } from './ps-table-bulk-bar';
|
|
23
25
|
import { PsTableCardList } from './ps-table-card-list';
|
|
24
26
|
import { PsTableColumnPicker } from './ps-table-column-picker';
|
|
@@ -48,7 +50,7 @@ const alignClassNames = {
|
|
|
48
50
|
left: 'text-left',
|
|
49
51
|
right: 'text-right tabular-nums',
|
|
50
52
|
};
|
|
51
|
-
export const PsDataTable = ({ caption, className, columns, defaultSort, density = 'normal', expandedRowIds, mobileLayout = 'cards', getBulkActions, getRowId, getRowLabel, getSearchText, grouping, isLoading = false, isPending = false, layout = 'auto', onPageChange, onQueryChange, onRowClick, onRowHover, page, pageSize, pinFirstColumn = true, query, renderExpandedRow, reorder, rows, searchingLabel = PsTableConfig.searchingLabel, searchPlaceholder = PsTableConfig.searchPlaceholder, toolbarActions, totalRows, }) => {
|
|
53
|
+
export const PsDataTable = ({ caption, className, clearSelectionKey, columns, cursorPaging, defaultSort, density = 'normal', expandedRowIds, mobileLayout = 'cards', getBulkActions, getRowId, getRowLabel, getSearchText, grouping, isLoading = false, isPending = false, isRefreshing = false, layout = 'auto', onPageChange, onQueryChange, onRowClick, onRowHover, page, pageSize, paginationPosition = PsTablePaginationPosition.bottom, pinFirstColumn = true, query, renderExpandedRow, reorder, rows, searchingLabel = PsTableConfig.searchingLabel, searchPlaceholder = PsTableConfig.searchPlaceholder, selectAllMatching, stickyTop, toolbarActions, totalRows, }) => {
|
|
52
54
|
const containerRef = useRef(null);
|
|
53
55
|
const [uncontrolledPage, setUncontrolledPage] = useState(1);
|
|
54
56
|
const [uncontrolledQuery, setUncontrolledQuery] = useState(() => PsTableQueryService.empty(defaultSort ? defaultSort : null));
|
|
@@ -56,7 +58,50 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
56
58
|
const [toggledGroupKeys, setToggledGroupKeys] = useState([]);
|
|
57
59
|
const [closingGroupKeys, setClosingGroupKeys] = useState([]);
|
|
58
60
|
const [openedGroupKeys, setOpenedGroupKeys] = useState([]);
|
|
61
|
+
const [seenClearKey, setSeenClearKey] = useState(clearSelectionKey);
|
|
62
|
+
const stickyRegionRef = useRef(null);
|
|
63
|
+
const stickySentinelRef = useRef(null);
|
|
64
|
+
const [stickyHeight, setStickyHeight] = useState(0);
|
|
65
|
+
const [isStuck, setIsStuck] = useState(false);
|
|
59
66
|
const currentQuery = query ? query : uncontrolledQuery;
|
|
67
|
+
if (seenClearKey !== clearSelectionKey) {
|
|
68
|
+
setSeenClearKey(clearSelectionKey);
|
|
69
|
+
setSelectedIds([]);
|
|
70
|
+
}
|
|
71
|
+
useLayoutEffect(() => {
|
|
72
|
+
const region = stickyRegionRef.current;
|
|
73
|
+
if (stickyTop === undefined || region === null)
|
|
74
|
+
return;
|
|
75
|
+
const measure = () => setStickyHeight(region.offsetHeight);
|
|
76
|
+
const observer = new ResizeObserver(measure);
|
|
77
|
+
measure();
|
|
78
|
+
observer.observe(region);
|
|
79
|
+
return () => observer.disconnect();
|
|
80
|
+
}, [stickyTop]);
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
const region = stickyRegionRef.current;
|
|
83
|
+
const sentinel = stickySentinelRef.current;
|
|
84
|
+
if (stickyTop === undefined || region === null || sentinel === null)
|
|
85
|
+
return;
|
|
86
|
+
let frame = 0;
|
|
87
|
+
const check = () => {
|
|
88
|
+
frame = 0;
|
|
89
|
+
setIsStuck(sentinel.getBoundingClientRect().top < Number.parseFloat(getComputedStyle(region).top));
|
|
90
|
+
};
|
|
91
|
+
const schedule = () => {
|
|
92
|
+
if (frame === 0)
|
|
93
|
+
frame = window.requestAnimationFrame(check);
|
|
94
|
+
};
|
|
95
|
+
check();
|
|
96
|
+
window.addEventListener('scroll', schedule, { passive: true });
|
|
97
|
+
window.addEventListener('resize', schedule);
|
|
98
|
+
return () => {
|
|
99
|
+
window.removeEventListener('scroll', schedule);
|
|
100
|
+
window.removeEventListener('resize', schedule);
|
|
101
|
+
if (frame !== 0)
|
|
102
|
+
window.cancelAnimationFrame(frame);
|
|
103
|
+
};
|
|
104
|
+
}, [stickyTop]);
|
|
60
105
|
const { filters, hiddenColumnIds, search, sort } = currentQuery;
|
|
61
106
|
const visibleColumns = columns.filter((column) => !hiddenColumnIds.includes(column.id));
|
|
62
107
|
const fittedWidths = useMemo(() => new Map(columns
|
|
@@ -158,7 +203,9 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
158
203
|
announcements: nesting.announcements,
|
|
159
204
|
canDrag: (id) => {
|
|
160
205
|
const row = find(id);
|
|
161
|
-
return row !== undefined &&
|
|
206
|
+
return (row !== undefined &&
|
|
207
|
+
(reorder.isRowDraggable === undefined || reorder.isRowDraggable(row)) &&
|
|
208
|
+
nesting.canDrag(row));
|
|
162
209
|
},
|
|
163
210
|
canDrop: (activeId, targetId, placement) => {
|
|
164
211
|
const active = find(activeId);
|
|
@@ -190,13 +237,16 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
190
237
|
groupKey: reorderKey(row),
|
|
191
238
|
handleLabel: reorder.getHandleLabel(label),
|
|
192
239
|
id: rowId,
|
|
240
|
+
isDraggable: reorder.isRowDraggable === undefined || reorder.isRowDraggable(row),
|
|
193
241
|
label,
|
|
194
242
|
};
|
|
195
243
|
})
|
|
196
244
|
: [], [getRowId, getRowLabel, reorder, reorderKey, visibleRows]);
|
|
197
|
-
const
|
|
245
|
+
const showsPagination = Boolean(pageSize) && (!grouping || isServerPaginated);
|
|
246
|
+
const renderPagination = (paginationClassName) => pageSize ? (_jsx(PsTablePagination, { className: paginationClassName, cursor: cursorPaging, isPartial: isPartial, loadedPageCount: pageCountOf(loadedRowCount), onPageChange: changePage, page: safePage, pageCount: pageCount, pageSize: pageSize, totalRows: rowCount })) : null;
|
|
247
|
+
const renderOverlayRow = (rowId, size) => {
|
|
198
248
|
const row = visibleRows.find((candidate) => getRowId(candidate) === rowId);
|
|
199
|
-
return row === undefined ? null : (
|
|
249
|
+
return row === undefined ? null : (_jsxs("table", { className: 'pointer-events-none w-full table-fixed border-collapse bg-surface text-left', style: size === null ? undefined : { width: size.width }, children: [size === null ? null : (_jsx("colgroup", { children: size.columnWidths.map((width, index) => (_jsx("col", { style: { width } }, index))) })), _jsx("tbody", { children: renderRow(PsTableReorderService.plain, row, undefined, undefined, true) })] }));
|
|
200
250
|
};
|
|
201
251
|
const renderReorder = (children) => reorder === undefined ? (children(PsTableReorderService.plain)) : (_jsx(Suspense, { fallback: children(PsTableReorderService.plain), children: _jsx(LazyReorderFrame, { announcements: reorder.announcements, enabled: reorder.enabled, instructions: reorder.instructions, items: reorderItems, nesting: nestingBinding, onReorder: reorder.onReorder, renderOverlay: renderOverlayRow, children: children }) }));
|
|
202
252
|
const changePage = (next) => {
|
|
@@ -227,12 +277,16 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
227
277
|
const selectedRows = selectedIds.length === 0 ? [] : queriedRows.filter((row) => selectedIdSet.has(getRowId(row)));
|
|
228
278
|
const pageIds = visibleRows.map((row) => getRowId(row));
|
|
229
279
|
const isPageSelected = pageIds.length > 0 && pageIds.every((rowId) => selectedIdSet.has(rowId));
|
|
230
|
-
const togglePage = () =>
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
280
|
+
const togglePage = () => {
|
|
281
|
+
if (isPageSelected && selectAllMatching !== undefined && selectAllMatching.isActive)
|
|
282
|
+
selectAllMatching.onDeactivate();
|
|
283
|
+
setSelectedIds((current) => {
|
|
284
|
+
const pageIdSet = new Set(pageIds);
|
|
285
|
+
return isPageSelected
|
|
286
|
+
? current.filter((rowId) => !pageIdSet.has(rowId))
|
|
287
|
+
: [...new Set([...current, ...pageIds])];
|
|
288
|
+
});
|
|
289
|
+
};
|
|
236
290
|
const bodyRef = useRef(null);
|
|
237
291
|
const [scrollParent, setScrollParent] = useState(null);
|
|
238
292
|
const [scrollMargin, setScrollMargin] = useState(0);
|
|
@@ -276,7 +330,7 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
276
330
|
}, [shouldVirtualize, visibleRows.length]);
|
|
277
331
|
const toggleRow = (rowId) => setSelectedIds((current) => current.includes(rowId) ? current.filter((id) => id !== rowId) : [...current, rowId]);
|
|
278
332
|
const renderCardList = (cardRows, emptyContent) => (_jsx(PsTableCardList, { columns: visibleColumns, emptyContent: emptyContent, expandedRowIds: expandedRowIds, footerColumns: hasFooter ? visibleColumns.filter((column) => column.footer !== undefined) : [], getRowId: getRowId, getRowLabel: (row) => (getRowLabel ? getRowLabel(row) : getRowId(row)), isSelectable: isSelectable, onRowClick: onRowClick, onRowHover: onRowHover, onToggleRow: toggleRow, renderExpandedRow: renderExpandedRow, rows: cardRows, selectedIds: selectedIds }));
|
|
279
|
-
const renderRow = (api, row, motion, virtualItem) => {
|
|
333
|
+
const renderRow = (api, row, motion, virtualItem, isOverlay = false) => {
|
|
280
334
|
const rowId = getRowId(row);
|
|
281
335
|
const isExpanded = expandedRowIds ? expandedRowIds.includes(rowId) : false;
|
|
282
336
|
const rowLabel = getRowLabel ? getRowLabel(row) : rowId;
|
|
@@ -288,7 +342,7 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
288
342
|
measureRef: virtualItem ? virtualizer.measureElement : undefined,
|
|
289
343
|
}
|
|
290
344
|
: { ref: virtualItem ? virtualizer.measureElement : undefined };
|
|
291
|
-
return (_jsxs(Fragment, { children: [_jsxs(Row, { ...rowExtras, "data-index": virtualItem ? virtualItem.index : undefined, "aria-expanded": renderExpandedRow ? isExpanded : undefined, "aria-label": onRowClick ? rowLabel : undefined, className: PallasoftClassNameService.merge('align-middle
|
|
345
|
+
return (_jsxs(Fragment, { children: [_jsxs(Row, { ...rowExtras, "data-index": virtualItem ? virtualItem.index : undefined, "aria-expanded": renderExpandedRow ? isExpanded : undefined, "aria-label": onRowClick ? rowLabel : undefined, className: PallasoftClassNameService.merge('align-middle hover:bg-mist/60', motion ? motion.className : '', onRowClick ? 'ps-focus-ring cursor-pointer' : '', isExpanded ? 'bg-mist/60' : ''), onClick: onRowClick ? () => onRowClick(row) : undefined, onFocus: onRowHover ? () => onRowHover(row) : undefined, onPointerEnter: onRowHover ? () => onRowHover(row) : undefined, onKeyDown: onRowClick
|
|
292
346
|
? (event) => {
|
|
293
347
|
if (event.target !== event.currentTarget)
|
|
294
348
|
return;
|
|
@@ -303,14 +357,25 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
303
357
|
((layout === 'fixed' && !column.wrap) ||
|
|
304
358
|
PsTableColumnSizeService.maxWidthOf(column) !== undefined)
|
|
305
359
|
? 'truncate'
|
|
306
|
-
: '', 'text-sm text-ink'), style: columnStyle(column, fittedWidths.get(column.id)), title: PsTableCellTooltipService.forCell(column, row), children: column.render(row) }, column.id)))] }), renderExpandedRow ? (_jsx("tr", { "aria-hidden": !isExpanded, className: isExpanded ? 'border-t-0' : 'border-0', children: _jsx("td", { className: 'p-0', colSpan: columnCount, children: _jsx(PsCollapse, { isOpen: isExpanded, children: _jsx("div", { className: 'w-0 min-w-full border-l-2 border-primary bg-mist/40 px-4 py-4', children: renderExpandedRow(row) }) }) }) })) : null] }, rowId));
|
|
360
|
+
: '', 'text-sm text-ink'), style: columnStyle(column, fittedWidths.get(column.id)), title: PsTableCellTooltipService.forCell(column, row), children: column.render(row) }, column.id)))] }), renderExpandedRow && !isOverlay ? (_jsx("tr", { "aria-hidden": !isExpanded, className: isExpanded ? 'border-t-0' : 'border-0', children: _jsx("td", { className: 'p-0', colSpan: columnCount, children: _jsx(PsCollapse, { isOpen: isExpanded, children: _jsx("div", { className: 'w-0 min-w-full border-l-2 border-primary bg-mist/40 px-4 py-4', children: renderExpandedRow(row) }) }) }) })) : null] }, rowId));
|
|
307
361
|
};
|
|
308
362
|
const renderEntries = (api, entries) => reorder
|
|
309
363
|
? PsTableReorderService.runs(entries, (entry) => reorderKey(entry.row), (entry) => getRowId(entry.row)).map((run) => api.run(run.id, run.items.map((entry) => getRowId(entry.row)), run.items.map((entry) => renderRow(api, entry.row, entry.motion, entry.virtualItem))))
|
|
310
364
|
: entries.map((entry) => renderRow(api, entry.row, entry.motion, entry.virtualItem));
|
|
311
365
|
if (isLoading)
|
|
312
366
|
return (_jsx(PsSkeletonTable, { className: className, columns: visibleColumns.length, hasToolbar: hasToolbar, rows: PsTableConfig.skeletonRows }));
|
|
313
|
-
|
|
367
|
+
const clearSelection = () => {
|
|
368
|
+
setSelectedIds([]);
|
|
369
|
+
if (selectAllMatching !== undefined && selectAllMatching.isActive)
|
|
370
|
+
selectAllMatching.onDeactivate();
|
|
371
|
+
};
|
|
372
|
+
const banner = selectAllMatching !== undefined &&
|
|
373
|
+
selectedRows.length > 0 &&
|
|
374
|
+
selectAllMatching.total > pageIds.length &&
|
|
375
|
+
(isPageSelected || selectAllMatching.isActive) ? (_jsx(PsSelectAllMatchingBanner, { isActive: selectAllMatching.isActive, noun: selectAllMatching.noun, onActivate: selectAllMatching.onActivate, onDeactivate: clearSelection, pageCount: pageIds.length, total: selectAllMatching.total })) : null;
|
|
376
|
+
const topContent = (_jsxs(_Fragment, { children: [isSelectable && selectedRows.length > 0 && getBulkActions ? (_jsx(PsTableBulkBar, { actions: getBulkActions(selectedRows), onClear: clearSelection, selectedCount: selectAllMatching !== undefined && selectAllMatching.isActive
|
|
377
|
+
? selectAllMatching.total
|
|
378
|
+
: selectedRows.length })) : hasToolbar || columns.some((column) => column.canHide) ? (_jsx(PsTableToolbar, { actions: _jsxs(_Fragment, { children: [toolbarActions, columns.some((column) => column.canHide) ? (_jsx(PsTableColumnPicker, { columns: columns
|
|
314
379
|
.filter((column) => column.canHide)
|
|
315
380
|
.map((column) => ({ id: column.id, label: column.label })), hiddenColumnIds: hiddenColumnIds, onChange: (nextHidden) => updateQuery({ hiddenColumnIds: nextHidden }) })) : null] }), columns: hasFilters ? columns : [], filters: filters, isSearchable: isSearchable, onClearFilters: clearFilters, onFilterChange: (columnId, value) => {
|
|
316
381
|
updateQuery({ filters: { ...filters, [columnId]: value } });
|
|
@@ -318,14 +383,23 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
318
383
|
}, onSearchChange: (next) => {
|
|
319
384
|
updateQuery({ search: next });
|
|
320
385
|
resetPage();
|
|
321
|
-
}, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null,
|
|
386
|
+
}, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null, banner] }));
|
|
387
|
+
return (_jsxs("div", { ref: containerRef, className: PallasoftClassNameService.merge('flex min-w-0 flex-col gap-3', className), children: [stickyTop === undefined ? (topContent) : (_jsxs(_Fragment, { children: [_jsx("div", { "aria-hidden": 'true', className: '-mb-3 h-0', ref: stickySentinelRef }), _jsx("div", { className: PallasoftClassNameService.merge('sticky z-sticky -mx-1 flex flex-col gap-3 bg-canvas px-1 pb-2 pt-1 transition-shadow', isStuck ? 'shadow-[0_8px_8px_-8px_rgb(var(--ps-color-ink-rgb,15_23_42)/0.18)]' : ''), ref: stickyRegionRef, style: { top: stickyTop }, children: topContent })] })), _jsxs("div", { "aria-busy": isRefreshing, className: PallasoftClassNameService.merge('min-w-0 rounded-[1.25rem] border border-line bg-surface', stickyTop === undefined ? 'overflow-hidden' : 'overflow-clip'), children: [showsPagination && paginationPosition !== PsTablePaginationPosition.bottom
|
|
388
|
+
? renderPagination('border-b border-t-0')
|
|
389
|
+
: null, mobileLayout === 'cards' ? (_jsxs("div", { className: PallasoftClassNameService.merge('transition-opacity duration-150 sm:hidden', isRefreshing ? 'opacity-60' : ''), children: [reorder && reorder.cardsReason ? (_jsx("p", { className: 'border-b border-line px-4 py-2 text-xs text-ink/55', children: reorder.cardsReason })) : null, grouping
|
|
322
390
|
? groups.map((group, groupIndex) => {
|
|
323
391
|
const isExpanded = isGroupExpanded(group.key, groupIndex);
|
|
324
392
|
return (_jsxs("div", { className: 'border-b border-line last:border-b-0', children: [_jsx(PsTableGroupHeader, { count: group.rows.length, isExpanded: isExpanded, label: grouping.getGroupLabel(group.key, group.rows), onToggle: () => toggleGroup(group.key, isExpanded), summary: grouping.renderGroupSummary
|
|
325
393
|
? grouping.renderGroupSummary(group.rows)
|
|
326
394
|
: undefined }), _jsx(PsCollapse, { isOpen: isExpanded && !closingGroupKeys.includes(group.key), children: renderCardList(group.rows, null) })] }, `group-${group.key}`));
|
|
327
395
|
})
|
|
328
|
-
: renderCardList(shouldVirtualize ? visibleRows.slice(0, cards.count) : visibleRows, visibleRows.length === 0 && isSearching ? (_jsx(PsTableSearchingState, { label: searchingLabel, skeletonRows: PsTableConfig.searchingSkeletonRows })) : visibleRows.length === 0 && isQueried ? (_jsxs("div", { className: 'px-4 py-10 text-center', children: [_jsx("p", { className: 'text-sm font-semibold text-ink', children: PsTableConfig.noMatchesTitle }), _jsx("button", { className: PallasoftClassNameService.merge(PsTableControlClasses.textButton, 'mt-2 text-primary'), onClick: clearFilters, type: 'button', children: "Clear search and filters" })] })) : null), !grouping && shouldVirtualize && cards.count < visibleRows.length ? (_jsx("div", { "aria-hidden": 'true', className: 'h-px', ref: cards.sentinelRef })) : null] })) : null, _jsx("div", { className: PallasoftClassNameService.merge(
|
|
396
|
+
: renderCardList(shouldVirtualize ? visibleRows.slice(0, cards.count) : visibleRows, visibleRows.length === 0 && isSearching ? (_jsx(PsTableSearchingState, { label: searchingLabel, skeletonRows: PsTableConfig.searchingSkeletonRows })) : visibleRows.length === 0 && isQueried ? (_jsxs("div", { className: 'px-4 py-10 text-center', children: [_jsx("p", { className: 'text-sm font-semibold text-ink', children: PsTableConfig.noMatchesTitle }), _jsx("button", { className: PallasoftClassNameService.merge(PsTableControlClasses.textButton, 'mt-2 text-primary'), onClick: clearFilters, type: 'button', children: "Clear search and filters" })] })) : null), !grouping && shouldVirtualize && cards.count < visibleRows.length ? (_jsx("div", { "aria-hidden": 'true', className: 'h-px', ref: cards.sentinelRef })) : null] })) : null, _jsx("div", { className: PallasoftClassNameService.merge(stickyTop === undefined
|
|
397
|
+
? 'ps-scrollbar overflow-x-auto overflow-y-hidden overscroll-x-contain'
|
|
398
|
+
: 'overflow-x-clip', 'transition-opacity duration-150', isRefreshing ? 'opacity-60' : '', mobileLayout === 'cards' ? 'hidden sm:block' : ''), "data-overflow-end": overflow.edges.end, "data-overflow-start": overflow.edges.start, onScroll: overflow.update, ref: overflow.ref, children: renderReorder((api) => (_jsxs("table", { className: PallasoftClassNameService.merge('w-full border-collapse text-left', layout === 'fixed' ? 'table-fixed' : ''), children: [caption ? _jsx("caption", { className: 'sr-only', children: caption }) : null, _jsx("thead", { className: PallasoftClassNameService.merge('bg-mist/70 text-[0.68rem] font-semibold uppercase tracking-[0.18em] text-ink/45', stickyTop === undefined
|
|
399
|
+
? ''
|
|
400
|
+
: 'sticky z-docked bg-surface [&_th]:border-b [&_th]:border-line [&>tr]:bg-mist/70'), style: stickyTop === undefined
|
|
401
|
+
? undefined
|
|
402
|
+
: { top: `calc(${stickyTop} + ${stickyHeight}px)` }, children: _jsxs("tr", { children: [reorder ? (_jsx("th", { className: PallasoftClassNameService.merge('w-9 min-w-9 pl-3 pr-0', pins.reorder), scope: 'col', children: reorder.enabled ? (_jsx("span", { className: 'sr-only', children: reorder.columnLabel })) : reorder.disabledReason ? (_jsx("span", { "aria-label": reorder.disabledReason, className: 'inline-flex cursor-help opacity-40', role: 'img', title: reorder.disabledReason, children: _jsx(PsDragGrip, {}) })) : null })) : null, isSelectable ? (_jsx("th", { className: PallasoftClassNameService.merge('w-10 min-w-10 pl-4 pr-0', pins.select), scope: 'col', children: _jsx(PsCheckbox, { isIndeterminate: !isPageSelected && pageIds.some((rowId) => selectedIdSet.has(rowId)), isSelected: isPageSelected, label: 'Select all rows on this page', onChange: togglePage }) })) : null, visibleColumns.map((column, columnIndex) => {
|
|
329
403
|
const align = column.align !== null && column.align !== undefined ? column.align : 'left';
|
|
330
404
|
const isSorted = sort !== null && sort.columnId === column.id;
|
|
331
405
|
return (_jsx("th", { "aria-sort": isSorted
|
|
@@ -356,5 +430,7 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
356
430
|
]
|
|
357
431
|
: renderEntries(api, visibleRows.map((row) => ({ row })))] }), hasFooter ? (_jsx("tfoot", { className: 'border-t-2 border-line bg-mist/50 text-sm font-semibold text-ink', children: _jsxs("tr", { children: [reorder ? _jsx("td", { className: pins.reorder }) : null, isSelectable ? _jsx("td", { className: pins.select }) : null, visibleColumns.map((column, columnIndex) => (_jsx("td", { className: PallasoftClassNameService.merge(densityClassNames[density].cell, placementOf(column, columnIndex), alignClassNames[column.align !== null && column.align !== undefined
|
|
358
432
|
? column.align
|
|
359
|
-
: 'left'], 'whitespace-nowrap'), children: column.footer }, column.id)))] }) })) : null] }))) }), visibleRows.length > 0 && isSearching ? (_jsx(PsTableSearchingState, { className: 'border-t border-line py-3', label: searchingLabel })) : null,
|
|
433
|
+
: 'left'], 'whitespace-nowrap'), children: column.footer }, column.id)))] }) })) : null] }))) }), visibleRows.length > 0 && isSearching ? (_jsx(PsTableSearchingState, { className: 'border-t border-line py-3', label: searchingLabel })) : null, showsPagination && paginationPosition !== PsTablePaginationPosition.top
|
|
434
|
+
? renderPagination('')
|
|
435
|
+
: null] })] }));
|
|
360
436
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
3
|
+
import { PsTableControlClasses } from '../constants/ps-table-control-classes';
|
|
4
|
+
export const PsSelectAllMatchingBanner = ({ className, isActive, noun = 'rows', onActivate, onDeactivate, pageCount, total, }) => (_jsxs("div", { "aria-live": 'polite', className: PallasoftClassNameService.merge('flex flex-wrap items-center justify-center gap-x-2 gap-y-1 rounded-ps-control bg-primary/10 px-3 py-2 text-sm text-ink', className), role: 'status', children: [_jsx("span", { children: isActive
|
|
5
|
+
? `All ${total.toLocaleString()} ${noun} matching are selected.`
|
|
6
|
+
: `${pageCount.toLocaleString()} ${noun} on this page are selected.` }), _jsx("button", { className: PallasoftClassNameService.merge(PsTableControlClasses.textButton, 'text-primary'), onClick: isActive ? onDeactivate : onActivate, type: 'button', children: isActive ? 'Clear selection' : `Select all ${total.toLocaleString()} matching` })] }));
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
3
3
|
import { PsTableControlClasses } from '../constants/ps-table-control-classes';
|
|
4
|
-
export const PsTablePagination = ({ className, isPartial = false, loadedPageCount, onPageChange, page, pageCount, pageSize, totalRows, }) => {
|
|
4
|
+
export const PsTablePagination = ({ className, cursor, isPartial = false, loadedPageCount, onPageChange, page, pageCount, pageSize, totalRows, }) => {
|
|
5
5
|
const firstRow = totalRows === 0 ? 0 : (page - 1) * pageSize + 1;
|
|
6
6
|
const lastRow = Math.min(page * pageSize, totalRows);
|
|
7
|
+
const approximately = cursor !== undefined && cursor.isEstimate ? 'about ' : '';
|
|
8
|
+
const pageNumber = cursor === undefined ? Math.min(page, pageCount) : cursor.pageNumber;
|
|
9
|
+
const cursorFirst = cursor === undefined ? firstRow : (cursor.pageNumber - 1) * pageSize + 1;
|
|
10
|
+
const cursorLast = cursor === undefined ? lastRow : Math.min(cursor.pageNumber * pageSize, totalRows);
|
|
7
11
|
return (_jsxs("nav", { "aria-label": 'Pagination', className: PallasoftClassNameService.merge('flex flex-wrap items-center justify-between gap-3 border-t border-line px-4 py-3', className), children: [_jsx("p", { className: 'text-sm text-ink/55', children: totalRows === 0
|
|
8
12
|
? 'No rows'
|
|
9
|
-
:
|
|
10
|
-
|
|
13
|
+
: cursor === undefined
|
|
14
|
+
? `${firstRow}–${lastRow} of ${totalRows}${isPartial ? '+' : ''}`
|
|
15
|
+
: `${cursorFirst}–${cursorLast} of ${approximately}${totalRows.toLocaleString()}` }), _jsxs("div", { className: 'flex items-center gap-2', children: [cursor !== undefined && cursor.pageSizeOptions !== undefined && cursor.onPageSizeChange ? (_jsxs("label", { className: 'flex items-center gap-2 text-sm text-ink/65', children: [_jsx("span", { className: 'hidden sm:inline', children: "Rows per page" }), _jsx("select", { "aria-label": 'Rows per page', className: 'ps-input-control ps-focus-ring ps-control ps-control-padding rounded-ps-control text-sm', onChange: (event) => cursor.onPageSizeChange?.(Number(event.target.value)), value: pageSize, children: cursor.pageSizeOptions.map((option) => (_jsx("option", { value: option, children: option }, option))) })] })) : null, _jsx("button", { className: PsTableControlClasses.paginationButton, disabled: cursor === undefined ? page <= 1 : !cursor.hasPrevious, onClick: () => (cursor === undefined ? onPageChange(page - 1) : cursor.onPrevious()), type: 'button', children: "Previous" }), _jsxs("span", { className: 'hidden text-sm text-ink/65 sm:inline', children: ["Page ", pageNumber, " of ", approximately, pageCount] }), _jsx("button", { className: PsTableControlClasses.paginationButton, disabled: cursor === undefined
|
|
16
|
+
? page >=
|
|
17
|
+
(loadedPageCount === undefined ? pageCount : Math.min(loadedPageCount, pageCount))
|
|
18
|
+
: !cursor.hasNext, onClick: () => (cursor === undefined ? onPageChange(page + 1) : cursor.onNext()), onFocus: cursor === undefined ? undefined : cursor.onNextIntent, onPointerEnter: cursor === undefined ? undefined : cursor.onNextIntent, type: 'button', children: "Next" })] })] }));
|
|
11
19
|
};
|
|
@@ -6,7 +6,7 @@ import { usePsTableReorder } from '../hooks/use-ps-table-reorder';
|
|
|
6
6
|
import { PsTableReorderService } from '../services/ps-table-reorder-service';
|
|
7
7
|
import { PsTableSortableRow } from './ps-table-sortable-row';
|
|
8
8
|
export const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
|
|
9
|
-
const { activeId, activeRunId, blockedReason, contextProps, drop, placements } = usePsTableReorder(input);
|
|
9
|
+
const { activeId, activeRunId, activeSize, blockedReason, contextProps, drop, placements } = usePsTableReorder(input);
|
|
10
10
|
const itemById = new Map(input.items.map((item) => [item.id, item]));
|
|
11
11
|
const { nesting } = input;
|
|
12
12
|
const api = {
|
|
@@ -19,7 +19,7 @@ export const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
|
|
|
19
19
|
return {
|
|
20
20
|
dropPlacement: drop !== null && drop.overId === rowId ? drop.placement : null,
|
|
21
21
|
handleLabel: item === undefined ? '' : item.handleLabel,
|
|
22
|
-
hasHandle: item !== undefined && input.enabled && nesting.canDrag(rowId),
|
|
22
|
+
hasHandle: item !== undefined && item.isDraggable && input.enabled && nesting.canDrag(rowId),
|
|
23
23
|
isDisabled: !input.enabled,
|
|
24
24
|
isDropTarget: item !== undefined,
|
|
25
25
|
rowId,
|
|
@@ -27,7 +27,11 @@ export const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
|
|
|
27
27
|
return {
|
|
28
28
|
dropPlacement: null,
|
|
29
29
|
handleLabel: item === undefined ? '' : item.handleLabel,
|
|
30
|
-
hasHandle: item !== undefined &&
|
|
30
|
+
hasHandle: item !== undefined &&
|
|
31
|
+
item.isDraggable &&
|
|
32
|
+
input.enabled &&
|
|
33
|
+
placement !== undefined &&
|
|
34
|
+
placement.size > 1,
|
|
31
35
|
isDisabled: activeRunId !== null && (placement === undefined || placement.runId !== activeRunId),
|
|
32
36
|
isDropTarget: false,
|
|
33
37
|
rowId,
|
|
@@ -35,5 +39,5 @@ export const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
|
|
|
35
39
|
},
|
|
36
40
|
run: (runId, ids, runChildren) => (_jsx(SortableContext, { items: ids, strategy: nesting === undefined ? verticalListSortingStrategy : PsTableReorderService.still, children: runChildren }, runId)),
|
|
37
41
|
};
|
|
38
|
-
return (_jsxs(DndContext, { ...contextProps, children: [children(api), blockedReason === null ? null : (_jsx("p", { className: 'pointer-events-none fixed bottom-6 left-1/2 z-50 max-w-sm -translate-x-1/2 rounded-ps-control bg-ink px-3 py-2 text-sm text-white shadow-lg', role: 'status', children: blockedReason })), _jsx(PsDragOverlay, { className: PsDragClasses.lifted, frames: PsDragListLiftFrames, children: activeId === null ? null : renderOverlay(activeId) })] }));
|
|
42
|
+
return (_jsxs(DndContext, { ...contextProps, children: [children(api), blockedReason === null ? null : (_jsx("p", { className: 'pointer-events-none fixed bottom-6 left-1/2 z-50 max-w-sm -translate-x-1/2 rounded-ps-control bg-ink px-3 py-2 text-sm text-white shadow-lg', role: 'status', children: blockedReason })), _jsx(PsDragOverlay, { className: PsDragClasses.lifted, frames: PsDragListLiftFrames, isDropAnimated: nesting === undefined, children: activeId === null ? null : renderOverlay(activeId, activeSize) })] }));
|
|
39
43
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const PsTableDropClasses = {
|
|
2
|
-
after: '[&>td]:
|
|
3
|
-
before: '[&>td]:
|
|
2
|
+
after: '[&>td]:bg-[linear-gradient(rgb(var(--ps-color-primary-rgb)),rgb(var(--ps-color-primary-rgb)))] [&>td]:bg-[length:100%_2px] [&>td]:bg-bottom [&>td]:bg-no-repeat',
|
|
3
|
+
before: '[&>td]:bg-[linear-gradient(rgb(var(--ps-color-primary-rgb)),rgb(var(--ps-color-primary-rgb)))] [&>td]:bg-[length:100%_2px] [&>td]:bg-top [&>td]:bg-no-repeat',
|
|
4
4
|
into: 'bg-primary/10 [&>td]:shadow-[inset_0_0_0_2px_rgb(var(--ps-color-primary-rgb)/0.5)]',
|
|
5
5
|
};
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { PsServerRowsConfig } from '../constants/ps-server-rows-config';
|
|
3
|
+
import { PsServerRowsService } from '../services/ps-server-rows-service';
|
|
4
|
+
const scheduleIdle = (run) => {
|
|
5
|
+
if (typeof window.requestIdleCallback === 'function') {
|
|
6
|
+
const handle = window.requestIdleCallback(run, { timeout: PsServerRowsConfig.idleTimeoutMs });
|
|
7
|
+
return () => window.cancelIdleCallback(handle);
|
|
8
|
+
}
|
|
9
|
+
const timer = window.setTimeout(run, PsServerRowsConfig.idleFallbackMs);
|
|
10
|
+
return () => window.clearTimeout(timer);
|
|
11
|
+
};
|
|
12
|
+
export const usePsServerRows = ({ columns, debounceMs = PsServerRowsConfig.debounceMs, getSearchText, initialPage = null, initialQuery, isPrefetching = true, load, pageSize, }) => {
|
|
13
|
+
const [query, setQuery] = useState(initialQuery);
|
|
14
|
+
const [search, setSearch] = useState(initialQuery.search);
|
|
15
|
+
const [reloadToken, setReloadToken] = useState(0);
|
|
16
|
+
const [isLoading, setIsLoading] = useState(initialPage === null);
|
|
17
|
+
const [error, setError] = useState(null);
|
|
18
|
+
const queryKey = PsServerRowsService.queryKey(query, search, pageSize);
|
|
19
|
+
const [navigation, setNavigation] = useState({
|
|
20
|
+
cursor: null,
|
|
21
|
+
pageNumber: 1,
|
|
22
|
+
queryKey,
|
|
23
|
+
});
|
|
24
|
+
const [entry, setEntry] = useState(() => initialPage === null
|
|
25
|
+
? null
|
|
26
|
+
: {
|
|
27
|
+
key: PsServerRowsService.cacheKey(queryKey, null),
|
|
28
|
+
page: initialPage,
|
|
29
|
+
queryKey,
|
|
30
|
+
});
|
|
31
|
+
const cache = useRef(new Map());
|
|
32
|
+
const inflight = useRef(new Map());
|
|
33
|
+
const loadRef = useRef(load);
|
|
34
|
+
const limitRef = useRef(pageSize);
|
|
35
|
+
const queryRef = useRef(query);
|
|
36
|
+
const searchRef = useRef(search);
|
|
37
|
+
const isPrimed = useRef(false);
|
|
38
|
+
loadRef.current = load;
|
|
39
|
+
limitRef.current = pageSize;
|
|
40
|
+
queryRef.current = query;
|
|
41
|
+
searchRef.current = search;
|
|
42
|
+
if (initialPage !== null && !isPrimed.current) {
|
|
43
|
+
isPrimed.current = true;
|
|
44
|
+
PsServerRowsService.remember(cache.current, PsServerRowsService.cacheKey(queryKey, null), initialPage);
|
|
45
|
+
}
|
|
46
|
+
if (navigation.queryKey !== queryKey)
|
|
47
|
+
setNavigation({ cursor: null, pageNumber: 1, queryKey });
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (query.search === search)
|
|
50
|
+
return;
|
|
51
|
+
const timer = window.setTimeout(() => setSearch(query.search), debounceMs);
|
|
52
|
+
return () => window.clearTimeout(timer);
|
|
53
|
+
}, [debounceMs, query.search, search]);
|
|
54
|
+
const fetchPage = useCallback((cursor, signal, key) => {
|
|
55
|
+
const pending = inflight.current.get(key);
|
|
56
|
+
if (pending !== undefined)
|
|
57
|
+
return pending;
|
|
58
|
+
const request = loadRef.current({ ...queryRef.current, search: searchRef.current }, cursor, signal, limitRef.current);
|
|
59
|
+
const tracked = request.then((page) => {
|
|
60
|
+
inflight.current.delete(key);
|
|
61
|
+
if (!signal.aborted)
|
|
62
|
+
PsServerRowsService.remember(cache.current, key, page);
|
|
63
|
+
return page;
|
|
64
|
+
}, (failure) => {
|
|
65
|
+
inflight.current.delete(key);
|
|
66
|
+
throw failure;
|
|
67
|
+
});
|
|
68
|
+
inflight.current.set(key, tracked);
|
|
69
|
+
return tracked;
|
|
70
|
+
}, []);
|
|
71
|
+
const generation = useRef(null);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
const controller = new AbortController();
|
|
74
|
+
generation.current = controller;
|
|
75
|
+
return () => {
|
|
76
|
+
controller.abort();
|
|
77
|
+
inflight.current.clear();
|
|
78
|
+
};
|
|
79
|
+
}, [queryKey, reloadToken]);
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
const controller = generation.current;
|
|
82
|
+
if (controller === null)
|
|
83
|
+
return;
|
|
84
|
+
const key = PsServerRowsService.cacheKey(queryKey, navigation.cursor);
|
|
85
|
+
const cached = PsServerRowsService.recall(cache.current, key);
|
|
86
|
+
let isStale = false;
|
|
87
|
+
if (cached !== undefined) {
|
|
88
|
+
setEntry({ key, page: cached, queryKey });
|
|
89
|
+
setIsLoading(false);
|
|
90
|
+
setError(null);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
setIsLoading(true);
|
|
94
|
+
setError(null);
|
|
95
|
+
fetchPage(navigation.cursor, controller.signal, key).then((page) => {
|
|
96
|
+
if (isStale || controller.signal.aborted)
|
|
97
|
+
return;
|
|
98
|
+
setEntry({ key, page, queryKey });
|
|
99
|
+
setIsLoading(false);
|
|
100
|
+
}, (failure) => {
|
|
101
|
+
if (isStale || controller.signal.aborted)
|
|
102
|
+
return;
|
|
103
|
+
setError(failure);
|
|
104
|
+
setIsLoading(false);
|
|
105
|
+
});
|
|
106
|
+
return () => {
|
|
107
|
+
isStale = true;
|
|
108
|
+
};
|
|
109
|
+
}, [fetchPage, navigation.cursor, queryKey, reloadToken]);
|
|
110
|
+
const nextCursor = entry !== null && entry.queryKey === queryKey ? entry.page.nextCursor : null;
|
|
111
|
+
const prefetch = useCallback(() => {
|
|
112
|
+
const controller = generation.current;
|
|
113
|
+
if (controller === null || controller.signal.aborted || nextCursor === null)
|
|
114
|
+
return;
|
|
115
|
+
const key = PsServerRowsService.cacheKey(queryKey, nextCursor);
|
|
116
|
+
if (cache.current.has(key) || inflight.current.has(key))
|
|
117
|
+
return;
|
|
118
|
+
fetchPage(nextCursor, controller.signal, key).catch(() => undefined);
|
|
119
|
+
}, [fetchPage, nextCursor, queryKey]);
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (!isPrefetching || isLoading || nextCursor === null)
|
|
122
|
+
return;
|
|
123
|
+
return scheduleIdle(prefetch);
|
|
124
|
+
}, [isLoading, isPrefetching, nextCursor, prefetch]);
|
|
125
|
+
const onQueryChange = useCallback((next) => setQuery(next), []);
|
|
126
|
+
const goNext = useCallback(() => {
|
|
127
|
+
if (nextCursor === null)
|
|
128
|
+
return;
|
|
129
|
+
setNavigation((current) => ({
|
|
130
|
+
...current,
|
|
131
|
+
cursor: nextCursor,
|
|
132
|
+
pageNumber: current.pageNumber + 1,
|
|
133
|
+
}));
|
|
134
|
+
}, [nextCursor]);
|
|
135
|
+
const previousCursor = entry !== null && entry.queryKey === queryKey ? entry.page.prevCursor : null;
|
|
136
|
+
const goPrevious = useCallback(() => {
|
|
137
|
+
if (previousCursor === null)
|
|
138
|
+
return;
|
|
139
|
+
setNavigation((current) => ({
|
|
140
|
+
...current,
|
|
141
|
+
cursor: previousCursor,
|
|
142
|
+
pageNumber: Math.max(current.pageNumber - 1, 1),
|
|
143
|
+
}));
|
|
144
|
+
}, [previousCursor]);
|
|
145
|
+
const reload = useCallback(() => {
|
|
146
|
+
cache.current.clear();
|
|
147
|
+
setNavigation((current) => ({ ...current, cursor: null, pageNumber: 1 }));
|
|
148
|
+
setReloadToken((current) => current + 1);
|
|
149
|
+
}, []);
|
|
150
|
+
const retry = useCallback(() => setReloadToken((current) => current + 1), []);
|
|
151
|
+
const updateRows = useCallback((update, totalDelta = 0) => {
|
|
152
|
+
setEntry((current) => {
|
|
153
|
+
if (current === null)
|
|
154
|
+
return current;
|
|
155
|
+
const page = {
|
|
156
|
+
...current.page,
|
|
157
|
+
rows: update(current.page.rows),
|
|
158
|
+
total: Math.max(current.page.total + totalDelta, 0),
|
|
159
|
+
};
|
|
160
|
+
cache.current.clear();
|
|
161
|
+
PsServerRowsService.remember(cache.current, current.key, page);
|
|
162
|
+
return { ...current, page };
|
|
163
|
+
});
|
|
164
|
+
}, []);
|
|
165
|
+
const isCurrent = entry !== null &&
|
|
166
|
+
entry.queryKey === queryKey &&
|
|
167
|
+
entry.key === PsServerRowsService.cacheKey(queryKey, navigation.cursor);
|
|
168
|
+
const baseRows = useMemo(() => (entry === null ? [] : entry.page.rows), [entry]);
|
|
169
|
+
const isSameQuery = entry !== null && entry.queryKey === queryKey;
|
|
170
|
+
const rows = useMemo(() => isSameQuery ? baseRows : PsServerRowsService.preview(baseRows, columns, query, getSearchText), [baseRows, columns, getSearchText, isSameQuery, query]);
|
|
171
|
+
const total = entry === null ? 0 : entry.page.total;
|
|
172
|
+
const totalIsEstimate = entry === null ? false : entry.page.totalIsEstimate;
|
|
173
|
+
const isPending = isLoading || query.search !== search || !isCurrent;
|
|
174
|
+
const cursorPaging = useMemo(() => ({
|
|
175
|
+
hasNext: nextCursor !== null,
|
|
176
|
+
hasPrevious: previousCursor !== null,
|
|
177
|
+
isEstimate: totalIsEstimate,
|
|
178
|
+
isPending,
|
|
179
|
+
onNext: goNext,
|
|
180
|
+
onNextIntent: prefetch,
|
|
181
|
+
onPrevious: goPrevious,
|
|
182
|
+
pageNumber: navigation.pageNumber,
|
|
183
|
+
}), [
|
|
184
|
+
goNext,
|
|
185
|
+
goPrevious,
|
|
186
|
+
isPending,
|
|
187
|
+
navigation.pageNumber,
|
|
188
|
+
nextCursor,
|
|
189
|
+
prefetch,
|
|
190
|
+
previousCursor,
|
|
191
|
+
totalIsEstimate,
|
|
192
|
+
]);
|
|
193
|
+
return {
|
|
194
|
+
cursorPaging,
|
|
195
|
+
error,
|
|
196
|
+
hasFailed: error !== null,
|
|
197
|
+
isPending,
|
|
198
|
+
onQueryChange,
|
|
199
|
+
pageCount: PsServerRowsService.pageCount(total, pageSize),
|
|
200
|
+
pageNumber: navigation.pageNumber,
|
|
201
|
+
query,
|
|
202
|
+
reload,
|
|
203
|
+
retry,
|
|
204
|
+
rows,
|
|
205
|
+
total,
|
|
206
|
+
totalIsEstimate,
|
|
207
|
+
updateRows,
|
|
208
|
+
};
|
|
209
|
+
};
|
|
@@ -9,6 +9,7 @@ const pointerThenCenter = (args) => {
|
|
|
9
9
|
};
|
|
10
10
|
export const usePsTableReorder = ({ announcements: texts, enabled, instructions, items, nesting, onReorder, }) => {
|
|
11
11
|
const [activeId, setActiveId] = useState(null);
|
|
12
|
+
const [activeSize, setActiveSize] = useState(null);
|
|
12
13
|
const [drop, setDrop] = useState(null);
|
|
13
14
|
const [blockedReason, setBlockedReason] = useState(null);
|
|
14
15
|
const dropRef = useRef(null);
|
|
@@ -101,6 +102,7 @@ export const usePsTableReorder = ({ announcements: texts, enabled, instructions,
|
|
|
101
102
|
const { active, over } = event;
|
|
102
103
|
const target = resolveDrop(event);
|
|
103
104
|
setActiveId(null);
|
|
105
|
+
setActiveSize(null);
|
|
104
106
|
setDrop(null);
|
|
105
107
|
setBlockedReason(null);
|
|
106
108
|
dropRef.current = null;
|
|
@@ -122,6 +124,7 @@ export const usePsTableReorder = ({ announcements: texts, enabled, instructions,
|
|
|
122
124
|
};
|
|
123
125
|
return {
|
|
124
126
|
activeId,
|
|
127
|
+
activeSize,
|
|
125
128
|
blockedReason,
|
|
126
129
|
activeRunId: activePlacement === undefined ? null : activePlacement.runId,
|
|
127
130
|
contextProps: {
|
|
@@ -130,13 +133,17 @@ export const usePsTableReorder = ({ announcements: texts, enabled, instructions,
|
|
|
130
133
|
modifiers: [PsTableReorderService.lockToVerticalAxis],
|
|
131
134
|
onDragCancel: () => {
|
|
132
135
|
setActiveId(null);
|
|
136
|
+
setActiveSize(null);
|
|
133
137
|
setDrop(null);
|
|
134
138
|
setBlockedReason(null);
|
|
135
139
|
dropRef.current = null;
|
|
136
140
|
},
|
|
137
141
|
onDragEnd: handleDragEnd,
|
|
138
142
|
onDragMove: nesting === undefined ? undefined : handleDragMove,
|
|
139
|
-
onDragStart: ({ active }) =>
|
|
143
|
+
onDragStart: ({ active, activatorEvent }) => {
|
|
144
|
+
setActiveSize(PsTableReorderService.measure(activatorEvent));
|
|
145
|
+
setActiveId(String(active.id));
|
|
146
|
+
},
|
|
140
147
|
sensors,
|
|
141
148
|
},
|
|
142
149
|
drop,
|
package/dist/esm/index.js
CHANGED
|
@@ -47,3 +47,15 @@ export * from './interfaces/table-reorder-nesting';
|
|
|
47
47
|
export * from './interfaces/table-nesting-announcements';
|
|
48
48
|
export * from './constants/ps-table-drop-placement';
|
|
49
49
|
export * from './services/ps-table-reorder-service';
|
|
50
|
+
export * from './constants/ps-table-pagination-position';
|
|
51
|
+
export * from './interfaces/table-drag-size';
|
|
52
|
+
export * from './components/ps-select-all-matching-banner';
|
|
53
|
+
export * from './constants/ps-server-rows-config';
|
|
54
|
+
export * from './hooks/use-ps-server-rows';
|
|
55
|
+
export * from './interfaces/ps-server-entry';
|
|
56
|
+
export * from './interfaces/ps-server-page';
|
|
57
|
+
export * from './interfaces/ps-server-rows-input';
|
|
58
|
+
export * from './interfaces/ps-server-rows-result';
|
|
59
|
+
export * from './interfaces/ps-table-cursor-paging';
|
|
60
|
+
export * from './interfaces/ps-table-select-all-matching';
|
|
61
|
+
export * from './services/ps-server-rows-service';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|