@dbcdk/react-components 0.0.14 → 0.0.16

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 (52) hide show
  1. package/dist/components/accordion/Accordion.d.ts +2 -1
  2. package/dist/components/accordion/Accordion.js +14 -3
  3. package/dist/components/accordion/Accordion.module.css +1 -1
  4. package/dist/components/accordion/components/AccordionRow.d.ts +2 -1
  5. package/dist/components/accordion/components/AccordionRow.js +8 -6
  6. package/dist/components/accordion/components/AccordionRow.module.css +12 -8
  7. package/dist/components/button/Button.d.ts +1 -0
  8. package/dist/components/button/Button.js +3 -3
  9. package/dist/components/button/Button.module.css +12 -1
  10. package/dist/components/card/Card.module.css +1 -1
  11. package/dist/components/chip/Chip.js +11 -1
  12. package/dist/components/chip/Chip.module.css +92 -30
  13. package/dist/components/circle/Circle.d.ts +1 -1
  14. package/dist/components/circle/Circle.module.css +5 -1
  15. package/dist/components/clear-button/ClearButton.js +1 -1
  16. package/dist/components/clear-button/ClearButton.module.css +3 -0
  17. package/dist/components/code-block/CodeBlock.d.ts +7 -3
  18. package/dist/components/code-block/CodeBlock.js +35 -2
  19. package/dist/components/code-block/CodeBlock.module.css +49 -2
  20. package/dist/components/filter-field/FilterField.d.ts +2 -1
  21. package/dist/components/filter-field/FilterField.js +2 -2
  22. package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.d.ts +5 -3
  23. package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.js +4 -3
  24. package/dist/components/forms/checkbox/Checkbox.d.ts +1 -1
  25. package/dist/components/forms/checkbox/Checkbox.module.css +11 -0
  26. package/dist/components/hyperlink/Hyperlink.module.css +0 -1
  27. package/dist/components/overlay/modal/Modal.js +1 -1
  28. package/dist/components/overlay/side-panel/SidePanel.js +1 -1
  29. package/dist/components/page-layout/PageLayout.module.css +0 -2
  30. package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.js +1 -1
  31. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.js +1 -1
  32. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +0 -4
  33. package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.d.ts +2 -1
  34. package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.js +2 -2
  35. package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.module.css +9 -0
  36. package/dist/components/split-pane/SplitPane.js +1 -1
  37. package/dist/components/state-page/StatePage.module.css +1 -1
  38. package/dist/components/table/Table.js +18 -3
  39. package/dist/components/table/Table.module.css +35 -30
  40. package/dist/components/table/components/empty-state/EmptyState.d.ts +7 -22
  41. package/dist/components/table/components/empty-state/EmptyState.js +12 -8
  42. package/dist/components/table/components/empty-state/EmptyState.module.css +2 -14
  43. package/dist/components/tabs/Tabs.js +1 -1
  44. package/dist/components/tabs/Tabs.module.css +1 -1
  45. package/dist/components/toast/Toast.js +1 -1
  46. package/dist/hooks/useTableSelection.d.ts +1 -1
  47. package/dist/hooks/useTableSelection.js +97 -77
  48. package/dist/hooks/useViewportFill.js +12 -0
  49. package/dist/src/styles/styles.css +8 -0
  50. package/dist/styles/styles.css +8 -0
  51. package/dist/styles/themes/dbc/base.css +1 -0
  52. package/package.json +1 -1
@@ -69,7 +69,7 @@ function isFilterActive(value) {
69
69
  return value != null;
70
70
  }
71
71
  const VALUELESS_OPERATORS = ['isEmpty', 'isNotEmpty'];
72
- export function FilterField({ field, control, operator, value, onChange, operators, options = [], single = true, size = 'md', label, placeholder = 'Type value…', disabled, 'data-cy': dataCy, ...inputProps }) {
72
+ export function FilterField({ field, control, operator, value, onChange, operators, options = [], single = true, size = 'md', label, placeholder = 'Type value…', disabled, 'data-cy': dataCy, width, ...inputProps }) {
73
73
  var _a, _b;
74
74
  const ops = useMemo(() => operators !== null && operators !== void 0 ? operators : DEFAULT_TEXT_OPERATORS, [operators]);
75
75
  // internal operator state (source of truth for UI)
@@ -137,7 +137,7 @@ export function FilterField({ field, control, operator, value, onChange, operato
137
137
  clearTimeout(debounceRef.current);
138
138
  };
139
139
  }, []);
140
- return (_jsxs("div", { ...(dataCy ? { 'data-cy': dataCy } : {}), className: `${styles.filterField} ${styles[size]} ${active ? styles.active : ''}`, children: [label ? _jsx("span", { className: `${styles.label} ${styles[size]}`, children: label }) : null, _jsx(OperatorDropdown, { value: selectedOperator, onChange: handleOperatorChange, operators: ops, size: size, disabled: disabled }), _jsx("div", { className: `${control === 'input' ? 'dbc-flex dbc-flex-grow' : styles.valueWrapper}`, children: control === 'input' ? (_jsx(Input, { ...inputProps, value: localValue, onChange: e => {
140
+ return (_jsxs("div", { ...(dataCy ? { 'data-cy': dataCy } : {}), className: `${styles.filterField} ${styles[size]} ${active ? styles.active : ''}`, children: [label ? _jsx("span", { className: `${styles.label} ${styles[size]}`, children: label }) : null, _jsx(OperatorDropdown, { value: selectedOperator, onChange: handleOperatorChange, operators: ops, size: size, disabled: disabled }), _jsx("div", { className: `${control === 'input' ? 'dbc-flex dbc-flex-grow' : styles.valueWrapper}`, style: { width }, children: control === 'input' ? (_jsx(Input, { ...inputProps, value: localValue, onChange: e => {
141
141
  const next = e.currentTarget.value;
142
142
  isTypingRef.current = true;
143
143
  setLocalValue(next);
@@ -13,15 +13,17 @@ interface ChipMultiToggleProps {
13
13
  onChange: (nextSelectedValues: string[]) => void;
14
14
  onToggle?: (toggledValue: string, isSelected: boolean) => void;
15
15
  size?: 'sm' | 'md' | 'lg';
16
- selectedSeverity: 'neutral' | 'info' | 'success' | 'warning' | 'danger';
16
+ selectedSeverity: 'neutral' | 'info' | 'success' | 'warning' | 'danger' | 'brand';
17
17
  unselectedSeverity?: 'neutral' | 'info' | 'success' | 'warning' | 'danger' | null;
18
18
  fullWidth?: boolean;
19
19
  disabled?: boolean;
20
+ showSelectedIcon?: boolean;
20
21
  showAllOption?: boolean;
21
22
  allLabel?: string;
22
23
  allIcon?: React.ReactNode;
24
+ dataCy?: string;
23
25
  /**
24
- * If true, clicking "All" toggles between:
26
+ * If true, clicking "All" toggles between:
25
27
  * - All (no filtering) => []
26
28
  * - None (match nothing) => [noneValue]
27
29
  */
@@ -32,5 +34,5 @@ interface ChipMultiToggleProps {
32
34
  */
33
35
  noneValue?: string;
34
36
  }
35
- export declare function ChipMultiToggle({ label, options, selectedValues, onChange, onToggle, size, selectedSeverity, unselectedSeverity, fullWidth, disabled, showAllOption, allLabel, allIcon, allTogglesNone, noneValue, }: ChipMultiToggleProps): JSX.Element;
37
+ export declare function ChipMultiToggle({ label, options, selectedValues, onChange, onToggle, size, selectedSeverity, unselectedSeverity, fullWidth, disabled, showAllOption, allLabel, allIcon, showSelectedIcon, allTogglesNone, noneValue, dataCy, }: ChipMultiToggleProps): JSX.Element;
36
38
  export {};
@@ -1,9 +1,10 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Check } from 'lucide-react';
3
4
  import React from 'react';
4
5
  import { Chip } from '../../../components/chip/Chip';
5
6
  import styles from './ChipMultiToggle.module.css';
6
- export function ChipMultiToggle({ label, options, selectedValues = [], onChange, onToggle, size = 'sm', selectedSeverity = 'info', unselectedSeverity = null, fullWidth = false, disabled = false, showAllOption = false, allLabel = 'Alle', allIcon, allTogglesNone = false, noneValue = '__none__', }) {
7
+ export function ChipMultiToggle({ label, options, selectedValues = [], onChange, onToggle, size = 'sm', selectedSeverity = 'info', unselectedSeverity = null, fullWidth = false, disabled = false, showAllOption = false, allLabel = 'Alle', allIcon, showSelectedIcon = false, allTogglesNone = false, noneValue = '__none__', dataCy, }) {
7
8
  const selectedSet = React.useMemo(() => new Set(selectedValues), [selectedValues]);
8
9
  const isNoneSelected = allTogglesNone && selectedSet.has(noneValue);
9
10
  const isAllSelected = showAllOption && !isNoneSelected && selectedSet.size === 0;
@@ -45,9 +46,9 @@ export function ChipMultiToggle({ label, options, selectedValues = [], onChange,
45
46
  onChange([]);
46
47
  }
47
48
  };
48
- return (_jsxs("div", { className: `${styles.wrapper} ${fullWidth ? styles.fullWidth : ''}`, children: [label && _jsx("span", { className: styles.label, children: label }), _jsxs("div", { className: styles.container, children: [showAllOption && (_jsx("button", { type: "button", className: styles.chipButton, onClick: toggleAll, "aria-pressed": isAllSelected || isNoneSelected, disabled: disabled, children: _jsx(Chip, { size: size, severity: (isAllSelected || isNoneSelected ? selectedSeverity : unselectedSeverity), customIcon: allIcon, type: unselectedSeverity === null ? 'outlined' : 'rounded', children: isNoneSelected ? `${allLabel} (ingen)` : allLabel }) }, "__all__")), options.map(opt => {
49
+ return (_jsxs("div", { className: `${styles.wrapper} ${fullWidth ? styles.fullWidth : ''}`, "data-cy": dataCy, children: [label && _jsx("span", { className: styles.label, children: label }), _jsxs("div", { className: styles.container, children: [showAllOption && (_jsx("button", { type: "button", className: styles.chipButton, onClick: toggleAll, "aria-pressed": isAllSelected || isNoneSelected, disabled: disabled, children: _jsx(Chip, { size: size, severity: (isAllSelected || isNoneSelected ? selectedSeverity : unselectedSeverity), customIcon: allIcon, type: unselectedSeverity === null ? 'outlined' : 'rounded', children: isNoneSelected ? `${allLabel} (ingen)` : allLabel }) }, "__all__")), options.map(opt => {
49
50
  // If none-state is active, everything else should look unselected
50
51
  const isSelected = !isNoneSelected && selectedSet.has(opt.value);
51
- return (_jsx("button", { type: "button", className: styles.chipButton, onClick: () => toggleValue(opt.value), "aria-pressed": isSelected, disabled: disabled, children: _jsx(Chip, { size: size, severity: (isSelected ? selectedSeverity : unselectedSeverity), customIcon: opt.icon, type: unselectedSeverity === null ? 'outlined' : 'rounded', children: opt.label }) }, opt.value));
52
+ return (_jsx("button", { type: "button", className: styles.chipButton, onClick: () => toggleValue(opt.value), "aria-pressed": isSelected, disabled: disabled, children: _jsx(Chip, { size: size, severity: (isSelected ? selectedSeverity : unselectedSeverity), customIcon: showSelectedIcon ? isSelected ? _jsx(Check, {}) : null : opt.icon, type: unselectedSeverity === null ? 'outlined' : 'rounded', children: opt.label }) }, opt.value));
52
53
  })] })] }));
53
54
  }
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import type { JSX, ReactNode } from 'react';
3
- type Variant = 'default' | 'primary' | 'outlined';
3
+ type Variant = 'default' | 'primary' | 'outlined' | 'success';
4
4
  type Size = 'sm' | 'md' | 'lg';
5
5
  interface CheckboxProps {
6
6
  checked?: boolean;
@@ -60,3 +60,14 @@
60
60
  color: var(--color-fg-default);
61
61
  }
62
62
  }
63
+
64
+ .success.checked {
65
+ background-color: var(--color-status-success);
66
+ &:not(:hover) {
67
+ border-color: transparent;
68
+ }
69
+
70
+ .icon {
71
+ color: var(--color-fg-on-brand);
72
+ }
73
+ }
@@ -2,7 +2,6 @@
2
2
  display: inline-flex;
3
3
  gap: var(--spacing-xs);
4
4
  position: relative;
5
- font-weight: normal;
6
5
  background: none;
7
6
  border: none;
8
7
  padding: 0;
@@ -88,5 +88,5 @@ export function Modal({ isOpen, onRequestClose, header, content, children, prima
88
88
  const resolvedSecondaryAction = secondaryAction !== null && secondaryAction !== void 0 ? secondaryAction : (primaryAction ? { label: 'Luk', onClick: onRequestCloseRef.current } : undefined);
89
89
  const shouldRenderFooter = Boolean(primaryAction || resolvedSecondaryAction);
90
90
  const body = children !== null && children !== void 0 ? children : content;
91
- return (_jsx("div", { className: styles.overlay, onClick: handleOverlayClick, children: _jsxs("div", { "data-cy": dataCy, ref: dialogRef, className: `${styles.modal} ${disableContentSpacing ? '' : styles.contentSpacing}`, onClick: stopPropagation, role: "dialog", "aria-modal": "true", "aria-labelledby": header ? titleId : undefined, children: [_jsxs("div", { className: styles.header, children: [header && (_jsx(Headline, { severity: severity, size: 3, disableMargin: true, children: header })), _jsx(Button, { type: "button", variant: "inline", onClick: () => onRequestCloseRef.current(), "aria-label": "Luk", children: _jsx(X, {}) })] }), _jsx("div", { className: styles.body, children: body }), shouldRenderFooter && (_jsxs("div", { className: styles.footer, children: [resolvedSecondaryAction && (_jsxs(Button, { type: "button", variant: "outlined", onClick: resolvedSecondaryAction.onClick, disabled: isLoading, children: [resolvedSecondaryAction.icon && (_jsx("span", { className: styles.icon, children: resolvedSecondaryAction.icon })), _jsx("span", { children: resolvedSecondaryAction.label })] })), primaryAction && (_jsxs(Button, { type: "button", variant: "primary", onClick: primaryAction.onClick, disabled: primaryAction.disabled || isLoading, loading: isLoading, children: [primaryAction.icon && _jsx("span", { className: styles.icon, children: primaryAction.icon }), _jsx("span", { children: primaryAction.label })] }))] }))] }) }));
91
+ return (_jsx("div", { className: styles.overlay, onClick: handleOverlayClick, children: _jsxs("div", { "data-cy": dataCy, ref: dialogRef, className: `${styles.modal} ${disableContentSpacing ? '' : styles.contentSpacing}`, onClick: stopPropagation, role: "dialog", "aria-modal": "true", "aria-labelledby": header ? titleId : undefined, children: [_jsxs("div", { className: styles.header, children: [header && (_jsx(Headline, { severity: severity, size: 3, disableMargin: true, children: header })), _jsx(Button, { type: "button", variant: "inline", onClick: () => onRequestCloseRef.current(), "aria-label": "Luk", shape: "round", icon: _jsx(X, {}) })] }), _jsx("div", { className: styles.body, children: body }), shouldRenderFooter && (_jsxs("div", { className: styles.footer, children: [resolvedSecondaryAction && (_jsxs(Button, { type: "button", variant: "outlined", onClick: resolvedSecondaryAction.onClick, disabled: isLoading, children: [resolvedSecondaryAction.icon && (_jsx("span", { className: styles.icon, children: resolvedSecondaryAction.icon })), _jsx("span", { children: resolvedSecondaryAction.label })] })), primaryAction && (_jsxs(Button, { type: "button", variant: "primary", onClick: primaryAction.onClick, disabled: primaryAction.disabled || isLoading, loading: isLoading, children: [primaryAction.icon && _jsx("span", { className: styles.icon, children: primaryAction.icon }), _jsx("span", { children: primaryAction.label })] }))] }))] }) }));
92
92
  }
@@ -76,7 +76,7 @@ export function SidePanel({ isOpen, onClose, children, header, headerAddition, a
76
76
  }, "data-cy": "details-panel", role: "dialog", "aria-modal": "true", children: [hasDetails ? (_jsxs("aside", { className: styles.detailsCol, "data-cy": "details-panel-details", children: [_jsxs("div", { className: styles.detailsHeader, children: [_jsx("div", { className: styles.detailsTitle, children: detailsHeader }), _jsxs("div", { className: styles.detailsHeaderActions, children: [detailsHeaderAddition, onCloseDetails ? (_jsx(Button, { type: "button", size: "sm", variant: "outlined", onClick: e => {
77
77
  e.stopPropagation();
78
78
  onCloseDetails();
79
- }, children: "Luk" })) : null] })] }), _jsx("div", { className: styles.detailsContent, children: details })] })) : null, _jsxs("section", { className: styles.mainCol, "data-cy": "details-panel-main", children: [_jsx("div", { className: styles.header, children: _jsxs("div", { className: "dbc-flex dbc-justify-between", children: [_jsx(Headline, { size: 3, disableMargin: true, severity: severity, marker: showHeaderMarker, children: header }), _jsxs("span", { className: "dbc-flex dbc-items-center dbc-gap-xs", children: [headerAddition, _jsx(Button, { type: "button", size: "sm", variant: "inline", onClick: e => {
79
+ }, children: "Luk" })) : null] })] }), _jsx("div", { className: styles.detailsContent, children: details })] })) : null, _jsxs("section", { className: styles.mainCol, "data-cy": "details-panel-main", children: [_jsx("div", { className: styles.header, children: _jsxs("div", { className: "dbc-flex dbc-justify-between", children: [_jsx(Headline, { size: 3, disableMargin: true, severity: severity, marker: showHeaderMarker, children: header }), _jsxs("span", { className: "dbc-flex dbc-items-center dbc-gap-xs", children: [headerAddition, _jsx(Button, { type: "button", size: "md", shape: "round", variant: "inline", onClick: e => {
80
80
  e.stopPropagation();
81
81
  onClose(e);
82
82
  }, "aria-label": "Luk panel", "data-cy": "side-panel-close-button", children: _jsx(X, {}) })] })] }) }), _jsx("div", { className: styles.content, "data-cy": "details-panel-content", children: children }), actions && _jsx("div", { className: styles.actions, children: actions })] })] })] }), document.body);
@@ -34,7 +34,6 @@
34
34
  .sidebar {
35
35
  min-width: 0;
36
36
  overflow: hidden;
37
- border-right: var(--border-width-thin) solid var(--color-border-subtle);
38
37
  background: var(--color-bg-surface);
39
38
  }
40
39
 
@@ -72,7 +71,6 @@
72
71
  .header {
73
72
  min-width: 0;
74
73
  background: var(--color-bg-surface);
75
- border-bottom: var(--border-width-thin) solid var(--color-border-subtle);
76
74
  flex: 0 0 auto;
77
75
  }
78
76
 
@@ -62,5 +62,5 @@ export function ExpandableSidebarItem({ items, label, icon, component: Component
62
62
  }
63
63
  return (_jsx(SidebarItem, { component: item.component, label: item.label, icon: item.icon, href: item.href }, key));
64
64
  };
65
- return (_jsxs("div", { className: `${styles.container} ${expanded ? styles.expanded : ''}`, children: [_jsx(Component, { onClick: () => toggleAccordion(undefined, true), children: _jsx(SidebarItemContent, { icon: icon, label: label, href: href, disableActiveStyles: expanded, suffixIcon: isSidebarCollapsed ? null : (_jsx(Button, { variant: "outlined", onClick: toggleAccordion, children: _jsx(ChevronDown, { className: `${styles.chevron} ${expanded ? styles.chevronExpanded : ''}` }) })) }) }), expanded && !isSidebarCollapsed && (_jsx("div", { onAnimationEnd: handleAnimationEnd, className: `${styles.childrenContainer} ${closing ? 'animate--collapse' : ''} ${expanded ? 'animate--expand' : 'visually-hidden'}`, children: items.map((item, idx) => renderNavItem(item, `${href}-${idx}`)) }))] }));
65
+ return (_jsxs("div", { className: `${styles.container}`, children: [_jsx(Component, { onClick: () => toggleAccordion(undefined, true), children: _jsx(SidebarItemContent, { headerStyle: expanded, icon: icon, label: label, href: href, disableActiveStyles: expanded, suffixIcon: isSidebarCollapsed ? null : (_jsx(Button, { variant: "outlined", onClick: toggleAccordion, children: _jsx(ChevronDown, { className: `${styles.chevron} ${expanded ? styles.chevronExpanded : ''}` }) })) }) }), expanded && !isSidebarCollapsed && (_jsx("div", { onAnimationEnd: handleAnimationEnd, className: `${styles.childrenContainer} ${closing ? 'animate--collapse' : ''} ${expanded ? 'animate--expand' : 'visually-hidden'}`, children: items.map((item, idx) => renderNavItem(item, `${href}-${idx}`)) }))] }));
66
66
  }
@@ -10,5 +10,5 @@ export function SidebarContainer({ logo, // DBC Digital (company)
10
10
  productLogo, // DataIO (product)
11
11
  activeLink, }) {
12
12
  const { isSidebarCollapsed, handleSidebarCollapseChange } = useSidebar();
13
- return (_jsxs("div", { className: `${styles.container} ${isSidebarCollapsed ? styles.collapsed : ''}`, children: [_jsx("div", { className: styles.header, children: _jsxs("div", { className: styles.productHeader, children: [_jsx("div", { className: styles.productLogo, children: productLogo }), _jsx(Button, { size: "md", variant: "inline", "aria-label": "Collapse sidebar", icon: _jsx(ChevronLeft, { className: isSidebarCollapsed ? styles.collapsedIcon : '' }), onClick: () => handleSidebarCollapseChange(!isSidebarCollapsed) })] }) }), _jsxs("div", { className: styles.content, children: [_jsx("div", { className: styles.filter, children: _jsx(SidenavFiltering, {}) }), _jsx("div", { className: `${styles.links} hideScrollBar`, children: _jsx(SidebarItems, { activeLink: activeLink }) })] }), _jsx("div", { className: styles.footer, children: _jsx("div", { className: styles.companyLogo, children: logo !== null && logo !== void 0 ? logo : _jsx(Logo, {}) }) })] }));
13
+ return (_jsxs("div", { className: `${styles.container} ${isSidebarCollapsed ? styles.collapsed : ''}`, children: [_jsx("div", { className: styles.header, children: _jsxs("div", { className: styles.productHeader, children: [_jsx("div", { className: styles.productLogo, children: productLogo }), _jsx(Button, { size: "md", variant: "inline", shape: "round", "aria-label": "Collapse sidebar", icon: _jsx(ChevronLeft, { className: isSidebarCollapsed ? styles.collapsedIcon : '' }), onClick: () => handleSidebarCollapseChange(!isSidebarCollapsed) })] }) }), _jsxs("div", { className: styles.content, children: [_jsx("div", { className: styles.filter, children: _jsx(SidenavFiltering, {}) }), _jsx("div", { className: `${styles.links} hideScrollBar`, children: _jsx(SidebarItems, { activeLink: activeLink }) })] }), _jsx("div", { className: styles.footer, children: _jsx("div", { className: styles.companyLogo, children: logo !== null && logo !== void 0 ? logo : _jsx(Logo, {}) }) })] }));
14
14
  }
@@ -71,10 +71,6 @@
71
71
 
72
72
  /* Collapse button */
73
73
  .productHeader button {
74
- border-radius: 0;
75
- min-height: var(--component-size-md);
76
- padding: var(--spacing-xs);
77
- color: var(--color-fg-muted);
78
74
  flex: 0 0 auto;
79
75
  }
80
76
 
@@ -6,5 +6,6 @@ export interface SidebarItemContentProps {
6
6
  suffixIcon?: React.ReactNode;
7
7
  href?: string;
8
8
  disableActiveStyles?: boolean;
9
+ headerStyle?: boolean;
9
10
  }
10
- export declare function SidebarItemContent({ icon, label, suffixIcon, href, disableActiveStyles, }: SidebarItemContentProps): JSX.Element;
11
+ export declare function SidebarItemContent({ icon, label, suffixIcon, href, disableActiveStyles, headerStyle, }: SidebarItemContentProps): JSX.Element;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import styles from './SidebarItemContent.module.css';
3
3
  import { useSidebar } from '../../providers/SidebarProvider';
4
- export function SidebarItemContent({ icon, label, suffixIcon, href, disableActiveStyles = false, }) {
4
+ export function SidebarItemContent({ icon, label, suffixIcon, href, disableActiveStyles = false, headerStyle, }) {
5
5
  const { activeLink, isSidebarCollapsed } = useSidebar();
6
- return (_jsxs("span", { className: `${styles.container} ${!disableActiveStyles && activeLink === href ? styles.active : ''} ${isSidebarCollapsed ? styles.collapsed : ''}`, children: [_jsxs("span", { children: [_jsx("span", { className: styles.icon, children: icon }), !isSidebarCollapsed && _jsx("span", { className: styles.label, children: label })] }), suffixIcon && !isSidebarCollapsed && _jsx("span", { className: styles.suffixIcon, children: suffixIcon })] }));
6
+ return (_jsxs("span", { className: `${styles.container} ${!disableActiveStyles && activeLink === href ? styles.active : ''} ${isSidebarCollapsed ? styles.collapsed : ''} ${headerStyle ? styles.headerStyle : ''}`, children: [_jsxs("span", { children: [_jsx("span", { className: styles.icon, children: icon }), !isSidebarCollapsed && _jsx("span", { className: styles.label, children: label })] }), suffixIcon && !isSidebarCollapsed && _jsx("span", { className: styles.suffixIcon, children: suffixIcon })] }));
7
7
  }
@@ -73,3 +73,12 @@
73
73
  outline: none;
74
74
  box-shadow: var(--focus-ring);
75
75
  }
76
+
77
+ .headerStyle .label {
78
+ font-weight: 500;
79
+ color: var(--color-fg-subtle);
80
+ font-size: var(--font-size-xs);
81
+ text-transform: uppercase;
82
+ letter-spacing: 0.04em;
83
+ transition: 0.15s ease-in-out;
84
+ }
@@ -6,7 +6,7 @@ import styles from './SplitPane.module.css';
6
6
  function clamp(n, min, max) {
7
7
  return Math.max(min, Math.min(max, n));
8
8
  }
9
- export function SplitPane({ children, initialPrimarySize = 300, minPrimarySize = 160, minSecondarySize = 160, direction = 'horizontal', showDivider = 'hover', gutterSize = 8, storageKey, }) {
9
+ export function SplitPane({ children, initialPrimarySize = 300, minPrimarySize = 160, minSecondarySize = 160, direction = 'horizontal', showDivider = 'hover', gutterSize = 16, storageKey, }) {
10
10
  return (_jsx(SplitPaneProvider, { direction: direction, initialPrimarySize: initialPrimarySize, minPrimarySize: minPrimarySize, minSecondarySize: minSecondarySize, storageKey: storageKey, children: _jsx(SplitPaneContainer, { showDivider: showDivider, gutterSize: gutterSize, children: children }) }));
11
11
  }
12
12
  function SplitPaneContainer({ children, showDivider, gutterSize, }) {
@@ -4,6 +4,6 @@
4
4
  }
5
5
 
6
6
  .illustration svg {
7
- width: 300px;
7
+ width: 200px;
8
8
  height: auto;
9
9
  }
@@ -28,12 +28,24 @@ export function Table({ data, columns, selectedRows, onRowSelect, selectionMode
28
28
  const nextDir = getNextSortDirection(column.sortable, active, sortDirection !== null && sortDirection !== void 0 ? sortDirection : null);
29
29
  onSortChange(column, nextDir);
30
30
  };
31
- return (_jsx("th", { style: getColumnStyle(column.id, columnStyles, column.align, 'middle'), "aria-sort": ariaSort, className: `${styles.th}`, children: _jsxs("div", { className: styles.thInner, children: [column.sortable ? (_jsxs("button", { type: "button", className: styles.thButton, onClick: toggleSort, onKeyDown: e => {
31
+ return (_jsx("th", { style: getColumnStyle(column.id, columnStyles, column.align, 'middle'), "aria-sort": ariaSort, className: `${styles.th}`, children: _jsxs("div", { className: styles.thInner, children: [column.sortable ? (_jsxs("button", { type: "button", className: [
32
+ styles.thButton,
33
+ column.align === 'right' ? styles.thButtonRight : '',
34
+ column.align === 'center' ? styles.thButtonCenter : '',
35
+ ].join(' '), onClick: toggleSort, onKeyDown: e => {
32
36
  if (shouldToggleOnKey(e.key)) {
33
37
  e.preventDefault();
34
38
  toggleSort();
35
39
  }
36
- }, children: [_jsx("span", { className: styles.thLabel, 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: styles.thLabel, children: getHeaderLabel(column.header) })), headerExtras ? (_jsx("div", { className: styles.thExtras, children: headerExtras({ column, index }) })) : null] }) }, column.id));
40
+ }, children: [_jsx("span", { className: [
41
+ styles.thLabel,
42
+ column.align === 'right' ? styles.thLabelRight : '',
43
+ column.align === 'center' ? styles.thLabelCenter : '',
44
+ ].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: [
45
+ styles.thLabel,
46
+ column.align === 'right' ? styles.thLabelRight : '',
47
+ column.align === 'center' ? styles.thLabelCenter : '',
48
+ ].join(' '), children: getHeaderLabel(column.header) })), headerExtras ? (_jsx("div", { className: styles.thExtras, children: headerExtras({ column, index }) })) : null] }) }, column.id));
37
49
  })] }) }), loading && !data.length ? (_jsx("tbody", { className: `${styles.tBody} ${striped ? styles.striped : ''}`, children: Array.from({ length: take !== null && take !== void 0 ? take : 5 }).map((_, rowIndex) => (_jsx("tr", { children: filteredColumns.map((column, colIndex) => (_jsx("td", { style: getColumnStyle(column.id, columnStyles, column.align, 'middle'), className: `${styles.tableCell}`, children: _jsx(SkeletonLoaderItem, { height: 20, width: "100%" }) }, `${column.id}-${colIndex}`))) }, `loading-row-${rowIndex}`))) })) : (_jsx("tbody", { className: `${styles.tBody} ${striped ? styles.striped : ''}`, children: data === null || data === void 0 ? void 0 : data.map(row => {
38
50
  const rowSeverity = getRowSeverity === null || getRowSeverity === void 0 ? void 0 : getRowSeverity(row);
39
51
  const rowId = row[dataKey];
@@ -58,7 +70,10 @@ export function Table({ data, columns, selectedRows, onRowSelect, selectionMode
58
70
  ['--row-severity-color']: rowSeverity
59
71
  ? SeverityBgColor[rowSeverity]
60
72
  : undefined,
61
- }, className: `${onRowClick ? styles.clickableRow : ''} ${isSelected ? styles.selectedRow : ''} ${rowSeverity ? styles.severity : ''}`, children: [selectedRows && onRowSelect && dataKey && (_jsx("td", { className: `${styles.selectionCell}`, onClick: e => e.stopPropagation(), children: _jsx(Checkbox, { variant: "primary", checked: selectedRows.has(rowId), size: "sm", onChange: () => onRowSelect === null || onRowSelect === void 0 ? void 0 : onRowSelect(rowId, !selectedRows.has(rowId)) }) })), filteredColumns.map(column => (_jsx("td", { style: getColumnStyle(column.id, columnStyles, column.align, column.verticalAlign), className: `${styles.tableCell} ${shouldAllowWrap(column.allowWrap, isSelected, viewMode)
73
+ }, className: `${onRowClick ? styles.clickableRow : ''} ${isSelected ? styles.selectedRow : ''} ${rowSeverity ? styles.severity : ''}`, children: [selectedRows && onRowSelect && dataKey && (_jsx("td", { className: `${styles.selectionCell}`, children: _jsx(Checkbox, { variant: "primary", checked: selectedRows.has(rowId), size: "sm", onChange: (checked, e) => {
74
+ e.stopPropagation();
75
+ onRowSelect === null || onRowSelect === void 0 ? void 0 : onRowSelect(rowId, checked);
76
+ } }) })), filteredColumns.map(column => (_jsx("td", { style: getColumnStyle(column.id, columnStyles, column.align, column.verticalAlign), className: `${styles.tableCell} ${shouldAllowWrap(column.allowWrap, isSelected, viewMode)
62
77
  ? styles.allowWrap
63
78
  : styles.nowrap}`, children: getCellDisplayValue(row, column) }, column.id)))] }, getRowKey(rowId)));
64
79
  }) }))] }), !data.length && !loading && _jsx(TableEmptyState, { config: emptyConfig })] }));
@@ -13,7 +13,7 @@
13
13
  font-size: var(--font-size-sm);
14
14
  color: var(--color-fg-default);
15
15
  background: var(--color-bg-surface);
16
- table-layout: auto;
16
+ table-layout: fixed;
17
17
  }
18
18
 
19
19
  .tableScroll {
@@ -42,8 +42,7 @@
42
42
  position: relative;
43
43
 
44
44
  padding-block: var(--spacing-xs);
45
- padding-inline: var(--spacing-md);
46
- padding-right: var(--spacing-lg);
45
+ padding-inline: var(--spacing-sm);
47
46
 
48
47
  text-align: left;
49
48
  vertical-align: middle;
@@ -76,56 +75,62 @@
76
75
  .th > .thInner {
77
76
  display: flex;
78
77
  align-items: center;
79
- gap: var(--spacing-xxs);
80
78
  inline-size: 100%;
81
79
  }
82
80
 
83
- /* Actual interactive control for sorting */
81
+ /* Sort button (full width click target, but content aligns via modifiers) */
84
82
  .thButton {
85
83
  all: unset;
86
- display: inline-flex;
84
+ display: flex;
87
85
  align-items: center;
88
86
  gap: var(--spacing-xxs);
89
87
  inline-size: 100%;
90
-
91
88
  cursor: pointer;
92
89
  user-select: none;
93
-
94
- /* make it feel like a button */
95
90
  border-radius: var(--border-radius-default);
96
91
  padding-block: 2px;
97
92
  padding-inline: 2px;
93
+
94
+ /* default */
95
+ justify-content: flex-start;
98
96
  }
99
97
 
100
- .thButton:hover {
101
- background-color: var(--color-bg-contextual);
98
+ /* IMPORTANT: use consistent PascalCase suffixes */
99
+ .thButtonRight {
100
+ justify-content: flex-end;
102
101
  }
103
102
 
104
- .thButton:focus-visible {
105
- outline: none;
106
- box-shadow: var(--focus-ring);
103
+ .thButtonCenter {
104
+ justify-content: center;
107
105
  }
108
106
 
107
+ /* Label should NOT grow to fill space (it breaks alignment) */
109
108
  .thLabel {
110
109
  overflow: hidden;
111
110
  text-overflow: ellipsis;
112
111
  white-space: nowrap;
113
- flex-grow: 1;
112
+ flex: 0 1 auto;
113
+ min-width: 0;
114
+ }
115
+
116
+ /* Match label text alignment to column alignment */
117
+ .thLabelRight {
118
+ text-align: right;
119
+ }
120
+
121
+ .thLabelCenter {
122
+ text-align: center;
114
123
  }
115
124
 
116
125
  .thExtras {
117
- margin-inline-start: auto;
118
126
  display: inline-flex;
119
127
  align-items: center;
120
128
  }
121
129
 
122
- .inActiveSort {
123
- color: var(--color-fg-subtle);
124
- opacity: 0.4;
125
- }
126
-
127
130
  .sortIndicator {
128
- display: flex;
131
+ display: inline-flex;
132
+ flex: 0 0 auto;
133
+ align-items: center;
129
134
  }
130
135
 
131
136
  .sortIndicator svg {
@@ -187,12 +192,14 @@ th.selectionCell {
187
192
  padding-inline: var(--spacing-xxs);
188
193
  }
189
194
 
195
+ .table.severityTable .tBody tr td:first-child {
196
+ padding-inline-start: var(--spacing-md);
197
+ }
190
198
  .table.severityTable .tBody tr td.selectionCell,
191
199
  .table.severityTable td.selectionCell,
192
200
  .table.severityTable .th.selectionCell,
193
201
  .table.severityTable th.selectionCell {
194
202
  padding-inline: var(--spacing-xxs);
195
- padding-inline-start: 14px;
196
203
  }
197
204
 
198
205
  .table.sm.severityTable .tBody tr td.selectionCell,
@@ -280,14 +287,12 @@ th.selectionCell {
280
287
  .table.severityTable .tBody tr.severity td:first-child::before {
281
288
  content: '';
282
289
  position: absolute;
283
-
284
- left: 4px;
285
- top: 4px;
286
- bottom: 4px;
287
- width: 5px;
288
-
290
+ left: 2px;
291
+ top: 1px;
292
+ bottom: 1px;
293
+ width: 3px;
289
294
  background-color: var(--row-severity-color);
290
- border-radius: 3px;
295
+ border-radius: 1px;
291
296
 
292
297
  z-index: 0;
293
298
  }
@@ -1,39 +1,24 @@
1
1
  import type { ReactNode, JSX } from 'react';
2
2
  export type TableEmptyConfig = {
3
- /**
4
- * Whether to show the empty state at all.
5
- * Default: true
6
- */
7
3
  enabled?: boolean;
8
- /**
9
- * Title + description text (defaults are your current Danish copy)
10
- */
4
+ /** Keep it short + system-like */
11
5
  title?: string;
12
6
  description?: ReactNode;
13
7
  /**
14
- * Optional custom actions area. If provided, it will be rendered first.
15
- * Useful for passing your own buttons/links/etc.
8
+ * Optional custom actions (rendered after built-ins).
9
+ * Good for injecting extra links/buttons from the table page.
16
10
  */
17
11
  actions?: ReactNode;
18
- /**
19
- * Convenience callbacks for built-in default buttons.
20
- * If provided, the corresponding button is shown (unless hidden via showBack/showRefresh).
21
- */
22
12
  onBack?: () => void;
23
13
  onRefresh?: () => void;
24
- /**
25
- * Control default buttons
26
- */
14
+ /** New: clear filters */
15
+ onClearFilters?: () => void;
16
+ showClearFilters?: boolean;
17
+ clearFiltersLabel?: ReactNode;
27
18
  showBack?: boolean;
28
19
  showRefresh?: boolean;
29
- /**
30
- * Override default labels for default buttons
31
- */
32
20
  backLabel?: ReactNode;
33
21
  refreshLabel?: ReactNode;
34
- /**
35
- * Wrapper className for layout styling
36
- */
37
22
  className?: string;
38
23
  };
39
24
  export declare function TableEmptyState({ config }: {
@@ -1,16 +1,19 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ArrowLeft } from 'lucide-react';
3
3
  import { Button } from '../../../../components/button/Button';
4
- import { StatePage } from '../../../../components/state-page/StatePage';
5
4
  const defaultEmptyConfig = {
6
5
  enabled: true,
7
6
  title: 'Ingen resultater',
8
- description: _jsx("span", { children: "Pr\u00F8v at \u00E6ndre dine filtre eller tilf\u00F8je data." }),
9
- showBack: true,
10
- showRefresh: true,
11
- backLabel: (_jsxs(_Fragment, { children: [_jsx(ArrowLeft, {}), "Tilbage"] })),
7
+ description: _jsx("span", { children: "Ingen data at vise." }),
8
+ showBack: false,
9
+ showRefresh: false,
10
+ // default: show button if handler exists
11
+ showClearFilters: true,
12
+ backLabel: (_jsxs(_Fragment, { children: [_jsx(ArrowLeft, { size: 16 }), "Tilbage"] })),
12
13
  refreshLabel: _jsx(_Fragment, { children: "Indl\u00E6s igen" }),
13
- className: 'dbc-flex dbc-flex-column dbc-justify-center dbc-items-center dbc-flex-grow ',
14
+ clearFiltersLabel: _jsx(_Fragment, { children: "Nulstil alle filtre" }),
15
+ // "system-like": less hero, less vertical drama
16
+ className: 'dbc-flex dbc-flex-column dbc-items-start dbc-justify-start dbc-text-left dbc-gap-sm dbc-py-lg',
14
17
  };
15
18
  export function TableEmptyState({ config }) {
16
19
  const merged = {
@@ -21,6 +24,7 @@ export function TableEmptyState({ config }) {
21
24
  return null;
22
25
  const showBack = merged.showBack && typeof merged.onBack === 'function';
23
26
  const showRefresh = merged.showRefresh && typeof merged.onRefresh === 'function';
24
- const hasAnyActions = Boolean(merged.actions) || showBack || showRefresh;
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
+ const showClearFilters = merged.showClearFilters && typeof merged.onClearFilters === 'function';
28
+ const hasAnyActions = Boolean(merged.actions) || showBack || showRefresh || showClearFilters;
29
+ return (_jsxs("div", { className: merged.className, children: [_jsx("div", { className: "dbc-text-sm", children: merged.description }), hasAnyActions && (_jsxs("div", { className: "dbc-flex dbc-gap-sm dbc-mt-xs", children: [showClearFilters && (_jsx(Button, { type: "button", variant: "secondary", onClick: merged.onClearFilters, children: merged.clearFiltersLabel })), showRefresh && (_jsx(Button, { type: "button", variant: "secondary", onClick: merged.onRefresh, children: merged.refreshLabel })), showBack && (_jsx(Button, { type: "button", variant: "secondary", onClick: merged.onBack, children: merged.backLabel })), merged.actions] }))] }));
26
30
  }
@@ -1,16 +1,4 @@
1
- .container {
2
- position: absolute;
3
- inset: 0;
4
- display: flex;
5
- align-items: center;
6
- justify-content: center;
7
- transform: translateY(clamp(-32px, -6vh, -12px));
8
- padding-top: 56px;
9
- box-sizing: border-box;
10
- }
11
-
1
+ .container,
12
2
  .icon {
13
- inline-size: var(--component-size-lg);
14
- block-size: var(--component-size-lg);
15
- color: var(--color-fg-muted);
3
+ display: none;
16
4
  }
@@ -117,7 +117,7 @@ export function Tabs({ header, variant, panelStyle = false, tabs, value, default
117
117
  const selected = index === activeIndex;
118
118
  const tabDomId = `${uid}-tab-${String(tab.id)}`;
119
119
  const panelDomId = `${uid}-panel-${String(tab.id)}`;
120
- return (_jsx("div", { className: `${styles.tab} ${selected ? styles.active : ''}`, children: _jsxs("button", { id: tabDomId, type: "button", className: styles.tabButton, role: "tab", "aria-selected": selected, "aria-controls": panelDomId, tabIndex: selected ? 0 : -1, disabled: tab.disabled, onClick: () => setValue(tab.id), onKeyDown: e => onKeyDownTab(e, index), children: [tab.headerIcon ? _jsx("span", { className: styles.icon, children: tab.headerIcon }) : null, _jsx("span", { className: styles.label, children: tab.header }), tab.badge !== undefined && tab.badge > 0 ? (_jsx("span", { className: styles.badge, children: _jsx(Chip, { severity: "info", size: "sm", children: tab.badge.toLocaleString('da-DK') }) })) : null] }) }, tab.id));
120
+ return (_jsx("div", { className: `${styles.tab} ${selected ? styles.active : ''}`, children: _jsxs("button", { id: tabDomId, type: "button", className: styles.tabButton, role: "tab", "aria-selected": selected, "aria-controls": panelDomId, tabIndex: selected ? 0 : -1, disabled: tab.disabled, onClick: () => setValue(tab.id), onKeyDown: e => onKeyDownTab(e, index), children: [tab.headerIcon ? _jsx("span", { className: styles.icon, children: tab.headerIcon }) : null, _jsx("span", { className: styles.label, children: tab.header }), tab.badge !== undefined && tab.badge > 0 ? (_jsx("span", { className: styles.badge, children: _jsx(Chip, { severity: "neutral", size: "sm", children: tab.badge.toLocaleString('da-DK') }) })) : null] }) }, tab.id));
121
121
  }) }), _jsx("div", { id: activeTab ? `${uid}-panel-${String(activeTab.id)}` : undefined, role: "tabpanel", "aria-labelledby": activeTab ? `${uid}-tab-${String(activeTab.id)}` : undefined, className: styles.tabContent, children: activeTab === null || activeTab === void 0 ? void 0 : activeTab.content })] })] }));
122
122
  }
123
123
  Tabs.Item = TabsItem;
@@ -2,6 +2,7 @@
2
2
  display: flex;
3
3
  flex-direction: column;
4
4
  min-width: 0;
5
+ overflow: auto;
5
6
  }
6
7
 
7
8
  .headerContainer {
@@ -172,7 +173,6 @@
172
173
  }
173
174
 
174
175
  .outlined .tabContent {
175
- /* plain panel by default */
176
176
  background: transparent;
177
177
  padding: 0;
178
178
  }
@@ -7,6 +7,6 @@ import { Headline } from '../headline/Headline';
7
7
  export function Toast({ title, message, severity = 'info', action, onClose, }) {
8
8
  const showHeader = Boolean(title);
9
9
  const showMessage = Boolean(message);
10
- const CloseButton = onClose ? (_jsx(Button, { type: "button", variant: "inline", className: styles.closeButton, "aria-label": "Dismiss notification", onClick: onClose, children: _jsx(X, { className: styles.closeIcon, "aria-hidden": "true" }) })) : null;
10
+ const CloseButton = onClose ? (_jsx(Button, { type: "button", variant: "inline", shape: "round", className: styles.closeButton, "aria-label": "Dismiss notification", onClick: onClose, children: _jsx(X, { className: styles.closeIcon, "aria-hidden": "true" }) })) : null;
11
11
  return (_jsxs("div", { className: `${styles.toast} ${styles[severity]}`, role: "status", children: [_jsxs("div", { className: styles.content, children: [showHeader && (_jsxs("div", { className: styles.row, children: [_jsx(Headline, { size: 4, severity: severity, disableMargin: true, children: title }), CloseButton] })), showMessage && (_jsxs("div", { className: styles.row, children: [_jsx("div", { className: styles.message, children: message }), !showHeader && CloseButton] }))] }), action && (_jsx("div", { className: styles.actions, children: _jsx(Button, { type: "button", variant: "primary", onClick: action.onClick, children: action.label }) }))] }));
12
12
  }
@@ -16,7 +16,7 @@ export declare function useTableSelection<T>({ storageKey, items, getId, initial
16
16
  selectedItems: T[];
17
17
  selectedItemMap: Map<Id, T>;
18
18
  toggleItem: (item: T) => void;
19
- toggleId: (id: Id) => void;
19
+ toggleId: (id: Id, selected?: boolean) => void;
20
20
  clearSelection: () => void;
21
21
  allSelected: boolean;
22
22
  anySelected: boolean;