@dbcdk/react-components 0.0.9 → 0.0.12

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 (113) hide show
  1. package/dist/components/accordion/Accordion.d.ts +27 -0
  2. package/dist/components/accordion/Accordion.js +66 -0
  3. package/dist/components/accordion/Accordion.module.css +87 -0
  4. package/dist/components/button/Button.module.css +1 -0
  5. package/dist/components/card/Card.d.ts +21 -3
  6. package/dist/components/card/Card.js +17 -2
  7. package/dist/components/card/Card.module.css +59 -0
  8. package/dist/components/circle/Circle.d.ts +5 -1
  9. package/dist/components/circle/Circle.js +2 -2
  10. package/dist/components/circle/Circle.module.css +60 -4
  11. package/dist/components/code-block/CodeBlock.js +1 -1
  12. package/dist/components/code-block/CodeBlock.module.css +30 -17
  13. package/dist/components/copy-button/CopyButton.d.ts +1 -0
  14. package/dist/components/copy-button/CopyButton.js +10 -2
  15. package/dist/components/datetime-picker/DateTimePicker.d.ts +4 -8
  16. package/dist/components/datetime-picker/DateTimePicker.js +72 -92
  17. package/dist/components/datetime-picker/dateTimeHelpers.d.ts +14 -12
  18. package/dist/components/datetime-picker/dateTimeHelpers.js +25 -45
  19. package/dist/components/filter-field/FilterField.js +16 -11
  20. package/dist/components/filter-field/FilterField.module.css +133 -12
  21. package/dist/components/forms/checkbox/Checkbox.d.ts +4 -10
  22. package/dist/components/forms/checkbox/Checkbox.js +3 -5
  23. package/dist/components/forms/checkbox-group/CheckboxGroup.js +1 -1
  24. package/dist/components/forms/checkbox-group/CheckboxGroup.module.css +1 -1
  25. package/dist/components/forms/input/Input.d.ts +1 -0
  26. package/dist/components/forms/input/Input.js +2 -4
  27. package/dist/components/forms/input/Input.module.css +10 -11
  28. package/dist/components/forms/input-container/InputContainer.d.ts +2 -1
  29. package/dist/components/forms/input-container/InputContainer.js +3 -3
  30. package/dist/components/forms/input-container/InputContainer.module.css +65 -0
  31. package/dist/components/forms/radio-buttons/RadioButton.d.ts +36 -0
  32. package/dist/components/forms/radio-buttons/RadioButton.js +26 -0
  33. package/dist/components/forms/radio-buttons/RadioButtonGroup.d.ts +25 -0
  34. package/dist/components/forms/radio-buttons/RadioButtonGroup.js +19 -0
  35. package/dist/components/forms/radio-buttons/RadioButtons.module.css +117 -0
  36. package/dist/components/forms/select/Select.d.ts +1 -1
  37. package/dist/components/forms/select/Select.js +3 -3
  38. package/dist/components/forms/text-area/Textarea.js +3 -3
  39. package/dist/components/forms/text-area/Textarea.module.css +8 -1
  40. package/dist/components/headline/Headline.d.ts +2 -7
  41. package/dist/components/headline/Headline.js +5 -2
  42. package/dist/components/headline/Headline.module.css +61 -2
  43. package/dist/components/hyperlink/Hyperlink.d.ts +19 -6
  44. package/dist/components/hyperlink/Hyperlink.js +35 -7
  45. package/dist/components/hyperlink/Hyperlink.module.css +50 -2
  46. package/dist/components/icon/Icon.module.css +1 -0
  47. package/dist/components/interval-select/IntervalSelect.js +1 -1
  48. package/dist/components/menu/Menu.d.ts +32 -0
  49. package/dist/components/menu/Menu.js +73 -13
  50. package/dist/components/menu/Menu.module.css +72 -4
  51. package/dist/components/nav-bar/NavBar.d.ts +24 -6
  52. package/dist/components/overlay/modal/Modal.module.css +2 -2
  53. package/dist/components/overlay/side-panel/SidePanel.d.ts +12 -4
  54. package/dist/components/overlay/side-panel/SidePanel.js +77 -4
  55. package/dist/components/overlay/side-panel/SidePanel.module.css +149 -28
  56. package/dist/components/overlay/side-panel/useSidePanel.d.ts +1 -1
  57. package/dist/components/overlay/side-panel/useSidePanel.js +2 -2
  58. package/dist/components/overlay/tooltip/useTooltipTrigger.js +4 -2
  59. package/dist/components/page-layout/PageLayout.js +0 -2
  60. package/dist/components/popover/Popover.js +1 -1
  61. package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.d.ts +5 -5
  62. package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.js +36 -24
  63. package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.module.css +0 -3
  64. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.d.ts +3 -1
  65. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.js +4 -3
  66. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +109 -79
  67. package/dist/components/sidebar/components/sidebar-items/SidebarItems.js +16 -3
  68. package/dist/components/sidebar/components/sidebar-items/SidebarItems.module.css +20 -0
  69. package/dist/components/sidebar/providers/SidebarProvider.d.ts +4 -1
  70. package/dist/components/sidebar/providers/SidebarProvider.js +85 -58
  71. package/dist/components/skeleton-loader/SkeletonLoader.d.ts +1 -1
  72. package/dist/components/skeleton-loader/SkeletonLoader.js +15 -12
  73. package/dist/components/split-button/SplitButton.d.ts +1 -1
  74. package/dist/components/split-button/SplitButton.js +3 -1
  75. package/dist/components/split-button/SplitButton.module.css +4 -4
  76. package/dist/components/state-page/StatePage.d.ts +9 -0
  77. package/dist/components/state-page/StatePage.js +20 -0
  78. package/dist/components/state-page/StatePage.module.css +9 -0
  79. package/dist/components/state-page/empty.d.ts +2 -0
  80. package/dist/components/state-page/empty.js +2 -0
  81. package/dist/components/state-page/error.d.ts +2 -0
  82. package/dist/components/state-page/error.js +2 -0
  83. package/dist/components/state-page/notFound.d.ts +2 -0
  84. package/dist/components/state-page/notFound.js +2 -0
  85. package/dist/components/sticky-footer-layout/StickyFooterLayout.d.ts +19 -0
  86. package/dist/components/sticky-footer-layout/StickyFooterLayout.js +27 -0
  87. package/dist/components/table/Table.d.ts +9 -4
  88. package/dist/components/table/Table.js +6 -9
  89. package/dist/components/table/Table.module.css +180 -59
  90. package/dist/components/table/components/empty-state/EmptyState.d.ts +1 -1
  91. package/dist/components/table/components/empty-state/EmptyState.js +6 -7
  92. package/dist/components/table/components/table-settings/TableSettings.d.ts +13 -3
  93. package/dist/components/table/components/table-settings/TableSettings.js +55 -4
  94. package/dist/components/table/tanstack.d.ts +12 -1
  95. package/dist/components/table/tanstack.js +75 -23
  96. package/dist/components/toast/Toast.js +5 -1
  97. package/dist/components/toast/Toast.module.css +40 -15
  98. package/dist/components/toast/provider/ToastProvider.js +1 -0
  99. package/dist/hooks/useTableSettings.d.ts +23 -4
  100. package/dist/hooks/useTableSettings.js +64 -17
  101. package/dist/hooks/useTimeDuration.js +9 -3
  102. package/dist/hooks/useViewportFill.js +1 -0
  103. package/dist/index.d.ts +6 -0
  104. package/dist/index.js +6 -1
  105. package/dist/src/styles/styles.css +60 -25
  106. package/dist/styles/animation.d.ts +5 -0
  107. package/dist/styles/animation.js +5 -0
  108. package/dist/styles/styles.css +60 -25
  109. package/dist/styles/themes/dbc/dark.css +1 -1
  110. package/dist/styles/themes/dbc/light.css +2 -1
  111. package/dist/utils/localStorage.utils.d.ts +19 -0
  112. package/dist/utils/localStorage.utils.js +78 -0
  113. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import type { JSX, ReactNode } from 'react';
2
+ import type { HTMLAttributes, JSX, ReactNode } from 'react';
3
3
  import { Severity } from '../../constants/severity.types';
4
4
  import { PageChangeEvent } from '../../components/pagination/Pagination';
5
5
  import { ViewMode } from '../../hooks/useTableSettings';
@@ -19,13 +19,14 @@ export interface ColumnItem<T> {
19
19
  allowWrap?: boolean;
20
20
  emptyPlaceholder?: ReactNode;
21
21
  width?: number | string;
22
+ canHide?: boolean;
22
23
  }
23
24
  type HeaderExtrasArgs<T> = {
24
25
  column: ColumnItem<T>;
25
26
  index: number;
26
27
  };
27
28
  export type TableVariant = 'primary' | 'embedded';
28
- export interface TableProps<T extends Record<string, any>> {
29
+ export type TableProps<T extends Record<string, any>> = Omit<HTMLAttributes<HTMLTableElement>, 'onClick'> & {
29
30
  data: T[];
30
31
  dataKey: keyof T;
31
32
  columns: ColumnItem<T>[];
@@ -42,6 +43,10 @@ export interface TableProps<T extends Record<string, any>> {
42
43
  headerExtras?: (args: HeaderExtrasArgs<T>) => ReactNode;
43
44
  columnStyles?: Partial<Record<string, React.CSSProperties>>;
44
45
  headerBelowRow?: ReactNode;
46
+ /**
47
+ * NEW: optional toolbar area above the table (right now used for column selector)
48
+ */
49
+ toolbar?: ReactNode;
45
50
  striped?: boolean;
46
51
  fillViewport?: boolean;
47
52
  viewportBottomOffset?: number;
@@ -58,6 +63,6 @@ export interface TableProps<T extends Record<string, any>> {
58
63
  showFirstLast?: boolean;
59
64
  viewMode?: ViewMode;
60
65
  emptyConfig?: TableEmptyConfig;
61
- }
62
- export declare function Table<T extends Record<string, any>>({ data, columns, selectedRows, onRowSelect, selectionMode, onSortChange, onRowClick, sortById, sortDirection, dataKey, headerExtras, columnStyles, headerBelowRow, striped, fillViewport, viewportBottomOffset, viewportMin, viewportIncludeMarginTop, take, skip, paginationPlacement, totalItemsCount, onPageChange, loading, variant, size, getRowSeverity, showFirstLast, allRowsSelected, onSelectAllRows, viewMode, emptyConfig, }: TableProps<T>): JSX.Element;
66
+ } & Omit<HTMLAttributes<HTMLTableElement>, 'onClick'>;
67
+ export declare function Table<T extends Record<string, any>>({ data, columns, selectedRows, onRowSelect, selectionMode, onSortChange, onRowClick, sortById, sortDirection, dataKey, headerExtras, columnStyles, headerBelowRow, toolbar, striped, fillViewport, viewportBottomOffset, viewportMin, viewportIncludeMarginTop, take, skip, paginationPlacement, totalItemsCount, onPageChange, loading, variant, size, getRowSeverity, showFirstLast, allRowsSelected, onSelectAllRows, viewMode, emptyConfig, ...rest }: TableProps<T>): JSX.Element;
63
68
  export {};
@@ -9,7 +9,7 @@ import { Pagination } from '../../components/pagination/Pagination';
9
9
  import { SkeletonLoaderItem } from '../../components/skeleton-loader/skeleton-loader-item/SkeletonLoaderItem';
10
10
  import { TableEmptyState } from './components/empty-state/EmptyState';
11
11
  import styles from './Table.module.css';
12
- export function Table({ data, columns, selectedRows, onRowSelect, selectionMode = 'single', onSortChange, onRowClick, sortById, sortDirection, dataKey, headerExtras, columnStyles, headerBelowRow, striped, fillViewport = false, viewportBottomOffset = 0, viewportMin = 120, viewportIncludeMarginTop = false, take, skip, paginationPlacement = 'bottom', totalItemsCount, onPageChange, loading, variant = 'primary', size = 'md', getRowSeverity, showFirstLast = false, allRowsSelected, onSelectAllRows, viewMode, emptyConfig, }) {
12
+ export function Table({ data, columns, selectedRows, onRowSelect, selectionMode = 'single', onSortChange, onRowClick, sortById, sortDirection, dataKey, headerExtras, columnStyles, headerBelowRow, toolbar, striped, fillViewport = false, viewportBottomOffset = 0, viewportMin = 120, viewportIncludeMarginTop = false, take, skip, paginationPlacement = 'bottom', totalItemsCount, onPageChange, loading, variant = 'primary', size = 'md', getRowSeverity, showFirstLast = false, allRowsSelected, onSelectAllRows, viewMode, emptyConfig, ...rest }) {
13
13
  const filteredColumns = useMemo(() => columns.filter(c => !c.hidden), [columns]);
14
14
  const handlePageChange = useCallback((e) => {
15
15
  onPageChange === null || onPageChange === void 0 ? void 0 : onPageChange(e);
@@ -31,7 +31,7 @@ export function Table({ data, columns, selectedRows, onRowSelect, selectionMode
31
31
  min: viewportMin,
32
32
  includeMarginTop: viewportIncludeMarginTop,
33
33
  });
34
- const tableEl = (_jsxs(_Fragment, { children: [_jsxs("table", { className: `${styles.table} ${styles[variant]} ${styles[size]}`, children: [_jsxs("thead", { children: [_jsxs("tr", { children: [selectedRows && onRowSelect && dataKey && (_jsx("th", { className: `${styles.fitContent} ${styles.th}`, children: selectionMode === 'multiple' ? (_jsx(Checkbox, { size: "sm", variant: "primary", checked: allRowsSelected, onChange: checked => onSelectAllRows === null || onSelectAllRows === void 0 ? void 0 : onSelectAllRows(checked) })) : null })), filteredColumns.map((column, index) => {
34
+ const tableEl = (_jsxs(_Fragment, { children: [toolbar ? _jsx("div", { style: { marginBottom: 12 }, children: toolbar }) : null, _jsxs("table", { ...rest, className: `${styles.table} ${styles[variant]} ${styles[size]} ${getRowSeverity ? styles.severityTable : ''}`, children: [_jsxs("thead", { children: [_jsxs("tr", { children: [selectedRows && onRowSelect && dataKey && (_jsx("th", { className: `${styles.fitContent} ${styles.th} $`, children: selectionMode === 'multiple' ? (_jsx(Checkbox, { size: "sm", variant: "primary", checked: allRowsSelected, onChange: checked => onSelectAllRows === null || onSelectAllRows === void 0 ? void 0 : onSelectAllRows(checked) })) : null })), filteredColumns.map((column, index) => {
35
35
  const isActiveSort = sortById === column.id;
36
36
  const ariaSort = column.sortable && isActiveSort
37
37
  ? sortDirection === 'asc'
@@ -73,12 +73,9 @@ export function Table({ data, columns, selectedRows, onRowSelect, selectionMode
73
73
  e.stopPropagation();
74
74
  const isSelected = selectedRows.has(rowId);
75
75
  if (selectionMode === 'single') {
76
- // In single mode, treat modifier-click as "select this row"
77
- // (toggle if already selected)
78
76
  onRowSelect(rowId, !isSelected);
79
77
  }
80
78
  else {
81
- // multiple mode: toggle selection
82
79
  onRowSelect(rowId, !isSelected);
83
80
  }
84
81
  return;
@@ -88,11 +85,11 @@ export function Table({ data, columns, selectedRows, onRowSelect, selectionMode
88
85
  ['--row-severity-color']: rowSeverity
89
86
  ? SeverityBgColor[rowSeverity]
90
87
  : undefined,
91
- }, className: `${onRowClick ? styles.clickableRow : ''} ${(selectedRows === null || selectedRows === void 0 ? void 0 : selectedRows.has(row[dataKey])) ? styles.selectedRow : ''} ${rowSeverity ? styles.severity : ''}`, children: [selectedRows && onRowSelect && dataKey && (_jsx("td", { className: "fitContent", onClick: e => e.stopPropagation(), children: _jsx(Checkbox, { variant: "primary", checked: selectedRows.has(row[dataKey]), size: "sm", onChange: () => onRowSelect === null || onRowSelect === void 0 ? void 0 : onRowSelect(row[dataKey], !selectedRows.has(row[dataKey])) }) })), filteredColumns.map(column => {
88
+ }, className: `${onRowClick ? styles.clickableRow : ''} ${(selectedRows === null || selectedRows === void 0 ? void 0 : selectedRows.has(row[dataKey])) ? styles.selectedRow : ''} ${rowSeverity ? styles.severity : ''}`, children: [selectedRows && onRowSelect && dataKey && (_jsx("td", { className: `fitContent ${styles.selectionCell}`, onClick: e => e.stopPropagation(), children: _jsx(Checkbox, { variant: "primary", checked: selectedRows.has(row[dataKey]), size: "sm", onChange: () => onRowSelect === null || onRowSelect === void 0 ? void 0 : onRowSelect(row[dataKey], !selectedRows.has(row[dataKey])) }) })), filteredColumns.map(column => {
92
89
  var _a, _b;
93
90
  return (_jsx("td", { style: getColStyle(column.id, column.align, column.verticalAlign, column.width), className: `${styles.tableCell} ${column.fitContent ? 'fitContent' : ''} ${column.allowWrap ||
94
91
  (selectedRows === null || selectedRows === void 0 ? void 0 : selectedRows.has(row[dataKey])) ||
95
- viewMode === 'comfortable'
92
+ viewMode === 'wrapped'
96
93
  ? styles.allowWrap
97
94
  : styles.nowrap} `, children: column.render
98
95
  ? column.render(row) || ((_a = column.emptyPlaceholder) !== null && _a !== void 0 ? _a : '')
@@ -108,7 +105,7 @@ export function Table({ data, columns, selectedRows, onRowSelect, selectionMode
108
105
  gap: '20px',
109
106
  flexFlow: paginationPlacement === 'top' ? 'column-reverse' : 'column',
110
107
  position: 'relative',
111
- }, children: [_jsx("div", { ref: scrollRef, style: viewportStyle, className: styles.tableScroll, children: tableEl }), onPageChange && (_jsx(Pagination, { itemsCount: totalItemsCount, take: take, skip: skip, onPageChange: handlePageChange, showFirstLast: showFirstLast }))] }));
108
+ }, children: [_jsx("div", { ref: scrollRef, style: viewportStyle, className: styles.tableScroll, children: tableEl }), onPageChange && data.length > 0 && (_jsx(Pagination, { itemsCount: totalItemsCount, take: take, skip: skip, onPageChange: handlePageChange, showFirstLast: showFirstLast }))] }));
112
109
  }
113
110
  return (_jsxs("div", { style: {
114
111
  display: 'flex',
@@ -116,5 +113,5 @@ export function Table({ data, columns, selectedRows, onRowSelect, selectionMode
116
113
  gap: '20px',
117
114
  flexFlow: paginationPlacement === 'top' ? 'column-reverse' : 'column',
118
115
  position: 'relative',
119
- }, children: [tableEl, onPageChange && (_jsx(Pagination, { itemsCount: totalItemsCount, take: take, skip: skip, onPageChange: handlePageChange }))] }));
116
+ }, children: [tableEl, onPageChange && data.length > 0 && (_jsx(Pagination, { itemsCount: totalItemsCount, take: take, skip: skip, onPageChange: handlePageChange }))] }));
120
117
  }
@@ -1,3 +1,7 @@
1
+ /* =========================
2
+ Base table
3
+ ========================= */
4
+
1
5
  .table {
2
6
  inline-size: 100%;
3
7
  max-inline-size: 100%;
@@ -9,68 +13,64 @@
9
13
  background: var(--color-bg-surface);
10
14
  }
11
15
 
12
- .table.sm tr td {
13
- padding-block: var(--spacing-xs);
14
- padding-inline: var(--spacing-md);
15
- font-size: var(--font-size-xs);
16
+ .tableScroll {
17
+ position: relative;
18
+ overflow: auto;
19
+ max-inline-size: 100%;
20
+ -webkit-overflow-scrolling: touch;
16
21
  }
17
22
 
18
- .table.sm th {
19
- padding-inline: var(--spacing-md);
20
- }
23
+ /* =========================
24
+ Header
25
+ ========================= */
21
26
 
22
27
  .table thead {
23
28
  position: sticky;
24
29
  inset-block-start: 0;
25
- background-color: var(--color-bg-surface);
26
30
  z-index: 10;
31
+ background-color: var(--color-bg-surface);
27
32
  }
28
33
 
29
34
  .table.primary thead {
30
35
  box-shadow: var(--shadow-md);
31
36
  }
32
37
 
33
- .clickableRow {
34
- cursor: pointer;
35
- }
38
+ .table .th {
39
+ position: relative;
36
40
 
37
- .allowWrap {
38
- word-break: break-all !important;
39
- overflow-wrap: anywhere !important;
40
- }
41
+ padding-block: var(--spacing-xs);
42
+ padding-inline: var(--spacing-md);
43
+ padding-right: var(--spacing-lg);
41
44
 
42
- .clickableRow:hover {
43
- background-color: var(--color-bg-contextual);
44
- }
45
+ text-align: left;
46
+ vertical-align: middle;
45
47
 
46
- .selectedRow {
47
- background-color: var(--color-bg-selected);
48
- }
49
- .selectedRow:hover {
50
- background-color: var(--color-bg-selected-hover);
51
- }
48
+ background: inherit;
52
49
 
53
- .nowrap {
50
+ /* Typography */
51
+ font-size: var(--font-size-xs);
52
+ font-weight: var(--font-weight-normal);
53
+ letter-spacing: var(--letter-spacing-wide);
54
+ text-transform: uppercase;
55
+
56
+ color: var(--color-fg-subtle);
57
+
58
+ /* Truncation */
54
59
  white-space: nowrap;
55
60
  overflow: hidden;
56
61
  text-overflow: ellipsis;
62
+
63
+ /* Width control */
64
+ min-width: 0;
65
+ max-width: var(--card-label-width);
57
66
  }
58
67
 
59
- .table .th {
60
- color: var(--color-fg-muted);
61
- font-size: var(--font-size-xs);
62
- font-weight: var(--font-weight-medium);
63
- position: relative;
64
- padding-block: var(--spacing-xs);
68
+ /* Small variant: header padding */
69
+ .table.sm .th {
65
70
  padding-inline: var(--spacing-md);
66
- text-align: start;
67
- background: inherit;
68
- white-space: nowrap;
69
- min-width: unset;
70
- max-width: unset;
71
- vertical-align: middle;
72
71
  }
73
72
 
73
+ /* Sortable header behavior */
74
74
  .th.sortable {
75
75
  cursor: pointer;
76
76
  user-select: none;
@@ -78,10 +78,12 @@
78
78
  background-color var(--transition-fast) var(--ease-standard),
79
79
  color var(--transition-fast) var(--ease-standard);
80
80
  }
81
+
81
82
  .th.sortable:hover,
82
83
  th.sortable:hover {
83
84
  background-color: var(--color-bg-contextual);
84
85
  }
86
+
85
87
  .th.sortable:focus-visible,
86
88
  th.sortable:focus-visible {
87
89
  outline: none;
@@ -95,6 +97,7 @@ th.sortable:focus-visible {
95
97
  .sortIndicator {
96
98
  display: flex;
97
99
  }
100
+
98
101
  .sortIndicator svg {
99
102
  inline-size: var(--icon-size-sm);
100
103
  block-size: var(--icon-size-sm);
@@ -103,6 +106,7 @@ th.sortable:focus-visible {
103
106
  .th > .thInner {
104
107
  display: inline-block;
105
108
  }
109
+
106
110
  .th > .thInner > span {
107
111
  display: inline-flex;
108
112
  align-items: center;
@@ -110,53 +114,120 @@ th.sortable:focus-visible {
110
114
  inline-size: 100%;
111
115
  }
112
116
 
117
+ /* =========================
118
+ Body + cells
119
+ ========================= */
120
+
113
121
  .tBody::before {
114
122
  content: '';
115
123
  height: var(--spacing-xs);
116
124
  display: block;
117
125
  }
118
126
 
119
- .tBody tr td,
120
- .tbody tr td {
127
+ .tBody tr td {
121
128
  position: relative;
122
129
  padding-block: var(--spacing-xs);
123
130
  padding-inline: var(--spacing-sm);
131
+
124
132
  border-block-end: var(--border-width-thin) solid var(--color-border-default);
133
+
125
134
  text-align: start;
126
135
  vertical-align: top;
136
+
127
137
  overflow-wrap: break-word;
128
138
  word-break: normal;
129
139
  }
130
140
 
131
- .tBody tr.severity {
132
- background-repeat: no-repeat;
133
- background-size: 100% 100%;
134
- background-position: left top;
135
- background-image: linear-gradient(to right, var(--row-severity-color) 0 3px, transparent 3px);
141
+ /* Small variant applies to all cells by default */
142
+ .table.sm .tBody tr td {
143
+ padding-block: var(--spacing-xxs);
144
+ padding-inline: var(--spacing-md);
145
+ font-size: var(--font-size-xs);
146
+ line-height: var(--line-height-normal);
136
147
  }
137
148
 
138
- .striped tr:nth-child(even):not(.selectedRow):not(:hover) {
139
- background-color: var(--color-bg-surface-subtle);
149
+ /* =========================
150
+ Selection column
151
+ ========================= */
152
+
153
+ /*
154
+ Default (no severity rails):
155
+ Remove ALL inline padding for selection/checkbox cells in both header + body.
156
+
157
+ We deliberately use padding-inline (shorthand) so it reliably beats other
158
+ padding-inline shorthands (like .table.sm .tBody tr td).
159
+ */
160
+
161
+ /* Body selection cells (covers td with .selectionCell even if markup differs) */
162
+ .tBody tr td.selectionCell,
163
+ .table .tBody tr td.selectionCell,
164
+ .table td.selectionCell {
165
+ padding-inline: var(--spacing-xxs);
140
166
  }
141
167
 
142
- .td--numeric {
143
- text-align: end;
168
+ /* Header selection cells:
169
+ - covers your .th class
170
+ - AND real <th> elements that might not have the .th class */
171
+ .table .th.selectionCell,
172
+ .table th.selectionCell,
173
+ th.selectionCell {
174
+ padding-inline: var(--spacing-xxs);
144
175
  }
145
- .td--muted {
146
- color: var(--color-fg-muted);
176
+
177
+ /* Override the .table.sm .tBody tr td padding-inline (must be as-specific or more) */
178
+ .table.sm .tBody tr td.selectionCell,
179
+ .table.table.sm .tBody tr td.selectionCell {
180
+ padding-inline: var(--spacing-xxs);
147
181
  }
148
182
 
149
- .td .error {
150
- color: var(--color-danger);
183
+ /* If severity rails are enabled, reserve a left gutter (still no right padding) */
184
+ .table.severityTable .tBody tr td.selectionCell,
185
+ .table.severityTable td.selectionCell,
186
+ .table.severityTable .th.selectionCell,
187
+ .table.severityTable th.selectionCell {
188
+ padding-inline: var(--spacing-xxs);
189
+ padding-inline-start: 14px;
190
+ }
191
+
192
+ /* Ensure severityTable also wins in sm */
193
+ .table.sm.severityTable .tBody tr td.selectionCell,
194
+ .table.table.sm.severityTable .tBody tr td.selectionCell,
195
+ .table.sm.severityTable .th.selectionCell,
196
+ .table.table.sm.severityTable .th.selectionCell,
197
+ .table.sm.severityTable th.selectionCell {
198
+ padding-inline: var(--spacing-xxs);
199
+ padding-inline-start: 14px;
200
+ }
201
+
202
+ /* =========================
203
+ Rows (interaction + states)
204
+ ========================= */
205
+
206
+ .clickableRow {
207
+ cursor: pointer;
208
+ }
209
+
210
+ .clickableRow:hover {
211
+ background-color: var(--color-bg-contextual);
212
+ }
213
+
214
+ .selectedRow {
215
+ background-color: var(--color-bg-selected);
151
216
  }
217
+
218
+ .selectedRow:hover {
219
+ background-color: var(--color-bg-selected-hover);
220
+ }
221
+
152
222
  .tr--hover:hover {
153
223
  background-color: var(--color-bg-contextual);
154
224
  }
155
225
 
156
- .table .tbody tr:focus-within {
157
- outline: none;
158
- box-shadow: none;
226
+ .striped tr:nth-child(even):not(.selectedRow):not(:hover) {
227
+ background-color: var(--color-bg-surface-subtle);
159
228
  }
229
+
230
+ /* Focus ring */
160
231
  .table .tbody tr:focus-within {
161
232
  outline: none;
162
233
  box-shadow:
@@ -166,9 +237,59 @@ th.sortable:focus-visible {
166
237
  inset 0 -2px 0 var(--color-brand);
167
238
  }
168
239
 
169
- .tableScroll {
240
+ /* =========================
241
+ Content utilities
242
+ ========================= */
243
+
244
+ .nowrap {
245
+ white-space: nowrap;
246
+ overflow: hidden;
247
+ text-overflow: ellipsis;
248
+ }
249
+
250
+ .allowWrap {
251
+ word-break: break-all !important;
252
+ overflow-wrap: anywhere !important;
253
+ }
254
+
255
+ .td--numeric {
256
+ text-align: end;
257
+ }
258
+
259
+ .td--muted {
260
+ color: var(--color-fg-muted);
261
+ }
262
+
263
+ .td .error {
264
+ color: var(--color-danger);
265
+ }
266
+
267
+ /* =========================
268
+ Severity rail
269
+ ========================= */
270
+
271
+ .tBody tr.severity td:first-child {
170
272
  position: relative;
171
- overflow: auto;
172
- max-inline-size: 100%;
173
- -webkit-overflow-scrolling: touch;
273
+ }
274
+
275
+ /* Only render the rail when the table actually uses severity rails */
276
+ .table.severityTable .tBody tr.severity td:first-child::before {
277
+ content: '';
278
+ position: absolute;
279
+
280
+ left: 4px;
281
+ top: 4px;
282
+ bottom: 4px;
283
+ width: 5px;
284
+
285
+ background-color: var(--row-severity-color);
286
+ border-radius: 3px;
287
+
288
+ z-index: 0;
289
+ }
290
+
291
+ /* keep checkbox/content above the rail */
292
+ .table.severityTable .tBody tr.severity td:first-child > * {
293
+ position: relative;
294
+ z-index: 1;
174
295
  }
@@ -8,7 +8,7 @@ export type TableEmptyConfig = {
8
8
  /**
9
9
  * Title + description text (defaults are your current Danish copy)
10
10
  */
11
- title?: ReactNode;
11
+ title?: string;
12
12
  description?: ReactNode;
13
13
  /**
14
14
  * Optional custom actions area. If provided, it will be rendered first.
@@ -1,16 +1,15 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ArrowLeft, RefreshCw, SearchX } from 'lucide-react';
3
- import styles from './EmptyState.module.css';
4
- import { Button } from '../../../button/Button';
5
- import { Headline } from '../../../headline/Headline';
2
+ import { ArrowLeft } from 'lucide-react';
3
+ import { Button } from '../../../../components/button/Button';
4
+ import { StatePage } from '../../../../components/state-page/StatePage';
6
5
  const defaultEmptyConfig = {
7
6
  enabled: true,
8
- title: (_jsx(Headline, { disableMargin: true, size: 3, children: "Hov, der er ingen data" })),
7
+ title: 'Ingen resultater',
9
8
  description: _jsx("span", { children: "Pr\u00F8v at \u00E6ndre dine filtre eller tilf\u00F8je data." }),
10
9
  showBack: true,
11
10
  showRefresh: true,
12
11
  backLabel: (_jsxs(_Fragment, { children: [_jsx(ArrowLeft, {}), "Tilbage"] })),
13
- refreshLabel: (_jsxs(_Fragment, { children: [_jsx(RefreshCw, {}), "Indl\u00E6s igen"] })),
12
+ refreshLabel: _jsx(_Fragment, { children: "Indl\u00E6s igen" }),
14
13
  className: 'dbc-flex dbc-flex-column dbc-justify-center dbc-items-center dbc-flex-grow ',
15
14
  };
16
15
  export function TableEmptyState({ config }) {
@@ -23,5 +22,5 @@ export function TableEmptyState({ config }) {
23
22
  const showBack = merged.showBack && typeof merged.onBack === 'function';
24
23
  const showRefresh = merged.showRefresh && typeof merged.onRefresh === 'function';
25
24
  const hasAnyActions = Boolean(merged.actions) || showBack || showRefresh;
26
- return (_jsxs("div", { className: `${merged.className} ${styles.container}`, children: [_jsxs("span", { className: "dbc-flex dbc-flex-column dbc-gap-sm dbc-items-center", children: [_jsx(SearchX, { className: styles.icon }), merged.title, merged.description] }), hasAnyActions && (_jsxs("div", { className: "dbc-flex dbc-gap-sm", children: [merged.actions, showBack && (_jsx(Button, { onClick: merged.onBack, className: "dbc-mr-xxs", children: merged.backLabel })), showRefresh && _jsx(Button, { onClick: merged.onRefresh, children: merged.refreshLabel })] }))] }));
25
+ return (_jsx(StatePage, { type: "empty", header: merged.title, actions: _jsxs("div", { children: [hasAnyActions && (_jsxs("div", { className: "dbc-flex dbc-gap-sm", children: [showBack && (_jsx(Button, { type: "button", onClick: merged.onBack, children: merged.backLabel })), showRefresh && (_jsx(Button, { type: "button", onClick: merged.onRefresh, children: merged.refreshLabel }))] })), merged.actions] }), children: merged.description }));
27
26
  }
@@ -1,8 +1,18 @@
1
- import type { JSX } from 'react';
1
+ import { ColumnDef } from '@tanstack/react-table';
2
+ import type { JSX, ReactNode } from 'react';
2
3
  import { ViewMode } from '../../../../hooks/useTableSettings';
3
- interface TableSettingsProps {
4
+ import { ButtonSize } from '../../../button/Button';
5
+ interface TableSettingsProps<T extends Record<string, any>> {
4
6
  handleChangeViewMode: (mode: ViewMode) => void;
5
7
  viewMode: ViewMode;
8
+ columns?: ColumnDef<T>[];
9
+ visibleColumnIds?: string[];
10
+ onVisibleColumnIdsChange?: (nextVisibleIds: string[]) => void;
11
+ columnsLabel?: string;
12
+ allPresetLabel?: string;
13
+ standardPresetLabel?: string;
14
+ buttonSize?: ButtonSize;
15
+ additionalSettings?: (close?: () => void) => ReactNode;
6
16
  }
7
- export declare function TableSettings({ viewMode, handleChangeViewMode }: TableSettingsProps): JSX.Element;
17
+ export declare function TableSettings<T extends Record<string, any>>({ viewMode, handleChangeViewMode, columns, visibleColumnIds, onVisibleColumnIdsChange, columnsLabel, allPresetLabel, standardPresetLabel, buttonSize, additionalSettings, }: TableSettingsProps<T>): JSX.Element;
8
18
  export {};
@@ -1,12 +1,63 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
3
  import { ListChevronsDownUp, Settings } from 'lucide-react';
4
+ import { useMemo } from 'react';
3
5
  import { Button } from '../../../button/Button';
4
6
  import { Menu } from '../../../menu/Menu';
5
7
  import { Popover } from '../../../popover/Popover';
6
- export function TableSettings({ viewMode, handleChangeViewMode }) {
8
+ export function TableSettings({ viewMode, handleChangeViewMode, columns = [], visibleColumnIds = [], onVisibleColumnIdsChange, columnsLabel = 'Kolonner', allPresetLabel = 'Alle', standardPresetLabel = 'Standard', buttonSize = 'sm', additionalSettings, }) {
7
9
  const handleViewModeChange = (mode, close) => {
8
- handleChangeViewMode(mode === 'compact' ? 'compact' : 'comfortable');
10
+ handleChangeViewMode(mode === 'wrapped' ? 'compact' : 'wrapped');
9
11
  close === null || close === void 0 ? void 0 : close();
10
12
  };
11
- return (_jsx(Popover, { trigger: (onClick, icon) => (_jsxs(Button, { onClick: onClick, children: [_jsx(Settings, {}), icon] })), children: close => (_jsx(Menu, { children: _jsx(Menu.Item, { active: viewMode === 'compact', children: _jsxs("button", { type: "button", onClick: () => handleViewModeChange('compact', close), children: [_jsx(ListChevronsDownUp, {}), "Kompakt"] }) }) })) }));
13
+ const hideableColumns = useMemo(() => columns.filter(c => c.enableHiding !== false), [columns]);
14
+ const allPresetIds = useMemo(() => hideableColumns.map(c => c.id), [hideableColumns]);
15
+ const standardPresetIds = useMemo(() => hideableColumns.filter(c => { var _a; return ((_a = c.meta) === null || _a === void 0 ? void 0 : _a.hidden) !== true; }).map(c => c.id), [hideableColumns]);
16
+ const visibleSet = useMemo(() => new Set(visibleColumnIds), [visibleColumnIds]);
17
+ const visibleCount = useMemo(() => {
18
+ return hideableColumns.reduce((acc, c) => { var _a; return acc + (visibleSet.has((_a = c.id) !== null && _a !== void 0 ? _a : '') ? 1 : 0); }, 0);
19
+ }, [hideableColumns, visibleSet]);
20
+ const setVisibleIds = (nextIds) => {
21
+ if (!onVisibleColumnIdsChange)
22
+ return;
23
+ const safe = nextIds.length > 0 ? nextIds : standardPresetIds.length > 0 ? standardPresetIds : allPresetIds;
24
+ onVisibleColumnIdsChange(safe.filter((id) => typeof id === 'string' && Boolean(id)));
25
+ };
26
+ const toggleColumn = (id, nextVisible) => {
27
+ const next = new Set(visibleColumnIds);
28
+ if (nextVisible)
29
+ next.add(id);
30
+ else
31
+ next.delete(id);
32
+ setVisibleIds(Array.from(next));
33
+ };
34
+ const isAllActive = useMemo(() => {
35
+ if (!hideableColumns.length)
36
+ return false;
37
+ return hideableColumns.every(c => c.id && visibleSet.has(c.id));
38
+ }, [hideableColumns, visibleSet]);
39
+ const isStandardActive = useMemo(() => {
40
+ if (!hideableColumns.length)
41
+ return false;
42
+ const std = new Set(standardPresetIds);
43
+ return hideableColumns.every(c => c.id && visibleSet.has(c.id) === std.has(c.id));
44
+ }, [hideableColumns, visibleSet, standardPresetIds]);
45
+ // Required by your RadioButton component
46
+ const presetRadioName = 'table-columns-preset';
47
+ return (_jsx(Popover, { trigger: (onClick, icon) => (_jsxs(Button, { size: buttonSize, onClick: onClick, type: "button", children: [_jsx(Settings, {}), icon] })), children: close => (_jsxs(Menu, { children: [additionalSettings === null || additionalSettings === void 0 ? void 0 : additionalSettings(close), _jsx(Menu.Item, { active: viewMode === 'wrapped', children: _jsxs("button", { type: "button", onClick: () => handleViewModeChange(viewMode, close), children: [_jsx(ListChevronsDownUp, {}), "Ombryd tekst"] }) }), hideableColumns.length > 0 && onVisibleColumnIdsChange ? (_jsxs(_Fragment, { children: [_jsx(Menu.Separator, {}), _jsx("div", { style: { padding: '6px 10px', fontSize: 12, opacity: 0.7 }, children: columnsLabel }), _jsx(Menu.RadioItem, { name: presetRadioName, value: "all", checked: isAllActive, label: allPresetLabel, onValueChange: () => {
48
+ setVisibleIds(allPresetIds.filter((id) => typeof id === 'string' && Boolean(id)));
49
+ close === null || close === void 0 ? void 0 : close();
50
+ } }), _jsx(Menu.RadioItem, { name: presetRadioName, value: "standard", checked: isStandardActive, label: standardPresetLabel, onValueChange: () => {
51
+ setVisibleIds(standardPresetIds.filter((id) => typeof id === 'string' && Boolean(id)));
52
+ close === null || close === void 0 ? void 0 : close();
53
+ } }), _jsx(Menu.Separator, {}), hideableColumns.map(col => {
54
+ const isVisible = col.id ? visibleSet.has(col.id) : false;
55
+ const disableUncheckingLast = isVisible && visibleCount <= 1;
56
+ const label = col.header;
57
+ return (_jsx(Menu.CheckItem, { checked: isVisible, disabled: disableUncheckingLast, label: label, onCheckedChange: nextChecked => {
58
+ if (disableUncheckingLast)
59
+ return;
60
+ toggleColumn(col.id, nextChecked);
61
+ } }, col.id));
62
+ })] })) : null] })) }));
12
63
  }
@@ -3,12 +3,23 @@ import * as React from 'react';
3
3
  import { type TableProps, type TableVariant } from './Table';
4
4
  import { ViewMode } from '../../hooks/useTableSettings';
5
5
  type Filterable<T> = Array<keyof T>;
6
- export type TanstackTableProps<T extends Record<string, any>> = Omit<TableProps<T>, 'columns' | 'onSortChange' | 'sortById' | 'sortDirection' | 'headerBelowRow' | 'headerExtras' | 'columnStyles'> & {
6
+ export type TanstackTableProps<T extends Record<string, any>> = Omit<TableProps<T>, 'columns' | 'onSortChange' | 'sortById' | 'sortDirection' | 'headerBelowRow' | 'headerExtras' | 'columnStyles' | 'toolbar'> & {
7
7
  columns: ReadonlyArray<ColumnDef<T, any>>;
8
8
  filterable?: Filterable<T>;
9
9
  onSortingChange?: (sortBy: string | number | symbol | null, direction: 'asc' | 'desc' | null) => void;
10
+ initialSortBy?: string;
11
+ initialSortDirection?: 'asc' | 'desc';
10
12
  variant?: TableVariant;
11
13
  viewMode?: ViewMode;
14
+ /**
15
+ * TanStack-agnostic column visibility input.
16
+ *
17
+ * If provided, this list is the single source of truth for which columns are visible.
18
+ * If not provided (or empty), defaults are derived from ColumnDef meta.hidden.
19
+ *
20
+ * NOTE: Passing [] is treated as "unset" and will fall back to defaults.
21
+ */
22
+ visibleColumnIds?: string[];
12
23
  };
13
24
  export declare function TanstackTable<T extends Record<string, any>>(props: TanstackTableProps<T>): React.ReactNode;
14
25
  export {};