@dbcdk/react-components 0.0.198 → 0.0.200

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 (27) hide show
  1. package/dist/components/json-viewer/JsonViewer.module.css +5 -0
  2. package/dist/components/json-viewer/JsonViewerShell.cjs +2 -2
  3. package/dist/components/json-viewer/JsonViewerShell.js +2 -2
  4. package/dist/components/line-viewer/LineViewer.cjs +4 -13
  5. package/dist/components/line-viewer/LineViewer.d.ts +6 -1
  6. package/dist/components/line-viewer/LineViewer.js +5 -14
  7. package/dist/components/line-viewer/LineViewer.module.css +2 -0
  8. package/dist/components/table/TanstackTable.cjs +42 -85
  9. package/dist/components/table/TanstackTable.d.ts +5 -0
  10. package/dist/components/table/TanstackTable.js +43 -83
  11. package/dist/components/table/hooks/useClientPagination.cjs +66 -0
  12. package/dist/components/table/hooks/useClientPagination.d.ts +32 -0
  13. package/dist/components/table/hooks/useClientPagination.js +64 -0
  14. package/dist/components/table/hooks/useDistributedLayout.cjs +44 -0
  15. package/dist/components/table/hooks/useDistributedLayout.d.ts +16 -0
  16. package/dist/components/table/hooks/useDistributedLayout.js +42 -0
  17. package/dist/components/table/hooks/useResizeHeaderExtras.cjs +87 -0
  18. package/dist/components/table/hooks/useResizeHeaderExtras.d.ts +17 -0
  19. package/dist/components/table/hooks/useResizeHeaderExtras.js +62 -0
  20. package/dist/components/table/hooks/useUiSorting.cjs +18 -0
  21. package/dist/components/table/hooks/useUiSorting.d.ts +3 -0
  22. package/dist/components/table/hooks/useUiSorting.js +16 -0
  23. package/dist/components/xml-viewer/XmlViewer.cjs +4 -13
  24. package/dist/components/xml-viewer/XmlViewer.d.ts +6 -1
  25. package/dist/components/xml-viewer/XmlViewer.js +5 -14
  26. package/dist/components/xml-viewer/XmlViewer.module.css +2 -0
  27. package/package.json +1 -1
@@ -23,6 +23,10 @@
23
23
 
24
24
  display: flex;
25
25
  flex-direction: column;
26
+ /* In a parent with a definite height, scroll inside instead of overflowing it. */
27
+ max-height: 100%;
28
+ /* As a flex item, shrink below the content height instead of overflowing the parent. */
29
+ min-height: 0;
26
30
  gap: var(--spacing-xs);
27
31
  background: var(--json-pane-bg);
28
32
  color: var(--json-pane-text);
@@ -260,6 +264,7 @@
260
264
  text-align: left;
261
265
  cursor: pointer;
262
266
  font: inherit;
267
+ user-select: text;
263
268
  }
264
269
 
265
270
  .toggle {
@@ -186,7 +186,7 @@ function JsonViewerShell({
186
186
  {
187
187
  ref: scrollRef,
188
188
  className: [styles__default.default.rawPane, deleted ? styles__default.default.deleted : ""].filter(Boolean).join(" "),
189
- style: maxHeight !== void 0 ? { maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight } : void 0,
189
+ style: { maxHeight },
190
190
  children: [
191
191
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.floatingSpacer, "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(Button.Button, { variant: "inline", tabIndex: -1, children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { customIcon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings2, {}) }) }) }),
192
192
  rawJson
@@ -200,7 +200,7 @@ function JsonViewerShell({
200
200
  role: "tree",
201
201
  "aria-label": "JSON-visning",
202
202
  ref: scrollRef,
203
- style: maxHeight !== void 0 ? { maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight } : void 0,
203
+ style: { maxHeight },
204
204
  children: hasResults ? children : /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.emptyState, children: emptySearchText })
205
205
  }
206
206
  ) })
@@ -180,7 +180,7 @@ function JsonViewerShell({
180
180
  {
181
181
  ref: scrollRef,
182
182
  className: [styles.rawPane, deleted ? styles.deleted : ""].filter(Boolean).join(" "),
183
- style: maxHeight !== void 0 ? { maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight } : void 0,
183
+ style: { maxHeight },
184
184
  children: [
185
185
  /* @__PURE__ */ jsx("span", { className: styles.floatingSpacer, "aria-hidden": "true", children: /* @__PURE__ */ jsx(Button, { variant: "inline", tabIndex: -1, children: /* @__PURE__ */ jsx(Icon, { customIcon: /* @__PURE__ */ jsx(Settings2, {}) }) }) }),
186
186
  rawJson
@@ -194,7 +194,7 @@ function JsonViewerShell({
194
194
  role: "tree",
195
195
  "aria-label": "JSON-visning",
196
196
  ref: scrollRef,
197
- style: maxHeight !== void 0 ? { maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight } : void 0,
197
+ style: { maxHeight },
198
198
  children: hasResults ? children : /* @__PURE__ */ jsx("div", { className: styles.emptyState, children: emptySearchText })
199
199
  }
200
200
  ) })
@@ -35,10 +35,10 @@ function LineViewer({
35
35
  defaultViewMode = "highlighted",
36
36
  deleted = false,
37
37
  storageKey,
38
- borderless = false
38
+ borderless = false,
39
+ maxHeight
39
40
  }) {
40
41
  const tokens = react.useMemo(() => tokenizeLineFormat.tokenizeLineFormat(lines), [lines]);
41
- const codeRef = react.useRef(null);
42
42
  const { value: currentAppearance, setValue: setCurrentAppearance } = usePersistentState.usePersistentState({
43
43
  initialValue: appearance,
44
44
  storageKey: storageKey ? `${storageKey}:appearance` : void 0,
@@ -57,15 +57,6 @@ function LineViewer({
57
57
  const handleCopy = () => {
58
58
  void clipboard_utils.copyToClipboard(lines);
59
59
  };
60
- const handleDoubleClick = () => {
61
- const el = codeRef.current;
62
- const selection = typeof window !== "undefined" ? window.getSelection() : null;
63
- if (!el || !selection) return;
64
- const range = document.createRange();
65
- range.selectNodeContents(el);
66
- selection.removeAllRanges();
67
- selection.addRange(range);
68
- };
69
60
  return /* @__PURE__ */ jsxRuntime.jsxs(
70
61
  "section",
71
62
  {
@@ -167,11 +158,11 @@ function LineViewer({
167
158
  wrap ? styles__default.default.wrap : styles__default.default.noWrap,
168
159
  deleted ? styles__default.default.deleted : ""
169
160
  ].filter(Boolean).join(" "),
161
+ style: { maxHeight },
170
162
  tabIndex: 0,
171
- onDoubleClick: handleDoubleClick,
172
163
  children: [
173
164
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.toolbarSpacer, "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(Button.Button, { variant: "inline", tabIndex: -1, children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { customIcon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings2, {}) }) }) }),
174
- /* @__PURE__ */ jsxRuntime.jsx("code", { ref: codeRef, children: viewMode === "raw" ? lines : tokens.map((token, index) => {
165
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: viewMode === "raw" ? lines : tokens.map((token, index) => {
175
166
  const tokenClassName = TOKEN_CLASS[token.type];
176
167
  return tokenClassName ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: tokenClassName, children: token.value }, index) : token.value;
177
168
  }) })
@@ -21,5 +21,10 @@ export interface LineViewerProps {
21
21
  * modal).
22
22
  */
23
23
  borderless?: boolean;
24
+ /**
25
+ * Caps the height of the scrolling pane (a number is pixels), e.g. when the
26
+ * parent has no definite height for the viewer to fill.
27
+ */
28
+ maxHeight?: number | string;
24
29
  }
25
- export declare function LineViewer({ lines, size, appearance, defaultViewMode, deleted, storageKey, borderless, }: LineViewerProps): JSX.Element;
30
+ export declare function LineViewer({ lines, size, appearance, defaultViewMode, deleted, storageKey, borderless, maxHeight, }: LineViewerProps): JSX.Element;
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { TextWrap, ListTree, Code2, Sun, Moon, ClipboardCopy, Settings2 } from 'lucide-react';
4
- import { useMemo, useRef } from 'react';
4
+ import { useMemo } from 'react';
5
5
  import { Menu } from '../../components/menu/Menu';
6
6
  import { Popover } from '../../components/popover/Popover';
7
7
  import { ScrollFade } from '../../components/scroll-fade/ScrollFade';
@@ -29,10 +29,10 @@ function LineViewer({
29
29
  defaultViewMode = "highlighted",
30
30
  deleted = false,
31
31
  storageKey,
32
- borderless = false
32
+ borderless = false,
33
+ maxHeight
33
34
  }) {
34
35
  const tokens = useMemo(() => tokenizeLineFormat(lines), [lines]);
35
- const codeRef = useRef(null);
36
36
  const { value: currentAppearance, setValue: setCurrentAppearance } = usePersistentState({
37
37
  initialValue: appearance,
38
38
  storageKey: storageKey ? `${storageKey}:appearance` : void 0,
@@ -51,15 +51,6 @@ function LineViewer({
51
51
  const handleCopy = () => {
52
52
  void copyToClipboard(lines);
53
53
  };
54
- const handleDoubleClick = () => {
55
- const el = codeRef.current;
56
- const selection = typeof window !== "undefined" ? window.getSelection() : null;
57
- if (!el || !selection) return;
58
- const range = document.createRange();
59
- range.selectNodeContents(el);
60
- selection.removeAllRanges();
61
- selection.addRange(range);
62
- };
63
54
  return /* @__PURE__ */ jsxs(
64
55
  "section",
65
56
  {
@@ -161,11 +152,11 @@ function LineViewer({
161
152
  wrap ? styles.wrap : styles.noWrap,
162
153
  deleted ? styles.deleted : ""
163
154
  ].filter(Boolean).join(" "),
155
+ style: { maxHeight },
164
156
  tabIndex: 0,
165
- onDoubleClick: handleDoubleClick,
166
157
  children: [
167
158
  /* @__PURE__ */ jsx("span", { className: styles.toolbarSpacer, "aria-hidden": "true", children: /* @__PURE__ */ jsx(Button, { variant: "inline", tabIndex: -1, children: /* @__PURE__ */ jsx(Icon, { customIcon: /* @__PURE__ */ jsx(Settings2, {}) }) }) }),
168
- /* @__PURE__ */ jsx("code", { ref: codeRef, children: viewMode === "raw" ? lines : tokens.map((token, index) => {
159
+ /* @__PURE__ */ jsx("code", { children: viewMode === "raw" ? lines : tokens.map((token, index) => {
169
160
  const tokenClassName = TOKEN_CLASS[token.type];
170
161
  return tokenClassName ? /* @__PURE__ */ jsx("span", { className: tokenClassName, children: token.value }, index) : token.value;
171
162
  }) })
@@ -25,6 +25,8 @@
25
25
  flex-direction: column;
26
26
  /* In a parent with a definite height, scroll inside instead of overflowing it. */
27
27
  max-height: 100%;
28
+ /* As a flex item, shrink below the content height instead of overflowing the parent. */
29
+ min-height: 0;
28
30
  background: var(--line-pane-bg);
29
31
  border: var(--border-width-thin) solid var(--line-pane-border);
30
32
  border-radius: var(--border-radius-default);
@@ -4,14 +4,15 @@
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var reactTable = require('@tanstack/react-table');
6
6
  var React = require('react');
7
- var ColumnResizer = require('./components/column-resizer/ColumnResizer');
7
+ var useClientPagination = require('./hooks/useClientPagination');
8
+ var useDistributedLayout = require('./hooks/useDistributedLayout');
8
9
  var useElementWidth = require('./hooks/useElementWidth');
10
+ var useResizeHeaderExtras = require('./hooks/useResizeHeaderExtras');
11
+ var useUiSorting = require('./hooks/useUiSorting');
9
12
  var Table = require('./Table');
10
13
  var table_utils = require('./table.utils');
11
14
  var tanstackTable_utils = require('./tanstackTable.utils');
12
15
 
13
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
14
-
15
16
  function _interopNamespace(e) {
16
17
  if (e && e.__esModule) return e;
17
18
  var n = Object.create(null);
@@ -31,7 +32,6 @@ function _interopNamespace(e) {
31
32
  }
32
33
 
33
34
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
34
- var ColumnResizer__default = /*#__PURE__*/_interopDefault(ColumnResizer);
35
35
 
36
36
  function TanstackTable(props) {
37
37
  var _a, _b;
@@ -44,6 +44,11 @@ function TanstackTable(props) {
44
44
  optimisticSortingUi = true,
45
45
  visibleColumnIds: visibleColumnIdsProp,
46
46
  manualSorting,
47
+ clientPagination = false,
48
+ take: takeProp,
49
+ skip: skipProp,
50
+ totalItemsCount: totalItemsCountProp,
51
+ onPageChange,
47
52
  viewMode,
48
53
  striped,
49
54
  settings,
@@ -54,13 +59,16 @@ function TanstackTable(props) {
54
59
  ...tableProps
55
60
  } = props;
56
61
  const visibleColumnIds = visibleColumnIdsProp != null ? visibleColumnIdsProp : settings == null ? void 0 : settings.visibleColumnIds;
57
- const isControlledSorting = controlledSorting != null;
58
- const [uiSorting, setUiSorting] = React__namespace.useState(controlledSorting != null ? controlledSorting : []);
59
- React__namespace.useEffect(() => {
60
- if (!isControlledSorting) return;
61
- if (tanstackTable_utils.sortingEqual(uiSorting, controlledSorting)) return;
62
- setUiSorting(controlledSorting);
63
- }, [isControlledSorting, controlledSorting, uiSorting]);
62
+ const [uiSorting, setUiSorting] = useUiSorting.useUiSorting(controlledSorting);
63
+ const pagination = useClientPagination.useClientPagination({
64
+ enabled: clientPagination,
65
+ rowCount: data.length,
66
+ take: takeProp,
67
+ skip: skipProp,
68
+ totalItemsCount: totalItemsCountProp,
69
+ onPageChange
70
+ });
71
+ const { resetPage } = pagination;
64
72
  const columnVisibility = React__namespace.useMemo(
65
73
  () => tanstackTable_utils.buildColumnVisibilityFromVisibleIds(columns, visibleColumnIds),
66
74
  [columns, visibleColumnIds]
@@ -75,11 +83,13 @@ function TanstackTable(props) {
75
83
  state: {
76
84
  sorting: uiSorting,
77
85
  columnSizing,
78
- columnVisibility
86
+ columnVisibility,
87
+ ...pagination.paginationState ? { pagination: pagination.paginationState } : {}
79
88
  },
80
89
  onSortingChange: (updater) => {
81
90
  const next = typeof updater === "function" ? updater(uiSorting) : updater;
82
91
  if (optimisticSortingUi) setUiSorting(next);
92
+ resetPage();
83
93
  onSortingChange == null ? void 0 : onSortingChange(next);
84
94
  },
85
95
  onColumnSizingChange: (updater) => {
@@ -92,6 +102,7 @@ function TanstackTable(props) {
92
102
  getCoreRowModel: reactTable.getCoreRowModel(),
93
103
  getSortedRowModel: reactTable.getSortedRowModel(),
94
104
  manualSorting: manualSorting != null ? manualSorting : false,
105
+ ...pagination.tableOptions,
95
106
  enableColumnResizing: true,
96
107
  columnResizeMode: "onChange",
97
108
  defaultColumn: {
@@ -107,89 +118,34 @@ function TanstackTable(props) {
107
118
  hasOtherHeaderActions: headerActionsProp != null,
108
119
  showHeader: (_b = tableProps.showHeader) != null ? _b : true
109
120
  });
110
- const distributedLayout = React__namespace.useMemo(() => {
111
- if (availableWidth == null) return null;
112
- return tanstackTable_utils.buildDistributedColumnWidths({
113
- table,
114
- hasSelection: Boolean(selectedRows && onRowSelect && dataKey),
115
- hasContextMenu: actionReservePx > 0,
116
- contextMenuReservePx: actionReservePx,
117
- defaultMinPx: 80,
118
- columnSizing,
119
- availableWidth
120
- });
121
- }, [table, selectedRows, onRowSelect, dataKey, actionReservePx, columnSizing, availableWidth]);
121
+ const { distributedLayout, columnItems } = useDistributedLayout.useDistributedLayout({
122
+ table,
123
+ columns,
124
+ columnVisibility,
125
+ columnSizing,
126
+ availableWidth,
127
+ hasSelection: Boolean(selectedRows && onRowSelect && dataKey),
128
+ actionReservePx
129
+ });
122
130
  const initialLayoutReady = availableWidth != null;
123
- const resolvedLayout = React__namespace.useMemo(() => {
124
- const next = {};
125
- table.getVisibleLeafColumns().forEach((column) => {
126
- next[column.id] = {
127
- width: distributedLayout == null ? void 0 : distributedLayout.widths[column.id]
128
- };
129
- });
130
- return next;
131
- }, [table, distributedLayout]);
132
- const columnItems = React__namespace.useMemo(
133
- () => tanstackTable_utils.mapDefsToColumnItems(columns, columnVisibility, resolvedLayout),
134
- [columns, columnVisibility, resolvedLayout]
135
- );
136
131
  const visibleData = table.getRowModel().rows.map((r) => r.original);
137
132
  const { sortById, sortDirection } = tanstackTable_utils.getSortPropsFromSorting(uiSorting);
138
133
  const handleSortChange = React__namespace.useCallback(
139
134
  (column, direction) => {
140
135
  const next = direction == null ? [] : [{ id: column.id, desc: direction === "desc" }];
141
136
  if (optimisticSortingUi) setUiSorting(next);
137
+ resetPage();
142
138
  onSortingChange == null ? void 0 : onSortingChange(next);
143
139
  },
144
- [optimisticSortingUi, onSortingChange]
145
- );
146
- const headerExtras = React__namespace.useCallback(
147
- ({ index }) => {
148
- var _a2, _b2, _c, _d;
149
- const headerGroups = table.getHeaderGroups();
150
- const leafHeaders = headerGroups.length > 0 ? headerGroups[headerGroups.length - 1].headers : [];
151
- const header = leafHeaders[index];
152
- const neighbor = leafHeaders[index + 1];
153
- if (!header || !neighbor) return null;
154
- const canResize = (_c = (_b2 = (_a2 = header.column).getCanResize) == null ? void 0 : _b2.call(_a2)) != null ? _c : false;
155
- const handler = (_d = header.getResizeHandler) == null ? void 0 : _d.call(header);
156
- if (!canResize || !handler) return null;
157
- const handleResizeStart = (event) => {
158
- var _a3, _b3, _c2, _d2, _e;
159
- const startWidth = distributedLayout == null ? void 0 : distributedLayout.widths[header.column.id];
160
- const neighborStartWidth = distributedLayout == null ? void 0 : distributedLayout.widths[neighbor.column.id];
161
- if (startWidth == null || neighborStartWidth == null) return;
162
- const neighborReserve = index + 1 === leafHeaders.length - 1 ? actionReservePx : 0;
163
- const { columnDef } = header.column;
164
- const neighborDef = neighbor.column.columnDef;
165
- neighborResizeRef.current = {
166
- columnId: header.column.id,
167
- neighborId: neighbor.column.id,
168
- // TanStack drags from its own size (columnDef.size), not the width on
169
- // screen; applyNeighborResize only uses its offset from this.
170
- tanstackStartWidth: header.getSize(),
171
- startWidth,
172
- neighborStartWidth,
173
- min: (_a3 = columnDef.minSize) != null ? _a3 : 0,
174
- max: (_b3 = columnDef.maxSize) != null ? _b3 : Infinity,
175
- neighborMin: ((_c2 = neighborDef.minSize) != null ? _c2 : 0) + neighborReserve,
176
- neighborMax: ((_d2 = neighborDef.maxSize) != null ? _d2 : Infinity) + neighborReserve
177
- };
178
- const displayedWidths = (_e = distributedLayout == null ? void 0 : distributedLayout.widths) != null ? _e : {};
179
- setColumnSizing(
180
- (prev) => Object.fromEntries(
181
- Object.entries(prev).map(([id, width]) => {
182
- var _a4;
183
- return [id, (_a4 = displayedWidths[id]) != null ? _a4 : width];
184
- })
185
- )
186
- );
187
- handler(event);
188
- };
189
- return /* @__PURE__ */ jsxRuntime.jsx(ColumnResizer__default.default, { id: header.column.id, handler: handleResizeStart });
190
- },
191
- [table, distributedLayout, actionReservePx]
140
+ [optimisticSortingUi, setUiSorting, resetPage, onSortingChange]
192
141
  );
142
+ const headerExtras = useResizeHeaderExtras.useResizeHeaderExtras({
143
+ table,
144
+ widths: distributedLayout == null ? void 0 : distributedLayout.widths,
145
+ actionReservePx,
146
+ neighborResizeRef,
147
+ setColumnSizing
148
+ });
193
149
  return /* @__PURE__ */ jsxRuntime.jsx(
194
150
  Table.Table,
195
151
  {
@@ -202,6 +158,7 @@ function TanstackTable(props) {
202
158
  settingsColumns: columns,
203
159
  tableRootRef: containerRef,
204
160
  onSortChange: handleSortChange,
161
+ ...pagination.tableProps,
205
162
  dataKey,
206
163
  data: visibleData,
207
164
  columns: columnItems,
@@ -6,6 +6,11 @@ export type TanstackTableProps<T extends Record<string, any>> = Omit<TableProps<
6
6
  columns: ReadonlyArray<ColumnDef<T, any>>;
7
7
  sorting?: SortingState;
8
8
  manualSorting?: boolean;
9
+ /**
10
+ * Sorts all rows and then shows one page of `take` rows, starting at `skip`.
11
+ * Leave off when `data` is already a single page fetched from a server.
12
+ */
13
+ clientPagination?: boolean;
9
14
  onSortingChange?: (next: SortingState) => void;
10
15
  optimisticSortingUi?: boolean;
11
16
  variant?: TableVariant;
@@ -2,11 +2,14 @@
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import { useReactTable, getSortedRowModel, getCoreRowModel } from '@tanstack/react-table';
4
4
  import * as React from 'react';
5
- import ColumnResizer from './components/column-resizer/ColumnResizer';
5
+ import { useClientPagination } from './hooks/useClientPagination';
6
+ import { useDistributedLayout } from './hooks/useDistributedLayout';
6
7
  import { useElementWidth } from './hooks/useElementWidth';
8
+ import { useResizeHeaderExtras } from './hooks/useResizeHeaderExtras';
9
+ import { useUiSorting } from './hooks/useUiSorting';
7
10
  import { Table } from './Table';
8
11
  import { DEFAULT_COLUMN_PX, DEFAULT_VIEW_MODE, getActionsReserve, showsExpandToggle } from './table.utils';
9
- import { sortingEqual, buildColumnVisibilityFromVisibleIds, applyNeighborResize, buildDistributedColumnWidths, mapDefsToColumnItems, getSortPropsFromSorting } from './tanstackTable.utils';
12
+ import { buildColumnVisibilityFromVisibleIds, applyNeighborResize, getSortPropsFromSorting } from './tanstackTable.utils';
10
13
 
11
14
  function TanstackTable(props) {
12
15
  var _a, _b;
@@ -19,6 +22,11 @@ function TanstackTable(props) {
19
22
  optimisticSortingUi = true,
20
23
  visibleColumnIds: visibleColumnIdsProp,
21
24
  manualSorting,
25
+ clientPagination = false,
26
+ take: takeProp,
27
+ skip: skipProp,
28
+ totalItemsCount: totalItemsCountProp,
29
+ onPageChange,
22
30
  viewMode,
23
31
  striped,
24
32
  settings,
@@ -29,13 +37,16 @@ function TanstackTable(props) {
29
37
  ...tableProps
30
38
  } = props;
31
39
  const visibleColumnIds = visibleColumnIdsProp != null ? visibleColumnIdsProp : settings == null ? void 0 : settings.visibleColumnIds;
32
- const isControlledSorting = controlledSorting != null;
33
- const [uiSorting, setUiSorting] = React.useState(controlledSorting != null ? controlledSorting : []);
34
- React.useEffect(() => {
35
- if (!isControlledSorting) return;
36
- if (sortingEqual(uiSorting, controlledSorting)) return;
37
- setUiSorting(controlledSorting);
38
- }, [isControlledSorting, controlledSorting, uiSorting]);
40
+ const [uiSorting, setUiSorting] = useUiSorting(controlledSorting);
41
+ const pagination = useClientPagination({
42
+ enabled: clientPagination,
43
+ rowCount: data.length,
44
+ take: takeProp,
45
+ skip: skipProp,
46
+ totalItemsCount: totalItemsCountProp,
47
+ onPageChange
48
+ });
49
+ const { resetPage } = pagination;
39
50
  const columnVisibility = React.useMemo(
40
51
  () => buildColumnVisibilityFromVisibleIds(columns, visibleColumnIds),
41
52
  [columns, visibleColumnIds]
@@ -50,11 +61,13 @@ function TanstackTable(props) {
50
61
  state: {
51
62
  sorting: uiSorting,
52
63
  columnSizing,
53
- columnVisibility
64
+ columnVisibility,
65
+ ...pagination.paginationState ? { pagination: pagination.paginationState } : {}
54
66
  },
55
67
  onSortingChange: (updater) => {
56
68
  const next = typeof updater === "function" ? updater(uiSorting) : updater;
57
69
  if (optimisticSortingUi) setUiSorting(next);
70
+ resetPage();
58
71
  onSortingChange == null ? void 0 : onSortingChange(next);
59
72
  },
60
73
  onColumnSizingChange: (updater) => {
@@ -67,6 +80,7 @@ function TanstackTable(props) {
67
80
  getCoreRowModel: getCoreRowModel(),
68
81
  getSortedRowModel: getSortedRowModel(),
69
82
  manualSorting: manualSorting != null ? manualSorting : false,
83
+ ...pagination.tableOptions,
70
84
  enableColumnResizing: true,
71
85
  columnResizeMode: "onChange",
72
86
  defaultColumn: {
@@ -82,89 +96,34 @@ function TanstackTable(props) {
82
96
  hasOtherHeaderActions: headerActionsProp != null,
83
97
  showHeader: (_b = tableProps.showHeader) != null ? _b : true
84
98
  });
85
- const distributedLayout = React.useMemo(() => {
86
- if (availableWidth == null) return null;
87
- return buildDistributedColumnWidths({
88
- table,
89
- hasSelection: Boolean(selectedRows && onRowSelect && dataKey),
90
- hasContextMenu: actionReservePx > 0,
91
- contextMenuReservePx: actionReservePx,
92
- defaultMinPx: 80,
93
- columnSizing,
94
- availableWidth
95
- });
96
- }, [table, selectedRows, onRowSelect, dataKey, actionReservePx, columnSizing, availableWidth]);
99
+ const { distributedLayout, columnItems } = useDistributedLayout({
100
+ table,
101
+ columns,
102
+ columnVisibility,
103
+ columnSizing,
104
+ availableWidth,
105
+ hasSelection: Boolean(selectedRows && onRowSelect && dataKey),
106
+ actionReservePx
107
+ });
97
108
  const initialLayoutReady = availableWidth != null;
98
- const resolvedLayout = React.useMemo(() => {
99
- const next = {};
100
- table.getVisibleLeafColumns().forEach((column) => {
101
- next[column.id] = {
102
- width: distributedLayout == null ? void 0 : distributedLayout.widths[column.id]
103
- };
104
- });
105
- return next;
106
- }, [table, distributedLayout]);
107
- const columnItems = React.useMemo(
108
- () => mapDefsToColumnItems(columns, columnVisibility, resolvedLayout),
109
- [columns, columnVisibility, resolvedLayout]
110
- );
111
109
  const visibleData = table.getRowModel().rows.map((r) => r.original);
112
110
  const { sortById, sortDirection } = getSortPropsFromSorting(uiSorting);
113
111
  const handleSortChange = React.useCallback(
114
112
  (column, direction) => {
115
113
  const next = direction == null ? [] : [{ id: column.id, desc: direction === "desc" }];
116
114
  if (optimisticSortingUi) setUiSorting(next);
115
+ resetPage();
117
116
  onSortingChange == null ? void 0 : onSortingChange(next);
118
117
  },
119
- [optimisticSortingUi, onSortingChange]
120
- );
121
- const headerExtras = React.useCallback(
122
- ({ index }) => {
123
- var _a2, _b2, _c, _d;
124
- const headerGroups = table.getHeaderGroups();
125
- const leafHeaders = headerGroups.length > 0 ? headerGroups[headerGroups.length - 1].headers : [];
126
- const header = leafHeaders[index];
127
- const neighbor = leafHeaders[index + 1];
128
- if (!header || !neighbor) return null;
129
- const canResize = (_c = (_b2 = (_a2 = header.column).getCanResize) == null ? void 0 : _b2.call(_a2)) != null ? _c : false;
130
- const handler = (_d = header.getResizeHandler) == null ? void 0 : _d.call(header);
131
- if (!canResize || !handler) return null;
132
- const handleResizeStart = (event) => {
133
- var _a3, _b3, _c2, _d2, _e;
134
- const startWidth = distributedLayout == null ? void 0 : distributedLayout.widths[header.column.id];
135
- const neighborStartWidth = distributedLayout == null ? void 0 : distributedLayout.widths[neighbor.column.id];
136
- if (startWidth == null || neighborStartWidth == null) return;
137
- const neighborReserve = index + 1 === leafHeaders.length - 1 ? actionReservePx : 0;
138
- const { columnDef } = header.column;
139
- const neighborDef = neighbor.column.columnDef;
140
- neighborResizeRef.current = {
141
- columnId: header.column.id,
142
- neighborId: neighbor.column.id,
143
- // TanStack drags from its own size (columnDef.size), not the width on
144
- // screen; applyNeighborResize only uses its offset from this.
145
- tanstackStartWidth: header.getSize(),
146
- startWidth,
147
- neighborStartWidth,
148
- min: (_a3 = columnDef.minSize) != null ? _a3 : 0,
149
- max: (_b3 = columnDef.maxSize) != null ? _b3 : Infinity,
150
- neighborMin: ((_c2 = neighborDef.minSize) != null ? _c2 : 0) + neighborReserve,
151
- neighborMax: ((_d2 = neighborDef.maxSize) != null ? _d2 : Infinity) + neighborReserve
152
- };
153
- const displayedWidths = (_e = distributedLayout == null ? void 0 : distributedLayout.widths) != null ? _e : {};
154
- setColumnSizing(
155
- (prev) => Object.fromEntries(
156
- Object.entries(prev).map(([id, width]) => {
157
- var _a4;
158
- return [id, (_a4 = displayedWidths[id]) != null ? _a4 : width];
159
- })
160
- )
161
- );
162
- handler(event);
163
- };
164
- return /* @__PURE__ */ jsx(ColumnResizer, { id: header.column.id, handler: handleResizeStart });
165
- },
166
- [table, distributedLayout, actionReservePx]
118
+ [optimisticSortingUi, setUiSorting, resetPage, onSortingChange]
167
119
  );
120
+ const headerExtras = useResizeHeaderExtras({
121
+ table,
122
+ widths: distributedLayout == null ? void 0 : distributedLayout.widths,
123
+ actionReservePx,
124
+ neighborResizeRef,
125
+ setColumnSizing
126
+ });
168
127
  return /* @__PURE__ */ jsx(
169
128
  Table,
170
129
  {
@@ -177,6 +136,7 @@ function TanstackTable(props) {
177
136
  settingsColumns: columns,
178
137
  tableRootRef: containerRef,
179
138
  onSortChange: handleSortChange,
139
+ ...pagination.tableProps,
180
140
  dataKey,
181
141
  data: visibleData,
182
142
  columns: columnItems,
@@ -0,0 +1,66 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var reactTable = require('@tanstack/react-table');
5
+ var react = require('react');
6
+
7
+ const DEFAULT_CLIENT_PAGE_SIZE = 20;
8
+ function useClientPagination({
9
+ enabled,
10
+ rowCount,
11
+ take,
12
+ skip,
13
+ totalItemsCount,
14
+ onPageChange
15
+ }) {
16
+ const [uiSkip, setUiSkip] = react.useState(skip != null ? skip : 0);
17
+ const [prevSkip, setPrevSkip] = react.useState(skip);
18
+ const [uiTake, setUiTake] = react.useState(take != null ? take : DEFAULT_CLIENT_PAGE_SIZE);
19
+ const [prevTake, setPrevTake] = react.useState(take);
20
+ if (skip !== prevSkip) {
21
+ setPrevSkip(skip);
22
+ if (skip != null) setUiSkip(skip);
23
+ }
24
+ if (take !== prevTake) {
25
+ setPrevTake(take);
26
+ setUiTake(take != null ? take : DEFAULT_CLIENT_PAGE_SIZE);
27
+ }
28
+ const pageSize = Math.max(1, uiTake);
29
+ const lastPageIndex = Math.max(0, Math.ceil(rowCount / pageSize) - 1);
30
+ const pageIndex = Math.min(Math.floor(uiSkip / pageSize), lastPageIndex);
31
+ const handlePageChange = react.useCallback(
32
+ (e) => {
33
+ if (enabled) {
34
+ setUiSkip(e.skip);
35
+ setUiTake(e.take);
36
+ }
37
+ onPageChange == null ? void 0 : onPageChange(e);
38
+ },
39
+ [enabled, onPageChange]
40
+ );
41
+ const resetPage = react.useCallback(() => {
42
+ if (!enabled || uiSkip === 0) return;
43
+ setUiSkip(0);
44
+ onPageChange == null ? void 0 : onPageChange({
45
+ page: 1,
46
+ take: pageSize,
47
+ skip: 0,
48
+ totalPages: Math.max(1, Math.ceil(rowCount / pageSize))
49
+ });
50
+ }, [enabled, uiSkip, pageSize, rowCount, onPageChange]);
51
+ const paginationState = enabled ? { pageIndex, pageSize } : void 0;
52
+ const tableOptions = enabled ? {
53
+ getPaginationRowModel: reactTable.getPaginationRowModel(),
54
+ // Page changes go through handlePageChange; a new `data` reference must not reset the page.
55
+ autoResetPageIndex: false
56
+ } : {};
57
+ const tableProps = {
58
+ take: enabled ? pageSize : take,
59
+ skip: enabled ? pageIndex * pageSize : skip,
60
+ totalItemsCount: enabled ? rowCount : totalItemsCount,
61
+ onPageChange: enabled || onPageChange ? handlePageChange : void 0
62
+ };
63
+ return { paginationState, tableOptions, tableProps, resetPage };
64
+ }
65
+
66
+ exports.useClientPagination = useClientPagination;
@@ -0,0 +1,32 @@
1
+ import { type PaginationState } from '@tanstack/react-table';
2
+ import type { PageChangeEvent } from '../../pagination/Pagination';
3
+ type Args = {
4
+ enabled: boolean;
5
+ rowCount: number;
6
+ take?: number;
7
+ skip?: number;
8
+ totalItemsCount?: number;
9
+ onPageChange?: (e: PageChangeEvent) => void;
10
+ };
11
+ /**
12
+ * Page state for TanstackTable. When disabled, the pagination props pass
13
+ * through untouched, for data that is already one page from a server.
14
+ */
15
+ export declare function useClientPagination({ enabled, rowCount, take, skip, totalItemsCount, onPageChange, }: Args): {
16
+ paginationState: PaginationState | undefined;
17
+ tableOptions: {
18
+ getPaginationRowModel: (table: import("@tanstack/table-core").Table<unknown>) => () => import("@tanstack/table-core").RowModel<unknown>;
19
+ autoResetPageIndex: boolean;
20
+ } | {
21
+ getPaginationRowModel?: undefined;
22
+ autoResetPageIndex?: undefined;
23
+ };
24
+ tableProps: {
25
+ take: number | undefined;
26
+ skip: number | undefined;
27
+ totalItemsCount: number | undefined;
28
+ onPageChange: ((e: PageChangeEvent) => void) | undefined;
29
+ };
30
+ resetPage: () => void;
31
+ };
32
+ export {};
@@ -0,0 +1,64 @@
1
+ 'use client';
2
+ import { getPaginationRowModel } from '@tanstack/react-table';
3
+ import { useState, useCallback } from 'react';
4
+
5
+ const DEFAULT_CLIENT_PAGE_SIZE = 20;
6
+ function useClientPagination({
7
+ enabled,
8
+ rowCount,
9
+ take,
10
+ skip,
11
+ totalItemsCount,
12
+ onPageChange
13
+ }) {
14
+ const [uiSkip, setUiSkip] = useState(skip != null ? skip : 0);
15
+ const [prevSkip, setPrevSkip] = useState(skip);
16
+ const [uiTake, setUiTake] = useState(take != null ? take : DEFAULT_CLIENT_PAGE_SIZE);
17
+ const [prevTake, setPrevTake] = useState(take);
18
+ if (skip !== prevSkip) {
19
+ setPrevSkip(skip);
20
+ if (skip != null) setUiSkip(skip);
21
+ }
22
+ if (take !== prevTake) {
23
+ setPrevTake(take);
24
+ setUiTake(take != null ? take : DEFAULT_CLIENT_PAGE_SIZE);
25
+ }
26
+ const pageSize = Math.max(1, uiTake);
27
+ const lastPageIndex = Math.max(0, Math.ceil(rowCount / pageSize) - 1);
28
+ const pageIndex = Math.min(Math.floor(uiSkip / pageSize), lastPageIndex);
29
+ const handlePageChange = useCallback(
30
+ (e) => {
31
+ if (enabled) {
32
+ setUiSkip(e.skip);
33
+ setUiTake(e.take);
34
+ }
35
+ onPageChange == null ? void 0 : onPageChange(e);
36
+ },
37
+ [enabled, onPageChange]
38
+ );
39
+ const resetPage = useCallback(() => {
40
+ if (!enabled || uiSkip === 0) return;
41
+ setUiSkip(0);
42
+ onPageChange == null ? void 0 : onPageChange({
43
+ page: 1,
44
+ take: pageSize,
45
+ skip: 0,
46
+ totalPages: Math.max(1, Math.ceil(rowCount / pageSize))
47
+ });
48
+ }, [enabled, uiSkip, pageSize, rowCount, onPageChange]);
49
+ const paginationState = enabled ? { pageIndex, pageSize } : void 0;
50
+ const tableOptions = enabled ? {
51
+ getPaginationRowModel: getPaginationRowModel(),
52
+ // Page changes go through handlePageChange; a new `data` reference must not reset the page.
53
+ autoResetPageIndex: false
54
+ } : {};
55
+ const tableProps = {
56
+ take: enabled ? pageSize : take,
57
+ skip: enabled ? pageIndex * pageSize : skip,
58
+ totalItemsCount: enabled ? rowCount : totalItemsCount,
59
+ onPageChange: enabled || onPageChange ? handlePageChange : void 0
60
+ };
61
+ return { paginationState, tableOptions, tableProps, resetPage };
62
+ }
63
+
64
+ export { useClientPagination };
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var react = require('react');
5
+ var tanstackTable_utils = require('../tanstackTable.utils');
6
+
7
+ function useDistributedLayout({
8
+ table,
9
+ columns,
10
+ columnVisibility,
11
+ columnSizing,
12
+ availableWidth,
13
+ hasSelection,
14
+ actionReservePx
15
+ }) {
16
+ const distributedLayout = react.useMemo(() => {
17
+ if (availableWidth == null) return null;
18
+ return tanstackTable_utils.buildDistributedColumnWidths({
19
+ table,
20
+ hasSelection,
21
+ hasContextMenu: actionReservePx > 0,
22
+ contextMenuReservePx: actionReservePx,
23
+ defaultMinPx: 80,
24
+ columnSizing,
25
+ availableWidth
26
+ });
27
+ }, [table, hasSelection, actionReservePx, columnSizing, availableWidth]);
28
+ const resolvedLayout = react.useMemo(() => {
29
+ const next = {};
30
+ table.getVisibleLeafColumns().forEach((column) => {
31
+ next[column.id] = {
32
+ width: distributedLayout == null ? void 0 : distributedLayout.widths[column.id]
33
+ };
34
+ });
35
+ return next;
36
+ }, [table, distributedLayout]);
37
+ const columnItems = react.useMemo(
38
+ () => tanstackTable_utils.mapDefsToColumnItems(columns, columnVisibility, resolvedLayout),
39
+ [columns, columnVisibility, resolvedLayout]
40
+ );
41
+ return { distributedLayout, columnItems };
42
+ }
43
+
44
+ exports.useDistributedLayout = useDistributedLayout;
@@ -0,0 +1,16 @@
1
+ import type { ColumnDef, ColumnSizingState, Table, VisibilityState } from '@tanstack/react-table';
2
+ type Args<T> = {
3
+ table: Table<T>;
4
+ columns: ReadonlyArray<ColumnDef<T, any>>;
5
+ columnVisibility: VisibilityState;
6
+ columnSizing: ColumnSizingState;
7
+ availableWidth: number | undefined;
8
+ hasSelection: boolean;
9
+ actionReservePx: number;
10
+ };
11
+ /** Column widths that fill the measured container, mapped to Table column items. */
12
+ export declare function useDistributedLayout<T extends Record<string, any>>({ table, columns, columnVisibility, columnSizing, availableWidth, hasSelection, actionReservePx, }: Args<T>): {
13
+ distributedLayout: import("../columnLayout.utils").DistributedColumnWidths | null;
14
+ columnItems: import("../Table.types").ColumnItem<T>[];
15
+ };
16
+ export {};
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+ import { useMemo } from 'react';
3
+ import { buildDistributedColumnWidths, mapDefsToColumnItems } from '../tanstackTable.utils';
4
+
5
+ function useDistributedLayout({
6
+ table,
7
+ columns,
8
+ columnVisibility,
9
+ columnSizing,
10
+ availableWidth,
11
+ hasSelection,
12
+ actionReservePx
13
+ }) {
14
+ const distributedLayout = useMemo(() => {
15
+ if (availableWidth == null) return null;
16
+ return buildDistributedColumnWidths({
17
+ table,
18
+ hasSelection,
19
+ hasContextMenu: actionReservePx > 0,
20
+ contextMenuReservePx: actionReservePx,
21
+ defaultMinPx: 80,
22
+ columnSizing,
23
+ availableWidth
24
+ });
25
+ }, [table, hasSelection, actionReservePx, columnSizing, availableWidth]);
26
+ const resolvedLayout = useMemo(() => {
27
+ const next = {};
28
+ table.getVisibleLeafColumns().forEach((column) => {
29
+ next[column.id] = {
30
+ width: distributedLayout == null ? void 0 : distributedLayout.widths[column.id]
31
+ };
32
+ });
33
+ return next;
34
+ }, [table, distributedLayout]);
35
+ const columnItems = useMemo(
36
+ () => mapDefsToColumnItems(columns, columnVisibility, resolvedLayout),
37
+ [columns, columnVisibility, resolvedLayout]
38
+ );
39
+ return { distributedLayout, columnItems };
40
+ }
41
+
42
+ export { useDistributedLayout };
@@ -0,0 +1,87 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+ var React = require('react');
6
+ var ColumnResizer = require('../components/column-resizer/ColumnResizer');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ function _interopNamespace(e) {
11
+ if (e && e.__esModule) return e;
12
+ var n = Object.create(null);
13
+ if (e) {
14
+ Object.keys(e).forEach(function (k) {
15
+ if (k !== 'default') {
16
+ var d = Object.getOwnPropertyDescriptor(e, k);
17
+ Object.defineProperty(n, k, d.get ? d : {
18
+ enumerable: true,
19
+ get: function () { return e[k]; }
20
+ });
21
+ }
22
+ });
23
+ }
24
+ n.default = e;
25
+ return Object.freeze(n);
26
+ }
27
+
28
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
29
+ var ColumnResizer__default = /*#__PURE__*/_interopDefault(ColumnResizer);
30
+
31
+ function useResizeHeaderExtras({
32
+ table,
33
+ widths,
34
+ actionReservePx,
35
+ neighborResizeRef,
36
+ setColumnSizing
37
+ }) {
38
+ return React__namespace.useCallback(
39
+ ({ index }) => {
40
+ var _a, _b, _c, _d;
41
+ const headerGroups = table.getHeaderGroups();
42
+ const leafHeaders = headerGroups.length > 0 ? headerGroups[headerGroups.length - 1].headers : [];
43
+ const header = leafHeaders[index];
44
+ const neighbor = leafHeaders[index + 1];
45
+ if (!header || !neighbor) return null;
46
+ const canResize = (_c = (_b = (_a = header.column).getCanResize) == null ? void 0 : _b.call(_a)) != null ? _c : false;
47
+ const handler = (_d = header.getResizeHandler) == null ? void 0 : _d.call(header);
48
+ if (!canResize || !handler) return null;
49
+ const handleResizeStart = (event) => {
50
+ var _a2, _b2, _c2, _d2;
51
+ const startWidth = widths == null ? void 0 : widths[header.column.id];
52
+ const neighborStartWidth = widths == null ? void 0 : widths[neighbor.column.id];
53
+ if (startWidth == null || neighborStartWidth == null) return;
54
+ const neighborReserve = index + 1 === leafHeaders.length - 1 ? actionReservePx : 0;
55
+ const { columnDef } = header.column;
56
+ const neighborDef = neighbor.column.columnDef;
57
+ neighborResizeRef.current = {
58
+ columnId: header.column.id,
59
+ neighborId: neighbor.column.id,
60
+ // TanStack drags from its own size (columnDef.size), not the width on
61
+ // screen; applyNeighborResize only uses its offset from this.
62
+ tanstackStartWidth: header.getSize(),
63
+ startWidth,
64
+ neighborStartWidth,
65
+ min: (_a2 = columnDef.minSize) != null ? _a2 : 0,
66
+ max: (_b2 = columnDef.maxSize) != null ? _b2 : Infinity,
67
+ neighborMin: ((_c2 = neighborDef.minSize) != null ? _c2 : 0) + neighborReserve,
68
+ neighborMax: ((_d2 = neighborDef.maxSize) != null ? _d2 : Infinity) + neighborReserve
69
+ };
70
+ const displayedWidths = widths != null ? widths : {};
71
+ setColumnSizing(
72
+ (prev) => Object.fromEntries(
73
+ Object.entries(prev).map(([id, width]) => {
74
+ var _a3;
75
+ return [id, (_a3 = displayedWidths[id]) != null ? _a3 : width];
76
+ })
77
+ )
78
+ );
79
+ handler(event);
80
+ };
81
+ return /* @__PURE__ */ jsxRuntime.jsx(ColumnResizer__default.default, { id: header.column.id, handler: handleResizeStart });
82
+ },
83
+ [table, widths, actionReservePx, neighborResizeRef, setColumnSizing]
84
+ );
85
+ }
86
+
87
+ exports.useResizeHeaderExtras = useResizeHeaderExtras;
@@ -0,0 +1,17 @@
1
+ import type { ColumnSizingState, Table } from '@tanstack/react-table';
2
+ import * as React from 'react';
3
+ import type { DistributedColumnWidths } from '../columnLayout.utils';
4
+ import type { NeighborResize } from '../tanstackTable.utils';
5
+ type Args<T> = {
6
+ table: Table<T>;
7
+ widths: DistributedColumnWidths['widths'] | undefined;
8
+ actionReservePx: number;
9
+ neighborResizeRef: React.MutableRefObject<NeighborResize | null>;
10
+ setColumnSizing: React.Dispatch<React.SetStateAction<ColumnSizingState>>;
11
+ };
12
+ /** Renders a resize handle between each column and its right neighbour. */
13
+ export declare function useResizeHeaderExtras<T>({ table, widths, actionReservePx, neighborResizeRef, setColumnSizing, }: Args<T>): ({ index }: {
14
+ column: any;
15
+ index: number;
16
+ }) => import("react/jsx-runtime").JSX.Element | null;
17
+ export {};
@@ -0,0 +1,62 @@
1
+ 'use client';
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import ColumnResizer from '../components/column-resizer/ColumnResizer';
5
+
6
+ function useResizeHeaderExtras({
7
+ table,
8
+ widths,
9
+ actionReservePx,
10
+ neighborResizeRef,
11
+ setColumnSizing
12
+ }) {
13
+ return React.useCallback(
14
+ ({ index }) => {
15
+ var _a, _b, _c, _d;
16
+ const headerGroups = table.getHeaderGroups();
17
+ const leafHeaders = headerGroups.length > 0 ? headerGroups[headerGroups.length - 1].headers : [];
18
+ const header = leafHeaders[index];
19
+ const neighbor = leafHeaders[index + 1];
20
+ if (!header || !neighbor) return null;
21
+ const canResize = (_c = (_b = (_a = header.column).getCanResize) == null ? void 0 : _b.call(_a)) != null ? _c : false;
22
+ const handler = (_d = header.getResizeHandler) == null ? void 0 : _d.call(header);
23
+ if (!canResize || !handler) return null;
24
+ const handleResizeStart = (event) => {
25
+ var _a2, _b2, _c2, _d2;
26
+ const startWidth = widths == null ? void 0 : widths[header.column.id];
27
+ const neighborStartWidth = widths == null ? void 0 : widths[neighbor.column.id];
28
+ if (startWidth == null || neighborStartWidth == null) return;
29
+ const neighborReserve = index + 1 === leafHeaders.length - 1 ? actionReservePx : 0;
30
+ const { columnDef } = header.column;
31
+ const neighborDef = neighbor.column.columnDef;
32
+ neighborResizeRef.current = {
33
+ columnId: header.column.id,
34
+ neighborId: neighbor.column.id,
35
+ // TanStack drags from its own size (columnDef.size), not the width on
36
+ // screen; applyNeighborResize only uses its offset from this.
37
+ tanstackStartWidth: header.getSize(),
38
+ startWidth,
39
+ neighborStartWidth,
40
+ min: (_a2 = columnDef.minSize) != null ? _a2 : 0,
41
+ max: (_b2 = columnDef.maxSize) != null ? _b2 : Infinity,
42
+ neighborMin: ((_c2 = neighborDef.minSize) != null ? _c2 : 0) + neighborReserve,
43
+ neighborMax: ((_d2 = neighborDef.maxSize) != null ? _d2 : Infinity) + neighborReserve
44
+ };
45
+ const displayedWidths = widths != null ? widths : {};
46
+ setColumnSizing(
47
+ (prev) => Object.fromEntries(
48
+ Object.entries(prev).map(([id, width]) => {
49
+ var _a3;
50
+ return [id, (_a3 = displayedWidths[id]) != null ? _a3 : width];
51
+ })
52
+ )
53
+ );
54
+ handler(event);
55
+ };
56
+ return /* @__PURE__ */ jsx(ColumnResizer, { id: header.column.id, handler: handleResizeStart });
57
+ },
58
+ [table, widths, actionReservePx, neighborResizeRef, setColumnSizing]
59
+ );
60
+ }
61
+
62
+ export { useResizeHeaderExtras };
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var react = require('react');
5
+ var tanstackTable_utils = require('../tanstackTable.utils');
6
+
7
+ function useUiSorting(controlledSorting) {
8
+ const isControlled = controlledSorting != null;
9
+ const [uiSorting, setUiSorting] = react.useState(controlledSorting != null ? controlledSorting : []);
10
+ react.useEffect(() => {
11
+ if (!isControlled) return;
12
+ if (tanstackTable_utils.sortingEqual(uiSorting, controlledSorting)) return;
13
+ setUiSorting(controlledSorting);
14
+ }, [isControlled, controlledSorting, uiSorting]);
15
+ return [uiSorting, setUiSorting];
16
+ }
17
+
18
+ exports.useUiSorting = useUiSorting;
@@ -0,0 +1,3 @@
1
+ import type { SortingState } from '@tanstack/react-table';
2
+ /** Sorting shown in the header; follows `controlledSorting` when given, but can be set ahead of it. */
3
+ export declare function useUiSorting(controlledSorting: SortingState | undefined): readonly [SortingState, import("react").Dispatch<import("react").SetStateAction<SortingState>>];
@@ -0,0 +1,16 @@
1
+ 'use client';
2
+ import { useState, useEffect } from 'react';
3
+ import { sortingEqual } from '../tanstackTable.utils';
4
+
5
+ function useUiSorting(controlledSorting) {
6
+ const isControlled = controlledSorting != null;
7
+ const [uiSorting, setUiSorting] = useState(controlledSorting != null ? controlledSorting : []);
8
+ useEffect(() => {
9
+ if (!isControlled) return;
10
+ if (sortingEqual(uiSorting, controlledSorting)) return;
11
+ setUiSorting(controlledSorting);
12
+ }, [isControlled, controlledSorting, uiSorting]);
13
+ return [uiSorting, setUiSorting];
14
+ }
15
+
16
+ export { useUiSorting };
@@ -36,10 +36,10 @@ function XmlViewer({
36
36
  defaultViewMode = "highlighted",
37
37
  deleted = false,
38
38
  storageKey,
39
- borderless = false
39
+ borderless = false,
40
+ maxHeight
40
41
  }) {
41
42
  const tokens = react.useMemo(() => tokenizeXml.tokenizeXml(xml), [xml]);
42
- const codeRef = react.useRef(null);
43
43
  const { value: currentAppearance, setValue: setCurrentAppearance } = usePersistentState.usePersistentState({
44
44
  initialValue: appearance,
45
45
  storageKey: storageKey ? `${storageKey}:appearance` : void 0,
@@ -58,15 +58,6 @@ function XmlViewer({
58
58
  const handleCopy = () => {
59
59
  void clipboard_utils.copyToClipboard(xml);
60
60
  };
61
- const handleDoubleClick = () => {
62
- const el = codeRef.current;
63
- const selection = typeof window !== "undefined" ? window.getSelection() : null;
64
- if (!el || !selection) return;
65
- const range = document.createRange();
66
- range.selectNodeContents(el);
67
- selection.removeAllRanges();
68
- selection.addRange(range);
69
- };
70
61
  return /* @__PURE__ */ jsxRuntime.jsxs(
71
62
  "section",
72
63
  {
@@ -168,11 +159,11 @@ function XmlViewer({
168
159
  wrap ? styles__default.default.wrap : styles__default.default.noWrap,
169
160
  deleted ? styles__default.default.deleted : ""
170
161
  ].filter(Boolean).join(" "),
162
+ style: { maxHeight },
171
163
  tabIndex: 0,
172
- onDoubleClick: handleDoubleClick,
173
164
  children: [
174
165
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.toolbarSpacer, "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(Button.Button, { variant: "inline", tabIndex: -1, children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { customIcon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings2, {}) }) }) }),
175
- /* @__PURE__ */ jsxRuntime.jsx("code", { ref: codeRef, children: viewMode === "raw" ? xml : tokens.map((token, index) => {
166
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: viewMode === "raw" ? xml : tokens.map((token, index) => {
176
167
  const tokenClassName = TOKEN_CLASS[token.type];
177
168
  return tokenClassName ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: tokenClassName, children: token.value }, index) : token.value;
178
169
  }) })
@@ -21,6 +21,11 @@ export interface XmlViewerProps {
21
21
  * modal).
22
22
  */
23
23
  borderless?: boolean;
24
+ /**
25
+ * Caps the height of the scrolling pane (a number is pixels), e.g. when the
26
+ * parent has no definite height for the viewer to fill.
27
+ */
28
+ maxHeight?: number | string;
24
29
  }
25
- export declare function XmlViewer({ xml, size, appearance, defaultViewMode, deleted, storageKey, borderless, }: XmlViewerProps): JSX.Element;
30
+ export declare function XmlViewer({ xml, size, appearance, defaultViewMode, deleted, storageKey, borderless, maxHeight, }: XmlViewerProps): JSX.Element;
26
31
  export {};
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { TextWrap, ListTree, Code2, Sun, Moon, ClipboardCopy, Settings2 } from 'lucide-react';
4
- import { useMemo, useRef } from 'react';
4
+ import { useMemo } from 'react';
5
5
  import { Menu } from '../../components/menu/Menu';
6
6
  import { Popover } from '../../components/popover/Popover';
7
7
  import { ScrollFade } from '../../components/scroll-fade/ScrollFade';
@@ -30,10 +30,10 @@ function XmlViewer({
30
30
  defaultViewMode = "highlighted",
31
31
  deleted = false,
32
32
  storageKey,
33
- borderless = false
33
+ borderless = false,
34
+ maxHeight
34
35
  }) {
35
36
  const tokens = useMemo(() => tokenizeXml(xml), [xml]);
36
- const codeRef = useRef(null);
37
37
  const { value: currentAppearance, setValue: setCurrentAppearance } = usePersistentState({
38
38
  initialValue: appearance,
39
39
  storageKey: storageKey ? `${storageKey}:appearance` : void 0,
@@ -52,15 +52,6 @@ function XmlViewer({
52
52
  const handleCopy = () => {
53
53
  void copyToClipboard(xml);
54
54
  };
55
- const handleDoubleClick = () => {
56
- const el = codeRef.current;
57
- const selection = typeof window !== "undefined" ? window.getSelection() : null;
58
- if (!el || !selection) return;
59
- const range = document.createRange();
60
- range.selectNodeContents(el);
61
- selection.removeAllRanges();
62
- selection.addRange(range);
63
- };
64
55
  return /* @__PURE__ */ jsxs(
65
56
  "section",
66
57
  {
@@ -162,11 +153,11 @@ function XmlViewer({
162
153
  wrap ? styles.wrap : styles.noWrap,
163
154
  deleted ? styles.deleted : ""
164
155
  ].filter(Boolean).join(" "),
156
+ style: { maxHeight },
165
157
  tabIndex: 0,
166
- onDoubleClick: handleDoubleClick,
167
158
  children: [
168
159
  /* @__PURE__ */ jsx("span", { className: styles.toolbarSpacer, "aria-hidden": "true", children: /* @__PURE__ */ jsx(Button, { variant: "inline", tabIndex: -1, children: /* @__PURE__ */ jsx(Icon, { customIcon: /* @__PURE__ */ jsx(Settings2, {}) }) }) }),
169
- /* @__PURE__ */ jsx("code", { ref: codeRef, children: viewMode === "raw" ? xml : tokens.map((token, index) => {
160
+ /* @__PURE__ */ jsx("code", { children: viewMode === "raw" ? xml : tokens.map((token, index) => {
170
161
  const tokenClassName = TOKEN_CLASS[token.type];
171
162
  return tokenClassName ? /* @__PURE__ */ jsx("span", { className: tokenClassName, children: token.value }, index) : token.value;
172
163
  }) })
@@ -19,6 +19,8 @@
19
19
  flex-direction: column;
20
20
  /* In a parent with a definite height, scroll inside instead of overflowing it. */
21
21
  max-height: 100%;
22
+ /* As a flex item, shrink below the content height instead of overflowing the parent. */
23
+ min-height: 0;
22
24
  background: var(--xml-pane-bg);
23
25
  border: var(--border-width-thin) solid var(--xml-pane-border);
24
26
  border-radius: var(--border-radius-default);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dbcdk/react-components",
3
- "version": "0.0.198",
3
+ "version": "0.0.200",
4
4
  "description": "Reusable React components for DBC projects",
5
5
  "license": "ISC",
6
6
  "author": "",