@pallasoft/ps-table-components 1.22.2 → 1.24.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 (67) hide show
  1. package/dist/components/ps-data-table.d.ts +2 -1
  2. package/dist/components/ps-data-table.js +54 -15
  3. package/dist/components/ps-table-reorder-frame.js +15 -2
  4. package/dist/components/ps-table-reorder-handle.js +1 -1
  5. package/dist/components/ps-table-sort-header.js +1 -1
  6. package/dist/components/ps-table-sortable-row.d.ts +5 -1
  7. package/dist/components/ps-table-sortable-row.js +12 -4
  8. package/dist/constants/ps-table-drop-classes.d.ts +2 -0
  9. package/dist/constants/ps-table-drop-classes.js +8 -0
  10. package/dist/constants/ps-table-drop-placement.d.ts +6 -0
  11. package/dist/constants/ps-table-drop-placement.js +8 -0
  12. package/dist/constants/ps-table-pin-classes.d.ts +16 -0
  13. package/dist/constants/ps-table-pin-classes.js +19 -0
  14. package/dist/constants/table-breakpoint.d.ts +7 -0
  15. package/dist/constants/table-breakpoint.js +13 -0
  16. package/dist/esm/components/ps-data-table.js +55 -16
  17. package/dist/esm/components/ps-table-reorder-frame.js +15 -2
  18. package/dist/esm/components/ps-table-reorder-handle.js +1 -1
  19. package/dist/esm/components/ps-table-sort-header.js +1 -1
  20. package/dist/esm/components/ps-table-sortable-row.js +12 -4
  21. package/dist/esm/constants/ps-table-drop-classes.js +5 -0
  22. package/dist/esm/constants/ps-table-drop-placement.js +5 -0
  23. package/dist/esm/constants/ps-table-pin-classes.js +16 -0
  24. package/dist/esm/constants/table-breakpoint.js +10 -0
  25. package/dist/esm/hooks/use-ps-table-overflow.js +19 -0
  26. package/dist/esm/hooks/use-ps-table-reorder.js +89 -15
  27. package/dist/esm/index.js +10 -0
  28. package/dist/esm/interfaces/table-drop.js +1 -0
  29. package/dist/esm/interfaces/table-nesting-announcements.js +1 -0
  30. package/dist/esm/interfaces/table-overflow-edges.js +1 -0
  31. package/dist/esm/interfaces/table-pin-classes.js +1 -0
  32. package/dist/esm/interfaces/table-reorder-nesting-binding.js +1 -0
  33. package/dist/esm/interfaces/table-reorder-nesting.js +1 -0
  34. package/dist/esm/services/ps-table-column-size-service.js +7 -0
  35. package/dist/esm/services/ps-table-overflow-service.js +11 -0
  36. package/dist/esm/services/ps-table-pin-service.js +22 -0
  37. package/dist/esm/services/ps-table-reorder-service.js +38 -1
  38. package/dist/hooks/use-ps-table-overflow.d.ts +6 -0
  39. package/dist/hooks/use-ps-table-overflow.js +23 -0
  40. package/dist/hooks/use-ps-table-reorder.d.ts +6 -2
  41. package/dist/hooks/use-ps-table-reorder.js +87 -13
  42. package/dist/index.d.ts +10 -0
  43. package/dist/index.js +10 -0
  44. package/dist/interfaces/table-column.d.ts +5 -0
  45. package/dist/interfaces/table-drop.d.ts +5 -0
  46. package/dist/interfaces/table-drop.js +2 -0
  47. package/dist/interfaces/table-nesting-announcements.d.ts +5 -0
  48. package/dist/interfaces/table-nesting-announcements.js +2 -0
  49. package/dist/interfaces/table-overflow-edges.d.ts +4 -0
  50. package/dist/interfaces/table-overflow-edges.js +2 -0
  51. package/dist/interfaces/table-pin-classes.d.ts +5 -0
  52. package/dist/interfaces/table-pin-classes.js +2 -0
  53. package/dist/interfaces/table-reorder-nesting-binding.d.ts +8 -0
  54. package/dist/interfaces/table-reorder-nesting-binding.js +2 -0
  55. package/dist/interfaces/table-reorder-nesting.d.ts +8 -0
  56. package/dist/interfaces/table-reorder-nesting.js +2 -0
  57. package/dist/interfaces/table-reorder-row-props.d.ts +3 -0
  58. package/dist/interfaces/table-reorder.d.ts +4 -2
  59. package/dist/services/ps-table-column-size-service.d.ts +1 -0
  60. package/dist/services/ps-table-column-size-service.js +7 -0
  61. package/dist/services/ps-table-overflow-service.d.ts +6 -0
  62. package/dist/services/ps-table-overflow-service.js +15 -0
  63. package/dist/services/ps-table-pin-service.d.ts +5 -0
  64. package/dist/services/ps-table-pin-service.js +26 -0
  65. package/dist/services/ps-table-reorder-service.d.ts +11 -0
  66. package/dist/services/ps-table-reorder-service.js +38 -1
  67. package/package.json +1 -1
@@ -1,18 +1,21 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Fragment, Suspense, lazy, useDeferredValue, useEffect, useMemo, useRef, useState, } from 'react';
2
+ import { Fragment, Suspense, lazy, useCallback, useDeferredValue, useEffect, useMemo, useRef, useState, } from 'react';
3
3
  import { PsDragGrip, PsSkeletonTable } from '@pallasoft/ps-core-components';
4
4
  import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
5
5
  import { PsCheckbox } from '@pallasoft/ps-input-components';
6
6
  import { PsCollapse } from '@pallasoft/ps-motion-components';
7
7
  import { useVirtualizer, useWindowVirtualizer } from '@tanstack/react-virtual';
8
8
  import { PsTableControlClasses } from '../constants/ps-table-control-classes';
9
+ import { PsTableHideBelowClasses } from '../constants/table-breakpoint';
9
10
  import { PsTableConfig } from '../constants/table-config';
10
11
  import { usePsIncrementalCount } from '../hooks/use-ps-incremental-count';
11
12
  import { usePsScrollAnchor } from '../hooks/use-ps-scroll-anchor';
13
+ import { usePsTableOverflow } from '../hooks/use-ps-table-overflow';
12
14
  import { usePsTableStreamStatus } from '../hooks/use-ps-table-stream-status';
13
15
  import { PsTableCellTooltipService } from '../services/ps-table-cell-tooltip-service';
14
16
  import { PsTableColumnSizeService } from '../services/ps-table-column-size-service';
15
17
  import { PsTableGroupService } from '../services/ps-table-group-service';
18
+ import { PsTablePinService } from '../services/ps-table-pin-service';
16
19
  import { PsTableQueryService } from '../services/ps-table-query-service';
17
20
  import { PsTableReorderService } from '../services/ps-table-reorder-service';
18
21
  import { PsTableScrollService } from '../services/ps-table-scroll-service';
@@ -35,9 +38,9 @@ const densityClassNames = {
35
38
  head: 'px-5 py-3.5',
36
39
  },
37
40
  };
38
- const columnStyle = (column) => ({
39
- maxWidth: PsTableColumnSizeService.maxWidthOf(column),
40
- minWidth: PsTableColumnSizeService.minWidthOf(column),
41
+ const columnStyle = (column, fittedMinWidth) => ({
42
+ maxWidth: column.measure === undefined ? PsTableColumnSizeService.maxWidthOf(column) : undefined,
43
+ minWidth: fittedMinWidth === undefined ? PsTableColumnSizeService.minWidthOf(column) : fittedMinWidth,
41
44
  width: column.width,
42
45
  });
43
46
  const alignClassNames = {
@@ -45,7 +48,7 @@ const alignClassNames = {
45
48
  left: 'text-left',
46
49
  right: 'text-right tabular-nums',
47
50
  };
48
- 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, }) => {
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, }) => {
49
52
  const containerRef = useRef(null);
50
53
  const [uncontrolledPage, setUncontrolledPage] = useState(1);
51
54
  const [uncontrolledQuery, setUncontrolledQuery] = useState(() => PsTableQueryService.empty(defaultSort ? defaultSort : null));
@@ -56,6 +59,9 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
56
59
  const currentQuery = query ? query : uncontrolledQuery;
57
60
  const { filters, hiddenColumnIds, search, sort } = currentQuery;
58
61
  const visibleColumns = columns.filter((column) => !hiddenColumnIds.includes(column.id));
62
+ const fittedWidths = useMemo(() => new Map(columns
63
+ .filter((column) => column.measure !== undefined)
64
+ .map((column) => [column.id, PsTableColumnSizeService.fittedMinWidthOf(column, rows)])), [columns, rows]);
59
65
  const updateQuery = (patch) => {
60
66
  const next = { ...currentQuery, ...patch };
61
67
  if (!query)
@@ -70,6 +76,9 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
70
76
  const hasToolbar = isSearchable || hasFilters || Boolean(toolbarActions);
71
77
  const isSelectable = Boolean(getBulkActions);
72
78
  const columnCount = visibleColumns.length + (isSelectable ? 1 : 0) + (reorder ? 1 : 0);
79
+ const pins = PsTablePinService.leading(pinFirstColumn, Boolean(reorder), isSelectable);
80
+ const placementOf = (column, columnIndex) => PallasoftClassNameService.merge(column.hideBelow === undefined ? '' : PsTableHideBelowClasses[column.hideBelow], columnIndex === 0 ? pins.first : '', PsTablePinService.end(column.pinEnd === true));
81
+ const overflow = usePsTableOverflow(`${visibleColumns.map((column) => column.id).join(',')}:${rows.length}`);
73
82
  const deferredSearch = useDeferredValue(search);
74
83
  const queriedRows = useMemo(() => isServerPaginated
75
84
  ? rows
@@ -139,23 +148,48 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
139
148
  safePage,
140
149
  toggledGroupKeys,
141
150
  ]);
151
+ const reorderKey = useCallback((row) => reorder && reorder.groupKey ? reorder.groupKey(row) : PsTableReorderService.treeKey, [reorder]);
152
+ const nestingBinding = useMemo(() => {
153
+ if (reorder === undefined || reorder.nesting === undefined)
154
+ return undefined;
155
+ const { nesting } = reorder;
156
+ const find = (id) => visibleRows.find((candidate) => getRowId(candidate) === id);
157
+ return {
158
+ announcements: nesting.announcements,
159
+ canDrag: (id) => {
160
+ const row = find(id);
161
+ return row !== undefined && nesting.canDrag(row);
162
+ },
163
+ canDrop: (activeId, targetId, placement) => {
164
+ const active = find(activeId);
165
+ const target = find(targetId);
166
+ return (active !== undefined && target !== undefined && nesting.canDrop(active, target, placement));
167
+ },
168
+ onMove: (activeId, targetId, placement) => {
169
+ const active = find(activeId);
170
+ const target = find(targetId);
171
+ if (active !== undefined && target !== undefined)
172
+ nesting.onMove(active, target, placement);
173
+ },
174
+ };
175
+ }, [getRowId, reorder, visibleRows]);
142
176
  const reorderItems = useMemo(() => reorder
143
177
  ? visibleRows.map((row) => {
144
178
  const rowId = getRowId(row);
145
179
  const label = getRowLabel ? getRowLabel(row) : rowId;
146
180
  return {
147
- groupKey: reorder.groupKey(row),
181
+ groupKey: reorderKey(row),
148
182
  handleLabel: reorder.getHandleLabel(label),
149
183
  id: rowId,
150
184
  label,
151
185
  };
152
186
  })
153
- : [], [getRowId, getRowLabel, reorder, visibleRows]);
187
+ : [], [getRowId, getRowLabel, reorder, reorderKey, visibleRows]);
154
188
  const renderOverlayRow = (rowId) => {
155
189
  const row = visibleRows.find((candidate) => getRowId(candidate) === rowId);
156
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) }) }));
157
191
  };
158
- 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 }) }));
192
+ 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 }) }));
159
193
  const changePage = (next) => {
160
194
  if (onPageChange) {
161
195
  onPageChange(next);
@@ -239,7 +273,11 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
239
273
  const rowLabel = getRowLabel ? getRowLabel(row) : rowId;
240
274
  const Row = api.Row;
241
275
  const rowExtras = api.isActive
242
- ? { ...api.rowProps(rowId), measureRef: virtualItem ? virtualizer.measureElement : undefined }
276
+ ? {
277
+ ...api.rowProps(rowId),
278
+ handleCellClassName: pins.reorder,
279
+ measureRef: virtualItem ? virtualizer.measureElement : undefined,
280
+ }
243
281
  : { ref: virtualItem ? virtualizer.measureElement : undefined };
244
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
245
283
  ? (event) => {
@@ -252,13 +290,14 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
252
290
  }
253
291
  : undefined, style: motion && motion.delayMs !== undefined
254
292
  ? { animationDelay: `${motion.delayMs}ms` }
255
- : 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) ||
256
- PsTableColumnSizeService.maxWidthOf(column) !== undefined
293
+ : undefined, tabIndex: onRowClick ? 0 : undefined, children: [reorder && !api.isActive ? (_jsx("td", { className: PallasoftClassNameService.merge('w-9 min-w-9 pl-3 pr-0', pins.reorder) })) : null, isSelectable ? (_jsx("td", { className: PallasoftClassNameService.merge('w-10 min-w-10 pl-4 pr-0', pins.select), 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, columnIndex) => (_jsx("td", { className: PallasoftClassNameService.merge(densityClassNames[density].cell, placementOf(column, columnIndex), alignClassNames[column.align !== null && column.align !== undefined ? column.align : 'left'], column.wrap ? '' : 'whitespace-nowrap', column.measure === undefined &&
294
+ ((layout === 'fixed' && !column.wrap) ||
295
+ PsTableColumnSizeService.maxWidthOf(column) !== undefined)
257
296
  ? 'truncate'
258
- : '', '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));
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));
259
298
  };
260
299
  const renderEntries = (api, entries) => reorder
261
- ? 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))))
300
+ ? 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))))
262
301
  : entries.map((entry) => renderRow(api, entry.row, entry.motion, entry.virtualItem));
263
302
  if (isLoading)
264
303
  return (_jsx(PsSkeletonTable, { className: className, columns: visibleColumns.length, hasToolbar: hasToolbar, rows: PsTableConfig.skeletonRows }));
@@ -277,14 +316,14 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
277
316
  ? grouping.renderGroupSummary(group.rows)
278
317
  : undefined }), _jsx(PsCollapse, { isOpen: isExpanded && !closingGroupKeys.includes(group.key), children: renderCardList(group.rows, null) })] }, `group-${group.key}`));
279
318
  })
280
- : renderCardList(shouldVirtualize ? visibleRows.slice(0, cards.count) : visibleRows, visibleRows.length === 0 && isSearching ? (_jsx(PsTableSearchingState, { label: searchingLabel, skeletonRows: PsTableConfig.searchingSkeletonRows })) : visibleRows.length === 0 && isQueried ? (_jsxs("div", { className: 'px-4 py-10 text-center', children: [_jsx("p", { className: 'text-sm font-semibold text-ink', children: PsTableConfig.noMatchesTitle }), _jsx("button", { className: PallasoftClassNameService.merge(PsTableControlClasses.textButton, 'mt-2 text-primary'), onClick: clearFilters, type: 'button', children: "Clear search and filters" })] })) : null), !grouping && shouldVirtualize && cards.count < visibleRows.length ? (_jsx("div", { "aria-hidden": 'true', className: 'h-px', ref: cards.sentinelRef })) : null] })) : null, _jsx("div", { className: PallasoftClassNameService.merge('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) => {
319
+ : renderCardList(shouldVirtualize ? visibleRows.slice(0, cards.count) : visibleRows, visibleRows.length === 0 && isSearching ? (_jsx(PsTableSearchingState, { label: searchingLabel, skeletonRows: PsTableConfig.searchingSkeletonRows })) : visibleRows.length === 0 && isQueried ? (_jsxs("div", { className: 'px-4 py-10 text-center', children: [_jsx("p", { className: 'text-sm font-semibold text-ink', children: PsTableConfig.noMatchesTitle }), _jsx("button", { className: PallasoftClassNameService.merge(PsTableControlClasses.textButton, 'mt-2 text-primary'), onClick: clearFilters, type: 'button', children: "Clear search and filters" })] })) : null), !grouping && shouldVirtualize && cards.count < visibleRows.length ? (_jsx("div", { "aria-hidden": 'true', className: 'h-px', ref: cards.sentinelRef })) : null] })) : null, _jsx("div", { className: PallasoftClassNameService.merge('ps-scrollbar overflow-x-auto overflow-y-hidden overscroll-x-contain', mobileLayout === 'cards' ? 'hidden sm:block' : ''), "data-overflow-end": overflow.edges.end, "data-overflow-start": overflow.edges.start, onScroll: overflow.update, ref: overflow.ref, children: renderReorder((api) => (_jsxs("table", { className: PallasoftClassNameService.merge('w-full border-collapse text-left', layout === 'fixed' ? 'table-fixed' : ''), children: [caption ? _jsx("caption", { className: 'sr-only', children: caption }) : null, _jsx("thead", { className: 'bg-mist/70 text-[0.68rem] font-semibold uppercase tracking-[0.18em] text-ink/45', children: _jsxs("tr", { children: [reorder ? (_jsx("th", { className: PallasoftClassNameService.merge('w-9 min-w-9 pl-3 pr-0', pins.reorder), scope: 'col', children: reorder.enabled ? (_jsx("span", { className: 'sr-only', children: reorder.columnLabel })) : reorder.disabledReason ? (_jsx("span", { "aria-label": reorder.disabledReason, className: 'inline-flex cursor-help opacity-40', role: 'img', title: reorder.disabledReason, children: _jsx(PsDragGrip, {}) })) : null })) : null, isSelectable ? (_jsx("th", { className: PallasoftClassNameService.merge('w-10 min-w-10 pl-4 pr-0', pins.select), scope: 'col', children: _jsx(PsCheckbox, { isIndeterminate: !isPageSelected && pageIds.some((rowId) => selectedIdSet.has(rowId)), isSelected: isPageSelected, label: 'Select all rows on this page', onChange: togglePage }) })) : null, visibleColumns.map((column, columnIndex) => {
281
320
  const align = column.align !== null && column.align !== undefined ? column.align : 'left';
282
321
  const isSorted = sort !== null && sort.columnId === column.id;
283
322
  return (_jsx("th", { "aria-sort": isSorted
284
323
  ? sort.direction
285
324
  : column.sortValue && isQueryable
286
325
  ? 'none'
287
- : 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));
326
+ : undefined, className: PallasoftClassNameService.merge(densityClassNames[density].head, alignClassNames[align], placementOf(column, columnIndex), 'whitespace-nowrap'), scope: 'col', style: columnStyle(column, fittedWidths.get(column.id)), children: column.sortValue && isQueryable ? (_jsx(PsTableSortHeader, { align: align, columnId: column.id, label: column.label, onSort: updateSort, sort: sort })) : (column.label) }, column.id));
288
327
  })] }) }), _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: PallasoftClassNameService.merge(PsTableControlClasses.textButton, 'mt-3 text-primary hover:underline'), onClick: clearFilters, type: 'button', children: "Clear search and filters" })] }) })) : null, grouping
289
328
  ? groups.map((group, groupIndex) => {
290
329
  const isExpanded = isGroupExpanded(group.key, groupIndex);
@@ -306,7 +345,7 @@ export const PsDataTable = ({ caption, className, columns, defaultSort, density
306
345
  }))),
307
346
  _jsx("tr", { "aria-hidden": 'true', children: _jsx("td", { className: 'p-0', colSpan: columnCount, style: { height: virtualBottom } }) }, 'virtual-bottom'),
308
347
  ]
309
- : 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
348
+ : 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
310
349
  ? column.align
311
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] })] }));
312
351
  };
@@ -3,24 +3,37 @@ import { DndContext } from '@dnd-kit/core';
3
3
  import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
4
4
  import { PsDragClasses, PsDragListLiftFrames, PsDragOverlay } from '@pallasoft/ps-core-components';
5
5
  import { usePsTableReorder } from '../hooks/use-ps-table-reorder';
6
+ import { PsTableReorderService } from '../services/ps-table-reorder-service';
6
7
  import { PsTableSortableRow } from './ps-table-sortable-row';
7
8
  export const PsTableReorderFrame = ({ children, renderOverlay, ...input }) => {
8
- const { activeId, activeRunId, contextProps, placements } = usePsTableReorder(input);
9
+ const { activeId, activeRunId, contextProps, drop, placements } = usePsTableReorder(input);
9
10
  const itemById = new Map(input.items.map((item) => [item.id, item]));
11
+ const { nesting } = input;
10
12
  const api = {
11
13
  isActive: true,
12
14
  Row: PsTableSortableRow,
13
15
  rowProps: (rowId) => {
14
16
  const item = itemById.get(rowId);
15
17
  const placement = placements.get(rowId);
18
+ if (nesting !== undefined)
19
+ return {
20
+ dropPlacement: drop !== null && drop.overId === rowId ? drop.placement : null,
21
+ handleLabel: item === undefined ? '' : item.handleLabel,
22
+ hasHandle: item !== undefined && input.enabled && nesting.canDrag(rowId),
23
+ isDisabled: !input.enabled,
24
+ isDropTarget: item !== undefined,
25
+ rowId,
26
+ };
16
27
  return {
28
+ dropPlacement: null,
17
29
  handleLabel: item === undefined ? '' : item.handleLabel,
18
30
  hasHandle: item !== undefined && input.enabled && placement !== undefined && placement.size > 1,
19
31
  isDisabled: activeRunId !== null && (placement === undefined || placement.runId !== activeRunId),
32
+ isDropTarget: false,
20
33
  rowId,
21
34
  };
22
35
  },
23
- run: (runId, ids, runChildren) => (_jsx(SortableContext, { items: ids, strategy: verticalListSortingStrategy, children: runChildren }, runId)),
36
+ run: (runId, ids, runChildren) => (_jsx(SortableContext, { items: ids, strategy: nesting === undefined ? verticalListSortingStrategy : PsTableReorderService.still, children: runChildren }, runId)),
24
37
  };
25
38
  return (_jsxs(DndContext, { ...contextProps, children: [children(api), _jsx(PsDragOverlay, { className: PsDragClasses.lifted, frames: PsDragListLiftFrames, children: activeId === null ? null : renderOverlay(activeId) })] }));
26
39
  };
@@ -1,4 +1,4 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { PsDragGrip } from '@pallasoft/ps-core-components';
3
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-ps-control 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, {}) }));
4
+ export const PsTableReorderHandle = ({ activatorRef, className, label, ...buttonProps }) => (_jsx("button", { ...buttonProps, "aria-label": label, className: PallasoftClassNameService.merge('ps-focus-ring ps-touch-target relative flex h-7 w-6 shrink-0 cursor-grab touch-none items-center justify-center rounded-ps-control 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, {}) }));
@@ -10,5 +10,5 @@ export const PsTableSortHeader = ({ align, columnId, label, onSort, sort, }) =>
10
10
  : PsIconKey.SortDescending;
11
11
  return (_jsxs("button", { className: PallasoftClassNameService.merge('ps-focus-ring group inline-flex items-center gap-1 rounded-ps-control uppercase tracking-[0.18em] transition-colors hover:text-ink', align === 'right' ? 'flex-row-reverse' : '', isSorted ? 'text-ink' : ''), onClick: () => onSort(columnId), type: 'button', children: [_jsx("span", { children: label }), _jsx(PsIcon, { "aria-hidden": 'true', className: PallasoftClassNameService.merge('block h-3.5 w-3.5 shrink-0', isSorted
12
12
  ? 'text-primary'
13
- : 'opacity-0 group-hover:opacity-60 group-focus-visible:opacity-60'), iconKey: iconKey })] }));
13
+ : 'opacity-0 group-hover:opacity-60 group-focus-visible:opacity-60 [@media(hover:none)]:opacity-40'), iconKey: iconKey })] }));
14
14
  };
@@ -2,10 +2,18 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useSortable } from '@dnd-kit/sortable';
3
3
  import { CSS } from '@dnd-kit/utilities';
4
4
  import { PsDragConfig } from '@pallasoft/ps-core-components';
5
+ import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
6
+ import { PsTableDropClasses } from '../constants/ps-table-drop-classes';
5
7
  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) => {
8
+ export const PsTableSortableRow = ({ children, className, dropPlacement = null, handleCellClassName, handleLabel, hasHandle, isDisabled, isDropTarget = false, measureRef, rowId, style, ...rowProps }) => {
9
+ const { attributes, isDragging, listeners, setActivatorNodeRef, setNodeRef, transform, transition, } = useSortable({
10
+ disabled: {
11
+ draggable: isDisabled || !hasHandle,
12
+ droppable: isDisabled || (!hasHandle && !isDropTarget),
13
+ },
14
+ id: rowId,
15
+ });
16
+ return (_jsxs("tr", { ...rowProps, className: PallasoftClassNameService.merge(className, dropPlacement === null ? '' : PsTableDropClasses[dropPlacement]), ref: (element) => {
9
17
  setNodeRef(element);
10
18
  if (measureRef)
11
19
  measureRef(element);
@@ -14,5 +22,5 @@ export const PsTableSortableRow = ({ children, className, handleLabel, hasHandle
14
22
  opacity: isDragging ? PsDragConfig.placeholderOpacity : style?.opacity,
15
23
  transform: CSS.Translate.toString(transform),
16
24
  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] }));
25
+ }, children: [_jsx("td", { className: PallasoftClassNameService.merge('w-9 min-w-9 pl-3 pr-0', handleCellClassName), onClick: (event) => event.stopPropagation(), children: hasHandle ? (_jsx(PsTableReorderHandle, { ...attributes, ...listeners, activatorRef: setActivatorNodeRef, label: handleLabel })) : null }), children] }));
18
26
  };
@@ -0,0 +1,5 @@
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))]',
4
+ into: 'bg-primary/10 [&>td]:shadow-[inset_0_0_0_2px_rgb(var(--ps-color-primary-rgb)/0.5)]',
5
+ };
@@ -0,0 +1,5 @@
1
+ export const PsTableDropPlacement = {
2
+ after: 'after',
3
+ before: 'before',
4
+ into: 'into',
5
+ };
@@ -0,0 +1,16 @@
1
+ export const PsTablePinClasses = {
2
+ cell: 'ps-table-pin',
3
+ edge: 'ps-table-pin-start-edge',
4
+ end: 'ps-table-pin ps-table-pin-end right-0',
5
+ first: {
6
+ both: 'left-[4.75rem]',
7
+ none: 'left-0',
8
+ reorder: 'left-9',
9
+ select: 'left-10',
10
+ },
11
+ reorder: 'left-0',
12
+ select: {
13
+ alone: 'left-0',
14
+ withReorder: 'left-9',
15
+ },
16
+ };
@@ -0,0 +1,10 @@
1
+ export const PsTableBreakpoint = {
2
+ md: 'md',
3
+ lg: 'lg',
4
+ xl: 'xl',
5
+ };
6
+ export const PsTableHideBelowClasses = {
7
+ md: 'hidden md:table-cell',
8
+ lg: 'hidden lg:table-cell',
9
+ xl: 'hidden xl:table-cell',
10
+ };
@@ -0,0 +1,19 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { PsTableOverflowService } from '../services/ps-table-overflow-service';
3
+ export const usePsTableOverflow = (layoutKey) => {
4
+ const ref = useRef(null);
5
+ const [edges, setEdges] = useState(PsTableOverflowService.none);
6
+ const update = useCallback(() => {
7
+ const element = ref.current;
8
+ if (element === null)
9
+ return;
10
+ const next = PsTableOverflowService.edges(element.scrollLeft, element.clientWidth, element.scrollWidth);
11
+ setEdges((current) => (PsTableOverflowService.isSame(current, next) ? current : next));
12
+ }, []);
13
+ useEffect(() => {
14
+ update();
15
+ window.addEventListener('resize', update);
16
+ return () => window.removeEventListener('resize', update);
17
+ }, [layoutKey, update]);
18
+ return { edges, ref, update };
19
+ };
@@ -1,9 +1,16 @@
1
- import { useMemo, useState } from 'react';
2
- import { closestCenter, } from '@dnd-kit/core';
1
+ import { useMemo, useRef, useState } from 'react';
2
+ import { closestCenter, pointerWithin, } from '@dnd-kit/core';
3
3
  import { usePsDragSensors } from '@pallasoft/ps-core-components';
4
+ import { PsTableDropPlacement } from '../constants/ps-table-drop-placement';
4
5
  import { PsTableReorderService } from '../services/ps-table-reorder-service';
5
- export const usePsTableReorder = ({ announcements: texts, enabled, instructions, items, onReorder, }) => {
6
+ const pointerThenCenter = (args) => {
7
+ const within = pointerWithin(args);
8
+ return within.length > 0 ? within : closestCenter(args);
9
+ };
10
+ export const usePsTableReorder = ({ announcements: texts, enabled, instructions, items, nesting, onReorder, }) => {
6
11
  const [activeId, setActiveId] = useState(null);
12
+ const [drop, setDrop] = useState(null);
13
+ const dropRef = useRef(null);
7
14
  const sensors = usePsDragSensors();
8
15
  const runs = useMemo(() => enabled
9
16
  ? PsTableReorderService.runs(items, (item) => item.groupKey, (item) => item.id)
@@ -11,6 +18,7 @@ export const usePsTableReorder = ({ announcements: texts, enabled, instructions,
11
18
  const placements = useMemo(() => PsTableReorderService.placements(runs, (item) => item.id), [runs]);
12
19
  const activePlacement = activeId === null ? undefined : placements.get(activeId);
13
20
  const itemById = useMemo(() => new Map(items.map((item) => [item.id, item])), [items]);
21
+ const indexById = useMemo(() => new Map(items.map((item, index) => [item.id, index])), [items]);
14
22
  const labelOf = (id) => {
15
23
  const item = itemById.get(id);
16
24
  return item === undefined ? id : item.label;
@@ -23,23 +31,83 @@ export const usePsTableReorder = ({ announcements: texts, enabled, instructions,
23
31
  const placement = placements.get(id);
24
32
  return placement === undefined ? 0 : placement.size;
25
33
  };
34
+ const edgeFor = (activeKey, overId) => {
35
+ const activeIndex = indexById.get(activeKey);
36
+ const overIndex = indexById.get(overId);
37
+ return activeIndex !== undefined && overIndex !== undefined && activeIndex < overIndex
38
+ ? PsTableDropPlacement.after
39
+ : PsTableDropPlacement.before;
40
+ };
41
+ const resolveDrop = (event) => {
42
+ if (nesting === undefined || event.over === null || event.over.id === event.active.id)
43
+ return null;
44
+ const activeKey = String(event.active.id);
45
+ const overId = String(event.over.id);
46
+ const placement = PsTableReorderService.dropPlacement(PsTableReorderService.pointerY(event.activatorEvent, event.delta.y), event.over.rect, edgeFor(activeKey, overId), (candidate) => nesting.canDrop(activeKey, overId, candidate));
47
+ return placement === null ? null : { overId, placement };
48
+ };
49
+ const announcedDrop = (activeKey, overId) => {
50
+ if (nesting === undefined)
51
+ return null;
52
+ const known = dropRef.current;
53
+ if (known !== null && known.overId === overId)
54
+ return known;
55
+ const placement = edgeFor(activeKey, overId);
56
+ return nesting.canDrop(activeKey, overId, placement) ? { overId, placement } : null;
57
+ };
58
+ const describeDrop = (active, target) => nesting === undefined
59
+ ? undefined
60
+ : nesting.announcements[target.placement](labelOf(active), labelOf(target.overId));
26
61
  const announcements = {
27
62
  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))),
63
+ onDragEnd: ({ active, over }) => {
64
+ if (nesting !== undefined) {
65
+ const target = over === null ? null : announcedDrop(String(active.id), String(over.id));
66
+ return target === null
67
+ ? texts.cancelled(labelOf(String(active.id)))
68
+ : describeDrop(String(active.id), target);
69
+ }
70
+ return over === null
71
+ ? texts.cancelled(labelOf(String(active.id)))
72
+ : texts.dropped(labelOf(String(active.id)), positionOf(String(over.id)), countOf(String(over.id)));
73
+ },
74
+ onDragOver: ({ active, over }) => {
75
+ if (over === null)
76
+ return undefined;
77
+ if (nesting !== undefined) {
78
+ const target = announcedDrop(String(active.id), String(over.id));
79
+ return target === null ? undefined : describeDrop(String(active.id), target);
80
+ }
81
+ return texts.moved(labelOf(String(active.id)), positionOf(String(over.id)), countOf(String(over.id)));
82
+ },
34
83
  onDragStart: ({ active }) => texts.lifted(labelOf(String(active.id)), positionOf(String(active.id)), countOf(String(active.id))),
35
84
  };
36
- const handleDragEnd = ({ active, over }) => {
85
+ const handleDragMove = (event) => {
86
+ const next = resolveDrop(event);
87
+ dropRef.current = next;
88
+ setDrop((current) => current !== null &&
89
+ next !== null &&
90
+ current.overId === next.overId &&
91
+ current.placement === next.placement
92
+ ? current
93
+ : next);
94
+ };
95
+ const handleDragEnd = (event) => {
96
+ const { active, over } = event;
97
+ const target = resolveDrop(event);
37
98
  setActiveId(null);
38
- if (over === null || active.id === over.id)
99
+ setDrop(null);
100
+ dropRef.current = null;
101
+ if (nesting !== undefined) {
102
+ if (target !== null)
103
+ nesting.onMove(String(active.id), target.overId, target.placement);
104
+ return;
105
+ }
106
+ if (over === null || active.id === over.id || onReorder === undefined)
39
107
  return;
40
108
  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)
109
+ const sibling = placements.get(String(over.id));
110
+ if (placement === undefined || sibling === undefined || placement.runId !== sibling.runId)
43
111
  return;
44
112
  const run = runs.find((candidate) => candidate.id === placement.runId);
45
113
  if (run === undefined)
@@ -51,13 +119,19 @@ export const usePsTableReorder = ({ announcements: texts, enabled, instructions,
51
119
  activeRunId: activePlacement === undefined ? null : activePlacement.runId,
52
120
  contextProps: {
53
121
  accessibility: { announcements, screenReaderInstructions: { draggable: instructions } },
54
- collisionDetection: closestCenter,
122
+ collisionDetection: nesting === undefined ? closestCenter : pointerThenCenter,
55
123
  modifiers: [PsTableReorderService.lockToVerticalAxis],
56
- onDragCancel: () => setActiveId(null),
124
+ onDragCancel: () => {
125
+ setActiveId(null);
126
+ setDrop(null);
127
+ dropRef.current = null;
128
+ },
57
129
  onDragEnd: handleDragEnd,
130
+ onDragMove: nesting === undefined ? undefined : handleDragMove,
58
131
  onDragStart: ({ active }) => setActiveId(String(active.id)),
59
132
  sensors,
60
133
  },
134
+ drop,
61
135
  placements,
62
136
  };
63
137
  };
package/dist/esm/index.js CHANGED
@@ -3,7 +3,9 @@ export * from './components/ps-streamed-rows';
3
3
  export * from './components/ps-table-stream-status';
4
4
  export * from './components/ps-table-group-header';
5
5
  export * from './components/ps-table-pagination';
6
+ export * from './constants/table-breakpoint';
6
7
  export * from './constants/table-card-role';
8
+ export * from './constants/ps-table-pin-classes';
7
9
  export * from './constants/table-column-size';
8
10
  export * from './constants/table-filter-kind';
9
11
  export * from './interfaces/table-column';
@@ -27,6 +29,11 @@ export * from './services/ps-table-group-service';
27
29
  export * from './services/ps-table-query-service';
28
30
  export * from './services/ps-table-search-service';
29
31
  export * from './services/ps-table-scroll-service';
32
+ export * from './services/ps-table-pin-service';
33
+ export * from './services/ps-table-overflow-service';
34
+ export * from './hooks/use-ps-table-overflow';
35
+ export * from './interfaces/table-pin-classes';
36
+ export * from './interfaces/table-overflow-edges';
30
37
  export * from './services/ps-table-stream-service';
31
38
  export * from './components/ps-table-export-button';
32
39
  export * from './constants/ps-table-download-config';
@@ -36,4 +43,7 @@ export * from './hooks/use-ps-table-page';
36
43
  export * from './interfaces/ps-table-page-state';
37
44
  export * from './interfaces/table-reorder';
38
45
  export * from './interfaces/table-reorder-announcements';
46
+ export * from './interfaces/table-reorder-nesting';
47
+ export * from './interfaces/table-nesting-announcements';
48
+ export * from './constants/ps-table-drop-placement';
39
49
  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 {};
@@ -1,6 +1,13 @@
1
1
  import { PsTableColumnMaxWidths, PsTableColumnSize, PsTableColumnWidths, } from '../constants/table-column-size';
2
2
  import { PsTableFilterKind } from '../constants/table-filter-kind';
3
3
  export class PsTableColumnSizeService {
4
+ static fittedMinWidthOf(column, rows) {
5
+ const measure = column.measure;
6
+ if (measure === undefined)
7
+ return undefined;
8
+ const longest = rows.reduce((length, row) => Math.max(length, measure(row).length), 0);
9
+ return `max(${PsTableColumnSizeService.minWidthOf(column)}, calc(${(longest * 1.1).toFixed(1)}ch + 3rem))`;
10
+ }
4
11
  static sizeOf(column) {
5
12
  if (column.size !== undefined)
6
13
  return column.size;
@@ -0,0 +1,11 @@
1
+ export class PsTableOverflowService {
2
+ static edges(scrollLeft, clientWidth, scrollWidth) {
3
+ const start = Math.abs(scrollLeft) > 0.5;
4
+ const end = scrollWidth - clientWidth - Math.abs(scrollLeft) > 0.5;
5
+ return { end, start };
6
+ }
7
+ static isSame(current, next) {
8
+ return current.end === next.end && current.start === next.start;
9
+ }
10
+ }
11
+ PsTableOverflowService.none = { end: false, start: false };
@@ -0,0 +1,22 @@
1
+ import { PsTablePinClasses } from '../constants/ps-table-pin-classes';
2
+ export class PsTablePinService {
3
+ static leading(isPinned, hasReorder, isSelectable) {
4
+ if (!isPinned)
5
+ return { first: '', reorder: '', select: '' };
6
+ const firstOffset = hasReorder && isSelectable
7
+ ? PsTablePinClasses.first.both
8
+ : hasReorder
9
+ ? PsTablePinClasses.first.reorder
10
+ : isSelectable
11
+ ? PsTablePinClasses.first.select
12
+ : PsTablePinClasses.first.none;
13
+ return {
14
+ first: `${PsTablePinClasses.cell} ${PsTablePinClasses.edge} ${firstOffset}`,
15
+ reorder: `${PsTablePinClasses.cell} ${PsTablePinClasses.reorder}`,
16
+ select: `${PsTablePinClasses.cell} ${hasReorder ? PsTablePinClasses.select.withReorder : PsTablePinClasses.select.alone}`,
17
+ };
18
+ }
19
+ static end(isPinned) {
20
+ return isPinned ? PsTablePinClasses.end : '';
21
+ }
22
+ }