@pallasoft/ps-table-components 1.20.0 → 1.22.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 +5 -1
- package/dist/components/ps-data-table.js +80 -12
- package/dist/components/ps-table-pagination.d.ts +2 -1
- package/dist/components/ps-table-pagination.js +4 -2
- package/dist/components/ps-table-reorder-frame.d.ts +8 -0
- package/dist/components/ps-table-reorder-frame.js +30 -0
- package/dist/components/ps-table-reorder-handle.d.ts +6 -0
- package/dist/components/ps-table-reorder-handle.js +8 -0
- package/dist/components/ps-table-searching-state.d.ts +6 -0
- package/dist/components/ps-table-searching-state.js +7 -0
- package/dist/components/ps-table-sortable-row.d.ts +9 -0
- package/dist/components/ps-table-sortable-row.js +22 -0
- package/dist/constants/table-config.d.ts +2 -0
- package/dist/constants/table-config.js +2 -0
- package/dist/esm/components/ps-data-table.js +49 -14
- package/dist/esm/components/ps-table-pagination.js +4 -2
- package/dist/esm/components/ps-table-reorder-frame.js +26 -0
- package/dist/esm/components/ps-table-reorder-handle.js +4 -0
- package/dist/esm/components/ps-table-searching-state.js +3 -0
- package/dist/esm/components/ps-table-sortable-row.js +18 -0
- package/dist/esm/constants/table-config.js +2 -0
- package/dist/esm/hooks/use-ps-table-page.js +5 -0
- package/dist/esm/hooks/use-ps-table-reorder.js +63 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/interfaces/ps-table-page-state.js +1 -0
- package/dist/esm/interfaces/table-reorder-announcements.js +1 -0
- package/dist/esm/interfaces/table-reorder-api.js +1 -0
- package/dist/esm/interfaces/table-reorder-item.js +1 -0
- package/dist/esm/interfaces/table-reorder-placement.js +1 -0
- package/dist/esm/interfaces/table-reorder-row-props.js +1 -0
- package/dist/esm/interfaces/table-reorder-run.js +1 -0
- package/dist/esm/interfaces/table-reorder.js +1 -0
- package/dist/esm/interfaces/table-row-entry.js +1 -0
- package/dist/esm/services/ps-table-reorder-service.js +42 -0
- package/dist/hooks/use-ps-table-page.d.ts +2 -0
- package/dist/hooks/use-ps-table-page.js +9 -0
- package/dist/hooks/use-ps-table-reorder.d.ts +18 -0
- package/dist/hooks/use-ps-table-reorder.js +67 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/interfaces/ps-table-page-state.d.ts +4 -0
- package/dist/interfaces/ps-table-page-state.js +2 -0
- package/dist/interfaces/table-reorder-announcements.d.ts +6 -0
- package/dist/interfaces/table-reorder-announcements.js +2 -0
- package/dist/interfaces/table-reorder-api.d.ts +8 -0
- package/dist/interfaces/table-reorder-api.js +2 -0
- package/dist/interfaces/table-reorder-item.d.ts +6 -0
- package/dist/interfaces/table-reorder-item.js +2 -0
- package/dist/interfaces/table-reorder-placement.d.ts +6 -0
- package/dist/interfaces/table-reorder-placement.js +2 -0
- package/dist/interfaces/table-reorder-row-props.d.ts +6 -0
- package/dist/interfaces/table-reorder-row-props.js +2 -0
- package/dist/interfaces/table-reorder-run.d.ts +5 -0
- package/dist/interfaces/table-reorder-run.js +2 -0
- package/dist/interfaces/table-reorder.d.ts +12 -0
- package/dist/interfaces/table-reorder.js +2 -0
- package/dist/interfaces/table-row-entry.d.ts +7 -0
- package/dist/interfaces/table-row-entry.js +2 -0
- package/dist/services/ps-table-reorder-service.d.ts +11 -0
- package/dist/services/ps-table-reorder-service.js +46 -0
- package/package.json +6 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Fragment, useDeferredValue, useEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
-
import { PsSkeletonTable } from '@pallasoft/ps-core-components';
|
|
2
|
+
import { Fragment, Suspense, lazy, useDeferredValue, useEffect, useMemo, useRef, useState, } from 'react';
|
|
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';
|
|
@@ -13,14 +13,17 @@ import { PsTableCellTooltipService } from '../services/ps-table-cell-tooltip-ser
|
|
|
13
13
|
import { PsTableColumnSizeService } from '../services/ps-table-column-size-service';
|
|
14
14
|
import { PsTableGroupService } from '../services/ps-table-group-service';
|
|
15
15
|
import { PsTableQueryService } from '../services/ps-table-query-service';
|
|
16
|
+
import { PsTableReorderService } from '../services/ps-table-reorder-service';
|
|
16
17
|
import { PsTableScrollService } from '../services/ps-table-scroll-service';
|
|
17
18
|
import { PsTableBulkBar } from './ps-table-bulk-bar';
|
|
18
19
|
import { PsTableCardList } from './ps-table-card-list';
|
|
19
20
|
import { PsTableColumnPicker } from './ps-table-column-picker';
|
|
20
21
|
import { PsTableGroupHeader } from './ps-table-group-header';
|
|
21
22
|
import { PsTablePagination } from './ps-table-pagination';
|
|
23
|
+
import { PsTableSearchingState } from './ps-table-searching-state';
|
|
22
24
|
import { PsTableSortHeader } from './ps-table-sort-header';
|
|
23
25
|
import { PsTableToolbar } from './ps-table-toolbar';
|
|
26
|
+
const LazyReorderFrame = lazy(() => import('./ps-table-reorder-frame').then((module) => ({ default: module.PsTableReorderFrame })));
|
|
24
27
|
const densityClassNames = {
|
|
25
28
|
compact: {
|
|
26
29
|
cell: 'px-3 py-1.5 text-[0.8125rem] leading-5',
|
|
@@ -41,7 +44,7 @@ const alignClassNames = {
|
|
|
41
44
|
left: 'text-left',
|
|
42
45
|
right: 'text-right tabular-nums',
|
|
43
46
|
};
|
|
44
|
-
export const PsDataTable = ({ caption, className, columns, defaultSort, density = 'normal', expandedRowIds, mobileLayout = 'cards', getBulkActions, getRowId, getRowLabel, getSearchText, grouping, isLoading = false, layout = 'auto', onPageChange, onQueryChange, onRowClick, onRowHover, page, pageSize, query, renderExpandedRow, rows, searchPlaceholder = PsTableConfig.searchPlaceholder, toolbarActions, totalRows, }) => {
|
|
47
|
+
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, query, renderExpandedRow, reorder, rows, searchingLabel = PsTableConfig.searchingLabel, searchPlaceholder = PsTableConfig.searchPlaceholder, toolbarActions, totalRows, }) => {
|
|
45
48
|
const containerRef = useRef(null);
|
|
46
49
|
const [uncontrolledPage, setUncontrolledPage] = useState(1);
|
|
47
50
|
const [uncontrolledQuery, setUncontrolledQuery] = useState(() => PsTableQueryService.empty(defaultSort ? defaultSort : null));
|
|
@@ -65,7 +68,7 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
65
68
|
const hasFilters = columns.some((column) => column.filter) && isQueryable;
|
|
66
69
|
const hasToolbar = isSearchable || hasFilters || Boolean(toolbarActions);
|
|
67
70
|
const isSelectable = Boolean(getBulkActions);
|
|
68
|
-
const columnCount = visibleColumns.length + (isSelectable ? 1 : 0);
|
|
71
|
+
const columnCount = visibleColumns.length + (isSelectable ? 1 : 0) + (reorder ? 1 : 0);
|
|
69
72
|
const deferredSearch = useDeferredValue(search);
|
|
70
73
|
const queriedRows = useMemo(() => isServerPaginated
|
|
71
74
|
? rows
|
|
@@ -73,6 +76,8 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
73
76
|
const streamStatus = usePsTableStreamStatus();
|
|
74
77
|
const isQueried = search.trim() !== '' ||
|
|
75
78
|
Object.values(filters).some((value) => PsTableQueryService.isActive(value));
|
|
79
|
+
const isPartial = streamStatus !== null && !isServerPaginated && isQueried;
|
|
80
|
+
const isSearching = isPartial || isPending;
|
|
76
81
|
const currentPage = page !== null && page !== undefined ? page : uncontrolledPage;
|
|
77
82
|
const loadedRowCount = isServerPaginated ? totalRows : queriedRows.length;
|
|
78
83
|
const rowCount = streamStatus !== null && !isServerPaginated && !isQueried
|
|
@@ -133,6 +138,23 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
133
138
|
safePage,
|
|
134
139
|
toggledGroupKeys,
|
|
135
140
|
]);
|
|
141
|
+
const reorderItems = useMemo(() => reorder
|
|
142
|
+
? visibleRows.map((row) => {
|
|
143
|
+
const rowId = getRowId(row);
|
|
144
|
+
const label = getRowLabel ? getRowLabel(row) : rowId;
|
|
145
|
+
return {
|
|
146
|
+
groupKey: reorder.groupKey(row),
|
|
147
|
+
handleLabel: reorder.getHandleLabel(label),
|
|
148
|
+
id: rowId,
|
|
149
|
+
label,
|
|
150
|
+
};
|
|
151
|
+
})
|
|
152
|
+
: [], [getRowId, getRowLabel, reorder, visibleRows]);
|
|
153
|
+
const renderOverlayRow = (rowId) => {
|
|
154
|
+
const row = visibleRows.find((candidate) => getRowId(candidate) === rowId);
|
|
155
|
+
return row === undefined ? null : (_jsx("table", { className: 'w-full table-fixed border-collapse bg-surface text-left', children: _jsx("tbody", { children: renderRow(PsTableReorderService.plain, row) }) }));
|
|
156
|
+
};
|
|
157
|
+
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, onReorder: reorder.onReorder, renderOverlay: renderOverlayRow, children: children }) }));
|
|
136
158
|
const changePage = (next) => {
|
|
137
159
|
if (onPageChange) {
|
|
138
160
|
onPageChange(next);
|
|
@@ -210,11 +232,15 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
210
232
|
}, [shouldVirtualize, visibleRows.length]);
|
|
211
233
|
const toggleRow = (rowId) => setSelectedIds((current) => current.includes(rowId) ? current.filter((id) => id !== rowId) : [...current, rowId]);
|
|
212
234
|
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 }));
|
|
213
|
-
const renderRow = (row, motion, virtualItem) => {
|
|
235
|
+
const renderRow = (api, row, motion, virtualItem) => {
|
|
214
236
|
const rowId = getRowId(row);
|
|
215
237
|
const isExpanded = expandedRowIds ? expandedRowIds.includes(rowId) : false;
|
|
216
238
|
const rowLabel = getRowLabel ? getRowLabel(row) : rowId;
|
|
217
|
-
|
|
239
|
+
const Row = api.Row;
|
|
240
|
+
const rowExtras = api.isActive
|
|
241
|
+
? { ...api.rowProps(rowId), measureRef: virtualItem ? virtualizer.measureElement : undefined }
|
|
242
|
+
: { ref: virtualItem ? virtualizer.measureElement : undefined };
|
|
243
|
+
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 transition-colors', motion ? motion.className : '', onRowClick ? 'ps-focus-ring cursor-pointer hover:bg-mist/60' : 'hover:bg-mist/40', isExpanded ? 'bg-mist/40' : ''), onClick: onRowClick ? () => onRowClick(row) : undefined, onFocus: onRowHover ? () => onRowHover(row) : undefined, onPointerEnter: onRowHover ? () => onRowHover(row) : undefined, onKeyDown: onRowClick
|
|
218
244
|
? (event) => {
|
|
219
245
|
if (event.target !== event.currentTarget)
|
|
220
246
|
return;
|
|
@@ -225,11 +251,14 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
225
251
|
}
|
|
226
252
|
: undefined, style: motion && motion.delayMs !== undefined
|
|
227
253
|
? { animationDelay: `${motion.delayMs}ms` }
|
|
228
|
-
: undefined, tabIndex: onRowClick ? 0 : undefined, children: [isSelectable ? (_jsx("td", { className: 'w-10 pl-4 pr-0', onClick: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: _jsx(PsCheckbox, { isSelected: selectedIdSet.has(rowId), label: `Select ${rowLabel}`, onChange: () => toggleRow(rowId) }) })) : null, visibleColumns.map((column) => (_jsx("td", { className: PallasoftClassNameService.merge(densityClassNames[density].cell, alignClassNames[column.align !== null && column.align !== undefined ? column.align : 'left'], column.wrap ? '' : 'whitespace-nowrap', (layout === 'fixed' && !column.wrap) ||
|
|
254
|
+
: undefined, tabIndex: onRowClick ? 0 : undefined, children: [reorder && !api.isActive ? _jsx("td", { className: 'w-9 pl-3 pr-0' }) : null, isSelectable ? (_jsx("td", { className: 'w-10 pl-4 pr-0', onClick: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: _jsx(PsCheckbox, { isSelected: selectedIdSet.has(rowId), label: `Select ${rowLabel}`, onChange: () => toggleRow(rowId) }) })) : null, visibleColumns.map((column) => (_jsx("td", { className: PallasoftClassNameService.merge(densityClassNames[density].cell, alignClassNames[column.align !== null && column.align !== undefined ? column.align : 'left'], column.wrap ? '' : 'whitespace-nowrap', (layout === 'fixed' && !column.wrap) ||
|
|
229
255
|
PsTableColumnSizeService.maxWidthOf(column) !== undefined
|
|
230
256
|
? 'truncate'
|
|
231
257
|
: '', 'text-sm text-ink'), style: columnStyle(column), 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));
|
|
232
258
|
};
|
|
259
|
+
const renderEntries = (api, entries) => reorder
|
|
260
|
+
? PsTableReorderService.runs(entries, (entry) => reorder.groupKey(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))))
|
|
261
|
+
: entries.map((entry) => renderRow(api, entry.row, entry.motion, entry.virtualItem));
|
|
233
262
|
if (isLoading)
|
|
234
263
|
return (_jsx(PsSkeletonTable, { className: className, columns: visibleColumns.length, hasToolbar: hasToolbar, rows: PsTableConfig.skeletonRows }));
|
|
235
264
|
return (_jsxs("div", { ref: containerRef, className: PallasoftClassNameService.merge('flex min-w-0 flex-col gap-3', className), children: [isSelectable && selectedRows.length > 0 && getBulkActions ? (_jsx(PsTableBulkBar, { actions: getBulkActions(selectedRows), onClear: () => setSelectedIds([]), selectedCount: selectedRows.length })) : hasToolbar || columns.some((column) => column.canHide) ? (_jsx(PsTableToolbar, { actions: _jsxs(_Fragment, { children: [toolbarActions, columns.some((column) => column.canHide) ? (_jsx(PsTableColumnPicker, { columns: columns
|
|
@@ -240,14 +269,14 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
240
269
|
}, onSearchChange: (next) => {
|
|
241
270
|
updateQuery({ search: next });
|
|
242
271
|
resetPage();
|
|
243
|
-
}, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null, _jsxs("div", { className: 'min-w-0 overflow-hidden rounded-[1.25rem] border border-line bg-surface', children: [mobileLayout === 'cards' ? (_jsxs("div", { className: 'sm:hidden', children: [grouping
|
|
272
|
+
}, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null, _jsxs("div", { className: 'min-w-0 overflow-hidden rounded-[1.25rem] border border-line bg-surface', children: [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
|
|
244
273
|
? groups.map((group, groupIndex) => {
|
|
245
274
|
const isExpanded = isGroupExpanded(group.key, groupIndex);
|
|
246
275
|
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
|
|
247
276
|
? grouping.renderGroupSummary(group.rows)
|
|
248
277
|
: undefined }), _jsx(PsCollapse, { isOpen: isExpanded && !closingGroupKeys.includes(group.key), children: renderCardList(group.rows, null) })] }, `group-${group.key}`));
|
|
249
278
|
})
|
|
250
|
-
: renderCardList(shouldVirtualize ? visibleRows.slice(0, cards.count) : visibleRows, 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: 'ps-focus-ring mt-2 rounded-full px-3 py-1.5 text-sm font-semibold 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('ps-scrollbar overflow-x-auto overflow-y-hidden', mobileLayout === 'cards' ? 'hidden sm:block' : ''), children: _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: 'bg-mist/70 text-[0.68rem] font-semibold uppercase tracking-[0.18em] text-ink/45', children: _jsxs("tr", { children: [isSelectable ? (_jsx("th", { className: 'w-10 pl-4 pr-0', 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) => {
|
|
279
|
+
: 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: 'ps-focus-ring mt-2 rounded-full px-3 py-1.5 text-sm font-semibold 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('ps-scrollbar overflow-x-auto overflow-y-hidden', mobileLayout === 'cards' ? 'hidden sm:block' : ''), 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: 'bg-mist/70 text-[0.68rem] font-semibold uppercase tracking-[0.18em] text-ink/45', children: _jsxs("tr", { children: [reorder ? (_jsx("th", { className: 'w-9 pl-3 pr-0', 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: 'w-10 pl-4 pr-0', 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) => {
|
|
251
280
|
const align = column.align !== null && column.align !== undefined ? column.align : 'left';
|
|
252
281
|
const isSorted = sort !== null && sort.columnId === column.id;
|
|
253
282
|
return (_jsx("th", { "aria-sort": isSorted
|
|
@@ -255,22 +284,28 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
|
|
|
255
284
|
: column.sortValue && isQueryable
|
|
256
285
|
? 'none'
|
|
257
286
|
: undefined, className: PallasoftClassNameService.merge(densityClassNames[density].head, alignClassNames[align], 'whitespace-nowrap'), scope: 'col', style: columnStyle(column), children: column.sortValue && isQueryable ? (_jsx(PsTableSortHeader, { align: align, columnId: column.id, label: column.label, onSort: updateSort, sort: sort })) : (column.label) }, column.id));
|
|
258
|
-
})] }) }), _jsxs("tbody", { className: 'divide-y divide-line', ref: bodyRef, children: [visibleRows.length === 0 && isQueried ? (_jsx("tr", { children: _jsxs("td", { className: 'px-4 py-12 text-center', colSpan: columnCount, children: [_jsx("p", { className: 'text-sm font-semibold text-ink', children: PsTableConfig.noMatchesTitle }), _jsx("p", { className: 'mt-1 text-sm text-ink/55', children: PsTableConfig.noMatchesDescription }), _jsx("button", { className: 'ps-focus-ring mt-3 rounded-full px-3 py-1.5 text-sm font-semibold text-primary hover:underline', onClick: clearFilters, type: 'button', children: "Clear search and filters" })] }) })) : null, grouping
|
|
287
|
+
})] }) }), _jsxs("tbody", { className: 'divide-y divide-line', ref: bodyRef, children: [visibleRows.length === 0 && isSearching ? (_jsx("tr", { children: _jsx("td", { className: 'p-0', colSpan: columnCount, children: _jsx(PsTableSearchingState, { label: searchingLabel, skeletonRows: PsTableConfig.searchingSkeletonRows }) }) })) : null, visibleRows.length === 0 && isQueried && !isSearching ? (_jsx("tr", { children: _jsxs("td", { className: 'px-4 py-12 text-center', colSpan: columnCount, children: [_jsx("p", { className: 'text-sm font-semibold text-ink', children: PsTableConfig.noMatchesTitle }), _jsx("p", { className: 'mt-1 text-sm text-ink/55', children: PsTableConfig.noMatchesDescription }), _jsx("button", { className: 'ps-focus-ring mt-3 rounded-full px-3 py-1.5 text-sm font-semibold text-primary hover:underline', onClick: clearFilters, type: 'button', children: "Clear search and filters" })] }) })) : null, grouping
|
|
259
288
|
? groups.map((group, groupIndex) => {
|
|
260
289
|
const isExpanded = isGroupExpanded(group.key, groupIndex);
|
|
261
290
|
return (_jsxs(Fragment, { children: [_jsx("tr", { children: _jsx("td", { className: 'p-0', colSpan: columnCount, children: _jsx(PsTableGroupHeader, { count: group.rows.length, isExpanded: isExpanded, label: grouping.getGroupLabel(group.key, group.rows), onToggle: () => toggleGroup(group.key, isExpanded), summary: grouping.renderGroupSummary
|
|
262
291
|
? grouping.renderGroupSummary(group.rows)
|
|
263
292
|
: undefined }) }) }), isExpanded
|
|
264
|
-
? group.rows.map((row, rowIndex) =>
|
|
293
|
+
? renderEntries(api, group.rows.map((row, rowIndex) => ({
|
|
294
|
+
motion: groupRowMotion(group.key, rowIndex),
|
|
295
|
+
row,
|
|
296
|
+
})))
|
|
265
297
|
: null] }, `group-${group.key}`));
|
|
266
298
|
})
|
|
267
299
|
: shouldVirtualize
|
|
268
300
|
? [
|
|
269
301
|
_jsx("tr", { "aria-hidden": 'true', children: _jsx("td", { className: 'p-0', colSpan: columnCount, style: { height: virtualTop } }) }, 'virtual-top'),
|
|
270
|
-
...virtualItems.map((item) =>
|
|
302
|
+
...renderEntries(api, virtualItems.map((item) => ({
|
|
303
|
+
row: visibleRows[item.index],
|
|
304
|
+
virtualItem: item,
|
|
305
|
+
}))),
|
|
271
306
|
_jsx("tr", { "aria-hidden": 'true', children: _jsx("td", { className: 'p-0', colSpan: columnCount, style: { height: virtualBottom } }) }, 'virtual-bottom'),
|
|
272
307
|
]
|
|
273
|
-
: visibleRows.map((row) =>
|
|
308
|
+
: 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", {}) : null, isSelectable ? _jsx("td", {}) : null, visibleColumns.map((column) => (_jsx("td", { className: PallasoftClassNameService.merge(densityClassNames[density].cell, alignClassNames[column.align !== null && column.align !== undefined
|
|
274
309
|
? column.align
|
|
275
|
-
: 'left'], 'whitespace-nowrap'), children: column.footer }, column.id)))] }) })) : null] }) }), pageSize && (!grouping || isServerPaginated) ? (_jsx(PsTablePagination, { loadedPageCount: pageCountOf(loadedRowCount), onPageChange: changePage, page: safePage, pageCount: pageCount, pageSize: pageSize, totalRows: rowCount })) : null] })] }));
|
|
310
|
+
: '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, pageSize && (!grouping || isServerPaginated) ? (_jsx(PsTablePagination, { isPartial: isPartial, loadedPageCount: pageCountOf(loadedRowCount), onPageChange: changePage, page: safePage, pageCount: pageCount, pageSize: pageSize, totalRows: rowCount })) : null] })] }));
|
|
276
311
|
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
3
|
-
export const PsTablePagination = ({ className, loadedPageCount, onPageChange, page, pageCount, pageSize, totalRows, }) => {
|
|
3
|
+
export const PsTablePagination = ({ className, isPartial = false, loadedPageCount, onPageChange, page, pageCount, pageSize, totalRows, }) => {
|
|
4
4
|
const firstRow = totalRows === 0 ? 0 : (page - 1) * pageSize + 1;
|
|
5
5
|
const lastRow = Math.min(page * pageSize, totalRows);
|
|
6
6
|
const buttonClassName = 'ps-focus-ring inline-flex min-h-9 items-center rounded-full border border-line px-3.5 text-sm font-semibold text-ink transition-colors disabled:cursor-not-allowed disabled:opacity-40 enabled:hover:border-primary/40';
|
|
7
|
-
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
|
|
7
|
+
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
|
+
? 'No rows'
|
|
9
|
+
: `${firstRow}–${lastRow} of ${totalRows}${isPartial ? '+' : ''}` }), _jsxs("div", { className: 'flex items-center gap-2', children: [_jsx("button", { className: buttonClassName, disabled: page <= 1, onClick: () => onPageChange(page - 1), type: 'button', children: "Previous" }), _jsxs("span", { className: 'hidden text-sm text-ink/65 sm:inline', children: ["Page ", Math.min(page, pageCount), " of ", pageCount] }), _jsx("button", { className: buttonClassName, disabled: page >=
|
|
8
10
|
(loadedPageCount === undefined ? pageCount : Math.min(loadedPageCount, pageCount)), onClick: () => onPageChange(page + 1), type: 'button', children: "Next" })] })] }));
|
|
9
11
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { DndContext } from '@dnd-kit/core';
|
|
3
|
+
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
|
4
|
+
import { PsDragClasses, PsDragListLiftFrames, PsDragOverlay } from '@pallasoft/ps-core-components';
|
|
5
|
+
import { usePsTableReorder } from '../hooks/use-ps-table-reorder';
|
|
6
|
+
import { PsTableSortableRow } from './ps-table-sortable-row';
|
|
7
|
+
export const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
|
|
8
|
+
const { activeId, activeRunId, contextProps, placements } = usePsTableReorder(input);
|
|
9
|
+
const itemById = new Map(input.items.map((item) => [item.id, item]));
|
|
10
|
+
const api = {
|
|
11
|
+
isActive: true,
|
|
12
|
+
Row: PsTableSortableRow,
|
|
13
|
+
rowProps: (rowId) => {
|
|
14
|
+
const item = itemById.get(rowId);
|
|
15
|
+
const placement = placements.get(rowId);
|
|
16
|
+
return {
|
|
17
|
+
handleLabel: item === undefined ? '' : item.handleLabel,
|
|
18
|
+
hasHandle: item !== undefined && input.enabled && placement !== undefined && placement.size > 1,
|
|
19
|
+
isDisabled: activeRunId !== null && (placement === undefined || placement.runId !== activeRunId),
|
|
20
|
+
rowId,
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
run: (runId, ids, runChildren) => (_jsx(SortableContext, { items: ids, strategy: verticalListSortingStrategy, children: runChildren }, runId)),
|
|
24
|
+
};
|
|
25
|
+
return (_jsxs(DndContext, { ...contextProps, children: [children(api), _jsx(PsDragOverlay, { className: PsDragClasses.lifted, frames: PsDragListLiftFrames, children: activeId === null ? null : renderOverlay(activeId) })] }));
|
|
26
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { PsDragGrip } from '@pallasoft/ps-core-components';
|
|
3
|
+
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
4
|
+
export const PsTableReorderHandle = ({ activatorRef, className, label, ...buttonProps }) => (_jsx("button", { ...buttonProps, "aria-label": label, className: PallasoftClassNameService.merge('ps-focus-ring flex h-7 w-6 shrink-0 cursor-grab touch-none items-center justify-center rounded-md text-ink/40 transition-colors hover:bg-mist hover:text-ink active:cursor-grabbing', className), onClick: (event) => event.stopPropagation(), ref: activatorRef, title: label, type: 'button', children: _jsx(PsDragGrip, {}) }));
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
3
|
+
export const PsTableSearchingState = ({ className, label, skeletonRows = 0, }) => (_jsxs("div", { "aria-live": 'polite', className: PallasoftClassNameService.merge('flex flex-col gap-3 px-4 py-4', className), role: 'status', children: [Array.from({ length: skeletonRows }).map((_, index) => (_jsx("div", { className: 'h-4 w-full animate-pulse rounded-full bg-mist' }, index))), _jsx("p", { className: 'text-sm text-ink/55', children: label })] }));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useSortable } from '@dnd-kit/sortable';
|
|
3
|
+
import { CSS } from '@dnd-kit/utilities';
|
|
4
|
+
import { PsDragConfig } from '@pallasoft/ps-core-components';
|
|
5
|
+
import { PsTableReorderHandle } from './ps-table-reorder-handle';
|
|
6
|
+
export const PsTableSortableRow = ({ children, className, handleLabel, hasHandle, isDisabled, measureRef, rowId, style, ...rowProps }) => {
|
|
7
|
+
const { attributes, isDragging, listeners, setActivatorNodeRef, setNodeRef, transform, transition, } = useSortable({ disabled: isDisabled || !hasHandle, id: rowId });
|
|
8
|
+
return (_jsxs("tr", { ...rowProps, className: className, ref: (element) => {
|
|
9
|
+
setNodeRef(element);
|
|
10
|
+
if (measureRef)
|
|
11
|
+
measureRef(element);
|
|
12
|
+
}, style: {
|
|
13
|
+
...style,
|
|
14
|
+
opacity: isDragging ? PsDragConfig.placeholderOpacity : style?.opacity,
|
|
15
|
+
transform: CSS.Translate.toString(transform),
|
|
16
|
+
transition,
|
|
17
|
+
}, children: [_jsx("td", { className: 'w-9 pl-3 pr-0', onClick: (event) => event.stopPropagation(), children: hasHandle ? (_jsx(PsTableReorderHandle, { ...attributes, ...listeners, activatorRef: setActivatorNodeRef, label: handleLabel })) : null }), children] }));
|
|
18
|
+
};
|
|
@@ -4,6 +4,8 @@ export const PsTableConfig = {
|
|
|
4
4
|
groupOpenMs: 280,
|
|
5
5
|
groupRowStaggerMs: 16,
|
|
6
6
|
groupStaggerLimit: 12,
|
|
7
|
+
searchingLabel: 'Searching all rows…',
|
|
8
|
+
searchingSkeletonRows: 3,
|
|
7
9
|
noMatchesDescription: 'Try a different search or clear the filters.',
|
|
8
10
|
noMatchesTitle: 'Nothing matches',
|
|
9
11
|
rowEstimatePx: { compact: 37, normal: 57 },
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { useMemo, useState } from 'react';
|
|
2
|
+
import { closestCenter, } from '@dnd-kit/core';
|
|
3
|
+
import { usePsDragSensors } from '@pallasoft/ps-core-components';
|
|
4
|
+
import { PsTableReorderService } from '../services/ps-table-reorder-service';
|
|
5
|
+
export const usePsTableReorder = ({ announcements: texts, enabled, instructions, items, onReorder, }) => {
|
|
6
|
+
const [activeId, setActiveId] = useState(null);
|
|
7
|
+
const sensors = usePsDragSensors();
|
|
8
|
+
const runs = useMemo(() => enabled
|
|
9
|
+
? PsTableReorderService.runs(items, (item) => item.groupKey, (item) => item.id)
|
|
10
|
+
: [], [enabled, items]);
|
|
11
|
+
const placements = useMemo(() => PsTableReorderService.placements(runs, (item) => item.id), [runs]);
|
|
12
|
+
const activePlacement = activeId === null ? undefined : placements.get(activeId);
|
|
13
|
+
const itemById = useMemo(() => new Map(items.map((item) => [item.id, item])), [items]);
|
|
14
|
+
const labelOf = (id) => {
|
|
15
|
+
const item = itemById.get(id);
|
|
16
|
+
return item === undefined ? id : item.label;
|
|
17
|
+
};
|
|
18
|
+
const positionOf = (id) => {
|
|
19
|
+
const placement = placements.get(id);
|
|
20
|
+
return placement === undefined ? 0 : placement.index + 1;
|
|
21
|
+
};
|
|
22
|
+
const countOf = (id) => {
|
|
23
|
+
const placement = placements.get(id);
|
|
24
|
+
return placement === undefined ? 0 : placement.size;
|
|
25
|
+
};
|
|
26
|
+
const announcements = {
|
|
27
|
+
onDragCancel: ({ active }) => texts.cancelled(labelOf(String(active.id))),
|
|
28
|
+
onDragEnd: ({ active, over }) => over === null
|
|
29
|
+
? texts.cancelled(labelOf(String(active.id)))
|
|
30
|
+
: texts.dropped(labelOf(String(active.id)), positionOf(String(over.id)), countOf(String(over.id))),
|
|
31
|
+
onDragOver: ({ active, over }) => over === null
|
|
32
|
+
? undefined
|
|
33
|
+
: texts.moved(labelOf(String(active.id)), positionOf(String(over.id)), countOf(String(over.id))),
|
|
34
|
+
onDragStart: ({ active }) => texts.lifted(labelOf(String(active.id)), positionOf(String(active.id)), countOf(String(active.id))),
|
|
35
|
+
};
|
|
36
|
+
const handleDragEnd = ({ active, over }) => {
|
|
37
|
+
setActiveId(null);
|
|
38
|
+
if (over === null || active.id === over.id)
|
|
39
|
+
return;
|
|
40
|
+
const placement = placements.get(String(active.id));
|
|
41
|
+
const target = placements.get(String(over.id));
|
|
42
|
+
if (placement === undefined || target === undefined || placement.runId !== target.runId)
|
|
43
|
+
return;
|
|
44
|
+
const run = runs.find((candidate) => candidate.id === placement.runId);
|
|
45
|
+
if (run === undefined)
|
|
46
|
+
return;
|
|
47
|
+
onReorder(run.groupKey, PsTableReorderService.move(run.items.map((item) => item.id), String(active.id), String(over.id)));
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
activeId,
|
|
51
|
+
activeRunId: activePlacement === undefined ? null : activePlacement.runId,
|
|
52
|
+
contextProps: {
|
|
53
|
+
accessibility: { announcements, screenReaderInstructions: { draggable: instructions } },
|
|
54
|
+
collisionDetection: closestCenter,
|
|
55
|
+
modifiers: [PsTableReorderService.lockToVerticalAxis],
|
|
56
|
+
onDragCancel: () => setActiveId(null),
|
|
57
|
+
onDragEnd: handleDragEnd,
|
|
58
|
+
onDragStart: ({ active }) => setActiveId(String(active.id)),
|
|
59
|
+
sensors,
|
|
60
|
+
},
|
|
61
|
+
placements,
|
|
62
|
+
};
|
|
63
|
+
};
|
package/dist/esm/index.js
CHANGED
|
@@ -32,3 +32,8 @@ export * from './components/ps-table-export-button';
|
|
|
32
32
|
export * from './constants/ps-table-download-config';
|
|
33
33
|
export * from './services/ps-table-download-service';
|
|
34
34
|
export * from './services/ps-table-scroll-anchor-service';
|
|
35
|
+
export * from './hooks/use-ps-table-page';
|
|
36
|
+
export * from './interfaces/ps-table-page-state';
|
|
37
|
+
export * from './interfaces/table-reorder';
|
|
38
|
+
export * from './interfaces/table-reorder-announcements';
|
|
39
|
+
export * from './services/ps-table-reorder-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 {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export class PsTableReorderService {
|
|
2
|
+
static runs(items, getKey, getId) {
|
|
3
|
+
const runs = [];
|
|
4
|
+
for (const item of items) {
|
|
5
|
+
const groupKey = getKey(item);
|
|
6
|
+
const last = runs[runs.length - 1];
|
|
7
|
+
if (last !== undefined && last.groupKey === groupKey)
|
|
8
|
+
last.items.push(item);
|
|
9
|
+
else
|
|
10
|
+
runs.push({ groupKey, id: `${groupKey}:${getId(item)}`, items: [item] });
|
|
11
|
+
}
|
|
12
|
+
return runs;
|
|
13
|
+
}
|
|
14
|
+
static placements(runs, getId) {
|
|
15
|
+
const placements = new Map();
|
|
16
|
+
for (const run of runs)
|
|
17
|
+
run.items.forEach((item, index) => placements.set(getId(item), {
|
|
18
|
+
groupKey: run.groupKey,
|
|
19
|
+
index,
|
|
20
|
+
runId: run.id,
|
|
21
|
+
size: run.items.length,
|
|
22
|
+
}));
|
|
23
|
+
return placements;
|
|
24
|
+
}
|
|
25
|
+
static move(ids, activeId, overId) {
|
|
26
|
+
const from = ids.indexOf(activeId);
|
|
27
|
+
const to = ids.indexOf(overId);
|
|
28
|
+
if (from < 0 || to < 0 || from === to)
|
|
29
|
+
return ids;
|
|
30
|
+
const next = [...ids];
|
|
31
|
+
const [moved] = next.splice(from, 1);
|
|
32
|
+
next.splice(to, 0, moved);
|
|
33
|
+
return next;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
PsTableReorderService.plain = {
|
|
37
|
+
Row: 'tr',
|
|
38
|
+
isActive: false,
|
|
39
|
+
rowProps: (rowId) => ({ handleLabel: '', hasHandle: false, isDisabled: true, rowId }),
|
|
40
|
+
run: (_runId, _ids, children) => children,
|
|
41
|
+
};
|
|
42
|
+
PsTableReorderService.lockToVerticalAxis = ({ transform }) => ({ ...transform, x: 0 });
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePsTablePage = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const usePsTablePage = () => {
|
|
6
|
+
const [page, setPage] = (0, react_1.useState)(1);
|
|
7
|
+
return { onPageChange: setPage, page };
|
|
8
|
+
};
|
|
9
|
+
exports.usePsTablePage = usePsTablePage;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type DndContextProps } from '@dnd-kit/core';
|
|
2
|
+
import type { PsTableReorderAnnouncements } from '../interfaces/table-reorder-announcements';
|
|
3
|
+
import type { PsTableReorderItem } from '../interfaces/table-reorder-item';
|
|
4
|
+
import type { PsTableReorderPlacement } from '../interfaces/table-reorder-placement';
|
|
5
|
+
export interface UsePsTableReorderInput {
|
|
6
|
+
announcements: PsTableReorderAnnouncements;
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
instructions: string;
|
|
9
|
+
items: PsTableReorderItem[];
|
|
10
|
+
onReorder: (groupKey: string, orderedIds: string[]) => void;
|
|
11
|
+
}
|
|
12
|
+
export interface PsTableReorderState {
|
|
13
|
+
activeId: string | null;
|
|
14
|
+
activeRunId: string | null;
|
|
15
|
+
contextProps: Omit<DndContextProps, 'children'>;
|
|
16
|
+
placements: Map<string, PsTableReorderPlacement>;
|
|
17
|
+
}
|
|
18
|
+
export declare const usePsTableReorder: ({ announcements: texts, enabled, instructions, items, onReorder, }: UsePsTableReorderInput) => PsTableReorderState;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePsTableReorder = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const core_1 = require("@dnd-kit/core");
|
|
6
|
+
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
7
|
+
const ps_table_reorder_service_1 = require("../services/ps-table-reorder-service");
|
|
8
|
+
const usePsTableReorder = ({ announcements: texts, enabled, instructions, items, onReorder, }) => {
|
|
9
|
+
const [activeId, setActiveId] = (0, react_1.useState)(null);
|
|
10
|
+
const sensors = (0, ps_core_components_1.usePsDragSensors)();
|
|
11
|
+
const runs = (0, react_1.useMemo)(() => enabled
|
|
12
|
+
? ps_table_reorder_service_1.PsTableReorderService.runs(items, (item) => item.groupKey, (item) => item.id)
|
|
13
|
+
: [], [enabled, items]);
|
|
14
|
+
const placements = (0, react_1.useMemo)(() => ps_table_reorder_service_1.PsTableReorderService.placements(runs, (item) => item.id), [runs]);
|
|
15
|
+
const activePlacement = activeId === null ? undefined : placements.get(activeId);
|
|
16
|
+
const itemById = (0, react_1.useMemo)(() => new Map(items.map((item) => [item.id, item])), [items]);
|
|
17
|
+
const labelOf = (id) => {
|
|
18
|
+
const item = itemById.get(id);
|
|
19
|
+
return item === undefined ? id : item.label;
|
|
20
|
+
};
|
|
21
|
+
const positionOf = (id) => {
|
|
22
|
+
const placement = placements.get(id);
|
|
23
|
+
return placement === undefined ? 0 : placement.index + 1;
|
|
24
|
+
};
|
|
25
|
+
const countOf = (id) => {
|
|
26
|
+
const placement = placements.get(id);
|
|
27
|
+
return placement === undefined ? 0 : placement.size;
|
|
28
|
+
};
|
|
29
|
+
const announcements = {
|
|
30
|
+
onDragCancel: ({ active }) => texts.cancelled(labelOf(String(active.id))),
|
|
31
|
+
onDragEnd: ({ active, over }) => over === null
|
|
32
|
+
? texts.cancelled(labelOf(String(active.id)))
|
|
33
|
+
: texts.dropped(labelOf(String(active.id)), positionOf(String(over.id)), countOf(String(over.id))),
|
|
34
|
+
onDragOver: ({ active, over }) => over === null
|
|
35
|
+
? undefined
|
|
36
|
+
: texts.moved(labelOf(String(active.id)), positionOf(String(over.id)), countOf(String(over.id))),
|
|
37
|
+
onDragStart: ({ active }) => texts.lifted(labelOf(String(active.id)), positionOf(String(active.id)), countOf(String(active.id))),
|
|
38
|
+
};
|
|
39
|
+
const handleDragEnd = ({ active, over }) => {
|
|
40
|
+
setActiveId(null);
|
|
41
|
+
if (over === null || active.id === over.id)
|
|
42
|
+
return;
|
|
43
|
+
const placement = placements.get(String(active.id));
|
|
44
|
+
const target = placements.get(String(over.id));
|
|
45
|
+
if (placement === undefined || target === undefined || placement.runId !== target.runId)
|
|
46
|
+
return;
|
|
47
|
+
const run = runs.find((candidate) => candidate.id === placement.runId);
|
|
48
|
+
if (run === undefined)
|
|
49
|
+
return;
|
|
50
|
+
onReorder(run.groupKey, ps_table_reorder_service_1.PsTableReorderService.move(run.items.map((item) => item.id), String(active.id), String(over.id)));
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
activeId,
|
|
54
|
+
activeRunId: activePlacement === undefined ? null : activePlacement.runId,
|
|
55
|
+
contextProps: {
|
|
56
|
+
accessibility: { announcements, screenReaderInstructions: { draggable: instructions } },
|
|
57
|
+
collisionDetection: core_1.closestCenter,
|
|
58
|
+
modifiers: [ps_table_reorder_service_1.PsTableReorderService.lockToVerticalAxis],
|
|
59
|
+
onDragCancel: () => setActiveId(null),
|
|
60
|
+
onDragEnd: handleDragEnd,
|
|
61
|
+
onDragStart: ({ active }) => setActiveId(String(active.id)),
|
|
62
|
+
sensors,
|
|
63
|
+
},
|
|
64
|
+
placements,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
exports.usePsTableReorder = usePsTableReorder;
|
package/dist/index.d.ts
CHANGED
|
@@ -32,3 +32,8 @@ export * from './components/ps-table-export-button';
|
|
|
32
32
|
export * from './constants/ps-table-download-config';
|
|
33
33
|
export * from './services/ps-table-download-service';
|
|
34
34
|
export * from './services/ps-table-scroll-anchor-service';
|
|
35
|
+
export * from './hooks/use-ps-table-page';
|
|
36
|
+
export * from './interfaces/ps-table-page-state';
|
|
37
|
+
export * from './interfaces/table-reorder';
|
|
38
|
+
export * from './interfaces/table-reorder-announcements';
|
|
39
|
+
export * from './services/ps-table-reorder-service';
|
package/dist/index.js
CHANGED
|
@@ -48,3 +48,8 @@ __exportStar(require("./components/ps-table-export-button"), exports);
|
|
|
48
48
|
__exportStar(require("./constants/ps-table-download-config"), exports);
|
|
49
49
|
__exportStar(require("./services/ps-table-download-service"), exports);
|
|
50
50
|
__exportStar(require("./services/ps-table-scroll-anchor-service"), exports);
|
|
51
|
+
__exportStar(require("./hooks/use-ps-table-page"), exports);
|
|
52
|
+
__exportStar(require("./interfaces/ps-table-page-state"), exports);
|
|
53
|
+
__exportStar(require("./interfaces/table-reorder"), exports);
|
|
54
|
+
__exportStar(require("./interfaces/table-reorder-announcements"), exports);
|
|
55
|
+
__exportStar(require("./services/ps-table-reorder-service"), exports);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface PsTableReorderAnnouncements {
|
|
2
|
+
cancelled: (rowLabel: string) => string;
|
|
3
|
+
dropped: (rowLabel: string, position: number, count: number) => string;
|
|
4
|
+
lifted: (rowLabel: string, position: number, count: number) => string;
|
|
5
|
+
moved: (rowLabel: string, position: number, count: number) => string;
|
|
6
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ElementType, ReactNode } from 'react';
|
|
2
|
+
import type { PsTableReorderRowProps } from './table-reorder-row-props';
|
|
3
|
+
export interface PsTableReorderApi {
|
|
4
|
+
isActive: boolean;
|
|
5
|
+
Row: ElementType;
|
|
6
|
+
rowProps: (rowId: string) => PsTableReorderRowProps;
|
|
7
|
+
run: (runId: string, ids: string[], children: ReactNode) => ReactNode;
|
|
8
|
+
}
|