@dbcdk/react-components 0.0.194 → 0.0.196

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 (114) hide show
  1. package/README.md +78 -10
  2. package/dist/client.cjs +486 -614
  3. package/dist/client.d.ts +176 -102
  4. package/dist/client.js +102 -102
  5. package/dist/components/color-scheme-button/ColorSchemeButton.cjs +7 -4
  6. package/dist/components/color-scheme-button/ColorSchemeButton.d.ts +7 -3
  7. package/dist/components/color-scheme-button/ColorSchemeButton.js +7 -4
  8. package/dist/components/forms/text-area/Textarea.cjs +2 -1
  9. package/dist/components/forms/text-area/Textarea.d.ts +1 -0
  10. package/dist/components/forms/text-area/Textarea.js +2 -1
  11. package/dist/components/highlighted-text/HighlightedText.cjs +6 -3
  12. package/dist/components/highlighted-text/HighlightedText.js +7 -4
  13. package/dist/components/json-viewer/JsonNode.cjs +27 -67
  14. package/dist/components/json-viewer/JsonNode.d.ts +10 -0
  15. package/dist/components/json-viewer/JsonNode.js +28 -68
  16. package/dist/components/json-viewer/JsonRow.cjs +85 -0
  17. package/dist/components/json-viewer/JsonRow.d.ts +5 -0
  18. package/dist/components/json-viewer/JsonRow.js +79 -0
  19. package/dist/components/json-viewer/JsonViewer.cjs +36 -256
  20. package/dist/components/json-viewer/JsonViewer.js +37 -253
  21. package/dist/components/json-viewer/JsonViewerShell.cjs +208 -0
  22. package/dist/components/json-viewer/JsonViewerShell.d.ts +25 -0
  23. package/dist/components/json-viewer/JsonViewerShell.js +202 -0
  24. package/dist/components/json-viewer/TanstackJsonViewer.cjs +127 -0
  25. package/dist/components/json-viewer/TanstackJsonViewer.d.ts +17 -0
  26. package/dist/components/json-viewer/TanstackJsonViewer.js +121 -0
  27. package/dist/components/json-viewer/types.d.ts +27 -0
  28. package/dist/components/json-viewer/useJsonViewerState.cjs +111 -0
  29. package/dist/components/json-viewer/useJsonViewerState.d.ts +42 -0
  30. package/dist/components/json-viewer/useJsonViewerState.js +108 -0
  31. package/dist/components/json-viewer/utils.cjs +36 -0
  32. package/dist/components/json-viewer/utils.d.ts +21 -1
  33. package/dist/components/json-viewer/utils.js +35 -1
  34. package/dist/components/lightbox/Lightbox.cjs +2 -2
  35. package/dist/components/lightbox/Lightbox.d.ts +1 -1
  36. package/dist/components/lightbox/Lightbox.js +1 -1
  37. package/dist/components/line-viewer/LineViewer.cjs +2 -3
  38. package/dist/components/line-viewer/LineViewer.js +2 -3
  39. package/dist/components/line-viewer/tokenizeLineFormat.cjs +37 -17
  40. package/dist/components/line-viewer/tokenizeLineFormat.d.ts +5 -4
  41. package/dist/components/line-viewer/tokenizeLineFormat.js +37 -17
  42. package/dist/components/menu/Menu.cjs +3 -23
  43. package/dist/components/menu/Menu.js +3 -23
  44. package/dist/components/menu/Menu.module.css +4 -3
  45. package/dist/components/metric-tile/MetricTile.cjs +3 -2
  46. package/dist/components/metric-tile/MetricTile.js +2 -1
  47. package/dist/components/overlay/tooltip/Tooltip.cjs +7 -2
  48. package/dist/components/overlay/tooltip/Tooltip.d.ts +7 -1
  49. package/dist/components/overlay/tooltip/Tooltip.js +7 -2
  50. package/dist/components/overlay/tooltip/useTooltipTrigger.cjs +7 -1
  51. package/dist/components/overlay/tooltip/useTooltipTrigger.js +7 -1
  52. package/dist/components/page-layout/PageLayout.module.css +1 -1
  53. package/dist/components/table/Table.cjs +45 -16
  54. package/dist/components/table/Table.d.ts +1 -1
  55. package/dist/components/table/Table.js +47 -18
  56. package/dist/components/table/Table.module.css +135 -7
  57. package/dist/components/table/Table.types.d.ts +47 -1
  58. package/dist/components/table/TanstackTable.cjs +30 -2
  59. package/dist/components/table/TanstackTable.js +31 -3
  60. package/dist/components/table/components/TableBody.d.ts +1 -2
  61. package/dist/components/table/components/TableHeader.cjs +6 -4
  62. package/dist/components/table/components/TableHeader.d.ts +2 -1
  63. package/dist/components/table/components/TableHeader.js +6 -4
  64. package/dist/components/table/components/TableHeaderCell.cjs +5 -4
  65. package/dist/components/table/components/TableHeaderCell.d.ts +3 -1
  66. package/dist/components/table/components/TableHeaderCell.js +5 -4
  67. package/dist/components/table/components/TableRow.cjs +1 -1
  68. package/dist/components/table/components/TableRow.d.ts +1 -2
  69. package/dist/components/table/components/TableRow.js +1 -1
  70. package/dist/components/table/components/TableSkeletonBody.cjs +30 -0
  71. package/dist/components/table/components/TableSkeletonBody.d.ts +13 -0
  72. package/dist/components/table/components/TableSkeletonBody.js +24 -0
  73. package/dist/components/table/components/table-settings/TableHeaderSettings.cjs +28 -0
  74. package/dist/components/table/components/table-settings/TableHeaderSettings.d.ts +11 -0
  75. package/dist/components/table/components/table-settings/TableHeaderSettings.js +26 -0
  76. package/dist/components/table/components/table-settings/TableSettings.cjs +82 -32
  77. package/dist/components/table/components/table-settings/TableSettings.d.ts +14 -6
  78. package/dist/components/table/components/table-settings/TableSettings.js +84 -34
  79. package/dist/components/table/table.utils.cjs +2 -0
  80. package/dist/components/table/table.utils.d.ts +6 -0
  81. package/dist/components/table/table.utils.js +2 -1
  82. package/dist/components/table/tableSettings.utils.cjs +29 -0
  83. package/dist/components/table/tableSettings.utils.d.ts +9 -0
  84. package/dist/components/table/tableSettings.utils.js +24 -0
  85. package/dist/components/table/tanstackTable.utils.cjs +18 -0
  86. package/dist/components/table/tanstackTable.utils.js +19 -1
  87. package/dist/components/theme-button/ThemeButton.cjs +9 -4
  88. package/dist/components/theme-button/ThemeButton.d.ts +4 -2
  89. package/dist/components/theme-button/ThemeButton.js +9 -4
  90. package/dist/components/theme-script/ThemeScript.cjs +10 -4
  91. package/dist/components/theme-script/ThemeScript.d.ts +9 -1
  92. package/dist/components/theme-script/ThemeScript.js +10 -4
  93. package/dist/components/xml-viewer/XmlViewer.cjs +2 -3
  94. package/dist/components/xml-viewer/XmlViewer.js +2 -3
  95. package/dist/hooks/useColorScheme.cjs +18 -14
  96. package/dist/hooks/useColorScheme.d.ts +11 -1
  97. package/dist/hooks/useColorScheme.js +19 -15
  98. package/dist/hooks/useGetActiveTheme.cjs +4 -3
  99. package/dist/hooks/useGetActiveTheme.d.ts +7 -1
  100. package/dist/hooks/useGetActiveTheme.js +4 -3
  101. package/dist/hooks/useTableData.cjs +11 -7
  102. package/dist/hooks/useTableData.d.ts +8 -2
  103. package/dist/hooks/useTableData.js +11 -7
  104. package/dist/hooks/useTableSettings.cjs +25 -9
  105. package/dist/hooks/useTableSettings.d.ts +9 -6
  106. package/dist/hooks/useTableSettings.js +25 -9
  107. package/dist/hooks/useTheme.d.ts +6 -0
  108. package/dist/styles/styles.css +7 -3
  109. package/dist/styles/themes/dbc/colors.css +1 -1
  110. package/dist/styles.css +1 -476
  111. package/dist/tanstack-virtual.cjs +12 -0
  112. package/dist/tanstack-virtual.d.ts +1 -0
  113. package/dist/tanstack-virtual.js +1 -0
  114. package/package.json +13 -1
@@ -4,19 +4,23 @@
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var react = require('react');
6
6
  var Pagination = require('../../components/pagination/Pagination');
7
- var SkeletonLoader = require('../../components/skeleton-loader/SkeletonLoader');
8
7
  var useViewportFill = require('../../hooks/useViewportFill');
9
8
  var EmptyState = require('./components/empty-state/EmptyState');
9
+ var TableHeaderSettings = require('./components/table-settings/TableHeaderSettings');
10
10
  var TableBody = require('./components/TableBody');
11
11
  var TableHeader = require('./components/TableHeader');
12
+ var TableSkeletonBody = require('./components/TableSkeletonBody');
12
13
  var table_classes = require('./table.classes');
13
14
  var styles = require('./Table.module.css');
14
15
  var table_utils = require('./table.utils');
16
+ var tableSettings_utils = require('./tableSettings.utils');
15
17
 
16
18
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
17
19
 
18
20
  var styles__default = /*#__PURE__*/_interopDefault(styles);
19
21
 
22
+ const DEFAULT_SKELETON_ROWS = 10;
23
+ const MAX_SKELETON_ROWS = 20;
20
24
  function Table({
21
25
  data,
22
26
  dataKey,
@@ -30,7 +34,8 @@ function Table({
30
34
  emptyConfig,
31
35
  variant = "primary",
32
36
  size = "md",
33
- viewMode,
37
+ viewMode: viewModeProp,
38
+ striped: stripedProp,
34
39
  fillViewport = false,
35
40
  fillViewportBottomOffset = 0,
36
41
  containScrolling = true,
@@ -40,6 +45,9 @@ function Table({
40
45
  measuringLayout = false,
41
46
  toolbar,
42
47
  headerExtras,
48
+ headerActions: headerActionsProp,
49
+ settings,
50
+ settingsOptions,
43
51
  take,
44
52
  skip,
45
53
  totalItemsCount,
@@ -61,7 +69,22 @@ function Table({
61
69
  onPageChange,
62
70
  ...rest
63
71
  }) {
64
- const visibleColumns = react.useMemo(() => table_utils.getVisibleColumns(columns), [columns]);
72
+ var _a;
73
+ const viewMode = viewModeProp != null ? viewModeProp : settings == null ? void 0 : settings.viewMode;
74
+ const striped = (_a = stripedProp != null ? stripedProp : settings == null ? void 0 : settings.striped) != null ? _a : false;
75
+ const settingsVisibleIds = settings == null ? void 0 : settings.visibleColumnIds;
76
+ const visibleColumns = react.useMemo(
77
+ () => (settingsVisibleIds == null ? void 0 : settingsVisibleIds.length) ? tableSettings_utils.filterColumnsBySettings(columns, settingsVisibleIds) : table_utils.getVisibleColumns(columns),
78
+ [columns, settingsVisibleIds]
79
+ );
80
+ const headerActions = headerActionsProp != null || settings ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.headerActions, children: [
81
+ headerActionsProp,
82
+ settings ? /* @__PURE__ */ jsxRuntime.jsx(TableHeaderSettings.TableHeaderSettings, { settings, options: settingsOptions, columns }) : null
83
+ ] }) : null;
84
+ const toolbarContent = !showHeader && headerActions ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.toolbarWithHeaderActions, children: [
85
+ toolbar,
86
+ headerActions
87
+ ] }) : toolbar;
65
88
  const selectionInputName = react.useId();
66
89
  const internalTableRootRef = react.useRef(null);
67
90
  const tableRootRefWrapper = react.useRef(tableRootRef);
@@ -69,17 +92,14 @@ function Table({
69
92
  const hasPagination = Boolean(onPageChange && (loading || data.length > 0));
70
93
  const paginationRef = react.useRef(null);
71
94
  const [paginationHeight, setPaginationHeight] = react.useState(0);
72
- react.useEffect(() => {
95
+ react.useLayoutEffect(() => {
73
96
  const element = paginationRef.current;
74
97
  if (!element) return;
75
98
  const measure = () => setPaginationHeight(element.getBoundingClientRect().height);
76
- const frame = requestAnimationFrame(measure);
99
+ measure();
77
100
  const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(measure) : null;
78
101
  observer == null ? void 0 : observer.observe(element);
79
- return () => {
80
- cancelAnimationFrame(frame);
81
- observer == null ? void 0 : observer.disconnect();
82
- };
102
+ return () => observer == null ? void 0 : observer.disconnect();
83
103
  }, [hasPagination]);
84
104
  const [expandedRows, setExpandedRows] = react.useState(() => /* @__PURE__ */ new Set());
85
105
  const handleToggleRowExpand = react.useCallback((rowId) => {
@@ -99,7 +119,7 @@ function Table({
99
119
  min: 0
100
120
  });
101
121
  const fillViewportActive = fillViewport && containScrolling;
102
- const stickyTop = stickyHeader === "app-header" ? "var(--app-header-height, 60px)" : typeof stickyHeader === "number" ? `${stickyHeader}px` : void 0;
122
+ const stickyTop = stickyHeader === "app-header" ? "var(--app-header-height, 60px)" : typeof stickyHeader === "number" ? `${stickyHeader}px` : stickyHeader === void 0 && containScrolling ? "0px" : void 0;
103
123
  const setTableRootRef = react.useCallback((node) => {
104
124
  internalTableRootRef.current = node;
105
125
  if (typeof tableRootRefWrapper.current === "function") {
@@ -167,16 +187,17 @@ function Table({
167
187
  measuringLayout && styles__default.default.measuringLayout,
168
188
  getRowSeverity && styles__default.default.severityTable
169
189
  );
190
+ const tableElementClassName = table_classes.cx(styles__default.default.tableElement, striped && "dbc-table--striped");
170
191
  const tableShell = /* @__PURE__ */ jsxRuntime.jsx("div", { ...rest, className: tableClassName, children: /* @__PURE__ */ jsxRuntime.jsx(
171
192
  "div",
172
193
  {
173
194
  ref: setTableRootRef,
174
195
  className: styles__default.default.tableScroll,
175
196
  style: fillViewportActive ? viewportFillStyle : !containScrolling ? { overflow: "visible" } : void 0,
176
- children: loading && !data.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.emptyStateSlot, children: /* @__PURE__ */ jsxRuntime.jsx(SkeletonLoader.SkeletonLoader, { type: "table", rows: 5, columns: 3 }) }) : !data.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.emptyStateSlot, children: /* @__PURE__ */ jsxRuntime.jsx(EmptyState.TableEmptyState, { config: emptyConfig }) }) : /* @__PURE__ */ jsxRuntime.jsxs(
197
+ children: !loading && !data.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.emptyStateSlot, children: /* @__PURE__ */ jsxRuntime.jsx(EmptyState.TableEmptyState, { config: emptyConfig }) }) : /* @__PURE__ */ jsxRuntime.jsxs(
177
198
  "table",
178
199
  {
179
- className: styles__default.default.tableElement,
200
+ className: tableElementClassName,
180
201
  "aria-rowcount": data.length,
181
202
  "data-show-header": showHeader,
182
203
  style: tableWidth != null ? { width: tableWidth } : void 0,
@@ -197,17 +218,25 @@ function Table({
197
218
  columns: visibleColumns,
198
219
  hasSelection,
199
220
  selectionMode,
200
- hasRowContextMenu: Boolean(getContextMenuItems),
221
+ hasRowContextMenu: Boolean(getContextMenuItems) || expandableRows,
201
222
  allRowsSelected,
202
223
  onSelectAllRows,
203
224
  sortById,
204
225
  sortDirection,
205
226
  onSortChange,
206
227
  headerExtras,
228
+ headerActions,
207
229
  stickyTop
208
230
  }
209
231
  ),
210
- bodyContent
232
+ loading && !data.length ? /* @__PURE__ */ jsxRuntime.jsx(
233
+ TableSkeletonBody.TableSkeletonBody,
234
+ {
235
+ columns: visibleColumns,
236
+ rows: Math.min(take != null ? take : DEFAULT_SKELETON_ROWS, MAX_SKELETON_ROWS),
237
+ hasSelection
238
+ }
239
+ ) : bodyContent
211
240
  ]
212
241
  }
213
242
  )
@@ -223,7 +252,7 @@ function Table({
223
252
  ...!fillViewportActive && { blockSize: "auto", overflow: "visible" }
224
253
  },
225
254
  children: [
226
- toolbar ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.toolbarSlot, children: toolbar }) : null,
255
+ toolbarContent ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.toolbarSlot, children: toolbarContent }) : null,
227
256
  /* @__PURE__ */ jsxRuntime.jsx(
228
257
  "div",
229
258
  {
@@ -246,7 +275,7 @@ function Table({
246
275
  position: "relative"
247
276
  },
248
277
  children: [
249
- toolbar ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.toolbarSlot, children: toolbar }) : null,
278
+ toolbarContent ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.toolbarSlot, children: toolbarContent }) : null,
250
279
  tableShell,
251
280
  paginationEl
252
281
  ]
@@ -1,4 +1,4 @@
1
1
  import type { JSX } from 'react';
2
2
  import type { TableProps } from './Table.types';
3
- export declare function Table<T extends Record<string, any>>({ data, dataKey, columns, selectedRows, selectionMode, allRowsSelected, sortById, sortDirection, loading, emptyConfig, variant, size, viewMode, fillViewport, fillViewportBottomOffset, containScrolling, stickyHeader, tableWidth, tableRootRef, measuringLayout, toolbar, headerExtras, take, skip, totalItemsCount, paginationPlacement, showFirstLast, showGoToPage, showPageSize, pageSizeOptions, showHeader, getRowSeverity, getContextMenuItems, showContextMenuOnHover, expandableRows, onRowClick, onRowMouseEnter, onRowSelect, onSelectAllRows, onSortChange, onPageChange, ...rest }: TableProps<T>): JSX.Element;
3
+ export declare function Table<T extends Record<string, any>>({ data, dataKey, columns, selectedRows, selectionMode, allRowsSelected, sortById, sortDirection, loading, emptyConfig, variant, size, viewMode: viewModeProp, striped: stripedProp, fillViewport, fillViewportBottomOffset, containScrolling, stickyHeader, tableWidth, tableRootRef, measuringLayout, toolbar, headerExtras, headerActions: headerActionsProp, settings, settingsOptions, take, skip, totalItemsCount, paginationPlacement, showFirstLast, showGoToPage, showPageSize, pageSizeOptions, showHeader, getRowSeverity, getContextMenuItems, showContextMenuOnHover, expandableRows, onRowClick, onRowMouseEnter, onRowSelect, onSelectAllRows, onSortChange, onPageChange, ...rest }: TableProps<T>): JSX.Element;
4
4
  export type { ColumnItem } from './Table.types';
@@ -1,16 +1,20 @@
1
1
  'use client';
2
- import { jsx, jsxs } from 'react/jsx-runtime';
3
- import { useMemo, useId, useRef, useState, useEffect, useCallback } from 'react';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { useMemo, useId, useRef, useState, useLayoutEffect, useCallback } from 'react';
4
4
  import { Pagination } from '../../components/pagination/Pagination';
5
- import { SkeletonLoader } from '../../components/skeleton-loader/SkeletonLoader';
6
5
  import { useViewportFill } from '../../hooks/useViewportFill';
7
6
  import { TableEmptyState } from './components/empty-state/EmptyState';
7
+ import { TableHeaderSettings } from './components/table-settings/TableHeaderSettings';
8
8
  import { TableBody } from './components/TableBody';
9
9
  import { TableHeader } from './components/TableHeader';
10
+ import { TableSkeletonBody } from './components/TableSkeletonBody';
10
11
  import { cx } from './table.classes';
11
12
  import styles from './Table.module.css';
12
13
  import { getVisibleColumns, SELECTION_COLUMN_PX } from './table.utils';
14
+ import { filterColumnsBySettings } from './tableSettings.utils';
13
15
 
16
+ const DEFAULT_SKELETON_ROWS = 10;
17
+ const MAX_SKELETON_ROWS = 20;
14
18
  function Table({
15
19
  data,
16
20
  dataKey,
@@ -24,7 +28,8 @@ function Table({
24
28
  emptyConfig,
25
29
  variant = "primary",
26
30
  size = "md",
27
- viewMode,
31
+ viewMode: viewModeProp,
32
+ striped: stripedProp,
28
33
  fillViewport = false,
29
34
  fillViewportBottomOffset = 0,
30
35
  containScrolling = true,
@@ -34,6 +39,9 @@ function Table({
34
39
  measuringLayout = false,
35
40
  toolbar,
36
41
  headerExtras,
42
+ headerActions: headerActionsProp,
43
+ settings,
44
+ settingsOptions,
37
45
  take,
38
46
  skip,
39
47
  totalItemsCount,
@@ -55,7 +63,22 @@ function Table({
55
63
  onPageChange,
56
64
  ...rest
57
65
  }) {
58
- const visibleColumns = useMemo(() => getVisibleColumns(columns), [columns]);
66
+ var _a;
67
+ const viewMode = viewModeProp != null ? viewModeProp : settings == null ? void 0 : settings.viewMode;
68
+ const striped = (_a = stripedProp != null ? stripedProp : settings == null ? void 0 : settings.striped) != null ? _a : false;
69
+ const settingsVisibleIds = settings == null ? void 0 : settings.visibleColumnIds;
70
+ const visibleColumns = useMemo(
71
+ () => (settingsVisibleIds == null ? void 0 : settingsVisibleIds.length) ? filterColumnsBySettings(columns, settingsVisibleIds) : getVisibleColumns(columns),
72
+ [columns, settingsVisibleIds]
73
+ );
74
+ const headerActions = headerActionsProp != null || settings ? /* @__PURE__ */ jsxs("div", { className: styles.headerActions, children: [
75
+ headerActionsProp,
76
+ settings ? /* @__PURE__ */ jsx(TableHeaderSettings, { settings, options: settingsOptions, columns }) : null
77
+ ] }) : null;
78
+ const toolbarContent = !showHeader && headerActions ? /* @__PURE__ */ jsxs("div", { className: styles.toolbarWithHeaderActions, children: [
79
+ toolbar,
80
+ headerActions
81
+ ] }) : toolbar;
59
82
  const selectionInputName = useId();
60
83
  const internalTableRootRef = useRef(null);
61
84
  const tableRootRefWrapper = useRef(tableRootRef);
@@ -63,17 +86,14 @@ function Table({
63
86
  const hasPagination = Boolean(onPageChange && (loading || data.length > 0));
64
87
  const paginationRef = useRef(null);
65
88
  const [paginationHeight, setPaginationHeight] = useState(0);
66
- useEffect(() => {
89
+ useLayoutEffect(() => {
67
90
  const element = paginationRef.current;
68
91
  if (!element) return;
69
92
  const measure = () => setPaginationHeight(element.getBoundingClientRect().height);
70
- const frame = requestAnimationFrame(measure);
93
+ measure();
71
94
  const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(measure) : null;
72
95
  observer == null ? void 0 : observer.observe(element);
73
- return () => {
74
- cancelAnimationFrame(frame);
75
- observer == null ? void 0 : observer.disconnect();
76
- };
96
+ return () => observer == null ? void 0 : observer.disconnect();
77
97
  }, [hasPagination]);
78
98
  const [expandedRows, setExpandedRows] = useState(() => /* @__PURE__ */ new Set());
79
99
  const handleToggleRowExpand = useCallback((rowId) => {
@@ -93,7 +113,7 @@ function Table({
93
113
  min: 0
94
114
  });
95
115
  const fillViewportActive = fillViewport && containScrolling;
96
- const stickyTop = stickyHeader === "app-header" ? "var(--app-header-height, 60px)" : typeof stickyHeader === "number" ? `${stickyHeader}px` : void 0;
116
+ const stickyTop = stickyHeader === "app-header" ? "var(--app-header-height, 60px)" : typeof stickyHeader === "number" ? `${stickyHeader}px` : stickyHeader === void 0 && containScrolling ? "0px" : void 0;
97
117
  const setTableRootRef = useCallback((node) => {
98
118
  internalTableRootRef.current = node;
99
119
  if (typeof tableRootRefWrapper.current === "function") {
@@ -161,16 +181,17 @@ function Table({
161
181
  measuringLayout && styles.measuringLayout,
162
182
  getRowSeverity && styles.severityTable
163
183
  );
184
+ const tableElementClassName = cx(styles.tableElement, striped && "dbc-table--striped");
164
185
  const tableShell = /* @__PURE__ */ jsx("div", { ...rest, className: tableClassName, children: /* @__PURE__ */ jsx(
165
186
  "div",
166
187
  {
167
188
  ref: setTableRootRef,
168
189
  className: styles.tableScroll,
169
190
  style: fillViewportActive ? viewportFillStyle : !containScrolling ? { overflow: "visible" } : void 0,
170
- children: loading && !data.length ? /* @__PURE__ */ jsx("div", { className: styles.emptyStateSlot, children: /* @__PURE__ */ jsx(SkeletonLoader, { type: "table", rows: 5, columns: 3 }) }) : !data.length ? /* @__PURE__ */ jsx("div", { className: styles.emptyStateSlot, children: /* @__PURE__ */ jsx(TableEmptyState, { config: emptyConfig }) }) : /* @__PURE__ */ jsxs(
191
+ children: !loading && !data.length ? /* @__PURE__ */ jsx("div", { className: styles.emptyStateSlot, children: /* @__PURE__ */ jsx(TableEmptyState, { config: emptyConfig }) }) : /* @__PURE__ */ jsxs(
171
192
  "table",
172
193
  {
173
- className: styles.tableElement,
194
+ className: tableElementClassName,
174
195
  "aria-rowcount": data.length,
175
196
  "data-show-header": showHeader,
176
197
  style: tableWidth != null ? { width: tableWidth } : void 0,
@@ -191,17 +212,25 @@ function Table({
191
212
  columns: visibleColumns,
192
213
  hasSelection,
193
214
  selectionMode,
194
- hasRowContextMenu: Boolean(getContextMenuItems),
215
+ hasRowContextMenu: Boolean(getContextMenuItems) || expandableRows,
195
216
  allRowsSelected,
196
217
  onSelectAllRows,
197
218
  sortById,
198
219
  sortDirection,
199
220
  onSortChange,
200
221
  headerExtras,
222
+ headerActions,
201
223
  stickyTop
202
224
  }
203
225
  ),
204
- bodyContent
226
+ loading && !data.length ? /* @__PURE__ */ jsx(
227
+ TableSkeletonBody,
228
+ {
229
+ columns: visibleColumns,
230
+ rows: Math.min(take != null ? take : DEFAULT_SKELETON_ROWS, MAX_SKELETON_ROWS),
231
+ hasSelection
232
+ }
233
+ ) : bodyContent
205
234
  ]
206
235
  }
207
236
  )
@@ -217,7 +246,7 @@ function Table({
217
246
  ...!fillViewportActive && { blockSize: "auto", overflow: "visible" }
218
247
  },
219
248
  children: [
220
- toolbar ? /* @__PURE__ */ jsx("div", { className: styles.toolbarSlot, children: toolbar }) : null,
249
+ toolbarContent ? /* @__PURE__ */ jsx("div", { className: styles.toolbarSlot, children: toolbarContent }) : null,
221
250
  /* @__PURE__ */ jsx(
222
251
  "div",
223
252
  {
@@ -240,7 +269,7 @@ function Table({
240
269
  position: "relative"
241
270
  },
242
271
  children: [
243
- toolbar ? /* @__PURE__ */ jsx("div", { className: styles.toolbarSlot, children: toolbar }) : null,
272
+ toolbarContent ? /* @__PURE__ */ jsx("div", { className: styles.toolbarSlot, children: toolbarContent }) : null,
244
273
  tableShell,
245
274
  paginationEl
246
275
  ]
@@ -58,7 +58,9 @@
58
58
  --table-component-cell-fg: var(--table-cell-fg);
59
59
  --table-component-cell-border: var(--table-border-subtle);
60
60
  --table-component-divider: var(--table-divider);
61
- --table-row-actions-reserve-inline: calc(var(--component-size-md) + 4px + var(--spacing-2xs));
61
+ /* Row actions button (component-size-sm) plus its inline-end offset. Keep in
62
+ sync with ROW_ACTIONS_RESERVE_PX in table.utils.ts. */
63
+ --table-row-actions-reserve-inline: calc(var(--component-size-sm) + var(--spacing-2xs));
62
64
 
63
65
  display: flex;
64
66
  flex-direction: column;
@@ -78,7 +80,7 @@
78
80
 
79
81
  .embedded {
80
82
  --table-component-header-bg: transparent;
81
- --table-component-header-fg: var(--color-fg-subtle);
83
+ --table-component-header-fg: var(--color-fg-muted);
82
84
  --table-component-header-font-weight: var(--font-weight-default);
83
85
  --table-component-row-bg: transparent;
84
86
  --table-component-row-bg-hover: var(--color-bg-contextual-subtle);
@@ -101,6 +103,9 @@
101
103
  min-block-size: 0;
102
104
  min-inline-size: 0;
103
105
  overflow: auto;
106
+ /* Always reserve the scrollbar's space, so the column widths don't change
107
+ when the body starts or stops scrolling (e.g. skeleton rows vs. data). */
108
+ scrollbar-gutter: stable;
104
109
  background: var(--table-component-row-bg);
105
110
  -webkit-overflow-scrolling: touch;
106
111
  }
@@ -450,6 +455,75 @@
450
455
  pointer-events: auto;
451
456
  }
452
457
 
458
+ /*
459
+ * Header actions (e.g. the settings trigger) sit at the inline end of the last
460
+ * header cell, lined up with the row-actions buttons below. Stacked above
461
+ * .thOverlayExtras so the column resizer doesn't swallow clicks on them.
462
+ */
463
+ .thEndAction {
464
+ position: absolute;
465
+ inset-block-start: 50%;
466
+ inset-inline-end: var(--spacing-2xs);
467
+ transform: translateY(-50%);
468
+ display: inline-flex;
469
+ align-items: center;
470
+ z-index: 8;
471
+ }
472
+
473
+ .headerActions {
474
+ display: inline-flex;
475
+ align-items: center;
476
+ gap: var(--spacing-2xs);
477
+ }
478
+
479
+ /*
480
+ * Keep header actions low-key: the settings trigger uses the same colour as
481
+ * the header's sort arrows and a compact square button, and header actions are
482
+ * only shown while the table is hovered - like the row-actions buttons. They
483
+ * stay visible while focused or while their menu is open, and always on
484
+ * devices without hover. They remain clickable while faded out.
485
+ * `button[data-…]` outranks Button's own `.button.inline` colour rule.
486
+ */
487
+ .thEndAction button[data-table-settings-trigger] {
488
+ color: var(--color-disabled-fg);
489
+ inline-size: var(--component-size-sm);
490
+ min-inline-size: var(--component-size-sm);
491
+ padding: 0;
492
+ }
493
+
494
+ .thEndAction button[data-table-settings-trigger]:hover,
495
+ .thEndAction button[data-table-settings-trigger][aria-expanded='true'] {
496
+ color: var(--color-fg-subtle);
497
+ }
498
+
499
+ .thEndAction {
500
+ opacity: 0;
501
+ transition: opacity 0.15s;
502
+ }
503
+
504
+ .tableElement:hover .thEndAction,
505
+ .thEndAction:focus-within,
506
+ .thEndAction:has([aria-expanded='true']) {
507
+ opacity: 1;
508
+ }
509
+
510
+ @media (hover: none) {
511
+ .thEndAction {
512
+ opacity: 1;
513
+ }
514
+ }
515
+
516
+ .toolbarWithHeaderActions {
517
+ display: flex;
518
+ align-items: center;
519
+ justify-content: space-between;
520
+ gap: var(--spacing-sm);
521
+ }
522
+
523
+ .toolbarWithHeaderActions > .headerActions {
524
+ margin-inline-start: auto;
525
+ }
526
+
453
527
  .sortIndicator {
454
528
  display: inline-flex;
455
529
  flex: 0 0 auto;
@@ -475,6 +549,22 @@
475
549
  color: var(--color-disabled-fg);
476
550
  }
477
551
 
552
+ /* Loading placeholder: a full text line (the cell's line-height) with the bar
553
+ centred in it, matching the height of a real single-line row. */
554
+ .skeletonCellContent {
555
+ display: flex;
556
+ align-items: center;
557
+ block-size: 1lh;
558
+ }
559
+
560
+ /* Table rows (incl. striped ones) need stronger bars than the default
561
+ skeleton to be visible. [role='status'] outranks .skeletonLoader, which
562
+ sets these variables on the item itself. */
563
+ .skeletonCellContent > [role='status'] {
564
+ --skeleton-base: var(--opac-bg-dark);
565
+ --skeleton-sheen: var(--opac-bg-default);
566
+ }
567
+
478
568
  .cellContent {
479
569
  display: flex;
480
570
  inline-size: 100%;
@@ -558,8 +648,19 @@
558
648
  text-overflow: clip;
559
649
  }
560
650
 
561
- .row:focus-within > .cell:not(.selectionCell) .cellContent,
562
- .row:focus-within > .cell:not(.selectionCell) .cellValueEllipsis {
651
+ /*
652
+ * Unwrap the row's content only for keyboard focus. Mouse clicks on the row
653
+ * don't match :focus-visible, so the row keeps its height.
654
+ * The row-actions button is excluded outright: its menu returns focus to it
655
+ * programmatically on close, and browsers (notably Safari/Firefox on macOS,
656
+ * where clicking a button doesn't focus it) may treat that as :focus-visible.
657
+ */
658
+ .row:focus-visible > .cell:not(.selectionCell) .cellContent,
659
+ .row:focus-visible > .cell:not(.selectionCell) .cellValueEllipsis,
660
+ .row:has(:focus-visible:not([data-row-context-menu])) > .cell:not(.selectionCell) .cellContent,
661
+ .row:has(:focus-visible:not([data-row-context-menu]))
662
+ > .cell:not(.selectionCell)
663
+ .cellValueEllipsis {
563
664
  white-space: normal;
564
665
  overflow-wrap: break-word;
565
666
  word-break: normal;
@@ -569,8 +670,8 @@
569
670
 
570
671
  .contextMenuTrigger {
571
672
  color: var(--color-fg-subtle);
572
- min-inline-size: calc(var(--component-size-md) + 4px);
573
- min-block-size: calc(var(--component-size-md) + 4px);
673
+ min-inline-size: var(--component-size-sm);
674
+ min-block-size: var(--component-size-sm);
574
675
  border-radius: 999px;
575
676
  background-color: transparent;
576
677
  position: relative;
@@ -578,9 +679,17 @@
578
679
  overflow: hidden;
579
680
  }
580
681
 
682
+ /*
683
+ * The row-actions button is centred on the cell's first line of text (cell
684
+ * padding + half the 20px line height) rather than on the whole cell, so it
685
+ * stays put when the row grows (wrapping, selection, expansion). For a
686
+ * single-line row this is the same spot as centring on the cell.
687
+ */
581
688
  .contextMenuOverlay {
689
+ --table-row-actions-cell-padding-block: 6px;
690
+
582
691
  position: absolute;
583
- inset-block-start: 50%;
692
+ inset-block-start: calc(var(--table-row-actions-cell-padding-block) + 10px);
584
693
  inset-inline-end: var(--spacing-2xs);
585
694
  transform: translateY(-50%);
586
695
  display: inline-flex;
@@ -595,6 +704,25 @@
595
704
  z-index: 2;
596
705
  }
597
706
 
707
+ .md .contextMenuOverlay {
708
+ --table-row-actions-cell-padding-block: var(--spacing-xs);
709
+ }
710
+
711
+ .sm .contextMenuOverlay {
712
+ --table-row-actions-cell-padding-block: var(--spacing-xxs);
713
+ }
714
+
715
+ /* Follow the cell's own vertical alignment when it isn't top-aligned. */
716
+ .cell[data-vertical-align='middle'] .contextMenuOverlay {
717
+ inset-block-start: 50%;
718
+ }
719
+
720
+ .cell[data-vertical-align='bottom'] .contextMenuOverlay {
721
+ inset-block-start: auto;
722
+ inset-block-end: calc(var(--table-row-actions-cell-padding-block) + 10px);
723
+ transform: translateY(50%);
724
+ }
725
+
598
726
  .contextMenuHoverOnly .contextMenuOverlay {
599
727
  opacity: 0;
600
728
  pointer-events: none;
@@ -1,7 +1,7 @@
1
1
  import type { HTMLAttributes, ReactNode, Ref } from 'react';
2
2
  import type { PageChangeEvent } from '../../components/pagination/Pagination';
3
3
  import type { Severity } from '../../constants/severity.types';
4
- import type { ViewMode } from '../../hooks/useTableSettings';
4
+ export type ViewMode = 'compact' | 'wrapped';
5
5
  export interface ColumnItem<T> {
6
6
  id: string;
7
7
  header: string | (() => ReactNode);
@@ -21,6 +21,35 @@ export interface ColumnItem<T> {
21
21
  severity?: any;
22
22
  allowOverflow?: boolean;
23
23
  }
24
+ /** Visibility fields shared by Table columns and accessor-shaped adapters. */
25
+ export interface TableSettingsColumn extends Partial<Pick<ColumnItem<unknown>, 'id' | 'hidden' | 'canHide'>> {
26
+ accessorKey?: PropertyKey;
27
+ header?: unknown;
28
+ enableHiding?: boolean;
29
+ meta?: unknown;
30
+ }
31
+ /**
32
+ * Settings state + setters that drive the table's built-in header settings
33
+ * menu. This is the shape `useTableSettings` returns, so its result can be
34
+ * passed straight through: `settings={useTableSettings(...)}`.
35
+ */
36
+ export interface TableSettingsController {
37
+ viewMode: ViewMode;
38
+ setViewMode: (mode: ViewMode) => void;
39
+ visibleColumnIds?: string[];
40
+ setVisibleColumnIds?: (ids: string[]) => void;
41
+ striped?: boolean;
42
+ setStriped?: (nextStriped: boolean) => void;
43
+ }
44
+ /** Labels and extra items for the table's built-in header settings menu. */
45
+ export interface TableSettingsMenuOptions {
46
+ /** Accessible name and tooltip for the trigger. Defaults to "Visningsindstillinger". */
47
+ triggerLabel?: string;
48
+ columnsLabel?: string;
49
+ allPresetLabel?: string;
50
+ standardPresetLabel?: string;
51
+ additionalSettings?: (close?: () => void) => ReactNode;
52
+ }
24
53
  export type HeaderExtrasArgs<T> = {
25
54
  column: ColumnItem<T>;
26
55
  index: number;
@@ -57,16 +86,33 @@ export type TableProps<T extends Record<string, any>> = Omit<HTMLAttributes<HTML
57
86
  variant?: TableVariant;
58
87
  size?: 'sm' | 'md';
59
88
  viewMode?: ViewMode;
89
+ striped?: boolean;
60
90
  fillViewport?: boolean;
61
91
  /** Extra space below the table, in addition to measured layout/pagination spacing. Defaults to 0. */
62
92
  fillViewportBottomOffset?: number;
63
93
  containScrolling?: boolean;
94
+ /**
95
+ * Pins the header while scrolling. Defaults to the top of the table's own
96
+ * scroll area when `containScrolling` is on; `false` turns it off.
97
+ */
64
98
  stickyHeader?: StickyHeader;
65
99
  tableWidth?: number;
66
100
  tableRootRef?: Ref<HTMLDivElement>;
67
101
  measuringLayout?: boolean;
68
102
  toolbar?: ReactNode;
69
103
  headerExtras?: (args: HeaderExtrasArgs<T>) => ReactNode;
104
+ /**
105
+ * Content rendered at the inline end of the header row, above the row
106
+ * actions column. Falls back to the toolbar when `showHeader` is false.
107
+ */
108
+ headerActions?: ReactNode;
109
+ /**
110
+ * Shows a settings menu (stripes, text wrapping, column visibility) in the
111
+ * header and applies its state to the table. Pass the result of
112
+ * `useTableSettings`. Explicit `viewMode`/`striped` props take precedence.
113
+ */
114
+ settings?: TableSettingsController;
115
+ settingsOptions?: TableSettingsMenuOptions;
70
116
  take?: number;
71
117
  skip?: number;
72
118
  totalItemsCount?: number;