@dbcdk/react-components 0.0.9 → 0.0.10
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 +27 -0
- package/dist/components/accordion/Accordion.js +66 -0
- package/dist/components/accordion/Accordion.module.css +87 -0
- package/dist/components/button/Button.module.css +1 -0
- package/dist/components/circle/Circle.d.ts +4 -1
- package/dist/components/circle/Circle.js +2 -2
- package/dist/components/circle/Circle.module.css +54 -2
- package/dist/components/datetime-picker/DateTimePicker.d.ts +4 -8
- package/dist/components/datetime-picker/DateTimePicker.js +72 -92
- package/dist/components/datetime-picker/dateTimeHelpers.d.ts +14 -12
- package/dist/components/datetime-picker/dateTimeHelpers.js +25 -45
- package/dist/components/forms/checkbox/Checkbox.d.ts +2 -8
- package/dist/components/forms/checkbox/Checkbox.js +3 -5
- package/dist/components/forms/input/Input.d.ts +1 -0
- package/dist/components/forms/input/Input.js +2 -4
- package/dist/components/forms/input/Input.module.css +9 -11
- package/dist/components/forms/input-container/InputContainer.d.ts +2 -1
- package/dist/components/forms/input-container/InputContainer.js +3 -3
- package/dist/components/forms/input-container/InputContainer.module.css +65 -0
- package/dist/components/forms/radio-buttons/RadioButton.d.ts +36 -0
- package/dist/components/forms/radio-buttons/RadioButton.js +26 -0
- package/dist/components/forms/radio-buttons/RadioButtonGroup.d.ts +25 -0
- package/dist/components/forms/radio-buttons/RadioButtonGroup.js +19 -0
- package/dist/components/forms/radio-buttons/RadioButtons.module.css +117 -0
- package/dist/components/forms/select/Select.d.ts +1 -1
- package/dist/components/forms/select/Select.js +3 -3
- package/dist/components/forms/text-area/Textarea.js +3 -3
- package/dist/components/forms/text-area/Textarea.module.css +8 -1
- package/dist/components/headline/Headline.d.ts +2 -7
- package/dist/components/headline/Headline.js +5 -2
- package/dist/components/headline/Headline.module.css +61 -2
- package/dist/components/hyperlink/Hyperlink.d.ts +1 -0
- package/dist/components/hyperlink/Hyperlink.js +5 -1
- package/dist/components/icon/Icon.module.css +1 -0
- package/dist/components/interval-select/IntervalSelect.js +1 -1
- package/dist/components/nav-bar/NavBar.d.ts +24 -6
- package/dist/components/overlay/side-panel/SidePanel.d.ts +12 -4
- package/dist/components/overlay/side-panel/SidePanel.js +60 -4
- package/dist/components/overlay/side-panel/SidePanel.module.css +151 -28
- package/dist/components/overlay/side-panel/useSidePanel.d.ts +1 -1
- package/dist/components/overlay/side-panel/useSidePanel.js +2 -2
- package/dist/components/page-layout/PageLayout.js +0 -2
- package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.d.ts +5 -5
- package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.js +16 -8
- package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.module.css +0 -3
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.d.ts +3 -1
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.js +4 -3
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +109 -79
- package/dist/components/sidebar/components/sidebar-items/SidebarItems.js +16 -3
- package/dist/components/sidebar/components/sidebar-items/SidebarItems.module.css +20 -0
- package/dist/components/sidebar/providers/SidebarProvider.js +25 -46
- package/dist/components/skeleton-loader/SkeletonLoader.d.ts +1 -1
- package/dist/components/skeleton-loader/SkeletonLoader.js +15 -12
- package/dist/components/state-page/StatePage.d.ts +9 -0
- package/dist/components/state-page/StatePage.js +20 -0
- package/dist/components/state-page/StatePage.module.css +9 -0
- package/dist/components/state-page/empty.d.ts +2 -0
- package/dist/components/state-page/empty.js +2 -0
- package/dist/components/state-page/error.d.ts +2 -0
- package/dist/components/state-page/error.js +2 -0
- package/dist/components/state-page/notFound.d.ts +2 -0
- package/dist/components/state-page/notFound.js +2 -0
- package/dist/components/sticky-footer-layout/StickyFooterLayout.d.ts +19 -0
- package/dist/components/sticky-footer-layout/StickyFooterLayout.js +27 -0
- package/dist/components/table/Table.js +4 -4
- package/dist/components/table/Table.module.css +168 -60
- package/dist/components/table/components/empty-state/EmptyState.d.ts +1 -1
- package/dist/components/table/components/empty-state/EmptyState.js +6 -7
- package/dist/components/toast/Toast.js +5 -1
- package/dist/components/toast/Toast.module.css +40 -15
- package/dist/components/toast/provider/ToastProvider.js +1 -0
- package/dist/hooks/useTimeDuration.js +9 -3
- package/dist/hooks/useViewportFill.js +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -1
- package/dist/src/styles/styles.css +22 -3
- package/dist/styles/styles.css +22 -3
- package/dist/styles/themes/dbc/dark.css +1 -1
- package/dist/styles/themes/dbc/light.css +2 -1
- package/package.json +1 -1
|
@@ -10,13 +10,8 @@ interface HeadlineProps extends React.AriaAttributes {
|
|
|
10
10
|
subHeadline?: string | JSX.Element;
|
|
11
11
|
addition?: React.ReactNode;
|
|
12
12
|
icon?: React.ReactNode;
|
|
13
|
-
|
|
14
|
-
* Optional visual tone override:
|
|
15
|
-
* - dark: force normal foreground colours
|
|
16
|
-
* - light: force on-strong/light text
|
|
17
|
-
* If omitted, the headline simply inherits its colour from its parent.
|
|
18
|
-
*/
|
|
13
|
+
allowWrap?: boolean;
|
|
19
14
|
tone?: HeadlineTone;
|
|
20
15
|
}
|
|
21
|
-
export declare function Headline({ size, marker, disableMargin, children, severity, weight, subHeadline, addition, icon, tone, }: PropsWithChildren<HeadlineProps>): React.ReactNode;
|
|
16
|
+
export declare function Headline({ size, marker, disableMargin, children, severity, weight, subHeadline, addition, icon, tone, allowWrap, }: PropsWithChildren<HeadlineProps>): React.ReactNode;
|
|
22
17
|
export {};
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import styles from './Headline.module.css';
|
|
4
4
|
import { SeverityBgColor } from '../../constants/severity';
|
|
5
5
|
import { Icon } from '../icon/Icon';
|
|
6
|
-
export function Headline({ size = 2, marker, disableMargin, children, severity, weight = 600, subHeadline, addition, icon, tone, }) {
|
|
6
|
+
export function Headline({ size = 2, marker, disableMargin, children, severity, weight = 600, subHeadline, addition, icon, tone, allowWrap = true, }) {
|
|
7
7
|
const Tag = `h${size}`;
|
|
8
8
|
const containerClassName = [styles.headlineContainer, tone ? styles[`tone-${tone}`] : '']
|
|
9
9
|
.filter(Boolean)
|
|
@@ -15,8 +15,11 @@ export function Headline({ size = 2, marker, disableMargin, children, severity,
|
|
|
15
15
|
]
|
|
16
16
|
.filter(Boolean)
|
|
17
17
|
.join(' ');
|
|
18
|
+
const textClassName = [styles.text, allowWrap ? styles.wrap : styles.truncate]
|
|
19
|
+
.filter(Boolean)
|
|
20
|
+
.join(' ');
|
|
18
21
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: containerClassName, children: [_jsxs(Tag, { style: {
|
|
19
22
|
'--font-weight': weight,
|
|
20
23
|
'--marker-color': severity ? SeverityBgColor[severity] : undefined,
|
|
21
|
-
}, className: headlineClassName, children: [icon || (severity && !marker) ? _jsx(Icon, { customIcon: icon, severity: severity }) : null, children] }), addition] }), subHeadline && _jsx("div", { className: styles.subHeadline, children: subHeadline })] }));
|
|
24
|
+
}, className: headlineClassName, children: [icon || (severity && !marker) ? (_jsx("span", { className: styles.icon, children: _jsx(Icon, { customIcon: icon, severity: severity }) })) : null, _jsx("span", { className: textClassName, children: children })] }), addition] }), subHeadline && _jsx("div", { className: styles.subHeadline, children: subHeadline })] }));
|
|
22
25
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
align-items: center;
|
|
4
4
|
gap: var(--spacing-lg);
|
|
5
5
|
flex-wrap: wrap;
|
|
6
|
+
max-width: 100%;
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
/* Base headline: inherit colour from parent surface */
|
|
@@ -18,8 +19,6 @@
|
|
|
18
19
|
transition: color var(--transition-fast) var(--ease-standard);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
/* Optional tone overrides for special cases (hero sections, etc.) */
|
|
22
|
-
|
|
23
22
|
.tone-dark .headline {
|
|
24
23
|
color: var(--color-fg-default);
|
|
25
24
|
}
|
|
@@ -58,3 +57,63 @@
|
|
|
58
57
|
margin-block-start: calc(var(--spacing-2xs) * -1);
|
|
59
58
|
line-height: var(--line-height-normal);
|
|
60
59
|
}
|
|
60
|
+
|
|
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
|
+
|
|
98
|
+
.icon {
|
|
99
|
+
flex: 0 0 auto;
|
|
100
|
+
display: inline-flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* text wrapper must be the shrinkable element */
|
|
105
|
+
.text {
|
|
106
|
+
min-width: 0;
|
|
107
|
+
flex: 1 1 auto;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.truncate {
|
|
111
|
+
overflow: hidden;
|
|
112
|
+
white-space: nowrap;
|
|
113
|
+
text-overflow: ellipsis;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.wrap {
|
|
117
|
+
white-space: normal;
|
|
118
|
+
overflow: visible;
|
|
119
|
+
}
|
|
@@ -6,6 +6,10 @@ export function Hyperlink({ component, icon }) {
|
|
|
6
6
|
return React.cloneElement(component, {
|
|
7
7
|
...originalProps,
|
|
8
8
|
className: styles.link,
|
|
9
|
-
onClick: (e) =>
|
|
9
|
+
onClick: (e) => {
|
|
10
|
+
var _a;
|
|
11
|
+
e.stopPropagation();
|
|
12
|
+
(_a = originalProps === null || originalProps === void 0 ? void 0 : originalProps.onClick) === null || _a === void 0 ? void 0 : _a.call(originalProps, e);
|
|
13
|
+
},
|
|
10
14
|
}, _jsxs(_Fragment, { children: [_jsx("span", { className: styles.content, children: originalProps.children }), icon && _jsx("span", { className: styles.icon, children: icon })] }));
|
|
11
15
|
}
|
|
@@ -10,7 +10,7 @@ import { useTooltipTrigger } from '../../components/overlay/tooltip/useTooltipTr
|
|
|
10
10
|
import { Popover } from '../../components/popover/Popover';
|
|
11
11
|
export function IntervalSelect({
|
|
12
12
|
// InputContainer props
|
|
13
|
-
label, error, helpText, orientation = 'vertical', labelWidth = '
|
|
13
|
+
label, error, helpText, orientation = 'vertical', labelWidth = '160px', fullWidth = true, required,
|
|
14
14
|
// tooltip
|
|
15
15
|
tooltip, tooltipPlacement = 'right',
|
|
16
16
|
// IntervalSelect props
|
|
@@ -1,18 +1,36 @@
|
|
|
1
1
|
import type { ElementType, ReactNode, JSX } from 'react';
|
|
2
|
-
export type NavBarItem =
|
|
3
|
-
|
|
2
|
+
export type NavBarItem = NavBarLinkItem | NavBarExpandableItem | NavBarGroupItem;
|
|
3
|
+
type NavBarBase = {
|
|
4
4
|
label: string;
|
|
5
5
|
icon?: ReactNode;
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
tags?: string[];
|
|
8
|
+
};
|
|
9
|
+
/** Simple clickable item */
|
|
10
|
+
export type NavBarLinkItem = NavBarBase & {
|
|
11
|
+
type?: 'item';
|
|
12
|
+
href: string;
|
|
13
|
+
component?: ElementType<any>;
|
|
6
14
|
active?: boolean;
|
|
7
15
|
external?: boolean;
|
|
8
|
-
|
|
16
|
+
};
|
|
17
|
+
/** Clickable + expandable item (has href AND children) */
|
|
18
|
+
export type NavBarExpandableItem = NavBarBase & {
|
|
19
|
+
type: 'expandable';
|
|
9
20
|
href: string;
|
|
10
|
-
|
|
11
|
-
|
|
21
|
+
component?: ElementType<any>;
|
|
22
|
+
active?: boolean;
|
|
23
|
+
external?: boolean;
|
|
24
|
+
children: NavBarItem[];
|
|
25
|
+
};
|
|
26
|
+
/** Non-clickable group header */
|
|
27
|
+
export type NavBarGroupItem = NavBarBase & {
|
|
28
|
+
type: 'group';
|
|
29
|
+
children: NavBarItem[];
|
|
12
30
|
};
|
|
13
31
|
interface NavBarProps {
|
|
14
32
|
logo?: ReactNode;
|
|
15
|
-
items:
|
|
33
|
+
items: NavBarLinkItem[];
|
|
16
34
|
productName?: string;
|
|
17
35
|
addition?: ReactNode;
|
|
18
36
|
}
|
|
@@ -1,16 +1,24 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React, { type HTMLAttributes, type ReactNode } from 'react';
|
|
2
2
|
import { Severity } from '../../../constants/severity.types';
|
|
3
3
|
interface SidePanelProps {
|
|
4
4
|
children?: ReactNode;
|
|
5
5
|
header: ReactNode;
|
|
6
6
|
headerAddition?: ReactNode;
|
|
7
|
-
actions
|
|
8
|
-
onClose: () => void;
|
|
7
|
+
actions?: ReactNode;
|
|
8
|
+
onClose: (event?: React.MouseEvent<HTMLButtonElement> | React.MouseEvent<HTMLDivElement>) => void;
|
|
9
9
|
isOpen: boolean;
|
|
10
10
|
showBackdrop?: boolean;
|
|
11
11
|
severity?: Severity;
|
|
12
12
|
showHeaderMarker?: boolean;
|
|
13
13
|
width?: number | string;
|
|
14
|
+
/**
|
|
15
|
+
* Optional details pane (separate column).
|
|
16
|
+
*/
|
|
17
|
+
details?: ReactNode;
|
|
18
|
+
detailsHeader?: ReactNode;
|
|
19
|
+
detailsWidth?: number | string;
|
|
20
|
+
onCloseDetails?: () => void;
|
|
21
|
+
detailsHeaderAddition?: ReactNode;
|
|
14
22
|
}
|
|
15
|
-
export declare function SidePanel({ isOpen, onClose, children, header, headerAddition, actions, showBackdrop, severity, showHeaderMarker, width, ...props }: SidePanelProps & HTMLAttributes<HTMLElement>): ReactNode;
|
|
23
|
+
export declare function SidePanel({ isOpen, onClose, children, header, headerAddition, actions, showBackdrop, severity, showHeaderMarker, width, details, detailsHeader, detailsWidth, onCloseDetails, detailsHeaderAddition, ...props }: SidePanelProps & HTMLAttributes<HTMLElement>): ReactNode;
|
|
16
24
|
export {};
|
|
@@ -1,10 +1,66 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { X } from 'lucide-react';
|
|
3
|
+
import { useEffect, useRef, useState, } from 'react';
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
3
5
|
import { Button } from '../../../components/button/Button';
|
|
4
6
|
import { Headline } from '../../../components/headline/Headline';
|
|
5
7
|
import styles from './SidePanel.module.css';
|
|
6
|
-
export function SidePanel({ isOpen, onClose, children, header, headerAddition, actions, showBackdrop = true, severity, showHeaderMarker = true, width = '
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
export function SidePanel({ isOpen, onClose, children, header, headerAddition, actions, showBackdrop = true, severity, showHeaderMarker = true, width = '400px', details, detailsHeader = 'Output', detailsWidth = '420px', onCloseDetails, detailsHeaderAddition, ...props }) {
|
|
9
|
+
const [mounted, setMounted] = useState(false);
|
|
10
|
+
const [shouldRender, setShouldRender] = useState(isOpen);
|
|
11
|
+
const [isActive, setIsActive] = useState(false);
|
|
12
|
+
const panelRef = useRef(null);
|
|
13
|
+
useEffect(() => setMounted(true), []);
|
|
14
|
+
// OPEN: ensure rendered so animation can play
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (isOpen)
|
|
17
|
+
setShouldRender(true);
|
|
18
|
+
}, [isOpen]);
|
|
19
|
+
// Two-phase OPEN/CLOSE class toggle (lets CSS transitions kick in reliably)
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!shouldRender)
|
|
22
|
+
return;
|
|
23
|
+
if (!isOpen) {
|
|
24
|
+
setIsActive(false);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const raf = requestAnimationFrame(() => setIsActive(true));
|
|
28
|
+
return () => cancelAnimationFrame(raf);
|
|
29
|
+
}, [isOpen, shouldRender]);
|
|
30
|
+
// When closing: wait for transform transition end to unmount.
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!shouldRender)
|
|
33
|
+
return;
|
|
34
|
+
const el = panelRef.current;
|
|
35
|
+
if (!el)
|
|
36
|
+
return;
|
|
37
|
+
const onTransitionEnd = (e) => {
|
|
38
|
+
if (e.target !== el)
|
|
39
|
+
return;
|
|
40
|
+
if (e.propertyName !== 'transform')
|
|
41
|
+
return;
|
|
42
|
+
if (!isOpen)
|
|
43
|
+
setShouldRender(false);
|
|
44
|
+
};
|
|
45
|
+
el.addEventListener('transitionend', onTransitionEnd);
|
|
46
|
+
return () => el.removeEventListener('transitionend', onTransitionEnd);
|
|
47
|
+
}, [isOpen, shouldRender]);
|
|
48
|
+
if (!mounted)
|
|
49
|
+
return null;
|
|
50
|
+
if (!shouldRender)
|
|
51
|
+
return null;
|
|
52
|
+
const hasDetails = Boolean(details);
|
|
53
|
+
return createPortal(_jsxs(_Fragment, { children: [showBackdrop && (_jsx("div", { className: `${styles.backdrop} ${isActive ? styles.backdropOpen : ''}`, onClick: e => {
|
|
54
|
+
e.stopPropagation();
|
|
55
|
+
onClose(e);
|
|
56
|
+
} })), _jsxs("div", { ref: panelRef, ...props, className: `${styles.sidePanel} ${isActive ? styles.open : ''} ${hasDetails ? styles.withDetails : styles.noDetails}`, style: {
|
|
57
|
+
'--side-panel-width': width,
|
|
58
|
+
'--details-width': detailsWidth,
|
|
59
|
+
}, "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 => {
|
|
60
|
+
e.stopPropagation();
|
|
61
|
+
onCloseDetails();
|
|
62
|
+
}, 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 => {
|
|
63
|
+
e.stopPropagation();
|
|
64
|
+
onClose(e);
|
|
65
|
+
}, "aria-label": "Close panel", 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);
|
|
10
66
|
}
|
|
@@ -1,56 +1,179 @@
|
|
|
1
1
|
.sidePanel {
|
|
2
|
-
|
|
2
|
+
--col-pad: var(--spacing-md);
|
|
3
|
+
|
|
4
|
+
/* Dial these for “feel” */
|
|
5
|
+
--panel-dur: 220ms;
|
|
6
|
+
--panel-ease: cubic-bezier(0.22, 1, 0.36, 1); /* smooth spring-ish without overshoot */
|
|
7
|
+
|
|
8
|
+
/* Shadow + edge */
|
|
9
|
+
--shadow-opacity: 0.22;
|
|
10
|
+
--edge-opacity: 0.12;
|
|
11
|
+
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
padding: 0;
|
|
3
14
|
height: 100vh;
|
|
4
|
-
|
|
15
|
+
|
|
5
16
|
position: fixed;
|
|
6
17
|
right: 0;
|
|
7
18
|
top: 0;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
19
|
+
|
|
20
|
+
background-color: var(--color-bg-surface);
|
|
21
|
+
z-index: var(--z-drawer);
|
|
22
|
+
|
|
23
|
+
transform: translate3d(100%, 0, 0);
|
|
24
|
+
transition: transform var(--panel-dur) var(--panel-ease);
|
|
25
|
+
will-change: transform;
|
|
26
|
+
|
|
27
|
+
display: grid;
|
|
28
|
+
gap: var(--spacing-md);
|
|
29
|
+
align-items: stretch;
|
|
30
|
+
|
|
31
|
+
overflow: hidden;
|
|
32
|
+
pointer-events: auto;
|
|
33
|
+
|
|
34
|
+
/* Make pseudo-elements layer properly */
|
|
35
|
+
isolation: isolate;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* “Lift” layer: drop-shadow + edge highlight (both fade in) */
|
|
39
|
+
.sidePanel::before {
|
|
40
|
+
content: '';
|
|
41
|
+
position: absolute;
|
|
42
|
+
inset: 0;
|
|
43
|
+
pointer-events: none;
|
|
44
|
+
z-index: -1; /* behind content but within isolated stacking context */
|
|
45
|
+
|
|
46
|
+
/* 1) drop-shadow = smoother than animating box-shadow
|
|
47
|
+
2) edge gradient = premium depth cue */
|
|
48
|
+
filter: drop-shadow(0 16px 32px rgba(0, 0, 0, var(--shadow-opacity)));
|
|
49
|
+
opacity: 0;
|
|
50
|
+
|
|
51
|
+
/* Edge highlight from the left edge (panel’s leading edge) */
|
|
52
|
+
background: linear-gradient(
|
|
53
|
+
90deg,
|
|
54
|
+
rgba(255, 255, 255, var(--edge-opacity)),
|
|
55
|
+
rgba(255, 255, 255, 0) 36%
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
transition: opacity var(--panel-dur) var(--panel-ease);
|
|
59
|
+
|
|
60
|
+
will-change: opacity;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Optional: super subtle “sheen” right as it settles (feels snappy) */
|
|
64
|
+
.sidePanel.open::before {
|
|
65
|
+
opacity: 1;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.sidePanel.open {
|
|
69
|
+
transform: translate3d(0, 0, 0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* MAIN TRACK WIDTH = content + padding*2 */
|
|
73
|
+
.sidePanel.noDetails {
|
|
74
|
+
grid-template-columns: calc(var(--side-panel-width, 400px) + (var(--col-pad) * 2));
|
|
75
|
+
width: calc(var(--side-panel-width, 400px) + (var(--col-pad) * 2));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* when details show, main track stays the same; panel grows */
|
|
79
|
+
.sidePanel.withDetails {
|
|
80
|
+
grid-template-columns:
|
|
81
|
+
calc(var(--side-panel-width, 400px) + (var(--col-pad) * 2))
|
|
82
|
+
calc(var(--details-width, 420px) + (var(--col-pad) * 2));
|
|
83
|
+
|
|
84
|
+
width: calc(
|
|
85
|
+
(var(--side-panel-width, 400px) + (var(--col-pad) * 2)) +
|
|
86
|
+
(var(--details-width, 420px) + (var(--col-pad) * 2)) + var(--spacing-md)
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* columns get the padding */
|
|
91
|
+
.mainCol,
|
|
92
|
+
.detailsCol {
|
|
93
|
+
box-sizing: border-box;
|
|
94
|
+
padding: 0 var(--col-pad);
|
|
95
|
+
|
|
96
|
+
min-width: 0;
|
|
97
|
+
min-height: 0;
|
|
98
|
+
|
|
11
99
|
display: flex;
|
|
12
100
|
flex-direction: column;
|
|
13
101
|
gap: var(--spacing-md);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
background-color: var(--color-bg-surface);
|
|
19
|
-
max-height: 100vh;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.header {
|
|
105
|
+
padding: var(--spacing-md) 0;
|
|
20
106
|
}
|
|
21
107
|
|
|
22
108
|
.content {
|
|
23
109
|
flex: 1;
|
|
110
|
+
min-height: 0;
|
|
111
|
+
overflow: auto;
|
|
24
112
|
font-size: var(--font-size-sm);
|
|
25
113
|
}
|
|
26
114
|
|
|
27
|
-
.header {
|
|
28
|
-
position: sticky;
|
|
29
|
-
top: 0;
|
|
30
|
-
padding: var(--spacing-md) 0;
|
|
31
|
-
background: inherit;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
115
|
.actions {
|
|
35
|
-
position: sticky;
|
|
36
116
|
padding: var(--spacing-md) 0;
|
|
37
|
-
background: inherit;
|
|
38
|
-
bottom: 0;
|
|
39
117
|
display: flex;
|
|
40
118
|
justify-content: flex-end;
|
|
41
119
|
gap: var(--spacing-sm);
|
|
120
|
+
min-width: 0;
|
|
42
121
|
}
|
|
43
122
|
|
|
44
|
-
|
|
45
|
-
|
|
123
|
+
/* details styling */
|
|
124
|
+
.detailsCol {
|
|
125
|
+
border: 1px solid var(--color-border-subtle);
|
|
126
|
+
border-radius: var(--radius-md);
|
|
127
|
+
overflow: hidden;
|
|
128
|
+
background: var(--color-bg-surface);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.detailsHeader {
|
|
132
|
+
padding: var(--spacing-sm) 0;
|
|
133
|
+
border-bottom: 1px solid var(--color-border-subtle);
|
|
134
|
+
display: flex;
|
|
135
|
+
align-items: center;
|
|
136
|
+
justify-content: space-between;
|
|
137
|
+
gap: var(--spacing-sm);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.detailsTitle {
|
|
141
|
+
font-weight: 600;
|
|
46
142
|
}
|
|
47
143
|
|
|
144
|
+
.detailsHeaderActions {
|
|
145
|
+
display: flex;
|
|
146
|
+
align-items: center;
|
|
147
|
+
gap: var(--spacing-sm);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.detailsContent {
|
|
151
|
+
padding: var(--spacing-md) 0;
|
|
152
|
+
overflow: auto;
|
|
153
|
+
min-height: 0;
|
|
154
|
+
flex: 1 1 auto;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* Backdrop with a nice fade */
|
|
48
158
|
.backdrop {
|
|
49
159
|
position: fixed;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
width: 100%;
|
|
53
|
-
height: 100%;
|
|
54
|
-
background: var(--overlay-scrim);
|
|
160
|
+
inset: 0;
|
|
161
|
+
background-color: rgba(0, 0, 0, 0.45);
|
|
55
162
|
z-index: var(--z-backdrop);
|
|
163
|
+
|
|
164
|
+
opacity: 0;
|
|
165
|
+
transition: opacity var(--panel-dur) var(--panel-ease);
|
|
166
|
+
will-change: opacity;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.backdropOpen {
|
|
170
|
+
opacity: 1;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@media (prefers-reduced-motion: reduce) {
|
|
174
|
+
.sidePanel,
|
|
175
|
+
.sidePanel::before,
|
|
176
|
+
.backdrop {
|
|
177
|
+
transition: none !important;
|
|
178
|
+
}
|
|
56
179
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
|
-
export function useSidePanel() {
|
|
3
|
-
const [isOpen, setIsOpen] = useState(
|
|
2
|
+
export function useSidePanel(initialOpen = false) {
|
|
3
|
+
const [isOpen, setIsOpen] = useState(initialOpen);
|
|
4
4
|
const openSidePanel = () => setIsOpen(true);
|
|
5
5
|
const closeSidePanel = () => setIsOpen(false);
|
|
6
6
|
return {
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { PageLayoutHero } from './components/page-layout-hero/PageLayoutHero';
|
|
4
4
|
import styles from './PageLayout.module.css';
|
|
5
|
-
/** Base component */
|
|
6
5
|
const PageLayoutBase = ({ children, sidebar, header, containScrolling = false, orientation = 'vertical', }) => {
|
|
7
6
|
if (orientation === 'vertical') {
|
|
8
7
|
return (_jsx("div", { className: `${styles.container} ${styles.vertical} ${containScrolling ? styles.containScrolling : ''}`, children: _jsxs("div", { style: { flex: 1, display: 'flex', height: '100%', maxWidth: '100%' }, children: [sidebar, _jsxs("div", { style: {
|
|
@@ -17,7 +16,6 @@ const PageLayoutBase = ({ children, sidebar, header, containScrolling = false, o
|
|
|
17
16
|
display: 'flex',
|
|
18
17
|
flexDirection: 'column',
|
|
19
18
|
padding: 'var(--spacing-md)',
|
|
20
|
-
gap: 'var(--spacing-md)',
|
|
21
19
|
backgroundColor: 'var(--color-bg-surface)',
|
|
22
20
|
overflow: 'auto',
|
|
23
21
|
}, children: children })] })] }) }));
|
package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { NavBarItem } from '../../../../components/nav-bar/NavBar';
|
|
3
|
-
|
|
2
|
+
import type { NavBarItem } from '../../../../components/nav-bar/NavBar';
|
|
3
|
+
type ExpandableSidebarItemProps = {
|
|
4
4
|
items: NavBarItem[];
|
|
5
5
|
label: string;
|
|
6
|
-
component
|
|
6
|
+
component: React.ElementType;
|
|
7
7
|
icon: React.ReactNode;
|
|
8
|
-
href
|
|
9
|
-
}
|
|
8
|
+
href: string;
|
|
9
|
+
};
|
|
10
10
|
export declare function ExpandableSidebarItem({ items, label, icon, component: Component, href, }: ExpandableSidebarItemProps): React.ReactNode;
|
|
11
11
|
export {};
|
|
@@ -7,6 +7,9 @@ import { Button } from '../../../button/Button';
|
|
|
7
7
|
import { useSidebar } from '../../providers/SidebarProvider';
|
|
8
8
|
import { SidebarItemContent } from '../sidebar-item-content/SidebarItemContent';
|
|
9
9
|
import { SidebarItem } from '../SidebarItem';
|
|
10
|
+
import { ExpandableSidebarItem as ExpandableChild } from '../expandable-sidebar-item/ExpandableSidebarItem';
|
|
11
|
+
const isGroup = (item) => item.type === 'group';
|
|
12
|
+
const isExpandable = (item) => item.type === 'expandable';
|
|
10
13
|
export function ExpandableSidebarItem({ items, label, icon, component: Component, href, }) {
|
|
11
14
|
const { defaultExpanded, resetExpandAll, isSidebarCollapsed, handleSidebarCollapseChange, expandedItems, } = useSidebar();
|
|
12
15
|
const [expanded, setExpanded] = useState(false);
|
|
@@ -21,9 +24,8 @@ export function ExpandableSidebarItem({ items, label, icon, component: Component
|
|
|
21
24
|
}
|
|
22
25
|
}, [expandedItems, href]);
|
|
23
26
|
useEffect(() => {
|
|
24
|
-
if (defaultExpanded === null)
|
|
27
|
+
if (defaultExpanded === null)
|
|
25
28
|
return;
|
|
26
|
-
}
|
|
27
29
|
setExpanded(defaultExpanded);
|
|
28
30
|
}, [defaultExpanded]);
|
|
29
31
|
const handleAnimationEnd = useCallback(() => {
|
|
@@ -45,10 +47,16 @@ export function ExpandableSidebarItem({ items, label, icon, component: Component
|
|
|
45
47
|
setClosing(true);
|
|
46
48
|
}
|
|
47
49
|
}, [expanded, handleSidebarCollapseChange, isSidebarCollapsed, resetExpandAll]);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
const renderNavItem = (item, key) => {
|
|
51
|
+
var _a, _b;
|
|
52
|
+
if (isGroup(item)) {
|
|
53
|
+
return (_jsxs("div", { className: styles.group, children: [_jsx("div", { className: styles.groupLabel, children: item.label }), (_a = item.children) === null || _a === void 0 ? void 0 : _a.map((child, idx) => renderNavItem(child, `${key}-${idx}`))] }, key));
|
|
54
|
+
}
|
|
55
|
+
if (isExpandable(item)) {
|
|
56
|
+
return (_jsx(ExpandableChild, { items: (_b = item.children) !== null && _b !== void 0 ? _b : [], label: item.label, icon: item.icon, href: item.href, component: item.component }, key));
|
|
57
|
+
}
|
|
58
|
+
// Default item (type 'item' or undefined)
|
|
59
|
+
return (_jsx(SidebarItem, { component: item.component, label: item.label, icon: item.icon, href: item.href }, key));
|
|
60
|
+
};
|
|
61
|
+
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}`)) }))] }));
|
|
54
62
|
}
|
|
@@ -5,5 +5,7 @@ interface SidebarContainerProps {
|
|
|
5
5
|
productLogo?: ReactNode;
|
|
6
6
|
activeLink?: string;
|
|
7
7
|
}
|
|
8
|
-
export declare function SidebarContainer({ logo,
|
|
8
|
+
export declare function SidebarContainer({ logo, // DBC Digital (company)
|
|
9
|
+
productLogo, // DataIO (product)
|
|
10
|
+
activeLink, }: SidebarContainerProps): JSX.Element;
|
|
9
11
|
export {};
|
|
@@ -2,12 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { ChevronLeft } from 'lucide-react';
|
|
3
3
|
import { Logo } from '../../../../assets/logo';
|
|
4
4
|
import { Button } from '../../../../components/button/Button';
|
|
5
|
-
import { Headline } from '../../../../components/headline/Headline';
|
|
6
5
|
import { SidebarItems } from '../../../../components/sidebar/components/sidebar-items/SidebarItems';
|
|
7
6
|
import SidenavFiltering from '../../../../components/sidebar/components/sidenav-filteirng/SidenavFiltering';
|
|
8
7
|
import { useSidebar } from '../../../../components/sidebar/providers/SidebarProvider';
|
|
9
8
|
import styles from './SidebarContainer.module.css';
|
|
10
|
-
export function SidebarContainer({ logo,
|
|
9
|
+
export function SidebarContainer({ logo, // DBC Digital (company)
|
|
10
|
+
productLogo, // DataIO (product)
|
|
11
|
+
activeLink, }) {
|
|
11
12
|
const { isSidebarCollapsed, handleSidebarCollapseChange } = useSidebar();
|
|
12
|
-
return (_jsxs("div", { className: `${styles.container} ${isSidebarCollapsed ? styles.collapsed : ''}`, children: [
|
|
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
14
|
}
|