@pallasoft/ps-table-components 1.21.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.
Files changed (55) hide show
  1. package/dist/components/ps-data-table.d.ts +5 -1
  2. package/dist/components/ps-data-table.js +80 -12
  3. package/dist/components/ps-table-pagination.d.ts +2 -1
  4. package/dist/components/ps-table-pagination.js +4 -2
  5. package/dist/components/ps-table-reorder-frame.d.ts +8 -0
  6. package/dist/components/ps-table-reorder-frame.js +30 -0
  7. package/dist/components/ps-table-reorder-handle.d.ts +6 -0
  8. package/dist/components/ps-table-reorder-handle.js +8 -0
  9. package/dist/components/ps-table-searching-state.d.ts +6 -0
  10. package/dist/components/ps-table-searching-state.js +7 -0
  11. package/dist/components/ps-table-sortable-row.d.ts +9 -0
  12. package/dist/components/ps-table-sortable-row.js +22 -0
  13. package/dist/constants/table-config.d.ts +2 -0
  14. package/dist/constants/table-config.js +2 -0
  15. package/dist/esm/components/ps-data-table.js +49 -14
  16. package/dist/esm/components/ps-table-pagination.js +4 -2
  17. package/dist/esm/components/ps-table-reorder-frame.js +26 -0
  18. package/dist/esm/components/ps-table-reorder-handle.js +4 -0
  19. package/dist/esm/components/ps-table-searching-state.js +3 -0
  20. package/dist/esm/components/ps-table-sortable-row.js +18 -0
  21. package/dist/esm/constants/table-config.js +2 -0
  22. package/dist/esm/hooks/use-ps-table-reorder.js +63 -0
  23. package/dist/esm/index.js +3 -0
  24. package/dist/esm/interfaces/table-reorder-announcements.js +1 -0
  25. package/dist/esm/interfaces/table-reorder-api.js +1 -0
  26. package/dist/esm/interfaces/table-reorder-item.js +1 -0
  27. package/dist/esm/interfaces/table-reorder-placement.js +1 -0
  28. package/dist/esm/interfaces/table-reorder-row-props.js +1 -0
  29. package/dist/esm/interfaces/table-reorder-run.js +1 -0
  30. package/dist/esm/interfaces/table-reorder.js +1 -0
  31. package/dist/esm/interfaces/table-row-entry.js +1 -0
  32. package/dist/esm/services/ps-table-reorder-service.js +42 -0
  33. package/dist/hooks/use-ps-table-reorder.d.ts +18 -0
  34. package/dist/hooks/use-ps-table-reorder.js +67 -0
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.js +3 -0
  37. package/dist/interfaces/table-reorder-announcements.d.ts +6 -0
  38. package/dist/interfaces/table-reorder-announcements.js +2 -0
  39. package/dist/interfaces/table-reorder-api.d.ts +8 -0
  40. package/dist/interfaces/table-reorder-api.js +2 -0
  41. package/dist/interfaces/table-reorder-item.d.ts +6 -0
  42. package/dist/interfaces/table-reorder-item.js +2 -0
  43. package/dist/interfaces/table-reorder-placement.d.ts +6 -0
  44. package/dist/interfaces/table-reorder-placement.js +2 -0
  45. package/dist/interfaces/table-reorder-row-props.d.ts +6 -0
  46. package/dist/interfaces/table-reorder-row-props.js +2 -0
  47. package/dist/interfaces/table-reorder-run.d.ts +5 -0
  48. package/dist/interfaces/table-reorder-run.js +2 -0
  49. package/dist/interfaces/table-reorder.d.ts +12 -0
  50. package/dist/interfaces/table-reorder.js +2 -0
  51. package/dist/interfaces/table-row-entry.d.ts +7 -0
  52. package/dist/interfaces/table-row-entry.js +2 -0
  53. package/dist/services/ps-table-reorder-service.d.ts +11 -0
  54. package/dist/services/ps-table-reorder-service.js +46 -0
  55. package/package.json +5 -2
@@ -2,6 +2,7 @@ import { type ReactNode } from 'react';
2
2
  import type { PsTableColumn } from '../interfaces/table-column';
3
3
  import type { PsTableGrouping } from '../interfaces/table-grouping';
4
4
  import type { PsTableQuery } from '../interfaces/table-query';
5
+ import type { PsTableReorder } from '../interfaces/table-reorder';
5
6
  import type { PsTableRowAction } from '../interfaces/table-row-action';
6
7
  import type { PsTableSort } from '../interfaces/table-sort';
7
8
  export interface PsDataTableProps<RowData> {
@@ -18,6 +19,7 @@ export interface PsDataTableProps<RowData> {
18
19
  getSearchText?: (row: RowData) => string;
19
20
  grouping?: PsTableGrouping<RowData>;
20
21
  isLoading?: boolean;
22
+ isPending?: boolean;
21
23
  layout?: 'auto' | 'fixed';
22
24
  onPageChange?: (page: number) => void;
23
25
  onQueryChange?: (query: PsTableQuery) => void;
@@ -27,9 +29,11 @@ export interface PsDataTableProps<RowData> {
27
29
  pageSize?: number;
28
30
  query?: PsTableQuery;
29
31
  renderExpandedRow?: (row: RowData) => ReactNode;
32
+ reorder?: PsTableReorder<RowData>;
30
33
  rows: RowData[];
34
+ searchingLabel?: string;
31
35
  searchPlaceholder?: string;
32
36
  toolbarActions?: ReactNode;
33
37
  totalRows?: number;
34
38
  }
35
- export declare const PsDataTable: <RowData>({ caption, className, columns, defaultSort, density, expandedRowIds, mobileLayout, getBulkActions, getRowId, getRowLabel, getSearchText, grouping, isLoading, layout, onPageChange, onQueryChange, onRowClick, onRowHover, page, pageSize, query, renderExpandedRow, rows, searchPlaceholder, toolbarActions, totalRows, }: PsDataTableProps<RowData>) => import("react").JSX.Element;
39
+ 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, query, renderExpandedRow, reorder, rows, searchingLabel, searchPlaceholder, toolbarActions, totalRows, }: PsDataTableProps<RowData>) => import("react").JSX.Element;
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.PsDataTable = void 0;
4
37
  const jsx_runtime_1 = require("react/jsx-runtime");
@@ -16,14 +49,17 @@ const ps_table_cell_tooltip_service_1 = require("../services/ps-table-cell-toolt
16
49
  const ps_table_column_size_service_1 = require("../services/ps-table-column-size-service");
17
50
  const ps_table_group_service_1 = require("../services/ps-table-group-service");
18
51
  const ps_table_query_service_1 = require("../services/ps-table-query-service");
52
+ const ps_table_reorder_service_1 = require("../services/ps-table-reorder-service");
19
53
  const ps_table_scroll_service_1 = require("../services/ps-table-scroll-service");
20
54
  const ps_table_bulk_bar_1 = require("./ps-table-bulk-bar");
21
55
  const ps_table_card_list_1 = require("./ps-table-card-list");
22
56
  const ps_table_column_picker_1 = require("./ps-table-column-picker");
23
57
  const ps_table_group_header_1 = require("./ps-table-group-header");
24
58
  const ps_table_pagination_1 = require("./ps-table-pagination");
59
+ const ps_table_searching_state_1 = require("./ps-table-searching-state");
25
60
  const ps_table_sort_header_1 = require("./ps-table-sort-header");
26
61
  const ps_table_toolbar_1 = require("./ps-table-toolbar");
62
+ const LazyReorderFrame = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./ps-table-reorder-frame'))).then((module) => ({ default: module.PsTableReorderFrame })));
27
63
  const densityClassNames = {
28
64
  compact: {
29
65
  cell: 'px-3 py-1.5 text-[0.8125rem] leading-5',
@@ -44,7 +80,7 @@ const alignClassNames = {
44
80
  left: 'text-left',
45
81
  right: 'text-right tabular-nums',
46
82
  };
47
- 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 = table_config_1.PsTableConfig.searchPlaceholder, toolbarActions, totalRows, }) => {
83
+ 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 = table_config_1.PsTableConfig.searchingLabel, searchPlaceholder = table_config_1.PsTableConfig.searchPlaceholder, toolbarActions, totalRows, }) => {
48
84
  const containerRef = (0, react_1.useRef)(null);
49
85
  const [uncontrolledPage, setUncontrolledPage] = (0, react_1.useState)(1);
50
86
  const [uncontrolledQuery, setUncontrolledQuery] = (0, react_1.useState)(() => ps_table_query_service_1.PsTableQueryService.empty(defaultSort ? defaultSort : null));
@@ -68,7 +104,7 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
68
104
  const hasFilters = columns.some((column) => column.filter) && isQueryable;
69
105
  const hasToolbar = isSearchable || hasFilters || Boolean(toolbarActions);
70
106
  const isSelectable = Boolean(getBulkActions);
71
- const columnCount = visibleColumns.length + (isSelectable ? 1 : 0);
107
+ const columnCount = visibleColumns.length + (isSelectable ? 1 : 0) + (reorder ? 1 : 0);
72
108
  const deferredSearch = (0, react_1.useDeferredValue)(search);
73
109
  const queriedRows = (0, react_1.useMemo)(() => isServerPaginated
74
110
  ? rows
@@ -76,6 +112,8 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
76
112
  const streamStatus = (0, use_ps_table_stream_status_1.usePsTableStreamStatus)();
77
113
  const isQueried = search.trim() !== '' ||
78
114
  Object.values(filters).some((value) => ps_table_query_service_1.PsTableQueryService.isActive(value));
115
+ const isPartial = streamStatus !== null && !isServerPaginated && isQueried;
116
+ const isSearching = isPartial || isPending;
79
117
  const currentPage = page !== null && page !== undefined ? page : uncontrolledPage;
80
118
  const loadedRowCount = isServerPaginated ? totalRows : queriedRows.length;
81
119
  const rowCount = streamStatus !== null && !isServerPaginated && !isQueried
@@ -136,6 +174,23 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
136
174
  safePage,
137
175
  toggledGroupKeys,
138
176
  ]);
177
+ const reorderItems = (0, react_1.useMemo)(() => reorder
178
+ ? visibleRows.map((row) => {
179
+ const rowId = getRowId(row);
180
+ const label = getRowLabel ? getRowLabel(row) : rowId;
181
+ return {
182
+ groupKey: reorder.groupKey(row),
183
+ handleLabel: reorder.getHandleLabel(label),
184
+ id: rowId,
185
+ label,
186
+ };
187
+ })
188
+ : [], [getRowId, getRowLabel, reorder, visibleRows]);
189
+ const renderOverlayRow = (rowId) => {
190
+ const row = visibleRows.find((candidate) => getRowId(candidate) === rowId);
191
+ return row === undefined ? null : ((0, jsx_runtime_1.jsx)("table", { className: 'w-full table-fixed border-collapse bg-surface text-left', children: (0, jsx_runtime_1.jsx)("tbody", { children: renderRow(ps_table_reorder_service_1.PsTableReorderService.plain, row) }) }));
192
+ };
193
+ const renderReorder = (children) => reorder === undefined ? (children(ps_table_reorder_service_1.PsTableReorderService.plain)) : ((0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: children(ps_table_reorder_service_1.PsTableReorderService.plain), children: (0, jsx_runtime_1.jsx)(LazyReorderFrame, { announcements: reorder.announcements, enabled: reorder.enabled, instructions: reorder.instructions, items: reorderItems, onReorder: reorder.onReorder, renderOverlay: renderOverlayRow, children: children }) }));
139
194
  const changePage = (next) => {
140
195
  if (onPageChange) {
141
196
  onPageChange(next);
@@ -213,11 +268,15 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
213
268
  }, [shouldVirtualize, visibleRows.length]);
214
269
  const toggleRow = (rowId) => setSelectedIds((current) => current.includes(rowId) ? current.filter((id) => id !== rowId) : [...current, rowId]);
215
270
  const renderCardList = (cardRows, emptyContent) => ((0, jsx_runtime_1.jsx)(ps_table_card_list_1.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 }));
216
- const renderRow = (row, motion, virtualItem) => {
271
+ const renderRow = (api, row, motion, virtualItem) => {
217
272
  const rowId = getRowId(row);
218
273
  const isExpanded = expandedRowIds ? expandedRowIds.includes(rowId) : false;
219
274
  const rowLabel = getRowLabel ? getRowLabel(row) : rowId;
220
- return ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("tr", { "data-index": virtualItem ? virtualItem.index : undefined, ref: virtualItem ? virtualizer.measureElement : undefined, "aria-expanded": renderExpandedRow ? isExpanded : undefined, "aria-label": onRowClick ? rowLabel : undefined, className: ps_design_system_1.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
275
+ const Row = api.Row;
276
+ const rowExtras = api.isActive
277
+ ? { ...api.rowProps(rowId), measureRef: virtualItem ? virtualizer.measureElement : undefined }
278
+ : { ref: virtualItem ? virtualizer.measureElement : undefined };
279
+ return ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(Row, { ...rowExtras, "data-index": virtualItem ? virtualItem.index : undefined, "aria-expanded": renderExpandedRow ? isExpanded : undefined, "aria-label": onRowClick ? rowLabel : undefined, className: ps_design_system_1.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
221
280
  ? (event) => {
222
281
  if (event.target !== event.currentTarget)
223
282
  return;
@@ -228,11 +287,14 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
228
287
  }
229
288
  : undefined, style: motion && motion.delayMs !== undefined
230
289
  ? { animationDelay: `${motion.delayMs}ms` }
231
- : undefined, tabIndex: onRowClick ? 0 : undefined, children: [isSelectable ? ((0, jsx_runtime_1.jsx)("td", { className: 'w-10 pl-4 pr-0', onClick: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: (0, jsx_runtime_1.jsx)(ps_input_components_1.PsCheckbox, { isSelected: selectedIdSet.has(rowId), label: `Select ${rowLabel}`, onChange: () => toggleRow(rowId) }) })) : null, visibleColumns.map((column) => ((0, jsx_runtime_1.jsx)("td", { className: ps_design_system_1.PallasoftClassNameService.merge(densityClassNames[density].cell, alignClassNames[column.align !== null && column.align !== undefined ? column.align : 'left'], column.wrap ? '' : 'whitespace-nowrap', (layout === 'fixed' && !column.wrap) ||
290
+ : undefined, tabIndex: onRowClick ? 0 : undefined, children: [reorder && !api.isActive ? (0, jsx_runtime_1.jsx)("td", { className: 'w-9 pl-3 pr-0' }) : null, isSelectable ? ((0, jsx_runtime_1.jsx)("td", { className: 'w-10 pl-4 pr-0', onClick: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: (0, jsx_runtime_1.jsx)(ps_input_components_1.PsCheckbox, { isSelected: selectedIdSet.has(rowId), label: `Select ${rowLabel}`, onChange: () => toggleRow(rowId) }) })) : null, visibleColumns.map((column) => ((0, jsx_runtime_1.jsx)("td", { className: ps_design_system_1.PallasoftClassNameService.merge(densityClassNames[density].cell, alignClassNames[column.align !== null && column.align !== undefined ? column.align : 'left'], column.wrap ? '' : 'whitespace-nowrap', (layout === 'fixed' && !column.wrap) ||
232
291
  ps_table_column_size_service_1.PsTableColumnSizeService.maxWidthOf(column) !== undefined
233
292
  ? 'truncate'
234
293
  : '', 'text-sm text-ink'), style: columnStyle(column), title: ps_table_cell_tooltip_service_1.PsTableCellTooltipService.forCell(column, row), children: column.render(row) }, column.id)))] }), renderExpandedRow ? ((0, jsx_runtime_1.jsx)("tr", { "aria-hidden": !isExpanded, className: isExpanded ? 'border-t-0' : 'border-0', children: (0, jsx_runtime_1.jsx)("td", { className: 'p-0', colSpan: columnCount, children: (0, jsx_runtime_1.jsx)(ps_motion_components_1.PsCollapse, { isOpen: isExpanded, children: (0, jsx_runtime_1.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));
235
294
  };
295
+ const renderEntries = (api, entries) => reorder
296
+ ? ps_table_reorder_service_1.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))))
297
+ : entries.map((entry) => renderRow(api, entry.row, entry.motion, entry.virtualItem));
236
298
  if (isLoading)
237
299
  return ((0, jsx_runtime_1.jsx)(ps_core_components_1.PsSkeletonTable, { className: className, columns: visibleColumns.length, hasToolbar: hasToolbar, rows: table_config_1.PsTableConfig.skeletonRows }));
238
300
  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: [isSelectable && selectedRows.length > 0 && getBulkActions ? ((0, jsx_runtime_1.jsx)(ps_table_bulk_bar_1.PsTableBulkBar, { actions: getBulkActions(selectedRows), onClear: () => setSelectedIds([]), selectedCount: 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
@@ -243,14 +305,14 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
243
305
  }, onSearchChange: (next) => {
244
306
  updateQuery({ search: next });
245
307
  resetPage();
246
- }, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'min-w-0 overflow-hidden rounded-[1.25rem] border border-line bg-surface', children: [mobileLayout === 'cards' ? ((0, jsx_runtime_1.jsxs)("div", { className: 'sm:hidden', children: [grouping
308
+ }, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'min-w-0 overflow-hidden rounded-[1.25rem] border border-line bg-surface', children: [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
247
309
  ? groups.map((group, groupIndex) => {
248
310
  const isExpanded = isGroupExpanded(group.key, groupIndex);
249
311
  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
250
312
  ? grouping.renderGroupSummary(group.rows)
251
313
  : 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}`));
252
314
  })
253
- : renderCardList(shouldVirtualize ? visibleRows.slice(0, cards.count) : visibleRows, 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-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 ? ((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('ps-scrollbar overflow-x-auto overflow-y-hidden', mobileLayout === 'cards' ? 'hidden sm:block' : ''), children: (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: 'bg-mist/70 text-[0.68rem] font-semibold uppercase tracking-[0.18em] text-ink/45', children: (0, jsx_runtime_1.jsxs)("tr", { children: [isSelectable ? ((0, jsx_runtime_1.jsx)("th", { className: 'w-10 pl-4 pr-0', 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) => {
315
+ : 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-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 ? ((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('ps-scrollbar overflow-x-auto overflow-y-hidden', mobileLayout === 'cards' ? 'hidden sm:block' : ''), 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: 'bg-mist/70 text-[0.68rem] font-semibold uppercase tracking-[0.18em] text-ink/45', children: (0, jsx_runtime_1.jsxs)("tr", { children: [reorder ? ((0, jsx_runtime_1.jsx)("th", { className: 'w-9 pl-3 pr-0', 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: 'w-10 pl-4 pr-0', 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) => {
254
316
  const align = column.align !== null && column.align !== undefined ? column.align : 'left';
255
317
  const isSorted = sort !== null && sort.columnId === column.id;
256
318
  return ((0, jsx_runtime_1.jsx)("th", { "aria-sort": isSorted
@@ -258,23 +320,29 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
258
320
  : column.sortValue && isQueryable
259
321
  ? 'none'
260
322
  : undefined, className: ps_design_system_1.PallasoftClassNameService.merge(densityClassNames[density].head, alignClassNames[align], 'whitespace-nowrap'), scope: 'col', style: columnStyle(column), children: column.sortValue && isQueryable ? ((0, jsx_runtime_1.jsx)(ps_table_sort_header_1.PsTableSortHeader, { align: align, columnId: column.id, label: column.label, onSort: updateSort, sort: sort })) : (column.label) }, column.id));
261
- })] }) }), (0, jsx_runtime_1.jsxs)("tbody", { className: 'divide-y divide-line', ref: bodyRef, children: [visibleRows.length === 0 && isQueried ? ((0, jsx_runtime_1.jsx)("tr", { children: (0, jsx_runtime_1.jsxs)("td", { className: 'px-4 py-12 text-center', colSpan: columnCount, 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)("p", { className: 'mt-1 text-sm text-ink/55', children: table_config_1.PsTableConfig.noMatchesDescription }), (0, jsx_runtime_1.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
323
+ })] }) }), (0, jsx_runtime_1.jsxs)("tbody", { className: 'divide-y divide-line', ref: bodyRef, children: [visibleRows.length === 0 && isSearching ? ((0, jsx_runtime_1.jsx)("tr", { children: (0, jsx_runtime_1.jsx)("td", { className: 'p-0', colSpan: columnCount, children: (0, jsx_runtime_1.jsx)(ps_table_searching_state_1.PsTableSearchingState, { label: searchingLabel, skeletonRows: table_config_1.PsTableConfig.searchingSkeletonRows }) }) })) : null, visibleRows.length === 0 && isQueried && !isSearching ? ((0, jsx_runtime_1.jsx)("tr", { children: (0, jsx_runtime_1.jsxs)("td", { className: 'px-4 py-12 text-center', colSpan: columnCount, 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)("p", { className: 'mt-1 text-sm text-ink/55', children: table_config_1.PsTableConfig.noMatchesDescription }), (0, jsx_runtime_1.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
262
324
  ? groups.map((group, groupIndex) => {
263
325
  const isExpanded = isGroupExpanded(group.key, groupIndex);
264
326
  return ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("tr", { children: (0, jsx_runtime_1.jsx)("td", { className: 'p-0', colSpan: columnCount, 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
265
327
  ? grouping.renderGroupSummary(group.rows)
266
328
  : undefined }) }) }), isExpanded
267
- ? group.rows.map((row, rowIndex) => renderRow(row, groupRowMotion(group.key, rowIndex)))
329
+ ? renderEntries(api, group.rows.map((row, rowIndex) => ({
330
+ motion: groupRowMotion(group.key, rowIndex),
331
+ row,
332
+ })))
268
333
  : null] }, `group-${group.key}`));
269
334
  })
270
335
  : shouldVirtualize
271
336
  ? [
272
337
  (0, jsx_runtime_1.jsx)("tr", { "aria-hidden": 'true', children: (0, jsx_runtime_1.jsx)("td", { className: 'p-0', colSpan: columnCount, style: { height: virtualTop } }) }, 'virtual-top'),
273
- ...virtualItems.map((item) => renderRow(visibleRows[item.index], undefined, item)),
338
+ ...renderEntries(api, virtualItems.map((item) => ({
339
+ row: visibleRows[item.index],
340
+ virtualItem: item,
341
+ }))),
274
342
  (0, jsx_runtime_1.jsx)("tr", { "aria-hidden": 'true', children: (0, jsx_runtime_1.jsx)("td", { className: 'p-0', colSpan: columnCount, style: { height: virtualBottom } }) }, 'virtual-bottom'),
275
343
  ]
276
- : visibleRows.map((row) => renderRow(row))] }), hasFooter ? ((0, jsx_runtime_1.jsx)("tfoot", { className: 'border-t-2 border-line bg-mist/50 text-sm font-semibold text-ink', children: (0, jsx_runtime_1.jsxs)("tr", { children: [isSelectable ? (0, jsx_runtime_1.jsx)("td", {}) : null, visibleColumns.map((column) => ((0, jsx_runtime_1.jsx)("td", { className: ps_design_system_1.PallasoftClassNameService.merge(densityClassNames[density].cell, alignClassNames[column.align !== null && column.align !== undefined
344
+ : renderEntries(api, visibleRows.map((row) => ({ row })))] }), hasFooter ? ((0, jsx_runtime_1.jsx)("tfoot", { className: 'border-t-2 border-line bg-mist/50 text-sm font-semibold text-ink', children: (0, jsx_runtime_1.jsxs)("tr", { children: [reorder ? (0, jsx_runtime_1.jsx)("td", {}) : null, isSelectable ? (0, jsx_runtime_1.jsx)("td", {}) : null, visibleColumns.map((column) => ((0, jsx_runtime_1.jsx)("td", { className: ps_design_system_1.PallasoftClassNameService.merge(densityClassNames[density].cell, alignClassNames[column.align !== null && column.align !== undefined
277
345
  ? column.align
278
- : 'left'], 'whitespace-nowrap'), children: column.footer }, column.id)))] }) })) : null] }) }), pageSize && (!grouping || isServerPaginated) ? ((0, jsx_runtime_1.jsx)(ps_table_pagination_1.PsTablePagination, { loadedPageCount: pageCountOf(loadedRowCount), onPageChange: changePage, page: safePage, pageCount: pageCount, pageSize: pageSize, totalRows: rowCount })) : null] })] }));
346
+ : 'left'], 'whitespace-nowrap'), children: column.footer }, column.id)))] }) })) : null] }))) }), visibleRows.length > 0 && isSearching ? ((0, jsx_runtime_1.jsx)(ps_table_searching_state_1.PsTableSearchingState, { className: 'border-t border-line py-3', label: searchingLabel })) : null, pageSize && (!grouping || isServerPaginated) ? ((0, jsx_runtime_1.jsx)(ps_table_pagination_1.PsTablePagination, { isPartial: isPartial, loadedPageCount: pageCountOf(loadedRowCount), onPageChange: changePage, page: safePage, pageCount: pageCount, pageSize: pageSize, totalRows: rowCount })) : null] })] }));
279
347
  };
280
348
  exports.PsDataTable = PsDataTable;
@@ -1,5 +1,6 @@
1
1
  export interface PsTablePaginationProps {
2
2
  className?: string;
3
+ isPartial?: boolean;
3
4
  loadedPageCount?: number;
4
5
  onPageChange: (page: number) => void;
5
6
  page: number;
@@ -7,4 +8,4 @@ export interface PsTablePaginationProps {
7
8
  pageSize: number;
8
9
  totalRows: number;
9
10
  }
10
- export declare const PsTablePagination: ({ className, loadedPageCount, onPageChange, page, pageCount, pageSize, totalRows, }: PsTablePaginationProps) => import("react").JSX.Element;
11
+ export declare const PsTablePagination: ({ className, isPartial, loadedPageCount, onPageChange, page, pageCount, pageSize, totalRows, }: PsTablePaginationProps) => import("react").JSX.Element;
@@ -3,11 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  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
- const PsTablePagination = ({ className, loadedPageCount, onPageChange, page, pageCount, pageSize, totalRows, }) => {
6
+ const PsTablePagination = ({ className, isPartial = false, loadedPageCount, onPageChange, page, pageCount, pageSize, totalRows, }) => {
7
7
  const firstRow = totalRows === 0 ? 0 : (page - 1) * pageSize + 1;
8
8
  const lastRow = Math.min(page * pageSize, totalRows);
9
9
  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';
10
- 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-3 border-t border-line px-4 py-3', className), children: [(0, jsx_runtime_1.jsx)("p", { className: 'text-sm text-ink/55', children: totalRows === 0 ? 'No rows' : `${firstRow}–${lastRow} of ${totalRows}` }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex items-center gap-2', children: [(0, jsx_runtime_1.jsx)("button", { className: buttonClassName, disabled: page <= 1, onClick: () => onPageChange(page - 1), type: 'button', children: "Previous" }), (0, jsx_runtime_1.jsxs)("span", { className: 'hidden text-sm text-ink/65 sm:inline', children: ["Page ", Math.min(page, pageCount), " of ", pageCount] }), (0, jsx_runtime_1.jsx)("button", { className: buttonClassName, disabled: page >=
10
+ 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-3 border-t border-line px-4 py-3', className), children: [(0, jsx_runtime_1.jsx)("p", { className: 'text-sm text-ink/55', children: totalRows === 0
11
+ ? 'No rows'
12
+ : `${firstRow}–${lastRow} of ${totalRows}${isPartial ? '+' : ''}` }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex items-center gap-2', children: [(0, jsx_runtime_1.jsx)("button", { className: buttonClassName, disabled: page <= 1, onClick: () => onPageChange(page - 1), type: 'button', children: "Previous" }), (0, jsx_runtime_1.jsxs)("span", { className: 'hidden text-sm text-ink/65 sm:inline', children: ["Page ", Math.min(page, pageCount), " of ", pageCount] }), (0, jsx_runtime_1.jsx)("button", { className: buttonClassName, disabled: page >=
11
13
  (loadedPageCount === undefined ? pageCount : Math.min(loadedPageCount, pageCount)), onClick: () => onPageChange(page + 1), type: 'button', children: "Next" })] })] }));
12
14
  };
13
15
  exports.PsTablePagination = PsTablePagination;
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from 'react';
2
+ import { type UsePsTableReorderInput } from '../hooks/use-ps-table-reorder';
3
+ import type { PsTableReorderApi } from '../interfaces/table-reorder-api';
4
+ export interface PsTableReorderFrameProps extends UsePsTableReorderInput {
5
+ children: (api: PsTableReorderApi) => ReactNode;
6
+ renderOverlay: (rowId: string) => ReactNode;
7
+ }
8
+ export declare const PsTableReorderFrame: ({ children, renderOverlay, ...input }: PsTableReorderFrameProps) => import("react").JSX.Element;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsTableReorderFrame = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const core_1 = require("@dnd-kit/core");
6
+ const sortable_1 = require("@dnd-kit/sortable");
7
+ const ps_core_components_1 = require("@pallasoft/ps-core-components");
8
+ const use_ps_table_reorder_1 = require("../hooks/use-ps-table-reorder");
9
+ const ps_table_sortable_row_1 = require("./ps-table-sortable-row");
10
+ const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
11
+ const { activeId, activeRunId, contextProps, placements } = (0, use_ps_table_reorder_1.usePsTableReorder)(input);
12
+ const itemById = new Map(input.items.map((item) => [item.id, item]));
13
+ const api = {
14
+ isActive: true,
15
+ Row: ps_table_sortable_row_1.PsTableSortableRow,
16
+ rowProps: (rowId) => {
17
+ const item = itemById.get(rowId);
18
+ const placement = placements.get(rowId);
19
+ return {
20
+ handleLabel: item === undefined ? '' : item.handleLabel,
21
+ hasHandle: item !== undefined && input.enabled && placement !== undefined && placement.size > 1,
22
+ isDisabled: activeRunId !== null && (placement === undefined || placement.runId !== activeRunId),
23
+ rowId,
24
+ };
25
+ },
26
+ run: (runId, ids, runChildren) => ((0, jsx_runtime_1.jsx)(sortable_1.SortableContext, { items: ids, strategy: sortable_1.verticalListSortingStrategy, children: runChildren }, runId)),
27
+ };
28
+ return ((0, jsx_runtime_1.jsxs)(core_1.DndContext, { ...contextProps, children: [children(api), (0, jsx_runtime_1.jsx)(ps_core_components_1.PsDragOverlay, { className: ps_core_components_1.PsDragClasses.lifted, frames: ps_core_components_1.PsDragListLiftFrames, children: activeId === null ? null : renderOverlay(activeId) })] }));
29
+ };
30
+ exports.PsTableReorderFrame = PsTableReorderFrame;
@@ -0,0 +1,6 @@
1
+ import type { ComponentProps } from 'react';
2
+ export interface PsTableReorderHandleProps extends Omit<ComponentProps<'button'>, 'ref' | 'type'> {
3
+ activatorRef?: (element: HTMLElement | null) => void;
4
+ label: string;
5
+ }
6
+ export declare const PsTableReorderHandle: ({ activatorRef, className, label, ...buttonProps }: PsTableReorderHandleProps) => import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsTableReorderHandle = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_core_components_1 = require("@pallasoft/ps-core-components");
6
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
7
+ const PsTableReorderHandle = ({ activatorRef, className, label, ...buttonProps }) => ((0, jsx_runtime_1.jsx)("button", { ...buttonProps, "aria-label": label, className: ps_design_system_1.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: (0, jsx_runtime_1.jsx)(ps_core_components_1.PsDragGrip, {}) }));
8
+ exports.PsTableReorderHandle = PsTableReorderHandle;
@@ -0,0 +1,6 @@
1
+ export interface PsTableSearchingStateProps {
2
+ className?: string;
3
+ label: string;
4
+ skeletonRows?: number;
5
+ }
6
+ export declare const PsTableSearchingState: ({ className, label, skeletonRows, }: PsTableSearchingStateProps) => import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsTableSearchingState = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const PsTableSearchingState = ({ className, label, skeletonRows = 0, }) => ((0, jsx_runtime_1.jsxs)("div", { "aria-live": 'polite', className: ps_design_system_1.PallasoftClassNameService.merge('flex flex-col gap-3 px-4 py-4', className), role: 'status', children: [Array.from({ length: skeletonRows }).map((_, index) => ((0, jsx_runtime_1.jsx)("div", { className: 'h-4 w-full animate-pulse rounded-full bg-mist' }, index))), (0, jsx_runtime_1.jsx)("p", { className: 'text-sm text-ink/55', children: label })] }));
7
+ exports.PsTableSearchingState = PsTableSearchingState;
@@ -0,0 +1,9 @@
1
+ import type { ComponentProps } from 'react';
2
+ export interface PsTableSortableRowProps extends Omit<ComponentProps<'tr'>, 'ref'> {
3
+ handleLabel: string;
4
+ hasHandle: boolean;
5
+ isDisabled: boolean;
6
+ measureRef?: (element: HTMLTableRowElement | null) => void;
7
+ rowId: string;
8
+ }
9
+ export declare const PsTableSortableRow: ({ children, className, handleLabel, hasHandle, isDisabled, measureRef, rowId, style, ...rowProps }: PsTableSortableRowProps) => import("react").JSX.Element;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsTableSortableRow = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const sortable_1 = require("@dnd-kit/sortable");
6
+ const utilities_1 = require("@dnd-kit/utilities");
7
+ const ps_core_components_1 = require("@pallasoft/ps-core-components");
8
+ const ps_table_reorder_handle_1 = require("./ps-table-reorder-handle");
9
+ const PsTableSortableRow = ({ children, className, handleLabel, hasHandle, isDisabled, measureRef, rowId, style, ...rowProps }) => {
10
+ const { attributes, isDragging, listeners, setActivatorNodeRef, setNodeRef, transform, transition, } = (0, sortable_1.useSortable)({ disabled: isDisabled || !hasHandle, id: rowId });
11
+ return ((0, jsx_runtime_1.jsxs)("tr", { ...rowProps, className: className, ref: (element) => {
12
+ setNodeRef(element);
13
+ if (measureRef)
14
+ measureRef(element);
15
+ }, style: {
16
+ ...style,
17
+ opacity: isDragging ? ps_core_components_1.PsDragConfig.placeholderOpacity : style?.opacity,
18
+ transform: utilities_1.CSS.Translate.toString(transform),
19
+ transition,
20
+ }, children: [(0, jsx_runtime_1.jsx)("td", { className: 'w-9 pl-3 pr-0', onClick: (event) => event.stopPropagation(), children: hasHandle ? ((0, jsx_runtime_1.jsx)(ps_table_reorder_handle_1.PsTableReorderHandle, { ...attributes, ...listeners, activatorRef: setActivatorNodeRef, label: handleLabel })) : null }), children] }));
21
+ };
22
+ exports.PsTableSortableRow = PsTableSortableRow;
@@ -4,6 +4,8 @@ export declare const PsTableConfig: {
4
4
  readonly groupOpenMs: 280;
5
5
  readonly groupRowStaggerMs: 16;
6
6
  readonly groupStaggerLimit: 12;
7
+ readonly searchingLabel: "Searching all rows…";
8
+ readonly searchingSkeletonRows: 3;
7
9
  readonly noMatchesDescription: "Try a different search or clear the filters.";
8
10
  readonly noMatchesTitle: "Nothing matches";
9
11
  readonly rowEstimatePx: {
@@ -7,6 +7,8 @@ exports.PsTableConfig = {
7
7
  groupOpenMs: 280,
8
8
  groupRowStaggerMs: 16,
9
9
  groupStaggerLimit: 12,
10
+ searchingLabel: 'Searching all rows…',
11
+ searchingSkeletonRows: 3,
10
12
  noMatchesDescription: 'Try a different search or clear the filters.',
11
13
  noMatchesTitle: 'Nothing matches',
12
14
  rowEstimatePx: { compact: 37, normal: 57 },
@@ -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
- return (_jsxs(Fragment, { children: [_jsxs("tr", { "data-index": virtualItem ? virtualItem.index : undefined, ref: virtualItem ? virtualizer.measureElement : 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
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) => renderRow(row, groupRowMotion(group.key, 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) => renderRow(visibleRows[item.index], undefined, 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) => renderRow(row))] }), hasFooter ? (_jsx("tfoot", { className: 'border-t-2 border-line bg-mist/50 text-sm font-semibold text-ink', children: _jsxs("tr", { children: [isSelectable ? _jsx("td", {}) : null, visibleColumns.map((column) => (_jsx("td", { className: PallasoftClassNameService.merge(densityClassNames[density].cell, alignClassNames[column.align !== null && column.align !== undefined
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 ? 'No rows' : `${firstRow}–${lastRow} of ${totalRows}` }), _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 >=
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
@@ -34,3 +34,6 @@ export * from './services/ps-table-download-service';
34
34
  export * from './services/ps-table-scroll-anchor-service';
35
35
  export * from './hooks/use-ps-table-page';
36
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,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,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
@@ -34,3 +34,6 @@ export * from './services/ps-table-download-service';
34
34
  export * from './services/ps-table-scroll-anchor-service';
35
35
  export * from './hooks/use-ps-table-page';
36
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
@@ -50,3 +50,6 @@ __exportStar(require("./services/ps-table-download-service"), exports);
50
50
  __exportStar(require("./services/ps-table-scroll-anchor-service"), exports);
51
51
  __exportStar(require("./hooks/use-ps-table-page"), exports);
52
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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ export interface PsTableReorderItem {
2
+ groupKey: string;
3
+ handleLabel: string;
4
+ id: string;
5
+ label: string;
6
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ export interface PsTableReorderPlacement {
2
+ groupKey: string;
3
+ index: number;
4
+ runId: string;
5
+ size: number;
6
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ export interface PsTableReorderRowProps {
2
+ handleLabel: string;
3
+ hasHandle: boolean;
4
+ isDisabled: boolean;
5
+ rowId: string;
6
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ export interface PsTableReorderRun<Item> {
2
+ groupKey: string;
3
+ id: string;
4
+ items: Item[];
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,12 @@
1
+ import type { PsTableReorderAnnouncements } from './table-reorder-announcements';
2
+ export interface PsTableReorder<RowData> {
3
+ announcements: PsTableReorderAnnouncements;
4
+ cardsReason?: string;
5
+ columnLabel: string;
6
+ disabledReason?: string;
7
+ enabled: boolean;
8
+ getHandleLabel: (rowLabel: string) => string;
9
+ groupKey: (row: RowData) => string;
10
+ instructions: string;
11
+ onReorder: (groupKey: string, orderedIds: string[]) => void;
12
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,7 @@
1
+ import type { VirtualItem } from '@tanstack/react-virtual';
2
+ import type { PsTableRowMotion } from './table-row-motion';
3
+ export interface PsTableRowEntry<RowData> {
4
+ motion?: PsTableRowMotion;
5
+ row: RowData;
6
+ virtualItem?: VirtualItem;
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,11 @@
1
+ import type { Modifier } from '@dnd-kit/core';
2
+ import type { PsTableReorderApi } from '../interfaces/table-reorder-api';
3
+ import type { PsTableReorderPlacement } from '../interfaces/table-reorder-placement';
4
+ import type { PsTableReorderRun } from '../interfaces/table-reorder-run';
5
+ export declare class PsTableReorderService {
6
+ static readonly plain: PsTableReorderApi;
7
+ static runs<Item>(items: Item[], getKey: (item: Item) => string, getId: (item: Item) => string): PsTableReorderRun<Item>[];
8
+ static placements<Item>(runs: PsTableReorderRun<Item>[], getId: (item: Item) => string): Map<string, PsTableReorderPlacement>;
9
+ static move(ids: string[], activeId: string, overId: string): string[];
10
+ static readonly lockToVerticalAxis: Modifier;
11
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsTableReorderService = void 0;
4
+ class PsTableReorderService {
5
+ static runs(items, getKey, getId) {
6
+ const runs = [];
7
+ for (const item of items) {
8
+ const groupKey = getKey(item);
9
+ const last = runs[runs.length - 1];
10
+ if (last !== undefined && last.groupKey === groupKey)
11
+ last.items.push(item);
12
+ else
13
+ runs.push({ groupKey, id: `${groupKey}:${getId(item)}`, items: [item] });
14
+ }
15
+ return runs;
16
+ }
17
+ static placements(runs, getId) {
18
+ const placements = new Map();
19
+ for (const run of runs)
20
+ run.items.forEach((item, index) => placements.set(getId(item), {
21
+ groupKey: run.groupKey,
22
+ index,
23
+ runId: run.id,
24
+ size: run.items.length,
25
+ }));
26
+ return placements;
27
+ }
28
+ static move(ids, activeId, overId) {
29
+ const from = ids.indexOf(activeId);
30
+ const to = ids.indexOf(overId);
31
+ if (from < 0 || to < 0 || from === to)
32
+ return ids;
33
+ const next = [...ids];
34
+ const [moved] = next.splice(from, 1);
35
+ next.splice(to, 0, moved);
36
+ return next;
37
+ }
38
+ }
39
+ exports.PsTableReorderService = PsTableReorderService;
40
+ PsTableReorderService.plain = {
41
+ Row: 'tr',
42
+ isActive: false,
43
+ rowProps: (rowId) => ({ handleLabel: '', hasHandle: false, isDisabled: true, rowId }),
44
+ run: (_runId, _ids, children) => children,
45
+ };
46
+ PsTableReorderService.lockToVerticalAxis = ({ transform }) => ({ ...transform, x: 0 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pallasoft/ps-table-components",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -74,7 +74,10 @@
74
74
  "url": "git+https://github.com/pallasoft/ps-table-components.git"
75
75
  },
76
76
  "dependencies": {
77
- "@pallasoft/ps-core-components": "^2.10.0",
77
+ "@dnd-kit/core": "^6.3.1",
78
+ "@dnd-kit/sortable": "^10.0.0",
79
+ "@dnd-kit/utilities": "^3.2.2",
80
+ "@pallasoft/ps-core-components": "^2.23.0",
78
81
  "@pallasoft/ps-design-system": "^1.5.0",
79
82
  "@pallasoft/ps-icons": "^1.5.4",
80
83
  "@pallasoft/ps-input-components": "^1.14.0",