@pallasoft/ps-table-components 1.24.0 → 1.26.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.
@@ -1,4 +1,5 @@
1
1
  import { type ReactNode } from 'react';
2
+ import { PsTablePaginationPosition } from '../constants/ps-table-pagination-position';
2
3
  import type { PsTableColumn } from '../interfaces/table-column';
3
4
  import type { PsTableGrouping } from '../interfaces/table-grouping';
4
5
  import type { PsTableQuery } from '../interfaces/table-query';
@@ -27,6 +28,7 @@ export interface PsDataTableProps<RowData> {
27
28
  onRowHover?: (row: RowData) => void;
28
29
  page?: number;
29
30
  pageSize?: number;
31
+ paginationPosition?: PsTablePaginationPosition;
30
32
  pinFirstColumn?: boolean;
31
33
  query?: PsTableQuery;
32
34
  renderExpandedRow?: (row: RowData) => ReactNode;
@@ -37,4 +39,4 @@ export interface PsDataTableProps<RowData> {
37
39
  toolbarActions?: ReactNode;
38
40
  totalRows?: number;
39
41
  }
40
- 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, pinFirstColumn, query, renderExpandedRow, reorder, rows, searchingLabel, searchPlaceholder, toolbarActions, totalRows, }: PsDataTableProps<RowData>) => import("react").JSX.Element;
42
+ export declare const PsDataTable: <RowData>({ caption, className, columns, defaultSort, density, expandedRowIds, mobileLayout, getBulkActions, getRowId, getRowLabel, getSearchText, grouping, isLoading, isPending, layout, onPageChange, onQueryChange, onRowClick, onRowHover, page, pageSize, paginationPosition, pinFirstColumn, query, renderExpandedRow, reorder, rows, searchingLabel, searchPlaceholder, toolbarActions, totalRows, }: PsDataTableProps<RowData>) => import("react").JSX.Element;
@@ -42,6 +42,7 @@ const ps_input_components_1 = require("@pallasoft/ps-input-components");
42
42
  const ps_motion_components_1 = require("@pallasoft/ps-motion-components");
43
43
  const react_virtual_1 = require("@tanstack/react-virtual");
44
44
  const ps_table_control_classes_1 = require("../constants/ps-table-control-classes");
45
+ const ps_table_pagination_position_1 = require("../constants/ps-table-pagination-position");
45
46
  const table_breakpoint_1 = require("../constants/table-breakpoint");
46
47
  const table_config_1 = require("../constants/table-config");
47
48
  const use_ps_incremental_count_1 = require("../hooks/use-ps-incremental-count");
@@ -84,7 +85,7 @@ const alignClassNames = {
84
85
  left: 'text-left',
85
86
  right: 'text-right tabular-nums',
86
87
  };
87
- const PsDataTable = ({ caption, className, columns, defaultSort, density = 'normal', expandedRowIds, mobileLayout = 'cards', getBulkActions, getRowId, getRowLabel, getSearchText, grouping, isLoading = false, isPending = false, layout = 'auto', onPageChange, onQueryChange, onRowClick, onRowHover, page, pageSize, pinFirstColumn = true, query, renderExpandedRow, reorder, rows, searchingLabel = table_config_1.PsTableConfig.searchingLabel, searchPlaceholder = table_config_1.PsTableConfig.searchPlaceholder, toolbarActions, totalRows, }) => {
88
+ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'normal', expandedRowIds, mobileLayout = 'cards', getBulkActions, getRowId, getRowLabel, getSearchText, grouping, isLoading = false, isPending = false, layout = 'auto', onPageChange, onQueryChange, onRowClick, onRowHover, page, pageSize, paginationPosition = ps_table_pagination_position_1.PsTablePaginationPosition.bottom, pinFirstColumn = true, query, renderExpandedRow, reorder, rows, searchingLabel = table_config_1.PsTableConfig.searchingLabel, searchPlaceholder = table_config_1.PsTableConfig.searchPlaceholder, toolbarActions, totalRows, }) => {
88
89
  const containerRef = (0, react_1.useRef)(null);
89
90
  const [uncontrolledPage, setUncontrolledPage] = (0, react_1.useState)(1);
90
91
  const [uncontrolledQuery, setUncontrolledQuery] = (0, react_1.useState)(() => ps_table_query_service_1.PsTableQueryService.empty(defaultSort ? defaultSort : null));
@@ -194,13 +195,24 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
194
195
  announcements: nesting.announcements,
195
196
  canDrag: (id) => {
196
197
  const row = find(id);
197
- return row !== undefined && nesting.canDrag(row);
198
+ return (row !== undefined &&
199
+ (reorder.isRowDraggable === undefined || reorder.isRowDraggable(row)) &&
200
+ nesting.canDrag(row));
198
201
  },
199
202
  canDrop: (activeId, targetId, placement) => {
200
203
  const active = find(activeId);
201
204
  const target = find(targetId);
202
205
  return (active !== undefined && target !== undefined && nesting.canDrop(active, target, placement));
203
206
  },
207
+ getBlockedReason: nesting.getBlockedReason === undefined
208
+ ? undefined
209
+ : (activeId, targetId) => {
210
+ const active = find(activeId);
211
+ const target = find(targetId);
212
+ return active !== undefined && target !== undefined && nesting.getBlockedReason
213
+ ? nesting.getBlockedReason(active, target)
214
+ : null;
215
+ },
204
216
  onMove: (activeId, targetId, placement) => {
205
217
  const active = find(activeId);
206
218
  const target = find(targetId);
@@ -217,13 +229,16 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
217
229
  groupKey: reorderKey(row),
218
230
  handleLabel: reorder.getHandleLabel(label),
219
231
  id: rowId,
232
+ isDraggable: reorder.isRowDraggable === undefined || reorder.isRowDraggable(row),
220
233
  label,
221
234
  };
222
235
  })
223
236
  : [], [getRowId, getRowLabel, reorder, reorderKey, visibleRows]);
224
- const renderOverlayRow = (rowId) => {
237
+ const showsPagination = Boolean(pageSize) && (!grouping || isServerPaginated);
238
+ const renderPagination = (paginationClassName) => pageSize ? ((0, jsx_runtime_1.jsx)(ps_table_pagination_1.PsTablePagination, { className: paginationClassName, isPartial: isPartial, loadedPageCount: pageCountOf(loadedRowCount), onPageChange: changePage, page: safePage, pageCount: pageCount, pageSize: pageSize, totalRows: rowCount })) : null;
239
+ const renderOverlayRow = (rowId, size) => {
225
240
  const row = visibleRows.find((candidate) => getRowId(candidate) === rowId);
226
- 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) }) }));
241
+ return row === undefined ? null : ((0, jsx_runtime_1.jsxs)("table", { className: 'pointer-events-none w-full table-fixed border-collapse bg-surface text-left', style: size === null ? undefined : { width: size.width }, children: [size === null ? null : ((0, jsx_runtime_1.jsx)("colgroup", { children: size.columnWidths.map((width, index) => ((0, jsx_runtime_1.jsx)("col", { style: { width } }, index))) })), (0, jsx_runtime_1.jsx)("tbody", { children: renderRow(ps_table_reorder_service_1.PsTableReorderService.plain, row, undefined, undefined, true) })] }));
227
242
  };
228
243
  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, nesting: nestingBinding, onReorder: reorder.onReorder, renderOverlay: renderOverlayRow, children: children }) }));
229
244
  const changePage = (next) => {
@@ -303,7 +318,7 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
303
318
  }, [shouldVirtualize, visibleRows.length]);
304
319
  const toggleRow = (rowId) => setSelectedIds((current) => current.includes(rowId) ? current.filter((id) => id !== rowId) : [...current, rowId]);
305
320
  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 }));
306
- const renderRow = (api, row, motion, virtualItem) => {
321
+ const renderRow = (api, row, motion, virtualItem, isOverlay = false) => {
307
322
  const rowId = getRowId(row);
308
323
  const isExpanded = expandedRowIds ? expandedRowIds.includes(rowId) : false;
309
324
  const rowLabel = getRowLabel ? getRowLabel(row) : rowId;
@@ -315,7 +330,7 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
315
330
  measureRef: virtualItem ? virtualizer.measureElement : undefined,
316
331
  }
317
332
  : { ref: virtualItem ? virtualizer.measureElement : undefined };
318
- 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
333
+ 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 hover:bg-mist/60', motion ? motion.className : '', onRowClick ? 'ps-focus-ring cursor-pointer' : '', isExpanded ? 'bg-mist/60' : ''), onClick: onRowClick ? () => onRowClick(row) : undefined, onFocus: onRowHover ? () => onRowHover(row) : undefined, onPointerEnter: onRowHover ? () => onRowHover(row) : undefined, onKeyDown: onRowClick
319
334
  ? (event) => {
320
335
  if (event.target !== event.currentTarget)
321
336
  return;
@@ -330,7 +345,7 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
330
345
  ((layout === 'fixed' && !column.wrap) ||
331
346
  ps_table_column_size_service_1.PsTableColumnSizeService.maxWidthOf(column) !== undefined)
332
347
  ? 'truncate'
333
- : '', 'text-sm text-ink'), style: columnStyle(column, fittedWidths.get(column.id)), 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));
348
+ : '', 'text-sm text-ink'), style: columnStyle(column, fittedWidths.get(column.id)), title: ps_table_cell_tooltip_service_1.PsTableCellTooltipService.forCell(column, row), children: column.render(row) }, column.id)))] }), renderExpandedRow && !isOverlay ? ((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));
334
349
  };
335
350
  const renderEntries = (api, entries) => reorder
336
351
  ? ps_table_reorder_service_1.PsTableReorderService.runs(entries, (entry) => reorderKey(entry.row), (entry) => getRowId(entry.row)).map((run) => api.run(run.id, run.items.map((entry) => getRowId(entry.row)), run.items.map((entry) => renderRow(api, entry.row, entry.motion, entry.virtualItem))))
@@ -345,7 +360,9 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
345
360
  }, onSearchChange: (next) => {
346
361
  updateQuery({ search: next });
347
362
  resetPage();
348
- }, 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
363
+ }, 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: [showsPagination && paginationPosition !== ps_table_pagination_position_1.PsTablePaginationPosition.bottom
364
+ ? renderPagination('border-b border-t-0')
365
+ : null, mobileLayout === 'cards' ? ((0, jsx_runtime_1.jsxs)("div", { className: 'sm:hidden', children: [reorder && reorder.cardsReason ? ((0, jsx_runtime_1.jsx)("p", { className: 'border-b border-line px-4 py-2 text-xs text-ink/55', children: reorder.cardsReason })) : null, grouping
349
366
  ? groups.map((group, groupIndex) => {
350
367
  const isExpanded = isGroupExpanded(group.key, groupIndex);
351
368
  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
@@ -383,6 +400,8 @@ const PsDataTable = ({ caption, className, columns, defaultSort, density = 'norm
383
400
  ]
384
401
  : 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", { className: pins.reorder }) : null, isSelectable ? (0, jsx_runtime_1.jsx)("td", { className: pins.select }) : null, visibleColumns.map((column, columnIndex) => ((0, jsx_runtime_1.jsx)("td", { className: ps_design_system_1.PallasoftClassNameService.merge(densityClassNames[density].cell, placementOf(column, columnIndex), alignClassNames[column.align !== null && column.align !== undefined
385
402
  ? column.align
386
- : '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] })] }));
403
+ : '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, showsPagination && paginationPosition !== ps_table_pagination_position_1.PsTablePaginationPosition.top
404
+ ? renderPagination('')
405
+ : null] })] }));
387
406
  };
388
407
  exports.PsDataTable = PsDataTable;
@@ -1,8 +1,9 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import { type UsePsTableReorderInput } from '../hooks/use-ps-table-reorder';
3
+ import type { PsTableDragSize } from '../interfaces/table-drag-size';
3
4
  import type { PsTableReorderApi } from '../interfaces/table-reorder-api';
4
5
  export interface PsTableReorderFrameProps extends UsePsTableReorderInput {
5
6
  children: (api: PsTableReorderApi) => ReactNode;
6
- renderOverlay: (rowId: string) => ReactNode;
7
+ renderOverlay: (rowId: string, size: PsTableDragSize | null) => ReactNode;
7
8
  }
8
9
  export declare const PsTableReorderFrame: ({ children, renderOverlay, ...input }: PsTableReorderFrameProps) => import("react").JSX.Element;
@@ -9,7 +9,7 @@ const use_ps_table_reorder_1 = require("../hooks/use-ps-table-reorder");
9
9
  const ps_table_reorder_service_1 = require("../services/ps-table-reorder-service");
10
10
  const ps_table_sortable_row_1 = require("./ps-table-sortable-row");
11
11
  const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
12
- const { activeId, activeRunId, contextProps, drop, placements } = (0, use_ps_table_reorder_1.usePsTableReorder)(input);
12
+ const { activeId, activeRunId, activeSize, blockedReason, contextProps, drop, placements } = (0, use_ps_table_reorder_1.usePsTableReorder)(input);
13
13
  const itemById = new Map(input.items.map((item) => [item.id, item]));
14
14
  const { nesting } = input;
15
15
  const api = {
@@ -22,7 +22,7 @@ const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
22
22
  return {
23
23
  dropPlacement: drop !== null && drop.overId === rowId ? drop.placement : null,
24
24
  handleLabel: item === undefined ? '' : item.handleLabel,
25
- hasHandle: item !== undefined && input.enabled && nesting.canDrag(rowId),
25
+ hasHandle: item !== undefined && item.isDraggable && input.enabled && nesting.canDrag(rowId),
26
26
  isDisabled: !input.enabled,
27
27
  isDropTarget: item !== undefined,
28
28
  rowId,
@@ -30,7 +30,11 @@ const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
30
30
  return {
31
31
  dropPlacement: null,
32
32
  handleLabel: item === undefined ? '' : item.handleLabel,
33
- hasHandle: item !== undefined && input.enabled && placement !== undefined && placement.size > 1,
33
+ hasHandle: item !== undefined &&
34
+ item.isDraggable &&
35
+ input.enabled &&
36
+ placement !== undefined &&
37
+ placement.size > 1,
34
38
  isDisabled: activeRunId !== null && (placement === undefined || placement.runId !== activeRunId),
35
39
  isDropTarget: false,
36
40
  rowId,
@@ -38,6 +42,6 @@ const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
38
42
  },
39
43
  run: (runId, ids, runChildren) => ((0, jsx_runtime_1.jsx)(sortable_1.SortableContext, { items: ids, strategy: nesting === undefined ? sortable_1.verticalListSortingStrategy : ps_table_reorder_service_1.PsTableReorderService.still, children: runChildren }, runId)),
40
44
  };
41
- 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) })] }));
45
+ return ((0, jsx_runtime_1.jsxs)(core_1.DndContext, { ...contextProps, children: [children(api), blockedReason === null ? null : ((0, jsx_runtime_1.jsx)("p", { className: 'pointer-events-none fixed bottom-6 left-1/2 z-50 max-w-sm -translate-x-1/2 rounded-ps-control bg-ink px-3 py-2 text-sm text-white shadow-lg', role: 'status', children: blockedReason })), (0, jsx_runtime_1.jsx)(ps_core_components_1.PsDragOverlay, { className: ps_core_components_1.PsDragClasses.lifted, frames: ps_core_components_1.PsDragListLiftFrames, isDropAnimated: nesting === undefined, children: activeId === null ? null : renderOverlay(activeId, activeSize) })] }));
42
46
  };
43
47
  exports.PsTableReorderFrame = PsTableReorderFrame;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsTableDropClasses = void 0;
4
4
  exports.PsTableDropClasses = {
5
- after: '[&>td]:shadow-[inset_0_-2px_0_0_rgb(var(--ps-color-primary-rgb))]',
6
- before: '[&>td]:shadow-[inset_0_2px_0_0_rgb(var(--ps-color-primary-rgb))]',
5
+ after: '[&>td]:bg-[linear-gradient(rgb(var(--ps-color-primary-rgb)),rgb(var(--ps-color-primary-rgb)))] [&>td]:bg-[length:100%_2px] [&>td]:bg-bottom [&>td]:bg-no-repeat',
6
+ before: '[&>td]:bg-[linear-gradient(rgb(var(--ps-color-primary-rgb)),rgb(var(--ps-color-primary-rgb)))] [&>td]:bg-[length:100%_2px] [&>td]:bg-top [&>td]:bg-no-repeat',
7
7
  into: 'bg-primary/10 [&>td]:shadow-[inset_0_0_0_2px_rgb(var(--ps-color-primary-rgb)/0.5)]',
8
8
  };
@@ -0,0 +1,6 @@
1
+ export declare const PsTablePaginationPosition: {
2
+ readonly both: "both";
3
+ readonly bottom: "bottom";
4
+ readonly top: "top";
5
+ };
6
+ export type PsTablePaginationPosition = (typeof PsTablePaginationPosition)[keyof typeof PsTablePaginationPosition];
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsTablePaginationPosition = void 0;
4
+ exports.PsTablePaginationPosition = {
5
+ both: 'both',
6
+ bottom: 'bottom',
7
+ top: 'top',
8
+ };
@@ -6,6 +6,7 @@ import { PsCheckbox } from '@pallasoft/ps-input-components';
6
6
  import { PsCollapse } from '@pallasoft/ps-motion-components';
7
7
  import { useVirtualizer, useWindowVirtualizer } from '@tanstack/react-virtual';
8
8
  import { PsTableControlClasses } from '../constants/ps-table-control-classes';
9
+ import { PsTablePaginationPosition } from '../constants/ps-table-pagination-position';
9
10
  import { PsTableHideBelowClasses } from '../constants/table-breakpoint';
10
11
  import { PsTableConfig } from '../constants/table-config';
11
12
  import { usePsIncrementalCount } from '../hooks/use-ps-incremental-count';
@@ -48,7 +49,7 @@ const alignClassNames = {
48
49
  left: 'text-left',
49
50
  right: 'text-right tabular-nums',
50
51
  };
51
- export const PsDataTable = ({ caption, className, columns, defaultSort, density = 'normal', expandedRowIds, mobileLayout = 'cards', getBulkActions, getRowId, getRowLabel, getSearchText, grouping, isLoading = false, isPending = false, layout = 'auto', onPageChange, onQueryChange, onRowClick, onRowHover, page, pageSize, pinFirstColumn = true, query, renderExpandedRow, reorder, rows, searchingLabel = PsTableConfig.searchingLabel, searchPlaceholder = PsTableConfig.searchPlaceholder, toolbarActions, totalRows, }) => {
52
+ export const PsDataTable = ({ caption, className, columns, defaultSort, density = 'normal', expandedRowIds, mobileLayout = 'cards', getBulkActions, getRowId, getRowLabel, getSearchText, grouping, isLoading = false, isPending = false, layout = 'auto', onPageChange, onQueryChange, onRowClick, onRowHover, page, pageSize, paginationPosition = PsTablePaginationPosition.bottom, pinFirstColumn = true, query, renderExpandedRow, reorder, rows, searchingLabel = PsTableConfig.searchingLabel, searchPlaceholder = PsTableConfig.searchPlaceholder, toolbarActions, totalRows, }) => {
52
53
  const containerRef = useRef(null);
53
54
  const [uncontrolledPage, setUncontrolledPage] = useState(1);
54
55
  const [uncontrolledQuery, setUncontrolledQuery] = useState(() => PsTableQueryService.empty(defaultSort ? defaultSort : null));
@@ -158,13 +159,24 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
158
159
  announcements: nesting.announcements,
159
160
  canDrag: (id) => {
160
161
  const row = find(id);
161
- return row !== undefined && nesting.canDrag(row);
162
+ return (row !== undefined &&
163
+ (reorder.isRowDraggable === undefined || reorder.isRowDraggable(row)) &&
164
+ nesting.canDrag(row));
162
165
  },
163
166
  canDrop: (activeId, targetId, placement) => {
164
167
  const active = find(activeId);
165
168
  const target = find(targetId);
166
169
  return (active !== undefined && target !== undefined && nesting.canDrop(active, target, placement));
167
170
  },
171
+ getBlockedReason: nesting.getBlockedReason === undefined
172
+ ? undefined
173
+ : (activeId, targetId) => {
174
+ const active = find(activeId);
175
+ const target = find(targetId);
176
+ return active !== undefined && target !== undefined && nesting.getBlockedReason
177
+ ? nesting.getBlockedReason(active, target)
178
+ : null;
179
+ },
168
180
  onMove: (activeId, targetId, placement) => {
169
181
  const active = find(activeId);
170
182
  const target = find(targetId);
@@ -181,13 +193,16 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
181
193
  groupKey: reorderKey(row),
182
194
  handleLabel: reorder.getHandleLabel(label),
183
195
  id: rowId,
196
+ isDraggable: reorder.isRowDraggable === undefined || reorder.isRowDraggable(row),
184
197
  label,
185
198
  };
186
199
  })
187
200
  : [], [getRowId, getRowLabel, reorder, reorderKey, visibleRows]);
188
- const renderOverlayRow = (rowId) => {
201
+ const showsPagination = Boolean(pageSize) && (!grouping || isServerPaginated);
202
+ const renderPagination = (paginationClassName) => pageSize ? (_jsx(PsTablePagination, { className: paginationClassName, isPartial: isPartial, loadedPageCount: pageCountOf(loadedRowCount), onPageChange: changePage, page: safePage, pageCount: pageCount, pageSize: pageSize, totalRows: rowCount })) : null;
203
+ const renderOverlayRow = (rowId, size) => {
189
204
  const row = visibleRows.find((candidate) => getRowId(candidate) === rowId);
190
- 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) }) }));
205
+ return row === undefined ? null : (_jsxs("table", { className: 'pointer-events-none w-full table-fixed border-collapse bg-surface text-left', style: size === null ? undefined : { width: size.width }, children: [size === null ? null : (_jsx("colgroup", { children: size.columnWidths.map((width, index) => (_jsx("col", { style: { width } }, index))) })), _jsx("tbody", { children: renderRow(PsTableReorderService.plain, row, undefined, undefined, true) })] }));
191
206
  };
192
207
  const renderReorder = (children) => reorder === undefined ? (children(PsTableReorderService.plain)) : (_jsx(Suspense, { fallback: children(PsTableReorderService.plain), children: _jsx(LazyReorderFrame, { announcements: reorder.announcements, enabled: reorder.enabled, instructions: reorder.instructions, items: reorderItems, nesting: nestingBinding, onReorder: reorder.onReorder, renderOverlay: renderOverlayRow, children: children }) }));
193
208
  const changePage = (next) => {
@@ -267,7 +282,7 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
267
282
  }, [shouldVirtualize, visibleRows.length]);
268
283
  const toggleRow = (rowId) => setSelectedIds((current) => current.includes(rowId) ? current.filter((id) => id !== rowId) : [...current, rowId]);
269
284
  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 }));
270
- const renderRow = (api, row, motion, virtualItem) => {
285
+ const renderRow = (api, row, motion, virtualItem, isOverlay = false) => {
271
286
  const rowId = getRowId(row);
272
287
  const isExpanded = expandedRowIds ? expandedRowIds.includes(rowId) : false;
273
288
  const rowLabel = getRowLabel ? getRowLabel(row) : rowId;
@@ -279,7 +294,7 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
279
294
  measureRef: virtualItem ? virtualizer.measureElement : undefined,
280
295
  }
281
296
  : { ref: virtualItem ? virtualizer.measureElement : undefined };
282
- 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
297
+ return (_jsxs(Fragment, { children: [_jsxs(Row, { ...rowExtras, "data-index": virtualItem ? virtualItem.index : undefined, "aria-expanded": renderExpandedRow ? isExpanded : undefined, "aria-label": onRowClick ? rowLabel : undefined, className: PallasoftClassNameService.merge('align-middle hover:bg-mist/60', motion ? motion.className : '', onRowClick ? 'ps-focus-ring cursor-pointer' : '', isExpanded ? 'bg-mist/60' : ''), onClick: onRowClick ? () => onRowClick(row) : undefined, onFocus: onRowHover ? () => onRowHover(row) : undefined, onPointerEnter: onRowHover ? () => onRowHover(row) : undefined, onKeyDown: onRowClick
283
298
  ? (event) => {
284
299
  if (event.target !== event.currentTarget)
285
300
  return;
@@ -294,7 +309,7 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
294
309
  ((layout === 'fixed' && !column.wrap) ||
295
310
  PsTableColumnSizeService.maxWidthOf(column) !== undefined)
296
311
  ? 'truncate'
297
- : '', 'text-sm text-ink'), style: columnStyle(column, fittedWidths.get(column.id)), title: PsTableCellTooltipService.forCell(column, row), children: column.render(row) }, column.id)))] }), renderExpandedRow ? (_jsx("tr", { "aria-hidden": !isExpanded, className: isExpanded ? 'border-t-0' : 'border-0', children: _jsx("td", { className: 'p-0', colSpan: columnCount, children: _jsx(PsCollapse, { isOpen: isExpanded, children: _jsx("div", { className: 'w-0 min-w-full border-l-2 border-primary bg-mist/40 px-4 py-4', children: renderExpandedRow(row) }) }) }) })) : null] }, rowId));
312
+ : '', 'text-sm text-ink'), style: columnStyle(column, fittedWidths.get(column.id)), title: PsTableCellTooltipService.forCell(column, row), children: column.render(row) }, column.id)))] }), renderExpandedRow && !isOverlay ? (_jsx("tr", { "aria-hidden": !isExpanded, className: isExpanded ? 'border-t-0' : 'border-0', children: _jsx("td", { className: 'p-0', colSpan: columnCount, children: _jsx(PsCollapse, { isOpen: isExpanded, children: _jsx("div", { className: 'w-0 min-w-full border-l-2 border-primary bg-mist/40 px-4 py-4', children: renderExpandedRow(row) }) }) }) })) : null] }, rowId));
298
313
  };
299
314
  const renderEntries = (api, entries) => reorder
300
315
  ? PsTableReorderService.runs(entries, (entry) => reorderKey(entry.row), (entry) => getRowId(entry.row)).map((run) => api.run(run.id, run.items.map((entry) => getRowId(entry.row)), run.items.map((entry) => renderRow(api, entry.row, entry.motion, entry.virtualItem))))
@@ -309,7 +324,9 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
309
324
  }, onSearchChange: (next) => {
310
325
  updateQuery({ search: next });
311
326
  resetPage();
312
- }, 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
327
+ }, rows: rows, search: search, searchPlaceholder: searchPlaceholder })) : null, _jsxs("div", { className: 'min-w-0 overflow-hidden rounded-[1.25rem] border border-line bg-surface', children: [showsPagination && paginationPosition !== PsTablePaginationPosition.bottom
328
+ ? renderPagination('border-b border-t-0')
329
+ : null, mobileLayout === 'cards' ? (_jsxs("div", { className: 'sm:hidden', children: [reorder && reorder.cardsReason ? (_jsx("p", { className: 'border-b border-line px-4 py-2 text-xs text-ink/55', children: reorder.cardsReason })) : null, grouping
313
330
  ? groups.map((group, groupIndex) => {
314
331
  const isExpanded = isGroupExpanded(group.key, groupIndex);
315
332
  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
@@ -347,5 +364,7 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
347
364
  ]
348
365
  : renderEntries(api, visibleRows.map((row) => ({ row })))] }), hasFooter ? (_jsx("tfoot", { className: 'border-t-2 border-line bg-mist/50 text-sm font-semibold text-ink', children: _jsxs("tr", { children: [reorder ? _jsx("td", { className: pins.reorder }) : null, isSelectable ? _jsx("td", { className: pins.select }) : null, visibleColumns.map((column, columnIndex) => (_jsx("td", { className: PallasoftClassNameService.merge(densityClassNames[density].cell, placementOf(column, columnIndex), alignClassNames[column.align !== null && column.align !== undefined
349
366
  ? column.align
350
- : '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] })] }));
367
+ : 'left'], 'whitespace-nowrap'), children: column.footer }, column.id)))] }) })) : null] }))) }), visibleRows.length > 0 && isSearching ? (_jsx(PsTableSearchingState, { className: 'border-t border-line py-3', label: searchingLabel })) : null, showsPagination && paginationPosition !== PsTablePaginationPosition.top
368
+ ? renderPagination('')
369
+ : null] })] }));
351
370
  };
@@ -6,7 +6,7 @@ import { usePsTableReorder } from '../hooks/use-ps-table-reorder';
6
6
  import { PsTableReorderService } from '../services/ps-table-reorder-service';
7
7
  import { PsTableSortableRow } from './ps-table-sortable-row';
8
8
  export const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
9
- const { activeId, activeRunId, contextProps, drop, placements } = usePsTableReorder(input);
9
+ const { activeId, activeRunId, activeSize, blockedReason, contextProps, drop, placements } = usePsTableReorder(input);
10
10
  const itemById = new Map(input.items.map((item) => [item.id, item]));
11
11
  const { nesting } = input;
12
12
  const api = {
@@ -19,7 +19,7 @@ export const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
19
19
  return {
20
20
  dropPlacement: drop !== null && drop.overId === rowId ? drop.placement : null,
21
21
  handleLabel: item === undefined ? '' : item.handleLabel,
22
- hasHandle: item !== undefined && input.enabled && nesting.canDrag(rowId),
22
+ hasHandle: item !== undefined && item.isDraggable && input.enabled && nesting.canDrag(rowId),
23
23
  isDisabled: !input.enabled,
24
24
  isDropTarget: item !== undefined,
25
25
  rowId,
@@ -27,7 +27,11 @@ export const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
27
27
  return {
28
28
  dropPlacement: null,
29
29
  handleLabel: item === undefined ? '' : item.handleLabel,
30
- hasHandle: item !== undefined && input.enabled && placement !== undefined && placement.size > 1,
30
+ hasHandle: item !== undefined &&
31
+ item.isDraggable &&
32
+ input.enabled &&
33
+ placement !== undefined &&
34
+ placement.size > 1,
31
35
  isDisabled: activeRunId !== null && (placement === undefined || placement.runId !== activeRunId),
32
36
  isDropTarget: false,
33
37
  rowId,
@@ -35,5 +39,5 @@ export const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
35
39
  },
36
40
  run: (runId, ids, runChildren) => (_jsx(SortableContext, { items: ids, strategy: nesting === undefined ? verticalListSortingStrategy : PsTableReorderService.still, children: runChildren }, runId)),
37
41
  };
38
- return (_jsxs(DndContext, { ...contextProps, children: [children(api), _jsx(PsDragOverlay, { className: PsDragClasses.lifted, frames: PsDragListLiftFrames, children: activeId === null ? null : renderOverlay(activeId) })] }));
42
+ return (_jsxs(DndContext, { ...contextProps, children: [children(api), blockedReason === null ? null : (_jsx("p", { className: 'pointer-events-none fixed bottom-6 left-1/2 z-50 max-w-sm -translate-x-1/2 rounded-ps-control bg-ink px-3 py-2 text-sm text-white shadow-lg', role: 'status', children: blockedReason })), _jsx(PsDragOverlay, { className: PsDragClasses.lifted, frames: PsDragListLiftFrames, isDropAnimated: nesting === undefined, children: activeId === null ? null : renderOverlay(activeId, activeSize) })] }));
39
43
  };
@@ -1,5 +1,5 @@
1
1
  export const PsTableDropClasses = {
2
- after: '[&>td]:shadow-[inset_0_-2px_0_0_rgb(var(--ps-color-primary-rgb))]',
3
- before: '[&>td]:shadow-[inset_0_2px_0_0_rgb(var(--ps-color-primary-rgb))]',
2
+ after: '[&>td]:bg-[linear-gradient(rgb(var(--ps-color-primary-rgb)),rgb(var(--ps-color-primary-rgb)))] [&>td]:bg-[length:100%_2px] [&>td]:bg-bottom [&>td]:bg-no-repeat',
3
+ before: '[&>td]:bg-[linear-gradient(rgb(var(--ps-color-primary-rgb)),rgb(var(--ps-color-primary-rgb)))] [&>td]:bg-[length:100%_2px] [&>td]:bg-top [&>td]:bg-no-repeat',
4
4
  into: 'bg-primary/10 [&>td]:shadow-[inset_0_0_0_2px_rgb(var(--ps-color-primary-rgb)/0.5)]',
5
5
  };
@@ -0,0 +1,5 @@
1
+ export const PsTablePaginationPosition = {
2
+ both: 'both',
3
+ bottom: 'bottom',
4
+ top: 'top',
5
+ };
@@ -9,7 +9,9 @@ const pointerThenCenter = (args) => {
9
9
  };
10
10
  export const usePsTableReorder = ({ announcements: texts, enabled, instructions, items, nesting, onReorder, }) => {
11
11
  const [activeId, setActiveId] = useState(null);
12
+ const [activeSize, setActiveSize] = useState(null);
12
13
  const [drop, setDrop] = useState(null);
14
+ const [blockedReason, setBlockedReason] = useState(null);
13
15
  const dropRef = useRef(null);
14
16
  const sensors = usePsDragSensors();
15
17
  const runs = useMemo(() => enabled
@@ -85,6 +87,10 @@ export const usePsTableReorder = ({ announcements: texts, enabled, instructions,
85
87
  const handleDragMove = (event) => {
86
88
  const next = resolveDrop(event);
87
89
  dropRef.current = next;
90
+ const blocked = next === null && nesting !== undefined && nesting.getBlockedReason && event.over !== null
91
+ ? nesting.getBlockedReason(String(event.active.id), String(event.over.id))
92
+ : null;
93
+ setBlockedReason(blocked);
88
94
  setDrop((current) => current !== null &&
89
95
  next !== null &&
90
96
  current.overId === next.overId &&
@@ -96,7 +102,9 @@ export const usePsTableReorder = ({ announcements: texts, enabled, instructions,
96
102
  const { active, over } = event;
97
103
  const target = resolveDrop(event);
98
104
  setActiveId(null);
105
+ setActiveSize(null);
99
106
  setDrop(null);
107
+ setBlockedReason(null);
100
108
  dropRef.current = null;
101
109
  if (nesting !== undefined) {
102
110
  if (target !== null)
@@ -116,6 +124,8 @@ export const usePsTableReorder = ({ announcements: texts, enabled, instructions,
116
124
  };
117
125
  return {
118
126
  activeId,
127
+ activeSize,
128
+ blockedReason,
119
129
  activeRunId: activePlacement === undefined ? null : activePlacement.runId,
120
130
  contextProps: {
121
131
  accessibility: { announcements, screenReaderInstructions: { draggable: instructions } },
@@ -123,12 +133,17 @@ export const usePsTableReorder = ({ announcements: texts, enabled, instructions,
123
133
  modifiers: [PsTableReorderService.lockToVerticalAxis],
124
134
  onDragCancel: () => {
125
135
  setActiveId(null);
136
+ setActiveSize(null);
126
137
  setDrop(null);
138
+ setBlockedReason(null);
127
139
  dropRef.current = null;
128
140
  },
129
141
  onDragEnd: handleDragEnd,
130
142
  onDragMove: nesting === undefined ? undefined : handleDragMove,
131
- onDragStart: ({ active }) => setActiveId(String(active.id)),
143
+ onDragStart: ({ active, activatorEvent }) => {
144
+ setActiveSize(PsTableReorderService.measure(activatorEvent));
145
+ setActiveId(String(active.id));
146
+ },
132
147
  sensors,
133
148
  },
134
149
  drop,
package/dist/esm/index.js CHANGED
@@ -47,3 +47,5 @@ export * from './interfaces/table-reorder-nesting';
47
47
  export * from './interfaces/table-nesting-announcements';
48
48
  export * from './constants/ps-table-drop-placement';
49
49
  export * from './services/ps-table-reorder-service';
50
+ export * from './constants/ps-table-pagination-position';
51
+ export * from './interfaces/table-drag-size';
@@ -0,0 +1 @@
1
+ export {};
@@ -33,6 +33,18 @@ export class PsTableReorderService {
33
33
  next.splice(to, 0, moved);
34
34
  return next;
35
35
  }
36
+ static measure(activator) {
37
+ const target = activator === null ? null : activator.target;
38
+ const row = target instanceof Element ? target.closest('tr') : null;
39
+ if (row === null)
40
+ return null;
41
+ const rect = row.getBoundingClientRect();
42
+ return {
43
+ columnWidths: Array.from(row.children).map((cell) => cell.getBoundingClientRect().width),
44
+ height: rect.height,
45
+ width: rect.width,
46
+ };
47
+ }
36
48
  static pointerY(event, deltaY) {
37
49
  if (event instanceof MouseEvent)
38
50
  return event.clientY + deltaY;
@@ -1,4 +1,5 @@
1
1
  import { type DndContextProps } from '@dnd-kit/core';
2
+ import type { PsTableDragSize } from '../interfaces/table-drag-size';
2
3
  import type { PsTableDrop } from '../interfaces/table-drop';
3
4
  import type { PsTableReorderAnnouncements } from '../interfaces/table-reorder-announcements';
4
5
  import type { PsTableReorderItem } from '../interfaces/table-reorder-item';
@@ -15,7 +16,9 @@ export interface UsePsTableReorderInput {
15
16
  export interface PsTableReorderState {
16
17
  activeId: string | null;
17
18
  activeRunId: string | null;
19
+ activeSize: PsTableDragSize | null;
18
20
  contextProps: Omit<DndContextProps, 'children'>;
21
+ blockedReason: string | null;
19
22
  drop: PsTableDrop | null;
20
23
  placements: Map<string, PsTableReorderPlacement>;
21
24
  }
@@ -12,7 +12,9 @@ const pointerThenCenter = (args) => {
12
12
  };
13
13
  const usePsTableReorder = ({ announcements: texts, enabled, instructions, items, nesting, onReorder, }) => {
14
14
  const [activeId, setActiveId] = (0, react_1.useState)(null);
15
+ const [activeSize, setActiveSize] = (0, react_1.useState)(null);
15
16
  const [drop, setDrop] = (0, react_1.useState)(null);
17
+ const [blockedReason, setBlockedReason] = (0, react_1.useState)(null);
16
18
  const dropRef = (0, react_1.useRef)(null);
17
19
  const sensors = (0, ps_core_components_1.usePsDragSensors)();
18
20
  const runs = (0, react_1.useMemo)(() => enabled
@@ -88,6 +90,10 @@ const usePsTableReorder = ({ announcements: texts, enabled, instructions, items,
88
90
  const handleDragMove = (event) => {
89
91
  const next = resolveDrop(event);
90
92
  dropRef.current = next;
93
+ const blocked = next === null && nesting !== undefined && nesting.getBlockedReason && event.over !== null
94
+ ? nesting.getBlockedReason(String(event.active.id), String(event.over.id))
95
+ : null;
96
+ setBlockedReason(blocked);
91
97
  setDrop((current) => current !== null &&
92
98
  next !== null &&
93
99
  current.overId === next.overId &&
@@ -99,7 +105,9 @@ const usePsTableReorder = ({ announcements: texts, enabled, instructions, items,
99
105
  const { active, over } = event;
100
106
  const target = resolveDrop(event);
101
107
  setActiveId(null);
108
+ setActiveSize(null);
102
109
  setDrop(null);
110
+ setBlockedReason(null);
103
111
  dropRef.current = null;
104
112
  if (nesting !== undefined) {
105
113
  if (target !== null)
@@ -119,6 +127,8 @@ const usePsTableReorder = ({ announcements: texts, enabled, instructions, items,
119
127
  };
120
128
  return {
121
129
  activeId,
130
+ activeSize,
131
+ blockedReason,
122
132
  activeRunId: activePlacement === undefined ? null : activePlacement.runId,
123
133
  contextProps: {
124
134
  accessibility: { announcements, screenReaderInstructions: { draggable: instructions } },
@@ -126,12 +136,17 @@ const usePsTableReorder = ({ announcements: texts, enabled, instructions, items,
126
136
  modifiers: [ps_table_reorder_service_1.PsTableReorderService.lockToVerticalAxis],
127
137
  onDragCancel: () => {
128
138
  setActiveId(null);
139
+ setActiveSize(null);
129
140
  setDrop(null);
141
+ setBlockedReason(null);
130
142
  dropRef.current = null;
131
143
  },
132
144
  onDragEnd: handleDragEnd,
133
145
  onDragMove: nesting === undefined ? undefined : handleDragMove,
134
- onDragStart: ({ active }) => setActiveId(String(active.id)),
146
+ onDragStart: ({ active, activatorEvent }) => {
147
+ setActiveSize(ps_table_reorder_service_1.PsTableReorderService.measure(activatorEvent));
148
+ setActiveId(String(active.id));
149
+ },
135
150
  sensors,
136
151
  },
137
152
  drop,
package/dist/index.d.ts CHANGED
@@ -47,3 +47,5 @@ export * from './interfaces/table-reorder-nesting';
47
47
  export * from './interfaces/table-nesting-announcements';
48
48
  export * from './constants/ps-table-drop-placement';
49
49
  export * from './services/ps-table-reorder-service';
50
+ export * from './constants/ps-table-pagination-position';
51
+ export * from './interfaces/table-drag-size';
package/dist/index.js CHANGED
@@ -63,3 +63,5 @@ __exportStar(require("./interfaces/table-reorder-nesting"), exports);
63
63
  __exportStar(require("./interfaces/table-nesting-announcements"), exports);
64
64
  __exportStar(require("./constants/ps-table-drop-placement"), exports);
65
65
  __exportStar(require("./services/ps-table-reorder-service"), exports);
66
+ __exportStar(require("./constants/ps-table-pagination-position"), exports);
67
+ __exportStar(require("./interfaces/table-drag-size"), exports);
@@ -0,0 +1,5 @@
1
+ export interface PsTableDragSize {
2
+ columnWidths: number[];
3
+ height: number;
4
+ width: number;
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -2,5 +2,6 @@ export interface PsTableReorderItem {
2
2
  groupKey: string;
3
3
  handleLabel: string;
4
4
  id: string;
5
+ isDraggable: boolean;
5
6
  label: string;
6
7
  }
@@ -3,6 +3,7 @@ import type { PsTableNestingAnnouncements } from './table-nesting-announcements'
3
3
  export interface PsTableReorderNestingBinding {
4
4
  announcements: PsTableNestingAnnouncements;
5
5
  canDrag: (id: string) => boolean;
6
+ getBlockedReason?: (activeId: string, targetId: string) => string | null;
6
7
  canDrop: (activeId: string, targetId: string, placement: PsTableDropPlacement) => boolean;
7
8
  onMove: (activeId: string, targetId: string, placement: PsTableDropPlacement) => void;
8
9
  }
@@ -3,6 +3,7 @@ import type { PsTableNestingAnnouncements } from './table-nesting-announcements'
3
3
  export interface PsTableReorderNesting<RowData> {
4
4
  announcements: PsTableNestingAnnouncements;
5
5
  canDrag: (row: RowData) => boolean;
6
+ getBlockedReason?: (active: RowData, target: RowData) => string | null;
6
7
  canDrop: (active: RowData, target: RowData, placement: PsTableDropPlacement) => boolean;
7
8
  onMove: (active: RowData, target: RowData, placement: PsTableDropPlacement) => void;
8
9
  }
@@ -9,6 +9,7 @@ export interface PsTableReorder<RowData> {
9
9
  getHandleLabel: (rowLabel: string) => string;
10
10
  groupKey?: (row: RowData) => string;
11
11
  instructions: string;
12
+ isRowDraggable?: (row: RowData) => boolean;
12
13
  nesting?: PsTableReorderNesting<RowData>;
13
14
  onReorder?: (groupKey: string, orderedIds: string[]) => void;
14
15
  }
@@ -1,6 +1,7 @@
1
1
  import type { Modifier } from '@dnd-kit/core';
2
2
  import type { SortingStrategy } from '@dnd-kit/sortable';
3
3
  import { PsTableDropPlacement } from '../constants/ps-table-drop-placement';
4
+ import type { PsTableDragSize } from '../interfaces/table-drag-size';
4
5
  import type { PsTableReorderApi } from '../interfaces/table-reorder-api';
5
6
  import type { PsTableReorderPlacement } from '../interfaces/table-reorder-placement';
6
7
  import type { PsTableReorderRun } from '../interfaces/table-reorder-run';
@@ -11,6 +12,7 @@ export declare class PsTableReorderService {
11
12
  static runs<Item>(items: Item[], getKey: (item: Item) => string, getId: (item: Item) => string): PsTableReorderRun<Item>[];
12
13
  static placements<Item>(runs: PsTableReorderRun<Item>[], getId: (item: Item) => string): Map<string, PsTableReorderPlacement>;
13
14
  static move(ids: string[], activeId: string, overId: string): string[];
15
+ static measure(activator: Event | null): PsTableDragSize | null;
14
16
  static pointerY(event: Event | null, deltaY: number): number | null;
15
17
  static dropPlacement(pointerY: number | null, rect: {
16
18
  height: number;
@@ -36,6 +36,18 @@ class PsTableReorderService {
36
36
  next.splice(to, 0, moved);
37
37
  return next;
38
38
  }
39
+ static measure(activator) {
40
+ const target = activator === null ? null : activator.target;
41
+ const row = target instanceof Element ? target.closest('tr') : null;
42
+ if (row === null)
43
+ return null;
44
+ const rect = row.getBoundingClientRect();
45
+ return {
46
+ columnWidths: Array.from(row.children).map((cell) => cell.getBoundingClientRect().width),
47
+ height: rect.height,
48
+ width: rect.width,
49
+ };
50
+ }
39
51
  static pointerY(event, deltaY) {
40
52
  if (event instanceof MouseEvent)
41
53
  return event.clientY + deltaY;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pallasoft/ps-table-components",
3
- "version": "1.24.0",
3
+ "version": "1.26.0",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -78,7 +78,7 @@
78
78
  "@dnd-kit/core": "^6.3.1",
79
79
  "@dnd-kit/sortable": "^10.0.0",
80
80
  "@dnd-kit/utilities": "^3.2.2",
81
- "@pallasoft/ps-core-components": "^2.23.0",
81
+ "@pallasoft/ps-core-components": "^2.32.0",
82
82
  "@pallasoft/ps-icons": "^1.5.4",
83
83
  "@pallasoft/ps-input-components": "^1.14.0 || ^2.0.0",
84
84
  "@pallasoft/ps-motion-components": "^1.2.0",