@pallasoft/ps-table-components 1.26.0 → 1.27.1
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 +9 -1
- package/dist/components/ps-data-table.js +81 -12
- 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 +12 -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-pagination-config.d.ts +3 -0
- package/dist/constants/ps-table-pagination-config.js +6 -0
- package/dist/esm/components/ps-data-table.js +82 -13
- package/dist/esm/components/ps-select-all-matching-banner.js +6 -0
- package/dist/esm/components/ps-table-pagination.js +12 -4
- package/dist/esm/constants/ps-server-rows-config.js +8 -0
- package/dist/esm/constants/ps-table-pagination-config.js +3 -0
- package/dist/esm/hooks/use-ps-server-rows.js +209 -0
- package/dist/esm/index.js +11 -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/services/ps-server-rows-service.js +36 -0
- package/dist/hooks/use-ps-server-rows.d.ts +3 -0
- package/dist/hooks/use-ps-server-rows.js +213 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -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/services/ps-server-rows-service.d.ts +11 -0
- package/dist/services/ps-server-rows-service.js +40 -0
- package/package.json +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import { PsTablePaginationPosition } from '../constants/ps-table-pagination-position';
|
|
3
|
+
import type { PsTableCursorPaging } from '../interfaces/ps-table-cursor-paging';
|
|
4
|
+
import type { PsTableSelectAllMatching } from '../interfaces/ps-table-select-all-matching';
|
|
3
5
|
import type { PsTableColumn } from '../interfaces/table-column';
|
|
4
6
|
import type { PsTableGrouping } from '../interfaces/table-grouping';
|
|
5
7
|
import type { PsTableQuery } from '../interfaces/table-query';
|
|
@@ -9,7 +11,9 @@ import type { PsTableSort } from '../interfaces/table-sort';
|
|
|
9
11
|
export interface PsDataTableProps<RowData> {
|
|
10
12
|
caption?: string;
|
|
11
13
|
className?: string;
|
|
14
|
+
clearSelectionKey?: number | string;
|
|
12
15
|
columns: PsTableColumn<RowData>[];
|
|
16
|
+
cursorPaging?: PsTableCursorPaging;
|
|
13
17
|
defaultSort?: PsTableSort;
|
|
14
18
|
density?: 'compact' | 'normal';
|
|
15
19
|
expandedRowIds?: string[];
|
|
@@ -21,6 +25,7 @@ export interface PsDataTableProps<RowData> {
|
|
|
21
25
|
grouping?: PsTableGrouping<RowData>;
|
|
22
26
|
isLoading?: boolean;
|
|
23
27
|
isPending?: boolean;
|
|
28
|
+
isRefreshing?: boolean;
|
|
24
29
|
layout?: 'auto' | 'fixed';
|
|
25
30
|
onPageChange?: (page: number) => void;
|
|
26
31
|
onQueryChange?: (query: PsTableQuery) => void;
|
|
@@ -36,7 +41,10 @@ export interface PsDataTableProps<RowData> {
|
|
|
36
41
|
rows: RowData[];
|
|
37
42
|
searchingLabel?: string;
|
|
38
43
|
searchPlaceholder?: string;
|
|
44
|
+
selectAllMatching?: PsTableSelectAllMatching;
|
|
45
|
+
stickyClassName?: string;
|
|
46
|
+
stickyTop?: string;
|
|
39
47
|
toolbarActions?: ReactNode;
|
|
40
48
|
totalRows?: number;
|
|
41
49
|
}
|
|
42
|
-
export declare const PsDataTable: <RowData>({ caption, className, columns, defaultSort, density, expandedRowIds, mobileLayout, getBulkActions, getRowId, getRowLabel, getSearchText, grouping, isLoading, isPending, layout, onPageChange, onQueryChange, onRowClick, onRowHover, page, pageSize, paginationPosition, pinFirstColumn, query, renderExpandedRow, reorder, rows, searchingLabel, searchPlaceholder, toolbarActions, totalRows, }: PsDataTableProps<RowData>) => import("react").JSX.Element;
|
|
50
|
+
export declare const PsDataTable: <RowData>({ caption, className, clearSelectionKey, columns, cursorPaging, defaultSort, density, expandedRowIds, mobileLayout, getBulkActions, getRowId, getRowLabel, getSearchText, grouping, isLoading, isPending, isRefreshing, layout, onPageChange, onQueryChange, onRowClick, onRowHover, page, pageSize, paginationPosition, pinFirstColumn, query, renderExpandedRow, reorder, rows, searchingLabel, searchPlaceholder, selectAllMatching, stickyClassName, stickyTop, toolbarActions, totalRows, }: PsDataTableProps<RowData>) => import("react").JSX.Element;
|
|
@@ -42,6 +42,7 @@ const ps_input_components_1 = require("@pallasoft/ps-input-components");
|
|
|
42
42
|
const ps_motion_components_1 = require("@pallasoft/ps-motion-components");
|
|
43
43
|
const react_virtual_1 = require("@tanstack/react-virtual");
|
|
44
44
|
const ps_table_control_classes_1 = require("../constants/ps-table-control-classes");
|
|
45
|
+
const ps_table_pagination_config_1 = require("../constants/ps-table-pagination-config");
|
|
45
46
|
const ps_table_pagination_position_1 = require("../constants/ps-table-pagination-position");
|
|
46
47
|
const table_breakpoint_1 = require("../constants/table-breakpoint");
|
|
47
48
|
const table_config_1 = require("../constants/table-config");
|
|
@@ -56,6 +57,7 @@ const ps_table_pin_service_1 = require("../services/ps-table-pin-service");
|
|
|
56
57
|
const ps_table_query_service_1 = require("../services/ps-table-query-service");
|
|
57
58
|
const ps_table_reorder_service_1 = require("../services/ps-table-reorder-service");
|
|
58
59
|
const ps_table_scroll_service_1 = require("../services/ps-table-scroll-service");
|
|
60
|
+
const ps_select_all_matching_banner_1 = require("./ps-select-all-matching-banner");
|
|
59
61
|
const ps_table_bulk_bar_1 = require("./ps-table-bulk-bar");
|
|
60
62
|
const ps_table_card_list_1 = require("./ps-table-card-list");
|
|
61
63
|
const ps_table_column_picker_1 = require("./ps-table-column-picker");
|
|
@@ -85,7 +87,7 @@ const alignClassNames = {
|
|
|
85
87
|
left: 'text-left',
|
|
86
88
|
right: 'text-right tabular-nums',
|
|
87
89
|
};
|
|
88
|
-
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, paginationPosition = ps_table_pagination_position_1.PsTablePaginationPosition.bottom, pinFirstColumn = true, query, renderExpandedRow, reorder, rows, searchingLabel = table_config_1.PsTableConfig.searchingLabel, searchPlaceholder = table_config_1.PsTableConfig.searchPlaceholder, toolbarActions, totalRows, }) => {
|
|
90
|
+
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 = ps_table_pagination_position_1.PsTablePaginationPosition.bottom, pinFirstColumn = true, query, renderExpandedRow, reorder, rows, searchingLabel = table_config_1.PsTableConfig.searchingLabel, searchPlaceholder = table_config_1.PsTableConfig.searchPlaceholder, selectAllMatching, stickyClassName, stickyTop, toolbarActions, totalRows, }) => {
|
|
89
91
|
const containerRef = (0, react_1.useRef)(null);
|
|
90
92
|
const [uncontrolledPage, setUncontrolledPage] = (0, react_1.useState)(1);
|
|
91
93
|
const [uncontrolledQuery, setUncontrolledQuery] = (0, react_1.useState)(() => ps_table_query_service_1.PsTableQueryService.empty(defaultSort ? defaultSort : null));
|
|
@@ -93,7 +95,50 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
|
|
|
93
95
|
const [toggledGroupKeys, setToggledGroupKeys] = (0, react_1.useState)([]);
|
|
94
96
|
const [closingGroupKeys, setClosingGroupKeys] = (0, react_1.useState)([]);
|
|
95
97
|
const [openedGroupKeys, setOpenedGroupKeys] = (0, react_1.useState)([]);
|
|
98
|
+
const [seenClearKey, setSeenClearKey] = (0, react_1.useState)(clearSelectionKey);
|
|
99
|
+
const stickyRegionRef = (0, react_1.useRef)(null);
|
|
100
|
+
const stickySentinelRef = (0, react_1.useRef)(null);
|
|
101
|
+
const [stickyHeight, setStickyHeight] = (0, react_1.useState)(0);
|
|
102
|
+
const [isStuck, setIsStuck] = (0, react_1.useState)(false);
|
|
96
103
|
const currentQuery = query ? query : uncontrolledQuery;
|
|
104
|
+
if (seenClearKey !== clearSelectionKey) {
|
|
105
|
+
setSeenClearKey(clearSelectionKey);
|
|
106
|
+
setSelectedIds([]);
|
|
107
|
+
}
|
|
108
|
+
(0, react_1.useLayoutEffect)(() => {
|
|
109
|
+
const region = stickyRegionRef.current;
|
|
110
|
+
if (stickyTop === undefined || region === null)
|
|
111
|
+
return;
|
|
112
|
+
const measure = () => setStickyHeight(region.offsetHeight);
|
|
113
|
+
const observer = new ResizeObserver(measure);
|
|
114
|
+
measure();
|
|
115
|
+
observer.observe(region);
|
|
116
|
+
return () => observer.disconnect();
|
|
117
|
+
}, [stickyTop]);
|
|
118
|
+
(0, react_1.useEffect)(() => {
|
|
119
|
+
const region = stickyRegionRef.current;
|
|
120
|
+
const sentinel = stickySentinelRef.current;
|
|
121
|
+
if (stickyTop === undefined || region === null || sentinel === null)
|
|
122
|
+
return;
|
|
123
|
+
let frame = 0;
|
|
124
|
+
const check = () => {
|
|
125
|
+
frame = 0;
|
|
126
|
+
setIsStuck(sentinel.getBoundingClientRect().top < Number.parseFloat(getComputedStyle(region).top));
|
|
127
|
+
};
|
|
128
|
+
const schedule = () => {
|
|
129
|
+
if (frame === 0)
|
|
130
|
+
frame = window.requestAnimationFrame(check);
|
|
131
|
+
};
|
|
132
|
+
check();
|
|
133
|
+
window.addEventListener('scroll', schedule, { capture: true, passive: true });
|
|
134
|
+
window.addEventListener('resize', schedule);
|
|
135
|
+
return () => {
|
|
136
|
+
window.removeEventListener('scroll', schedule, { capture: true });
|
|
137
|
+
window.removeEventListener('resize', schedule);
|
|
138
|
+
if (frame !== 0)
|
|
139
|
+
window.cancelAnimationFrame(frame);
|
|
140
|
+
};
|
|
141
|
+
}, [stickyTop]);
|
|
97
142
|
const { filters, hiddenColumnIds, search, sort } = currentQuery;
|
|
98
143
|
const visibleColumns = columns.filter((column) => !hiddenColumnIds.includes(column.id));
|
|
99
144
|
const fittedWidths = (0, react_1.useMemo)(() => new Map(columns
|
|
@@ -235,7 +280,7 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
|
|
|
235
280
|
})
|
|
236
281
|
: [], [getRowId, getRowLabel, reorder, reorderKey, visibleRows]);
|
|
237
282
|
const showsPagination = Boolean(pageSize) && (!grouping || isServerPaginated);
|
|
238
|
-
const renderPagination = (paginationClassName) => pageSize ? ((0, jsx_runtime_1.jsx)(ps_table_pagination_1.PsTablePagination, { className: paginationClassName, isPartial: isPartial, loadedPageCount: pageCountOf(loadedRowCount), onPageChange: changePage, page: safePage, pageCount: pageCount, pageSize: pageSize, totalRows: rowCount })) : null;
|
|
283
|
+
const renderPagination = (paginationClassName) => pageSize ? ((0, jsx_runtime_1.jsx)(ps_table_pagination_1.PsTablePagination, { className: paginationClassName, cursor: cursorPaging, isPartial: isPartial, loadedPageCount: pageCountOf(loadedRowCount), onPageChange: changePage, page: safePage, pageCount: pageCount, pageSize: pageSize, totalRows: rowCount })) : null;
|
|
239
284
|
const renderOverlayRow = (rowId, size) => {
|
|
240
285
|
const row = visibleRows.find((candidate) => getRowId(candidate) === rowId);
|
|
241
286
|
return row === undefined ? null : ((0, jsx_runtime_1.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 : ((0, jsx_runtime_1.jsx)("colgroup", { children: size.columnWidths.map((width, index) => ((0, jsx_runtime_1.jsx)("col", { style: { width } }, index))) })), (0, jsx_runtime_1.jsx)("tbody", { children: renderRow(ps_table_reorder_service_1.PsTableReorderService.plain, row, undefined, undefined, true) })] }));
|
|
@@ -269,12 +314,16 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
|
|
|
269
314
|
const selectedRows = selectedIds.length === 0 ? [] : queriedRows.filter((row) => selectedIdSet.has(getRowId(row)));
|
|
270
315
|
const pageIds = visibleRows.map((row) => getRowId(row));
|
|
271
316
|
const isPageSelected = pageIds.length > 0 && pageIds.every((rowId) => selectedIdSet.has(rowId));
|
|
272
|
-
const togglePage = () =>
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
317
|
+
const togglePage = () => {
|
|
318
|
+
if (isPageSelected && selectAllMatching !== undefined && selectAllMatching.isActive)
|
|
319
|
+
selectAllMatching.onDeactivate();
|
|
320
|
+
setSelectedIds((current) => {
|
|
321
|
+
const pageIdSet = new Set(pageIds);
|
|
322
|
+
return isPageSelected
|
|
323
|
+
? current.filter((rowId) => !pageIdSet.has(rowId))
|
|
324
|
+
: [...new Set([...current, ...pageIds])];
|
|
325
|
+
});
|
|
326
|
+
};
|
|
278
327
|
const bodyRef = (0, react_1.useRef)(null);
|
|
279
328
|
const [scrollParent, setScrollParent] = (0, react_1.useState)(null);
|
|
280
329
|
const [scrollMargin, setScrollMargin] = (0, react_1.useState)(0);
|
|
@@ -352,7 +401,18 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
|
|
|
352
401
|
: entries.map((entry) => renderRow(api, entry.row, entry.motion, entry.virtualItem));
|
|
353
402
|
if (isLoading)
|
|
354
403
|
return ((0, jsx_runtime_1.jsx)(ps_core_components_1.PsSkeletonTable, { className: className, columns: visibleColumns.length, hasToolbar: hasToolbar, rows: table_config_1.PsTableConfig.skeletonRows }));
|
|
355
|
-
|
|
404
|
+
const clearSelection = () => {
|
|
405
|
+
setSelectedIds([]);
|
|
406
|
+
if (selectAllMatching !== undefined && selectAllMatching.isActive)
|
|
407
|
+
selectAllMatching.onDeactivate();
|
|
408
|
+
};
|
|
409
|
+
const banner = selectAllMatching !== undefined &&
|
|
410
|
+
selectedRows.length > 0 &&
|
|
411
|
+
selectAllMatching.total > pageIds.length &&
|
|
412
|
+
(isPageSelected || selectAllMatching.isActive) ? ((0, jsx_runtime_1.jsx)(ps_select_all_matching_banner_1.PsSelectAllMatchingBanner, { isActive: selectAllMatching.isActive, noun: selectAllMatching.noun, onActivate: selectAllMatching.onActivate, onDeactivate: clearSelection, pageCount: pageIds.length, total: selectAllMatching.total })) : null;
|
|
413
|
+
const topContent = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [isSelectable && selectedRows.length > 0 && getBulkActions ? ((0, jsx_runtime_1.jsx)(ps_table_bulk_bar_1.PsTableBulkBar, { actions: getBulkActions(selectedRows), onClear: clearSelection, selectedCount: selectAllMatching !== undefined && selectAllMatching.isActive
|
|
414
|
+
? selectAllMatching.total
|
|
415
|
+
: selectedRows.length })) : hasToolbar || columns.some((column) => column.canHide) ? ((0, jsx_runtime_1.jsx)(ps_table_toolbar_1.PsTableToolbar, { actions: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [toolbarActions, columns.some((column) => column.canHide) ? ((0, jsx_runtime_1.jsx)(ps_table_column_picker_1.PsTableColumnPicker, { columns: columns
|
|
356
416
|
.filter((column) => column.canHide)
|
|
357
417
|
.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) => {
|
|
358
418
|
updateQuery({ filters: { ...filters, [columnId]: value } });
|
|
@@ -360,16 +420,25 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
|
|
|
360
420
|
}, onSearchChange: (next) => {
|
|
361
421
|
updateQuery({ search: next });
|
|
362
422
|
resetPage();
|
|
363
|
-
}, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null,
|
|
423
|
+
}, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null, banner] }));
|
|
424
|
+
return ((0, jsx_runtime_1.jsxs)("div", { ref: containerRef, className: ps_design_system_1.PallasoftClassNameService.merge('flex min-w-0 flex-col gap-3', className), children: [stickyTop === undefined ? (topContent) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { "aria-hidden": 'true', className: '-mb-3 h-0', ref: stickySentinelRef }), (0, jsx_runtime_1.jsx)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('sticky z-sticky -mx-1 flex flex-col gap-3 px-1 pb-2 pt-1 transition-shadow', stickyClassName === undefined ? 'bg-canvas' : stickyClassName, isStuck ? 'shadow-[0_8px_8px_-8px_rgb(var(--ps-color-ink-rgb,15_23_42)/0.18)]' : ''), ref: stickyRegionRef, style: { top: stickyTop }, children: topContent })] })), (0, jsx_runtime_1.jsxs)("div", { "aria-busy": isRefreshing, className: ps_design_system_1.PallasoftClassNameService.merge('min-w-0 rounded-[1.25rem] border border-line bg-surface', stickyTop === undefined ? 'overflow-hidden' : 'overflow-clip'), children: [showsPagination &&
|
|
425
|
+
paginationPosition !== ps_table_pagination_position_1.PsTablePaginationPosition.bottom &&
|
|
426
|
+
rowCount > ps_table_pagination_config_1.PsTablePaginationConfig.topMinRows
|
|
364
427
|
? renderPagination('border-b border-t-0')
|
|
365
|
-
: null, mobileLayout === 'cards' ? ((0, jsx_runtime_1.jsxs)("div", { className: 'sm:hidden', children: [reorder && reorder.cardsReason ? ((0, jsx_runtime_1.jsx)("p", { className: 'border-b border-line px-4 py-2 text-xs text-ink/55', children: reorder.cardsReason })) : null, grouping
|
|
428
|
+
: null, mobileLayout === 'cards' ? ((0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('transition-opacity duration-150 sm:hidden', isRefreshing ? 'opacity-60' : ''), children: [reorder && reorder.cardsReason ? ((0, jsx_runtime_1.jsx)("p", { className: 'border-b border-line px-4 py-2 text-xs text-ink/55', children: reorder.cardsReason })) : null, grouping
|
|
366
429
|
? groups.map((group, groupIndex) => {
|
|
367
430
|
const isExpanded = isGroupExpanded(group.key, groupIndex);
|
|
368
431
|
return ((0, jsx_runtime_1.jsxs)("div", { className: 'border-b border-line last:border-b-0', children: [(0, jsx_runtime_1.jsx)(ps_table_group_header_1.PsTableGroupHeader, { count: group.rows.length, isExpanded: isExpanded, label: grouping.getGroupLabel(group.key, group.rows), onToggle: () => toggleGroup(group.key, isExpanded), summary: grouping.renderGroupSummary
|
|
369
432
|
? grouping.renderGroupSummary(group.rows)
|
|
370
433
|
: undefined }), (0, jsx_runtime_1.jsx)(ps_motion_components_1.PsCollapse, { isOpen: isExpanded && !closingGroupKeys.includes(group.key), children: renderCardList(group.rows, null) })] }, `group-${group.key}`));
|
|
371
434
|
})
|
|
372
|
-
: renderCardList(shouldVirtualize ? visibleRows.slice(0, cards.count) : visibleRows, visibleRows.length === 0 && isSearching ? ((0, jsx_runtime_1.jsx)(ps_table_searching_state_1.PsTableSearchingState, { label: searchingLabel, skeletonRows: table_config_1.PsTableConfig.searchingSkeletonRows })) : visibleRows.length === 0 && isQueried ? ((0, jsx_runtime_1.jsxs)("div", { className: 'px-4 py-10 text-center', children: [(0, jsx_runtime_1.jsx)("p", { className: 'text-sm font-semibold text-ink', children: table_config_1.PsTableConfig.noMatchesTitle }), (0, jsx_runtime_1.jsx)("button", { className: ps_design_system_1.PallasoftClassNameService.merge(ps_table_control_classes_1.PsTableControlClasses.textButton, 'mt-2 text-primary'), onClick: clearFilters, type: 'button', children: "Clear search and filters" })] })) : null), !grouping && shouldVirtualize && cards.count < visibleRows.length ? ((0, jsx_runtime_1.jsx)("div", { "aria-hidden": 'true', className: 'h-px', ref: cards.sentinelRef })) : null] })) : null, (0, jsx_runtime_1.jsx)("div", { className: ps_design_system_1.PallasoftClassNameService.merge(
|
|
435
|
+
: renderCardList(shouldVirtualize ? visibleRows.slice(0, cards.count) : visibleRows, visibleRows.length === 0 && isSearching ? ((0, jsx_runtime_1.jsx)(ps_table_searching_state_1.PsTableSearchingState, { label: searchingLabel, skeletonRows: table_config_1.PsTableConfig.searchingSkeletonRows })) : visibleRows.length === 0 && isQueried ? ((0, jsx_runtime_1.jsxs)("div", { className: 'px-4 py-10 text-center', children: [(0, jsx_runtime_1.jsx)("p", { className: 'text-sm font-semibold text-ink', children: table_config_1.PsTableConfig.noMatchesTitle }), (0, jsx_runtime_1.jsx)("button", { className: ps_design_system_1.PallasoftClassNameService.merge(ps_table_control_classes_1.PsTableControlClasses.textButton, 'mt-2 text-primary'), onClick: clearFilters, type: 'button', children: "Clear search and filters" })] })) : null), !grouping && shouldVirtualize && cards.count < visibleRows.length ? ((0, jsx_runtime_1.jsx)("div", { "aria-hidden": 'true', className: 'h-px', ref: cards.sentinelRef })) : null] })) : null, (0, jsx_runtime_1.jsx)("div", { className: ps_design_system_1.PallasoftClassNameService.merge(stickyTop === undefined
|
|
436
|
+
? 'ps-scrollbar overflow-x-auto overflow-y-hidden overscroll-x-contain'
|
|
437
|
+
: '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) => ((0, jsx_runtime_1.jsxs)("table", { className: ps_design_system_1.PallasoftClassNameService.merge('w-full border-collapse text-left', layout === 'fixed' ? 'table-fixed' : ''), children: [caption ? (0, jsx_runtime_1.jsx)("caption", { className: 'sr-only', children: caption }) : null, (0, jsx_runtime_1.jsx)("thead", { className: ps_design_system_1.PallasoftClassNameService.merge('bg-mist/70 text-[0.68rem] font-semibold uppercase tracking-[0.18em] text-ink/45', stickyTop === undefined
|
|
438
|
+
? ''
|
|
439
|
+
: 'sticky z-docked bg-surface [&_th]:border-b [&_th]:border-line [&>tr]:bg-mist/70'), style: stickyTop === undefined
|
|
440
|
+
? undefined
|
|
441
|
+
: { top: `calc(${stickyTop} + ${stickyHeight}px)` }, children: (0, jsx_runtime_1.jsxs)("tr", { children: [reorder ? ((0, jsx_runtime_1.jsx)("th", { className: ps_design_system_1.PallasoftClassNameService.merge('w-9 min-w-9 pl-3 pr-0', pins.reorder), scope: 'col', children: reorder.enabled ? ((0, jsx_runtime_1.jsx)("span", { className: 'sr-only', children: reorder.columnLabel })) : reorder.disabledReason ? ((0, jsx_runtime_1.jsx)("span", { "aria-label": reorder.disabledReason, className: 'inline-flex cursor-help opacity-40', role: 'img', title: reorder.disabledReason, children: (0, jsx_runtime_1.jsx)(ps_core_components_1.PsDragGrip, {}) })) : null })) : null, isSelectable ? ((0, jsx_runtime_1.jsx)("th", { className: ps_design_system_1.PallasoftClassNameService.merge('w-10 min-w-10 pl-4 pr-0', pins.select), scope: 'col', children: (0, jsx_runtime_1.jsx)(ps_input_components_1.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) => {
|
|
373
442
|
const align = column.align !== null && column.align !== undefined ? column.align : 'left';
|
|
374
443
|
const isSorted = sort !== null && sort.columnId === column.id;
|
|
375
444
|
return ((0, jsx_runtime_1.jsx)("th", { "aria-sort": isSorted
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface PsSelectAllMatchingBannerProps {
|
|
2
|
+
className?: string;
|
|
3
|
+
isActive: boolean;
|
|
4
|
+
noun?: string;
|
|
5
|
+
onActivate: () => void;
|
|
6
|
+
onDeactivate: () => void;
|
|
7
|
+
pageCount: number;
|
|
8
|
+
total: number;
|
|
9
|
+
}
|
|
10
|
+
export declare const PsSelectAllMatchingBanner: ({ className, isActive, noun, onActivate, onDeactivate, pageCount, total, }: PsSelectAllMatchingBannerProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsSelectAllMatchingBanner = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
6
|
+
const ps_table_control_classes_1 = require("../constants/ps-table-control-classes");
|
|
7
|
+
const PsSelectAllMatchingBanner = ({ className, isActive, noun = 'rows', onActivate, onDeactivate, pageCount, total, }) => ((0, jsx_runtime_1.jsxs)("div", { "aria-live": 'polite', className: ps_design_system_1.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: [(0, jsx_runtime_1.jsx)("span", { children: isActive
|
|
8
|
+
? `All ${total.toLocaleString()} ${noun} matching are selected.`
|
|
9
|
+
: `${pageCount.toLocaleString()} ${noun} on this page are selected.` }), (0, jsx_runtime_1.jsx)("button", { className: ps_design_system_1.PallasoftClassNameService.merge(ps_table_control_classes_1.PsTableControlClasses.textButton, 'text-primary'), onClick: isActive ? onDeactivate : onActivate, type: 'button', children: isActive ? 'Clear selection' : `Select all ${total.toLocaleString()} matching` })] }));
|
|
10
|
+
exports.PsSelectAllMatchingBanner = PsSelectAllMatchingBanner;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { PsTableCursorPaging } from '../interfaces/ps-table-cursor-paging';
|
|
1
2
|
export interface PsTablePaginationProps {
|
|
2
3
|
className?: string;
|
|
4
|
+
cursor?: PsTableCursorPaging;
|
|
3
5
|
isPartial?: boolean;
|
|
4
6
|
loadedPageCount?: number;
|
|
5
7
|
onPageChange: (page: number) => void;
|
|
@@ -8,4 +10,4 @@ export interface PsTablePaginationProps {
|
|
|
8
10
|
pageSize: number;
|
|
9
11
|
totalRows: number;
|
|
10
12
|
}
|
|
11
|
-
export declare const PsTablePagination: ({ className, isPartial, loadedPageCount, onPageChange, page, pageCount, pageSize, totalRows, }: PsTablePaginationProps) => import("react").JSX.Element;
|
|
13
|
+
export declare const PsTablePagination: ({ className, cursor, isPartial, loadedPageCount, onPageChange, page, pageCount, pageSize, totalRows, }: PsTablePaginationProps) => import("react").JSX.Element;
|
|
@@ -4,12 +4,20 @@ exports.PsTablePagination = void 0;
|
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
6
6
|
const ps_table_control_classes_1 = require("../constants/ps-table-control-classes");
|
|
7
|
-
const PsTablePagination = ({ className, isPartial = false, loadedPageCount, onPageChange, page, pageCount, pageSize, totalRows, }) => {
|
|
7
|
+
const PsTablePagination = ({ className, cursor, isPartial = false, loadedPageCount, onPageChange, page, pageCount, pageSize, totalRows, }) => {
|
|
8
8
|
const firstRow = totalRows === 0 ? 0 : (page - 1) * pageSize + 1;
|
|
9
9
|
const lastRow = Math.min(page * pageSize, totalRows);
|
|
10
|
-
|
|
10
|
+
const approximately = cursor !== undefined && cursor.isEstimate ? 'about ' : '';
|
|
11
|
+
const pageNumber = cursor === undefined ? Math.min(page, pageCount) : cursor.pageNumber;
|
|
12
|
+
const cursorFirst = cursor === undefined ? firstRow : (cursor.pageNumber - 1) * pageSize + 1;
|
|
13
|
+
const cursorLast = cursor === undefined ? lastRow : Math.min(cursor.pageNumber * pageSize, totalRows);
|
|
14
|
+
return ((0, jsx_runtime_1.jsxs)("nav", { "aria-label": 'Pagination', className: ps_design_system_1.PallasoftClassNameService.merge('flex flex-wrap items-center justify-between gap-x-6 gap-y-3 border-t border-line px-5 py-3', className), children: [(0, jsx_runtime_1.jsx)("p", { className: 'shrink-0 whitespace-nowrap text-sm text-ink/55', children: totalRows === 0
|
|
11
15
|
? 'No rows'
|
|
12
|
-
:
|
|
13
|
-
|
|
16
|
+
: cursor === undefined
|
|
17
|
+
? `${firstRow}–${lastRow} of ${totalRows}${isPartial ? '+' : ''}`
|
|
18
|
+
: `${cursorFirst}–${cursorLast} of ${approximately}${totalRows.toLocaleString()}` }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex shrink-0 items-center gap-3', children: [cursor !== undefined && cursor.pageSizeOptions !== undefined && cursor.onPageSizeChange ? ((0, jsx_runtime_1.jsxs)("label", { className: 'flex shrink-0 items-center gap-2 whitespace-nowrap text-sm text-ink/65', children: [(0, jsx_runtime_1.jsx)("span", { className: 'hidden sm:inline', children: "Rows per page" }), (0, jsx_runtime_1.jsx)("select", { "aria-label": 'Rows per page', className: 'ps-input-control ps-focus-ring ps-control ps-control-padding w-auto shrink-0 rounded-ps-control text-sm', onChange: (event) => cursor.onPageSizeChange?.(Number(event.target.value)), value: pageSize, children: cursor.pageSizeOptions.map((option) => ((0, jsx_runtime_1.jsx)("option", { value: option, children: option }, option))) })] })) : null, (0, jsx_runtime_1.jsx)("button", { className: ps_table_control_classes_1.PsTableControlClasses.paginationButton, disabled: cursor === undefined ? page <= 1 : !cursor.hasPrevious, onClick: () => (cursor === undefined ? onPageChange(page - 1) : cursor.onPrevious()), type: 'button', children: "Previous" }), (0, jsx_runtime_1.jsxs)("span", { className: 'hidden whitespace-nowrap text-sm text-ink/65 sm:inline', children: ["Page ", pageNumber, " of ", approximately, pageCount] }), (0, jsx_runtime_1.jsx)("button", { className: ps_table_control_classes_1.PsTableControlClasses.paginationButton, disabled: cursor === undefined
|
|
19
|
+
? page >=
|
|
20
|
+
(loadedPageCount === undefined ? pageCount : Math.min(loadedPageCount, pageCount))
|
|
21
|
+
: !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" })] })] }));
|
|
14
22
|
};
|
|
15
23
|
exports.PsTablePagination = PsTablePagination;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsServerRowsConfig = void 0;
|
|
4
|
+
exports.PsServerRowsConfig = {
|
|
5
|
+
cacheSize: 8,
|
|
6
|
+
debounceMs: 250,
|
|
7
|
+
defaultPageSize: 100,
|
|
8
|
+
idleFallbackMs: 200,
|
|
9
|
+
idleTimeoutMs: 1000,
|
|
10
|
+
pageSizes: [50, 100, 200],
|
|
11
|
+
};
|
|
@@ -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 { PsTablePaginationConfig } from '../constants/ps-table-pagination-config';
|
|
9
10
|
import { PsTablePaginationPosition } from '../constants/ps-table-pagination-position';
|
|
10
11
|
import { PsTableHideBelowClasses } from '../constants/table-breakpoint';
|
|
11
12
|
import { PsTableConfig } from '../constants/table-config';
|
|
@@ -20,6 +21,7 @@ import { PsTablePinService } from '../services/ps-table-pin-service';
|
|
|
20
21
|
import { PsTableQueryService } from '../services/ps-table-query-service';
|
|
21
22
|
import { PsTableReorderService } from '../services/ps-table-reorder-service';
|
|
22
23
|
import { PsTableScrollService } from '../services/ps-table-scroll-service';
|
|
24
|
+
import { PsSelectAllMatchingBanner } from './ps-select-all-matching-banner';
|
|
23
25
|
import { PsTableBulkBar } from './ps-table-bulk-bar';
|
|
24
26
|
import { PsTableCardList } from './ps-table-card-list';
|
|
25
27
|
import { PsTableColumnPicker } from './ps-table-column-picker';
|
|
@@ -49,7 +51,7 @@ const alignClassNames = {
|
|
|
49
51
|
left: 'text-left',
|
|
50
52
|
right: 'text-right tabular-nums',
|
|
51
53
|
};
|
|
52
|
-
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, paginationPosition = PsTablePaginationPosition.bottom, pinFirstColumn = true, query, renderExpandedRow, reorder, rows, searchingLabel = PsTableConfig.searchingLabel, searchPlaceholder = PsTableConfig.searchPlaceholder, toolbarActions, totalRows, }) => {
|
|
54
|
+
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, stickyClassName, stickyTop, toolbarActions, totalRows, }) => {
|
|
53
55
|
const containerRef = useRef(null);
|
|
54
56
|
const [uncontrolledPage, setUncontrolledPage] = useState(1);
|
|
55
57
|
const [uncontrolledQuery, setUncontrolledQuery] = useState(() => PsTableQueryService.empty(defaultSort ? defaultSort : null));
|
|
@@ -57,7 +59,50 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
57
59
|
const [toggledGroupKeys, setToggledGroupKeys] = useState([]);
|
|
58
60
|
const [closingGroupKeys, setClosingGroupKeys] = useState([]);
|
|
59
61
|
const [openedGroupKeys, setOpenedGroupKeys] = useState([]);
|
|
62
|
+
const [seenClearKey, setSeenClearKey] = useState(clearSelectionKey);
|
|
63
|
+
const stickyRegionRef = useRef(null);
|
|
64
|
+
const stickySentinelRef = useRef(null);
|
|
65
|
+
const [stickyHeight, setStickyHeight] = useState(0);
|
|
66
|
+
const [isStuck, setIsStuck] = useState(false);
|
|
60
67
|
const currentQuery = query ? query : uncontrolledQuery;
|
|
68
|
+
if (seenClearKey !== clearSelectionKey) {
|
|
69
|
+
setSeenClearKey(clearSelectionKey);
|
|
70
|
+
setSelectedIds([]);
|
|
71
|
+
}
|
|
72
|
+
useLayoutEffect(() => {
|
|
73
|
+
const region = stickyRegionRef.current;
|
|
74
|
+
if (stickyTop === undefined || region === null)
|
|
75
|
+
return;
|
|
76
|
+
const measure = () => setStickyHeight(region.offsetHeight);
|
|
77
|
+
const observer = new ResizeObserver(measure);
|
|
78
|
+
measure();
|
|
79
|
+
observer.observe(region);
|
|
80
|
+
return () => observer.disconnect();
|
|
81
|
+
}, [stickyTop]);
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
const region = stickyRegionRef.current;
|
|
84
|
+
const sentinel = stickySentinelRef.current;
|
|
85
|
+
if (stickyTop === undefined || region === null || sentinel === null)
|
|
86
|
+
return;
|
|
87
|
+
let frame = 0;
|
|
88
|
+
const check = () => {
|
|
89
|
+
frame = 0;
|
|
90
|
+
setIsStuck(sentinel.getBoundingClientRect().top < Number.parseFloat(getComputedStyle(region).top));
|
|
91
|
+
};
|
|
92
|
+
const schedule = () => {
|
|
93
|
+
if (frame === 0)
|
|
94
|
+
frame = window.requestAnimationFrame(check);
|
|
95
|
+
};
|
|
96
|
+
check();
|
|
97
|
+
window.addEventListener('scroll', schedule, { capture: true, passive: true });
|
|
98
|
+
window.addEventListener('resize', schedule);
|
|
99
|
+
return () => {
|
|
100
|
+
window.removeEventListener('scroll', schedule, { capture: true });
|
|
101
|
+
window.removeEventListener('resize', schedule);
|
|
102
|
+
if (frame !== 0)
|
|
103
|
+
window.cancelAnimationFrame(frame);
|
|
104
|
+
};
|
|
105
|
+
}, [stickyTop]);
|
|
61
106
|
const { filters, hiddenColumnIds, search, sort } = currentQuery;
|
|
62
107
|
const visibleColumns = columns.filter((column) => !hiddenColumnIds.includes(column.id));
|
|
63
108
|
const fittedWidths = useMemo(() => new Map(columns
|
|
@@ -199,7 +244,7 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
199
244
|
})
|
|
200
245
|
: [], [getRowId, getRowLabel, reorder, reorderKey, visibleRows]);
|
|
201
246
|
const showsPagination = Boolean(pageSize) && (!grouping || isServerPaginated);
|
|
202
|
-
const renderPagination = (paginationClassName) => pageSize ? (_jsx(PsTablePagination, { className: paginationClassName, isPartial: isPartial, loadedPageCount: pageCountOf(loadedRowCount), onPageChange: changePage, page: safePage, pageCount: pageCount, pageSize: pageSize, totalRows: rowCount })) : null;
|
|
247
|
+
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;
|
|
203
248
|
const renderOverlayRow = (rowId, size) => {
|
|
204
249
|
const row = visibleRows.find((candidate) => getRowId(candidate) === rowId);
|
|
205
250
|
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) })] }));
|
|
@@ -233,12 +278,16 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
233
278
|
const selectedRows = selectedIds.length === 0 ? [] : queriedRows.filter((row) => selectedIdSet.has(getRowId(row)));
|
|
234
279
|
const pageIds = visibleRows.map((row) => getRowId(row));
|
|
235
280
|
const isPageSelected = pageIds.length > 0 && pageIds.every((rowId) => selectedIdSet.has(rowId));
|
|
236
|
-
const togglePage = () =>
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
281
|
+
const togglePage = () => {
|
|
282
|
+
if (isPageSelected && selectAllMatching !== undefined && selectAllMatching.isActive)
|
|
283
|
+
selectAllMatching.onDeactivate();
|
|
284
|
+
setSelectedIds((current) => {
|
|
285
|
+
const pageIdSet = new Set(pageIds);
|
|
286
|
+
return isPageSelected
|
|
287
|
+
? current.filter((rowId) => !pageIdSet.has(rowId))
|
|
288
|
+
: [...new Set([...current, ...pageIds])];
|
|
289
|
+
});
|
|
290
|
+
};
|
|
242
291
|
const bodyRef = useRef(null);
|
|
243
292
|
const [scrollParent, setScrollParent] = useState(null);
|
|
244
293
|
const [scrollMargin, setScrollMargin] = useState(0);
|
|
@@ -316,7 +365,18 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
316
365
|
: entries.map((entry) => renderRow(api, entry.row, entry.motion, entry.virtualItem));
|
|
317
366
|
if (isLoading)
|
|
318
367
|
return (_jsx(PsSkeletonTable, { className: className, columns: visibleColumns.length, hasToolbar: hasToolbar, rows: PsTableConfig.skeletonRows }));
|
|
319
|
-
|
|
368
|
+
const clearSelection = () => {
|
|
369
|
+
setSelectedIds([]);
|
|
370
|
+
if (selectAllMatching !== undefined && selectAllMatching.isActive)
|
|
371
|
+
selectAllMatching.onDeactivate();
|
|
372
|
+
};
|
|
373
|
+
const banner = selectAllMatching !== undefined &&
|
|
374
|
+
selectedRows.length > 0 &&
|
|
375
|
+
selectAllMatching.total > pageIds.length &&
|
|
376
|
+
(isPageSelected || selectAllMatching.isActive) ? (_jsx(PsSelectAllMatchingBanner, { isActive: selectAllMatching.isActive, noun: selectAllMatching.noun, onActivate: selectAllMatching.onActivate, onDeactivate: clearSelection, pageCount: pageIds.length, total: selectAllMatching.total })) : null;
|
|
377
|
+
const topContent = (_jsxs(_Fragment, { children: [isSelectable && selectedRows.length > 0 && getBulkActions ? (_jsx(PsTableBulkBar, { actions: getBulkActions(selectedRows), onClear: clearSelection, selectedCount: selectAllMatching !== undefined && selectAllMatching.isActive
|
|
378
|
+
? selectAllMatching.total
|
|
379
|
+
: selectedRows.length })) : hasToolbar || columns.some((column) => column.canHide) ? (_jsx(PsTableToolbar, { actions: _jsxs(_Fragment, { children: [toolbarActions, columns.some((column) => column.canHide) ? (_jsx(PsTableColumnPicker, { columns: columns
|
|
320
380
|
.filter((column) => column.canHide)
|
|
321
381
|
.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) => {
|
|
322
382
|
updateQuery({ filters: { ...filters, [columnId]: value } });
|
|
@@ -324,16 +384,25 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
324
384
|
}, onSearchChange: (next) => {
|
|
325
385
|
updateQuery({ search: next });
|
|
326
386
|
resetPage();
|
|
327
|
-
}, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null,
|
|
387
|
+
}, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null, banner] }));
|
|
388
|
+
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 px-1 pb-2 pt-1 transition-shadow', stickyClassName === undefined ? 'bg-canvas' : stickyClassName, 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 &&
|
|
389
|
+
paginationPosition !== PsTablePaginationPosition.bottom &&
|
|
390
|
+
rowCount > PsTablePaginationConfig.topMinRows
|
|
328
391
|
? renderPagination('border-b border-t-0')
|
|
329
|
-
: null, mobileLayout === 'cards' ? (_jsxs("div", { className: 'sm:hidden', 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
|
|
392
|
+
: 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
|
|
330
393
|
? groups.map((group, groupIndex) => {
|
|
331
394
|
const isExpanded = isGroupExpanded(group.key, groupIndex);
|
|
332
395
|
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
|
|
333
396
|
? grouping.renderGroupSummary(group.rows)
|
|
334
397
|
: undefined }), _jsx(PsCollapse, { isOpen: isExpanded && !closingGroupKeys.includes(group.key), children: renderCardList(group.rows, null) })] }, `group-${group.key}`));
|
|
335
398
|
})
|
|
336
|
-
: 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(
|
|
399
|
+
: 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
|
|
400
|
+
? 'ps-scrollbar overflow-x-auto overflow-y-hidden overscroll-x-contain'
|
|
401
|
+
: '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
|
|
402
|
+
? ''
|
|
403
|
+
: 'sticky z-docked bg-surface [&_th]:border-b [&_th]:border-line [&>tr]:bg-mist/70'), style: stickyTop === undefined
|
|
404
|
+
? undefined
|
|
405
|
+
: { 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) => {
|
|
337
406
|
const align = column.align !== null && column.align !== undefined ? column.align : 'left';
|
|
338
407
|
const isSorted = sort !== null && sort.columnId === column.id;
|
|
339
408
|
return (_jsx("th", { "aria-sort": isSorted
|
|
@@ -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
|
-
|
|
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);
|
|
11
|
+
return (_jsxs("nav", { "aria-label": 'Pagination', className: PallasoftClassNameService.merge('flex flex-wrap items-center justify-between gap-x-6 gap-y-3 border-t border-line px-5 py-3', className), children: [_jsx("p", { className: 'shrink-0 whitespace-nowrap 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 shrink-0 items-center gap-3', children: [cursor !== undefined && cursor.pageSizeOptions !== undefined && cursor.onPageSizeChange ? (_jsxs("label", { className: 'flex shrink-0 items-center gap-2 whitespace-nowrap 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 w-auto shrink-0 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 whitespace-nowrap 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
|
};
|