@dbcdk/react-components 0.0.25 → 0.0.27

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 (50) hide show
  1. package/dist/components/__stories__/_data/table.d.ts +1 -1
  2. package/dist/components/__stories__/_data/table.js +11 -5
  3. package/dist/components/accordion/Accordion.module.css +3 -3
  4. package/dist/components/accordion/components/AccordionRow.module.css +5 -1
  5. package/dist/components/forms/checkbox/Checkbox.js +1 -1
  6. package/dist/components/forms/checkbox/Checkbox.module.css +16 -3
  7. package/dist/components/forms/select/Select.js +1 -1
  8. package/dist/components/headline/Headline.module.css +15 -44
  9. package/dist/components/menu/Menu.js +1 -1
  10. package/dist/components/nav-bar/NavBar.module.css +1 -1
  11. package/dist/components/panel/Panel.module.css +2 -2
  12. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +0 -1
  13. package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.module.css +1 -0
  14. package/dist/components/table/Table.d.ts +3 -65
  15. package/dist/components/table/Table.js +20 -159
  16. package/dist/components/table/Table.module.css +39 -6
  17. package/dist/components/table/Table.types.d.ts +58 -0
  18. package/dist/components/table/Table.types.js +1 -0
  19. package/dist/components/table/TanstackTable.d.ts +1 -1
  20. package/dist/components/table/TanstackTable.js +0 -1
  21. package/dist/components/table/components/TableBody.d.ts +19 -0
  22. package/dist/components/table/components/TableBody.js +10 -0
  23. package/dist/components/table/components/TableCell.d.ts +9 -0
  24. package/dist/components/table/components/TableCell.js +7 -0
  25. package/dist/components/table/components/TableHeader.d.ts +16 -0
  26. package/dist/components/table/components/TableHeader.js +7 -0
  27. package/dist/components/table/components/TableHeaderCell.d.ts +12 -0
  28. package/dist/components/table/components/TableHeaderCell.js +24 -0
  29. package/dist/components/table/components/TableLoadingBody.d.ts +10 -0
  30. package/dist/components/table/components/TableLoadingBody.js +10 -0
  31. package/dist/components/table/components/TablePagination.d.ts +0 -0
  32. package/dist/components/table/components/TablePagination.js +1 -0
  33. package/dist/components/table/components/TableRow.d.ts +19 -0
  34. package/dist/components/table/components/TableRow.js +38 -0
  35. package/dist/components/table/components/TableSelectionCell.d.ts +9 -0
  36. package/dist/components/table/components/TableSelectionCell.js +15 -0
  37. package/dist/components/table/hooks/useTableRowInteractions.d.ts +15 -0
  38. package/dist/components/table/hooks/useTableRowInteractions.js +25 -0
  39. package/dist/components/table/table.classes.d.ts +10 -0
  40. package/dist/components/table/table.classes.js +23 -0
  41. package/dist/components/table/table.utils.d.ts +7 -4
  42. package/dist/components/table/table.utils.js +14 -20
  43. package/dist/components/table/tanstackTable.utils.d.ts +1 -2
  44. package/dist/components/table/tanstackTable.utils.js +3 -2
  45. package/dist/components/tabs/Tabs.js +1 -1
  46. package/dist/utils/date/formatDate.d.ts +11 -3
  47. package/dist/utils/date/formatDate.js +35 -10
  48. package/package.json +1 -1
  49. package/dist/components/table/hooks/useAnimatedRowIds.d.ts +0 -9
  50. package/dist/components/table/hooks/useAnimatedRowIds.js +0 -76
@@ -1,5 +1,5 @@
1
1
  import type { JSX } from 'react';
2
- import { ColumnItem } from '../../../components/table/Table';
2
+ import { ColumnItem } from '../../../components/table/Table.types';
3
3
  type HarvestRunRow = {
4
4
  runId: string;
5
5
  source: string;
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Chip } from '../../../components/chip/Chip';
2
+ import { Circle } from '../../../components/circle/Circle';
3
3
  import { Table } from '../../../components/table/Table';
4
4
  const sources = ['onix.vendor-x', 'onix.vendor-y', 'cover.provider-z'];
5
5
  const statuses = ['Completed', 'Running', 'Failed'];
@@ -40,10 +40,16 @@ export const STORY_TABLE_COLUMNS = [
40
40
  header: 'Status',
41
41
  accessor: 'status',
42
42
  sortable: true,
43
- render: (row) => (_jsxs("span", { className: "dbc-flex dbc-flex-row dbc-flex-wrap dbc-gap-xs", children: [row.status === 'Completed' && (_jsx(Chip, { size: "sm", severity: "success", children: "Fuldf\u00F8rt" })), row.status === 'Running' && (_jsx(Chip, { size: "sm", severity: "info", children: "K\u00F8rer" })), row.status === 'Failed' && (_jsx(Chip, { size: "sm", severity: "error", children: "Fejlet" }))] })),
43
+ render: (row) => (_jsxs("span", { className: "dbc-flex dbc-flex-row dbc-flex-wrap dbc-gap-xs", children: [row.status === 'Completed' && (_jsx(Circle, { size: "xs", severity: "success", children: "Fuldf\u00F8rt" })), row.status === 'Running' && (_jsx(Circle, { pulse: true, size: "xs", severity: "info", children: "K\u00F8rer" })), row.status === 'Failed' && (_jsx(Circle, { size: "xs", severity: "error", children: "Fejlet" }))] })),
44
44
  },
45
- { id: 'updated', header: 'Opdateret', accessor: 'updated', sortable: true },
46
- { id: 'warnings', header: 'Advarsler', accessor: 'warnings', sortable: true },
47
- { id: 'failed', header: 'Fejlet', accessor: 'failed', sortable: true },
45
+ {
46
+ id: 'updated',
47
+ header: 'Opdateret',
48
+ accessor: 'updated',
49
+ sortable: true,
50
+ align: 'right',
51
+ },
52
+ { id: 'warnings', header: 'Advarsler', accessor: 'warnings', sortable: true, align: 'right' },
53
+ { id: 'failed', header: 'Fejlet', accessor: 'failed', sortable: true, align: 'right' },
48
54
  ];
49
55
  export const SampleTable = () => (_jsx(Table, { fillViewport: true, data: STORY_TABLE_DATA(), columns: STORY_TABLE_COLUMNS, dataKey: "runId", variant: "embedded" }));
@@ -10,9 +10,9 @@
10
10
 
11
11
  /* Size variables (consumed by AccordionRow) */
12
12
  .sm {
13
- --acc-trigger-py: var(--spacing-sm);
14
- --acc-trigger-px: var(--spacing-md);
15
- --acc-content-py: var(--spacing-sm);
13
+ --acc-trigger-py: var(--spacing-xs);
14
+ --acc-trigger-px: var(--spacing-sm);
15
+ --acc-content-py: var(--spacing-xs);
16
16
  }
17
17
 
18
18
  .md {
@@ -34,7 +34,6 @@
34
34
  min-width: 0;
35
35
  flex: 1 1 auto;
36
36
  overflow: hidden;
37
- justify-content: space-between;
38
37
  }
39
38
 
40
39
  .icon {
@@ -43,6 +42,11 @@
43
42
  align-items: center;
44
43
  }
45
44
 
45
+ .icon svg {
46
+ height: var(--icon-size-sm);
47
+ width: var(--icon-size-sm);
48
+ }
49
+
46
50
  .chevron {
47
51
  width: var(--icon-size-md);
48
52
  height: var(--icon-size-md);
@@ -4,7 +4,7 @@ import { Check } from 'lucide-react';
4
4
  import { useId, useState } from 'react';
5
5
  import styles from './Checkbox.module.css';
6
6
  import { InputContainer } from '../input-container/InputContainer';
7
- export function Checkbox({ checked: controlled, onChange, variant = 'outlined', disabled, label, size = 'md', modified, containerLabel, error, helpText, orientation = 'horizontal', labelWidth = '160px', fullWidth = false, required = false, noContainer = false, id, 'data-cy': dataCy, }) {
7
+ export function Checkbox({ checked: controlled, onChange, variant = 'default', disabled, label, size = 'md', modified, containerLabel, error, helpText, orientation = 'horizontal', labelWidth = '160px', fullWidth = false, required = false, noContainer = false, id, 'data-cy': dataCy, }) {
8
8
  const [internal, setInternal] = useState(false);
9
9
  const isChecked = controlled !== null && controlled !== void 0 ? controlled : internal;
10
10
  const generatedId = useId();
@@ -50,10 +50,11 @@
50
50
  pointer-events: none;
51
51
  }
52
52
 
53
- .outlined.checked {
54
- background-color: var(--opac-bg-default);
53
+ .default {
54
+ background-color: var(--color-bg-toolbar);
55
+ border-color: var(--color-bg-toolbar);
55
56
  &:not(:hover) {
56
- border-color: transparent;
57
+ border-color: var(--color-bg-toolbar);
57
58
  }
58
59
 
59
60
  .icon {
@@ -61,6 +62,18 @@
61
62
  }
62
63
  }
63
64
 
65
+ .outlined.checked {
66
+ background-color: transparent;
67
+ border-color: var(--color-border-default);
68
+ &:not(:hover) {
69
+ border-color: var(--color-brand);
70
+ }
71
+
72
+ .icon {
73
+ color: var(--color-brand);
74
+ }
75
+ }
76
+
64
77
  .success.checked {
65
78
  background-color: var(--color-status-success);
66
79
  &:not(:hover) {
@@ -112,7 +112,7 @@ export function Select({ label, error, helpText, orientation = 'vertical', label
112
112
  returnFocus: true, trigger: (toggle, icon, isOpen) => (_jsx(Button, { disabled: disabled, ...(tooltipEnabled ? triggerProps : {}), id: controlId, "data-cy": dataCy !== null && dataCy !== void 0 ? dataCy : 'select-button', onKeyDown: handleKeyDown, fullWidth: fullWidth, variant: variant, onClick: e => {
113
113
  resetActiveToSelected();
114
114
  toggle(e);
115
- }, size: size, type: "button", "data-forminput": true, "aria-haspopup": "listbox", "aria-expanded": !!isOpen, "aria-controls": listboxId, "aria-invalid": Boolean(error) || undefined, "aria-describedby": describedBy, children: _jsxs("span", { className: "dbc-flex dbc-justify-between dbc-items-center dbc-gap-xxs", style: { width: '100%' }, children: [_jsx("span", { children: selected ? selected.label : placeholder }), onClear && selected && _jsx(ClearButton, { onClick: onClear }), icon] }) })), children: _jsx(Menu, { onKeyDown: handleKeyDown, role: "listbox", children: options.map((opt, index) => {
115
+ }, size: size, type: "button", "data-forminput": true, "aria-haspopup": "listbox", "aria-expanded": !!isOpen, "aria-controls": listboxId, "aria-invalid": Boolean(error) || undefined, "aria-describedby": describedBy, children: _jsxs("span", { className: "dbc-flex dbc-justify-between dbc-items-center dbc-gap-xxs", style: { width: '100%' }, children: [_jsx("span", { children: selected ? selected.label : _jsx("span", { className: "dbc-muted-text", children: placeholder }) }), onClear && selected && _jsx(ClearButton, { onClick: onClear }), icon] }) })), children: _jsx(Menu, { onKeyDown: handleKeyDown, role: "listbox", children: options.map((opt, index) => {
116
116
  const isSelected = typeof opt.value === 'object' && typeof selectedValue === 'object' && datakey
117
117
  ? (selectedValue === null || selectedValue === void 0 ? void 0 : selectedValue[datakey]) === opt.value[datakey]
118
118
  : opt.value === selectedValue;
@@ -6,19 +6,25 @@
6
6
  max-width: 100%;
7
7
  }
8
8
 
9
- /* Base headline: inherit colour from parent surface */
9
+ /* Base headline */
10
10
  .headline {
11
11
  position: relative;
12
12
  display: inline-flex;
13
13
  align-items: center;
14
14
  gap: var(--spacing-xs);
15
- font-weight: var(--font-weight, var(--font-weight-bold));
15
+
16
+ /* Typography */
17
+ font-weight: var(--font-weight-medium, 500);
16
18
  letter-spacing: var(--letter-spacing-tight);
17
- color: inherit;
18
19
  line-height: var(--line-height-tight);
20
+ color: inherit;
21
+
22
+ /* Behaviour */
19
23
  transition: color var(--transition-fast) var(--ease-standard);
24
+ min-width: 0; /* required for truncation inside flex */
20
25
  }
21
26
 
27
+ /* Tone overrides */
22
28
  .tone-dark .headline {
23
29
  color: var(--color-fg-default);
24
30
  }
@@ -31,6 +37,7 @@
31
37
  margin: 0;
32
38
  }
33
39
 
40
+ /* Marker variant */
34
41
  .headline.marker {
35
42
  padding-inline-start: calc(var(--border-width-thick) + var(--spacing-sm));
36
43
  }
@@ -48,8 +55,7 @@
48
55
  pointer-events: none;
49
56
  }
50
57
 
51
- /* Subheadline inherits colour but is slightly softened,
52
- so it works on both light and dark text surfaces. */
58
+ /* Subheadline */
53
59
  .subHeadline {
54
60
  color: inherit;
55
61
  opacity: 0.85;
@@ -58,55 +64,20 @@
58
64
  line-height: var(--line-height-normal);
59
65
  }
60
66
 
61
- .headline {
62
- position: relative;
63
- display: inline-flex;
64
- align-items: center;
65
- gap: var(--spacing-xs);
66
- font-weight: var(--font-weight, var(--font-weight-bold));
67
- letter-spacing: var(--letter-spacing-tight);
68
- color: inherit;
69
- line-height: var(--line-height-tight);
70
- transition: color var(--transition-fast) var(--ease-standard);
71
-
72
- /* Needed so truncation works inside flex parents */
73
- min-width: 0;
74
- }
75
-
76
- /* Truncated variant (single line with ellipsis) */
77
- .truncate {
78
- white-space: nowrap;
79
- overflow: hidden;
80
- text-overflow: ellipsis;
81
- }
82
-
83
- .headline {
84
- position: relative;
85
- display: inline-flex;
86
- align-items: center;
87
- gap: var(--spacing-xs);
88
- font-weight: var(--font-weight, var(--font-weight-bold));
89
- letter-spacing: var(--letter-spacing-tight);
90
- color: inherit;
91
- line-height: var(--line-height-tight);
92
- transition: color var(--transition-fast) var(--ease-standard);
93
-
94
- /* helps inside flex parents */
95
- min-width: 0;
96
- }
97
-
67
+ /* Icon */
98
68
  .icon {
99
69
  flex: 0 0 auto;
100
70
  display: inline-flex;
101
71
  align-items: center;
102
72
  }
103
73
 
104
- /* text wrapper must be the shrinkable element */
74
+ /* Text wrapper (allows shrinking inside flex layouts) */
105
75
  .text {
106
- min-width: 0;
107
76
  flex: 1 1 auto;
77
+ min-width: 0;
108
78
  }
109
79
 
80
+ /* Text behaviour */
110
81
  .truncate {
111
82
  overflow: hidden;
112
83
  white-space: nowrap;
@@ -57,7 +57,7 @@ const MenuItem = React.forwardRef(({ children, active, disabled, className, item
57
57
  });
58
58
  MenuItem.displayName = 'Menu.Item';
59
59
  const MenuCheckItem = React.forwardRef(({ label, checked, disabled, onCheckedChange, className, ...liProps }, ref) => {
60
- return (_jsx("li", { ref: ref, role: "none", className: [styles.row, className].filter(Boolean).join(' '), ...liProps, children: _jsx("div", { className: styles.interactiveChild, children: _jsx(Checkbox, { noContainer: true, checked: checked, disabled: disabled, label: label, onChange: (next, _e) => onCheckedChange === null || onCheckedChange === void 0 ? void 0 : onCheckedChange(next) }) }) }));
60
+ return (_jsx("li", { ref: ref, role: "none", className: [styles.row, className].filter(Boolean).join(' '), ...liProps, children: _jsx("div", { className: styles.interactiveChild, children: _jsx(Checkbox, { variant: "default", size: "md", noContainer: true, checked: checked, disabled: disabled, label: label, onChange: (next, _e) => onCheckedChange === null || onCheckedChange === void 0 ? void 0 : onCheckedChange(next) }) }) }));
61
61
  });
62
62
  MenuCheckItem.displayName = 'Menu.CheckItem';
63
63
  const MenuRadioItem = React.forwardRef(({ name, value, checked, disabled, label, onValueChange, className, ...liProps }, ref) => {
@@ -46,7 +46,7 @@
46
46
  font-size: var(--font-size-sm);
47
47
  text-decoration: none;
48
48
  border-radius: var(--border-radius-default);
49
- padding-block: calc(var(--spacing-xs) + var(--density));
49
+ padding-block: var(--spacing-xs);
50
50
  padding-inline: var(--spacing-sm);
51
51
  transition:
52
52
  background-color var(--transition-fast) var(--ease-standard),
@@ -1,5 +1,5 @@
1
1
  .container {
2
- border: 1px solid var(--color-border-strong);
2
+ border: var(--border-width-thin) solid var(--color-border-default);
3
3
  border-radius: var(--border-radius-default);
4
4
  background-color: var(--color-bg-surface);
5
5
  box-sizing: border-box;
@@ -8,7 +8,7 @@
8
8
  }
9
9
 
10
10
  .header {
11
- border-bottom: 1px solid var(--color-border-strong);
11
+ border-bottom: var(--border-width-thin) solid var(--color-border-default);
12
12
  }
13
13
 
14
14
  .content {
@@ -9,7 +9,6 @@
9
9
  inline-size: var(--sidebar-width);
10
10
  box-sizing: border-box;
11
11
  border-inline-end: var(--border-width-thin) solid var(--color-border-default);
12
-
13
12
  transition:
14
13
  width var(--transition-fast) var(--ease-standard),
15
14
  inline-size var(--transition-fast) var(--ease-standard);
@@ -11,6 +11,7 @@
11
11
  cursor: pointer;
12
12
  position: relative;
13
13
  border-radius: var(--border-radius-default);
14
+ user-select: none;
14
15
  transition:
15
16
  background-color var(--transition-fast) var(--ease-standard),
16
17
  color var(--transition-fast) var(--ease-standard);
@@ -1,65 +1,3 @@
1
- import type { HTMLAttributes, JSX, ReactNode } from 'react';
2
- import { PageChangeEvent } from '../../components/pagination/Pagination';
3
- import { Severity } from '../../constants/severity.types';
4
- import { ViewMode } from '../../hooks/useTableSettings';
5
- import { TableEmptyConfig } from './components/empty-state/EmptyState';
6
- import { SortDirection } from './table.utils';
7
- export interface ColumnItem<T> {
8
- id: string;
9
- header: string | (() => ReactNode);
10
- accessor?: keyof T;
11
- sortable?: boolean;
12
- sortFunction?: (a: T, b: T) => -1 | 0 | 1;
13
- render?: (item: T) => ReactNode;
14
- hidden?: boolean;
15
- align?: 'left' | 'right' | 'center';
16
- verticalAlign?: 'top' | 'middle' | 'bottom';
17
- divider?: 'right' | 'left';
18
- allowWrap?: boolean;
19
- emptyPlaceholder?: ReactNode;
20
- canHide?: boolean;
21
- severity?: any;
22
- }
23
- type HeaderExtrasArgs<T> = {
24
- column: ColumnItem<T>;
25
- index: number;
26
- };
27
- export type TableVariant = 'primary' | 'embedded';
28
- export type TableProps<T extends Record<string, any>> = Omit<HTMLAttributes<HTMLDivElement>, 'onClick' | 'onMouseEnter'> & {
29
- data: T[];
30
- dataKey: keyof T;
31
- columns: ColumnItem<T>[];
32
- selectedRows?: Set<number | string>;
33
- selectionMode?: 'single' | 'multiple';
34
- allRowsSelected?: boolean;
35
- onRowClick?: (row: T) => void;
36
- onRowMouseEnter?: (row: T) => void;
37
- onRowSelect?: (rowId: number | string, isSelected: boolean) => void;
38
- onSelectAllRows?: (isSelected: boolean) => void;
39
- onSortChange?: (column: ColumnItem<T>, direction: SortDirection) => void;
40
- sortById?: string;
41
- sortDirection?: SortDirection;
42
- loading?: boolean;
43
- headerExtras?: (args: HeaderExtrasArgs<T>) => ReactNode;
44
- gridTemplateColumns?: string;
45
- toolbar?: ReactNode;
46
- striped?: boolean;
47
- fillViewport?: boolean;
48
- viewportBottomOffset?: number;
49
- viewportMin?: number;
50
- viewportIncludeMarginTop?: boolean;
51
- take?: number;
52
- skip?: number;
53
- paginationPlacement?: 'top' | 'bottom';
54
- totalItemsCount?: number;
55
- onPageChange?: (e: PageChangeEvent) => void;
56
- variant?: TableVariant;
57
- size?: 'sm' | 'md';
58
- getRowSeverity?: (row: T) => Severity | undefined;
59
- showFirstLast?: boolean;
60
- viewMode?: ViewMode;
61
- emptyConfig?: TableEmptyConfig;
62
- animateNewRows?: boolean;
63
- };
64
- export declare function Table<T extends Record<string, any>>({ data, columns, selectedRows, onRowSelect, selectionMode, onSortChange, onRowClick, onRowMouseEnter, sortById, sortDirection, dataKey, headerExtras, gridTemplateColumns, toolbar, striped, fillViewport, viewportBottomOffset, viewportMin, viewportIncludeMarginTop, take, skip, paginationPlacement, totalItemsCount, onPageChange, loading, variant, size, getRowSeverity, showFirstLast, allRowsSelected, onSelectAllRows, viewMode, emptyConfig, animateNewRows, ...rest }: TableProps<T>): JSX.Element;
65
- export {};
1
+ import type { JSX } from 'react';
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, striped, fillViewport, gridTemplateColumns, toolbar, headerExtras, take, skip, totalItemsCount, paginationPlacement, showFirstLast, getRowSeverity, onRowClick, onRowMouseEnter, onRowSelect, onSelectAllRows, onSortChange, onPageChange, ...rest }: TableProps<T>): JSX.Element;
@@ -1,170 +1,31 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { ArrowDown, ArrowUp } from 'lucide-react';
4
3
  import { useCallback, useMemo } from 'react';
5
- import { Checkbox } from '../../components/forms/checkbox/Checkbox';
6
4
  import { Pagination } from '../../components/pagination/Pagination';
7
- import { SkeletonLoaderItem } from '../../components/skeleton-loader/skeleton-loader-item/SkeletonLoaderItem';
8
- import { SeverityBgColor } from '../../constants/severity';
9
5
  import { TableEmptyState } from './components/empty-state/EmptyState';
10
- import { useAnimatedNewRowIds } from './hooks/useAnimatedRowIds';
6
+ import { TableBody } from './components/TableBody';
7
+ import { TableHeader } from './components/TableHeader';
8
+ import { TableLoadingBody } from './components/TableLoadingBody';
9
+ import { cx } from './table.classes';
11
10
  import styles from './Table.module.css';
12
- import { getAriaSort, getCellDisplayValue, getHeaderLabel, getNextSortDirection, getVisibleColumns, isModifierClick, shouldAllowWrap, shouldToggleOnKey, isActiveSort, } from './table.utils';
13
- function buildDefaultGridTemplate(args) {
14
- const { hasSelection, colCount } = args;
15
- const parts = [];
16
- if (hasSelection)
17
- parts.push('34px');
18
- for (let i = 0; i < colCount; i++)
19
- parts.push('minmax(120px, 1fr)');
20
- return parts.join(' ');
21
- }
22
- export function Table({ data, columns, selectedRows, onRowSelect, selectionMode = 'single', onSortChange, onRowClick, onRowMouseEnter, sortById, sortDirection, dataKey, headerExtras, gridTemplateColumns, toolbar, striped, fillViewport = false, viewportBottomOffset, viewportMin, viewportIncludeMarginTop, take, skip, paginationPlacement = 'bottom', totalItemsCount, onPageChange, loading, variant = 'primary', size = 'md', getRowSeverity, showFirstLast = false, allRowsSelected, onSelectAllRows, viewMode, emptyConfig, animateNewRows = true, ...rest }) {
23
- void viewportBottomOffset;
24
- void viewportMin;
25
- void viewportIncludeMarginTop;
26
- const filteredColumns = useMemo(() => getVisibleColumns(columns), [columns]);
27
- const handlePageChange = useCallback((e) => onPageChange === null || onPageChange === void 0 ? void 0 : onPageChange(e), [onPageChange]);
28
- const hasSelection = Boolean(selectedRows && onRowSelect && dataKey);
29
- const newRowIds = useAnimatedNewRowIds({
30
- data,
31
- dataKey,
32
- enabled: animateNewRows,
33
- animationDurationMs: 1000,
34
- });
11
+ import { buildDefaultGridTemplate, getVisibleColumns } from './table.utils';
12
+ export function Table({ data, dataKey, columns, selectedRows, selectionMode = 'single', allRowsSelected, sortById, sortDirection, loading, emptyConfig, variant = 'primary', size = 'md', viewMode, striped, fillViewport = false, gridTemplateColumns, toolbar, headerExtras, take, skip, totalItemsCount, paginationPlacement = 'bottom', showFirstLast = false, getRowSeverity, onRowClick, onRowMouseEnter, onRowSelect, onSelectAllRows, onSortChange, onPageChange, ...rest }) {
13
+ const visibleColumns = useMemo(() => getVisibleColumns(columns), [columns]);
14
+ const hasSelection = Boolean(selectedRows && onRowSelect);
35
15
  const template = useMemo(() => {
36
- return (gridTemplateColumns !== null && gridTemplateColumns !== void 0 ? gridTemplateColumns : buildDefaultGridTemplate({ hasSelection, colCount: filteredColumns.length }));
37
- }, [gridTemplateColumns, hasSelection, filteredColumns.length]);
16
+ return (gridTemplateColumns !== null && gridTemplateColumns !== void 0 ? gridTemplateColumns : buildDefaultGridTemplate({
17
+ hasSelection,
18
+ colCount: visibleColumns.length,
19
+ }));
20
+ }, [gridTemplateColumns, hasSelection, visibleColumns.length]);
38
21
  const gridStyle = useMemo(() => ({ ['--grid-template']: template }), [template]);
39
- const headerEl = (_jsxs("div", { className: styles.headerRow, style: gridStyle, role: "row", children: [hasSelection && (_jsx("div", { className: `${styles.headerCell} ${styles.selectionCell}`, role: "columnheader", 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) => {
40
- var _a;
41
- const active = isActiveSort(sortById, column.id);
42
- const ariaSort = getAriaSort(column.sortable, active, sortDirection !== null && sortDirection !== void 0 ? sortDirection : null);
43
- const align = (_a = column.align) !== null && _a !== void 0 ? _a : 'left';
44
- const extraContent = headerExtras === null || headerExtras === void 0 ? void 0 : headerExtras({ column, index });
45
- const toggleSort = () => {
46
- if (!onSortChange || !column.sortable)
47
- return;
48
- const nextDir = getNextSortDirection(column.sortable, active, sortDirection !== null && sortDirection !== void 0 ? sortDirection : null);
49
- onSortChange(column, nextDir);
50
- };
51
- const dividerClass = column.divider === 'left'
52
- ? styles.dividerLeft
53
- : column.divider === 'right'
54
- ? styles.dividerRight
55
- : '';
56
- return (_jsxs("div", { className: [styles.headerCell, dividerClass].filter(Boolean).join(' '), role: "columnheader", "aria-sort": ariaSort, "data-align": align, "data-divider": column.divider, children: [_jsx("div", { className: [
57
- styles.thInner,
58
- align === 'right' ? styles.thInnerRight : '',
59
- align === 'center' ? styles.thInnerCenter : '',
60
- ]
61
- .filter(Boolean)
62
- .join(' '), children: _jsx("div", { className: [
63
- styles.thMain,
64
- align === 'right' ? styles.thMainRight : '',
65
- align === 'center' ? styles.thMainCenter : '',
66
- ]
67
- .filter(Boolean)
68
- .join(' '), children: column.sortable ? (_jsxs("button", { type: "button", className: [
69
- styles.thButton,
70
- align === 'right' ? styles.thButtonRight : '',
71
- align === 'center' ? styles.thButtonCenter : '',
72
- ]
73
- .filter(Boolean)
74
- .join(' '), onClick: toggleSort, onKeyDown: e => {
75
- if (shouldToggleOnKey(e.key)) {
76
- e.preventDefault();
77
- toggleSort();
78
- }
79
- }, children: [_jsx("span", { className: [
80
- styles.thLabel,
81
- align === 'right' ? styles.thLabelRight : '',
82
- align === 'center' ? styles.thLabelCenter : '',
83
- ]
84
- .filter(Boolean)
85
- .join(' '), children: getHeaderLabel(column.header) }), _jsxs("span", { className: styles.sortIndicator, "aria-hidden": "true", children: [active && sortDirection === 'asc' && _jsx(ArrowUp, {}), active && sortDirection === 'desc' && (_jsx(ArrowDown, { className: styles.descending })), !active && (_jsx(ArrowDown, { className: `${styles.descending} ${styles.inActiveSort}` }))] })] })) : (_jsx("span", { className: [
86
- styles.thLabel,
87
- align === 'right' ? styles.thLabelRight : '',
88
- align === 'center' ? styles.thLabelCenter : '',
89
- ]
90
- .filter(Boolean)
91
- .join(' '), children: getHeaderLabel(column.header) })) }) }), extraContent != null ? (_jsx("div", { className: styles.thOverlayExtras, children: extraContent })) : null] }, column.id));
92
- })] }));
93
- const loadingBodyEl = (_jsx("div", { className: styles.body, role: "rowgroup", children: Array.from({ length: take !== null && take !== void 0 ? take : 5 }).map((_, rowIndex) => (_jsxs("div", { className: styles.row, style: gridStyle, role: "row", children: [hasSelection ? (_jsx("div", { className: `${styles.cell} ${styles.selectionCell}`, role: "cell" })) : null, filteredColumns.map(column => {
94
- var _a;
95
- const dividerClass = column.divider === 'left'
96
- ? styles.dividerLeft
97
- : column.divider === 'right'
98
- ? styles.dividerRight
99
- : '';
100
- return (_jsx("div", { className: [styles.cell, dividerClass].filter(Boolean).join(' '), role: "cell", "data-align": (_a = column.align) !== null && _a !== void 0 ? _a : 'left', "data-divider": column.divider, children: _jsx("div", { className: styles.cellContent, children: _jsx("div", { className: styles.cellValueEllipsis, children: _jsx(SkeletonLoaderItem, { height: 20, width: "100%" }) }) }) }, column.id));
101
- })] }, `loading-row-${rowIndex}`))) }));
102
- const dataBodyEl = (_jsx("div", { className: `${styles.body} ${striped ? styles.striped : ''}`, role: "rowgroup", children: data.map(row => {
103
- const rowSeverity = getRowSeverity === null || getRowSeverity === void 0 ? void 0 : getRowSeverity(row);
104
- const rowId = String(row[dataKey]);
105
- const isNewRow = animateNewRows && newRowIds.has(rowId);
106
- const isSelected = Boolean(selectedRows === null || selectedRows === void 0 ? void 0 : selectedRows.has(rowId));
107
- return (_jsxs("div", { className: [
108
- styles.row,
109
- onRowClick ? styles.clickableRow : '',
110
- isSelected ? styles.selectedRow : '',
111
- rowSeverity ? styles.severity : '',
112
- isNewRow ? styles.newRow : '',
113
- ]
114
- .filter(Boolean)
115
- .join(' '), style: {
116
- ...gridStyle,
117
- ['--row-severity-color']: rowSeverity
118
- ? SeverityBgColor[rowSeverity]
119
- : undefined,
120
- }, role: "row", tabIndex: onRowClick ? 0 : -1, onKeyDown: e => {
121
- if (!onRowClick)
122
- return;
123
- if (shouldToggleOnKey(e.key)) {
124
- e.preventDefault();
125
- onRowClick(row);
126
- }
127
- }, onMouseEnter: () => onRowMouseEnter === null || onRowMouseEnter === void 0 ? void 0 : onRowMouseEnter(row), onClick: e => {
128
- const canSelect = Boolean(selectedRows && onRowSelect && dataKey);
129
- if (isModifierClick(e) && canSelect) {
130
- e.preventDefault();
131
- e.stopPropagation();
132
- onRowSelect(rowId, !selectedRows.has(rowId));
133
- return;
134
- }
135
- onRowClick === null || onRowClick === void 0 ? void 0 : onRowClick(row);
136
- }, children: [hasSelection && (_jsx("div", { className: `${styles.cell} ${styles.selectionCell}`, role: "cell", children: _jsx(Checkbox, { variant: "primary", checked: selectedRows.has(rowId), size: "sm", onChange: (checked, e) => {
137
- e.stopPropagation();
138
- onRowSelect === null || onRowSelect === void 0 ? void 0 : onRowSelect(rowId, checked);
139
- } }) })), filteredColumns.map(column => {
140
- var _a;
141
- const dividerClass = column.divider === 'left'
142
- ? styles.dividerLeft
143
- : column.divider === 'right'
144
- ? styles.dividerRight
145
- : '';
146
- const allowWrap = shouldAllowWrap(column.allowWrap, isSelected, viewMode);
147
- const cellValue = getCellDisplayValue(row, column);
148
- return (_jsx("div", { className: [
149
- styles.cell,
150
- allowWrap ? styles.allowWrap : styles.nowrap,
151
- dividerClass,
152
- ]
153
- .filter(Boolean)
154
- .join(' '), role: "cell", "data-align": (_a = column.align) !== null && _a !== void 0 ? _a : 'left', "data-divider": column.divider, children: _jsx("div", { className: styles.cellContent, children: allowWrap ? (cellValue) : (_jsx("div", { className: styles.cellValueEllipsis, children: cellValue })) }) }, column.id));
155
- })] }, `gridRow-${rowId}`));
156
- }) }));
157
- const bodyContent = loading && !data.length ? loadingBodyEl : dataBodyEl;
158
- const tableClassName = [
159
- styles.tableRoot,
160
- styles[variant],
161
- styles[size],
162
- getRowSeverity ? styles.severityTable : '',
163
- ]
164
- .filter(Boolean)
165
- .join(' ');
166
- const tableShell = (_jsx("div", { ...rest, className: tableClassName, role: "table", "aria-rowcount": data.length, children: _jsxs("div", { className: styles.tableContent, children: [_jsx("div", { className: styles.header, role: "rowgroup", children: headerEl }), _jsx("div", { className: styles.bodyScroll, children: !data.length && !loading ? (_jsx("div", { className: styles.emptyStateSlot, children: _jsx(TableEmptyState, { config: emptyConfig }) })) : (bodyContent) })] }) }));
167
- const paginationEl = onPageChange && data.length > 0 ? (_jsx("div", { className: `${styles.paginationSlot} ${paginationPlacement === 'top' ? styles.paginationSlotTop : ''}`, children: _jsx(Pagination, { itemsCount: totalItemsCount, take: take, skip: skip, onPageChange: handlePageChange, showFirstLast: showFirstLast }) })) : null;
22
+ const handlePageChange = useCallback((e) => {
23
+ onPageChange === null || onPageChange === void 0 ? void 0 : onPageChange(e);
24
+ }, [onPageChange]);
25
+ const bodyContent = loading && !data.length ? (_jsx(TableLoadingBody, { rows: take !== null && take !== void 0 ? take : 5, columns: visibleColumns, hasSelection: hasSelection, gridStyle: gridStyle })) : (_jsx(TableBody, { data: data, dataKey: dataKey, columns: visibleColumns, gridStyle: gridStyle, striped: striped, selectedRows: selectedRows, hasSelection: hasSelection, viewMode: viewMode, getRowSeverity: getRowSeverity, onRowClick: onRowClick, onRowMouseEnter: onRowMouseEnter, onRowSelect: onRowSelect }));
26
+ const paginationEl = onPageChange && data.length > 0 ? (_jsx("div", { className: cx(styles.paginationSlot, paginationPlacement === 'top' && styles.paginationSlotTop), children: _jsx(Pagination, { itemsCount: totalItemsCount, take: take, skip: skip, onPageChange: handlePageChange, showFirstLast: showFirstLast }) })) : null;
27
+ const tableClassName = cx(styles.tableRoot, styles[variant], styles[size], getRowSeverity && styles.severityTable);
28
+ const tableShell = (_jsx("div", { ...rest, className: tableClassName, role: "table", "aria-rowcount": data.length, children: _jsxs("div", { className: styles.tableContent, children: [_jsx("div", { className: styles.header, role: "rowgroup", children: _jsx(TableHeader, { columns: visibleColumns, gridStyle: gridStyle, hasSelection: hasSelection, selectionMode: selectionMode, allRowsSelected: allRowsSelected, onSelectAllRows: onSelectAllRows, sortById: sortById, sortDirection: sortDirection, onSortChange: onSortChange, headerExtras: headerExtras }) }), _jsx("div", { className: styles.bodyScroll, children: !data.length && !loading ? (_jsx("div", { className: styles.emptyStateSlot, children: _jsx(TableEmptyState, { config: emptyConfig }) })) : (bodyContent) })] }) }));
168
29
  if (fillViewport) {
169
30
  return (_jsxs("div", { className: styles.fillViewportRoot, style: {
170
31
  flexDirection: paginationPlacement === 'top' ? 'column-reverse' : 'column',
@@ -186,14 +186,40 @@
186
186
  text-align: center;
187
187
  }
188
188
 
189
- .selectionCell {
190
- padding-block: inherit !important;
191
- padding-inline: 0 !important;
189
+ .headerCell.selectionCell,
190
+ .cell.selectionCell {
192
191
  overflow: visible !important;
193
192
  display: flex;
194
193
  align-items: center;
195
- justify-content: flex-start;
194
+ justify-content: center;
196
195
  min-width: 0;
196
+ padding-inline: 0;
197
+ padding-block: 0;
198
+ cursor: pointer;
199
+ }
200
+
201
+ .headerCell.selectionCell:hover button,
202
+ .cell.selectionCell:hover button {
203
+ border-color: var(--color-fg-default);
204
+ }
205
+
206
+ .headerCell.selectionCell:focus-within > button,
207
+ .cell.selectionCell:focus-within > button {
208
+ border-color: var(--color-fg-default);
209
+ }
210
+
211
+ .selectionHitArea {
212
+ display: inline-flex;
213
+ align-items: center;
214
+ justify-content: center;
215
+ inline-size: 100%;
216
+ block-size: 100%;
217
+ min-block-size: var(--component-size-md);
218
+ padding: 4px;
219
+ }
220
+
221
+ .sm .selectionHitArea {
222
+ min-block-size: var(--component-size-sm);
197
223
  }
198
224
 
199
225
  .clickableRow {
@@ -361,11 +387,18 @@
361
387
  --row-rail-offset: calc(var(--row-rail-width) + var(--row-rail-gap));
362
388
  --row-rail-inset-block: 1px;
363
389
  --row-rail-radius: 0 2px 2px 0;
390
+ --selection-rail-compensation: calc(var(--row-rail-offset) / 2);
364
391
  }
365
392
 
366
- .severityTable .headerRow .selectionCell,
393
+ .severityTable .headerCell.selectionCell,
367
394
  .severityTable .row .selectionCell {
368
- padding-inline-start: var(--row-rail-offset) !important;
395
+ padding-inline-start: 0 !important;
396
+ }
397
+
398
+ .severityTable .headerCell.selectionCell .selectionHitArea,
399
+ .severityTable .row .selectionCell .selectionHitArea {
400
+ inline-size: calc(100% - var(--selection-rail-compensation));
401
+ margin-inline-start: var(--selection-rail-compensation);
369
402
  }
370
403
 
371
404
  .severityTable .row.severity {