@dbcdk/react-components 0.0.13 → 0.0.15
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.
- package/dist/components/accordion/Accordion.d.ts +2 -1
- package/dist/components/accordion/Accordion.js +14 -3
- package/dist/components/accordion/Accordion.module.css +1 -1
- package/dist/components/accordion/components/AccordionRow.d.ts +2 -1
- package/dist/components/accordion/components/AccordionRow.js +8 -6
- package/dist/components/accordion/components/AccordionRow.module.css +12 -8
- package/dist/components/button/Button.d.ts +1 -0
- package/dist/components/button/Button.js +3 -3
- package/dist/components/button/Button.module.css +12 -1
- package/dist/components/card/Card.module.css +1 -1
- package/dist/components/chip/Chip.js +11 -1
- package/dist/components/chip/Chip.module.css +92 -30
- package/dist/components/circle/Circle.d.ts +1 -1
- package/dist/components/circle/Circle.module.css +5 -1
- package/dist/components/clear-button/ClearButton.js +1 -1
- package/dist/components/clear-button/ClearButton.module.css +3 -0
- package/dist/components/code-block/CodeBlock.d.ts +7 -3
- package/dist/components/code-block/CodeBlock.js +35 -2
- package/dist/components/code-block/CodeBlock.module.css +49 -2
- package/dist/components/filter-field/FilterField.d.ts +2 -1
- package/dist/components/filter-field/FilterField.js +22 -7
- package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.d.ts +4 -3
- package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.js +3 -2
- package/dist/components/forms/checkbox/Checkbox.d.ts +1 -1
- package/dist/components/forms/checkbox/Checkbox.module.css +11 -0
- package/dist/components/hyperlink/Hyperlink.module.css +0 -1
- package/dist/components/overlay/modal/Modal.js +1 -1
- package/dist/components/overlay/side-panel/SidePanel.js +1 -1
- package/dist/components/page-layout/PageLayout.module.css +0 -2
- package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.js +1 -1
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.js +1 -1
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +0 -4
- package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.d.ts +2 -1
- package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.js +2 -2
- package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.module.css +9 -0
- package/dist/components/split-pane/SplitPane.js +1 -1
- package/dist/components/state-page/StatePage.module.css +1 -1
- package/dist/components/table/Table.js +5 -2
- package/dist/components/table/Table.module.css +19 -16
- package/dist/components/table/components/empty-state/EmptyState.d.ts +7 -22
- package/dist/components/table/components/empty-state/EmptyState.js +12 -8
- package/dist/components/table/components/empty-state/EmptyState.module.css +2 -14
- package/dist/components/tabs/Tabs.js +1 -1
- package/dist/components/tabs/Tabs.module.css +1 -1
- package/dist/components/toast/Toast.js +1 -1
- package/dist/hooks/useTableSelection.d.ts +1 -1
- package/dist/hooks/useTableSelection.js +97 -77
- package/dist/hooks/useViewportFill.js +12 -0
- package/dist/src/styles/styles.css +8 -0
- package/dist/styles/styles.css +8 -0
- package/dist/styles/themes/dbc/base.css +1 -0
- package/package.json +1 -1
|
@@ -68,24 +68,29 @@ function isFilterActive(value) {
|
|
|
68
68
|
return value.trim().length > 0;
|
|
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, width, ...inputProps }) {
|
|
72
73
|
var _a, _b;
|
|
73
|
-
const [selectedOperator, setSelectedOperator] = useState(operator);
|
|
74
74
|
const ops = useMemo(() => operators !== null && operators !== void 0 ? operators : DEFAULT_TEXT_OPERATORS, [operators]);
|
|
75
|
+
// internal operator state (source of truth for UI)
|
|
76
|
+
const [selectedOperator, setSelectedOperator] = useState(operator);
|
|
77
|
+
// "active" based on value only (as requested)
|
|
75
78
|
const active = isFilterActive(value);
|
|
76
|
-
//
|
|
77
|
-
const [localValue, setLocalValue] = useState((_a = value) !== null && _a !== void 0 ? _a : '');
|
|
78
|
-
const debounceRef = useRef(null);
|
|
79
|
-
const isTypingRef = useRef(false);
|
|
79
|
+
// Overwrite internal operator if parent sends a new one
|
|
80
80
|
useEffect(() => {
|
|
81
81
|
if (ops.includes(operator)) {
|
|
82
82
|
setSelectedOperator(operator);
|
|
83
83
|
}
|
|
84
84
|
}, [operator, ops]);
|
|
85
|
+
// Local state ONLY for input control (to avoid URL->props lag)
|
|
86
|
+
const [localValue, setLocalValue] = useState((_a = value) !== null && _a !== void 0 ? _a : '');
|
|
87
|
+
const debounceRef = useRef(null);
|
|
88
|
+
const isTypingRef = useRef(false);
|
|
85
89
|
const emit = (next) => {
|
|
86
90
|
var _a, _b;
|
|
87
91
|
const nextOperator = (_a = next.operator) !== null && _a !== void 0 ? _a : selectedOperator;
|
|
88
92
|
const nextValue = (_b = next.value) !== null && _b !== void 0 ? _b : value;
|
|
93
|
+
// Always keep internal operator in sync when user picks one
|
|
89
94
|
if (next.operator)
|
|
90
95
|
setSelectedOperator(nextOperator);
|
|
91
96
|
onChange({
|
|
@@ -94,6 +99,16 @@ export function FilterField({ field, control, operator, value, onChange, operato
|
|
|
94
99
|
value: nextValue,
|
|
95
100
|
});
|
|
96
101
|
};
|
|
102
|
+
const handleOperatorChange = (op) => {
|
|
103
|
+
setSelectedOperator(op);
|
|
104
|
+
if (!active && !VALUELESS_OPERATORS.includes(op))
|
|
105
|
+
return;
|
|
106
|
+
if (VALUELESS_OPERATORS.includes(op)) {
|
|
107
|
+
emit({ operator: op, value: null });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
emit({ operator: op });
|
|
111
|
+
};
|
|
97
112
|
const scheduleEmitValue = (nextVal) => {
|
|
98
113
|
if (debounceRef.current)
|
|
99
114
|
clearTimeout(debounceRef.current);
|
|
@@ -122,7 +137,7 @@ export function FilterField({ field, control, operator, value, onChange, operato
|
|
|
122
137
|
clearTimeout(debounceRef.current);
|
|
123
138
|
};
|
|
124
139
|
}, []);
|
|
125
|
-
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:
|
|
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 => {
|
|
126
141
|
const next = e.currentTarget.value;
|
|
127
142
|
isTypingRef.current = true;
|
|
128
143
|
setLocalValue(next);
|
|
@@ -13,15 +13,16 @@ 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;
|
|
23
24
|
/**
|
|
24
|
-
*
|
|
25
|
+
* If true, clicking "All" toggles between:
|
|
25
26
|
* - All (no filtering) => []
|
|
26
27
|
* - None (match nothing) => [noneValue]
|
|
27
28
|
*/
|
|
@@ -32,5 +33,5 @@ interface ChipMultiToggleProps {
|
|
|
32
33
|
*/
|
|
33
34
|
noneValue?: string;
|
|
34
35
|
}
|
|
35
|
-
export declare function ChipMultiToggle({ label, options, selectedValues, onChange, onToggle, size, selectedSeverity, unselectedSeverity, fullWidth, disabled, showAllOption, allLabel, allIcon, allTogglesNone, noneValue, }: ChipMultiToggleProps): JSX.Element;
|
|
36
|
+
export declare function ChipMultiToggle({ label, options, selectedValues, onChange, onToggle, size, selectedSeverity, unselectedSeverity, fullWidth, disabled, showAllOption, allLabel, allIcon, showSelectedIcon, allTogglesNone, noneValue, }: ChipMultiToggleProps): JSX.Element;
|
|
36
37
|
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__', }) {
|
|
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;
|
|
@@ -48,6 +49,6 @@ export function ChipMultiToggle({ label, options, selectedValues = [], onChange,
|
|
|
48
49
|
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
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;
|
|
@@ -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",
|
|
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: "
|
|
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}
|
|
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
|
}
|
|
@@ -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
|
}
|
package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.module.css
CHANGED
|
@@ -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 =
|
|
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, }) {
|
|
@@ -28,7 +28,7 @@ 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
|
|
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} ${column.align ? styles[`thButton${column.align}`] : ''}`, onClick: toggleSort, onKeyDown: e => {
|
|
32
32
|
if (shouldToggleOnKey(e.key)) {
|
|
33
33
|
e.preventDefault();
|
|
34
34
|
toggleSort();
|
|
@@ -58,7 +58,10 @@ export function Table({ data, columns, selectedRows, onRowSelect, selectionMode
|
|
|
58
58
|
['--row-severity-color']: rowSeverity
|
|
59
59
|
? SeverityBgColor[rowSeverity]
|
|
60
60
|
: undefined,
|
|
61
|
-
}, className: `${onRowClick ? styles.clickableRow : ''} ${isSelected ? styles.selectedRow : ''} ${rowSeverity ? styles.severity : ''}`, children: [selectedRows && onRowSelect && dataKey && (_jsx("td", { className: `${styles.selectionCell}`,
|
|
61
|
+
}, 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) => {
|
|
62
|
+
e.stopPropagation();
|
|
63
|
+
onRowSelect === null || onRowSelect === void 0 ? void 0 : onRowSelect(rowId, checked);
|
|
64
|
+
} }) })), filteredColumns.map(column => (_jsx("td", { style: getColumnStyle(column.id, columnStyles, column.align, column.verticalAlign), className: `${styles.tableCell} ${shouldAllowWrap(column.allowWrap, isSelected, viewMode)
|
|
62
65
|
? styles.allowWrap
|
|
63
66
|
: styles.nowrap}`, children: getCellDisplayValue(row, column) }, column.id)))] }, getRowKey(rowId)));
|
|
64
67
|
}) }))] }), !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:
|
|
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-
|
|
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,7 +75,6 @@
|
|
|
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
|
|
|
@@ -87,18 +85,24 @@
|
|
|
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
90
|
/* make it feel like a button */
|
|
95
91
|
border-radius: var(--border-radius-default);
|
|
96
92
|
padding-block: 2px;
|
|
97
93
|
padding-inline: 2px;
|
|
98
94
|
}
|
|
99
95
|
|
|
96
|
+
.thButtonright {
|
|
97
|
+
justify-content: flex-end;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.thButtoncenter {
|
|
101
|
+
justify-content: center;
|
|
102
|
+
}
|
|
103
|
+
|
|
100
104
|
.thButton:hover {
|
|
101
|
-
|
|
105
|
+
color: var(--color-fg-default);
|
|
102
106
|
}
|
|
103
107
|
|
|
104
108
|
.thButton:focus-visible {
|
|
@@ -110,7 +114,6 @@
|
|
|
110
114
|
overflow: hidden;
|
|
111
115
|
text-overflow: ellipsis;
|
|
112
116
|
white-space: nowrap;
|
|
113
|
-
flex-grow: 1;
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
.thExtras {
|
|
@@ -187,12 +190,14 @@ th.selectionCell {
|
|
|
187
190
|
padding-inline: var(--spacing-xxs);
|
|
188
191
|
}
|
|
189
192
|
|
|
193
|
+
.table.severityTable .tBody tr td:first-child {
|
|
194
|
+
padding-inline-start: var(--spacing-md);
|
|
195
|
+
}
|
|
190
196
|
.table.severityTable .tBody tr td.selectionCell,
|
|
191
197
|
.table.severityTable td.selectionCell,
|
|
192
198
|
.table.severityTable .th.selectionCell,
|
|
193
199
|
.table.severityTable th.selectionCell {
|
|
194
200
|
padding-inline: var(--spacing-xxs);
|
|
195
|
-
padding-inline-start: 14px;
|
|
196
201
|
}
|
|
197
202
|
|
|
198
203
|
.table.sm.severityTable .tBody tr td.selectionCell,
|
|
@@ -280,14 +285,12 @@ th.selectionCell {
|
|
|
280
285
|
.table.severityTable .tBody tr.severity td:first-child::before {
|
|
281
286
|
content: '';
|
|
282
287
|
position: absolute;
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
width: 5px;
|
|
288
|
-
|
|
288
|
+
left: 2px;
|
|
289
|
+
top: 1px;
|
|
290
|
+
bottom: 1px;
|
|
291
|
+
width: 3px;
|
|
289
292
|
background-color: var(--row-severity-color);
|
|
290
|
-
border-radius:
|
|
293
|
+
border-radius: 1px;
|
|
291
294
|
|
|
292
295
|
z-index: 0;
|
|
293
296
|
}
|
|
@@ -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
|
|
15
|
-
*
|
|
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
|
-
|
|
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: "
|
|
9
|
-
showBack:
|
|
10
|
-
showRefresh:
|
|
11
|
-
|
|
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
|
-
|
|
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
|
|
25
|
-
|
|
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
|
-
|
|
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: "
|
|
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;
|