@dbcdk/react-components 0.0.66 → 0.0.67
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/button-select/ButtonSelect.d.ts +16 -0
- package/dist/components/button-select/ButtonSelect.js +7 -0
- package/dist/components/button-select/ButtonSelect.module.css +40 -0
- package/dist/components/nav-bar/NavBar.d.ts +1 -0
- package/dist/components/overlay/tooltip/Tooltip.module.css +0 -2
- package/dist/components/sidebar/Sidebar.d.ts +6 -1
- package/dist/components/sidebar/Sidebar.js +2 -3
- package/dist/components/sidebar/components/SidebarItem.d.ts +2 -1
- package/dist/components/sidebar/components/SidebarItem.js +2 -2
- package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.d.ts +2 -1
- package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.js +3 -3
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.d.ts +7 -2
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.js +132 -2
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +67 -5
- package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.d.ts +2 -1
- package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.js +3 -2
- package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.module.css +11 -0
- package/dist/components/sidebar/components/sidebar-items/SidebarItems.js +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +2 -6
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { ButtonSize } from '../../components/button/Button';
|
|
4
|
+
export interface ButtonSelectOption<T extends string = string> {
|
|
5
|
+
value: T;
|
|
6
|
+
label: React.ReactNode;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface ButtonSelectProps<T extends string = string> {
|
|
10
|
+
options: ButtonSelectOption<T>[];
|
|
11
|
+
value: T;
|
|
12
|
+
onChange: (value: T) => void;
|
|
13
|
+
size?: ButtonSize;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function ButtonSelect<T extends string>({ options, value, onChange, size, disabled, }: ButtonSelectProps<T>): JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Button } from '../../components/button/Button';
|
|
4
|
+
import styles from './ButtonSelect.module.css';
|
|
5
|
+
export function ButtonSelect({ options, value, onChange, size = 'md', disabled, }) {
|
|
6
|
+
return (_jsx("div", { className: styles.group, role: "group", children: options.map(option => (_jsx(Button, { className: option.value === value ? `${styles.btn} ${styles.btnSelected}` : styles.btn, variant: "outlined", shape: "default", size: size, active: option.value === value, disabled: disabled || option.disabled, onClick: () => onChange(option.value), children: option.label }, option.value))) }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
.group {
|
|
2
|
+
display: inline-flex;
|
|
3
|
+
flex-wrap: nowrap;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/* Collapse the doubled border between adjacent buttons */
|
|
7
|
+
.group .btn:not(:first-child) {
|
|
8
|
+
margin-left: -1px;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Flatten interior corners */
|
|
12
|
+
.group .btn:not(:first-child) {
|
|
13
|
+
border-top-left-radius: 0;
|
|
14
|
+
border-bottom-left-radius: 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.group .btn:not(:last-child) {
|
|
18
|
+
border-top-right-radius: 0;
|
|
19
|
+
border-bottom-right-radius: 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Raise hovered / focused / selected button so its full border is visible above siblings */
|
|
23
|
+
.group .btn:hover,
|
|
24
|
+
.group .btn:focus-visible,
|
|
25
|
+
.group .btn:active,
|
|
26
|
+
.group .btnSelected {
|
|
27
|
+
position: relative;
|
|
28
|
+
z-index: 1;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Selected state follows the system's selection language (same as Chip) */
|
|
32
|
+
.group .btnSelected {
|
|
33
|
+
background-color: var(--color-bg-selected);
|
|
34
|
+
color: var(--color-brand);
|
|
35
|
+
border-color: var(--color-border-selected);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.group .btnSelected:hover {
|
|
39
|
+
background-color: var(--color-bg-selected);
|
|
40
|
+
}
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
position: fixed;
|
|
22
22
|
z-index: var(--z-tooltip);
|
|
23
23
|
pointer-events: none;
|
|
24
|
-
|
|
25
24
|
background: var(--color-fg-default);
|
|
26
25
|
color: var(--color-fg-on-strong);
|
|
27
26
|
font-size: var(--font-size-xs);
|
|
@@ -30,7 +29,6 @@
|
|
|
30
29
|
border-radius: var(--border-radius-default);
|
|
31
30
|
|
|
32
31
|
/*
|
|
33
|
-
✅ Width behavior:
|
|
34
32
|
- Don't let it run wild horizontally
|
|
35
33
|
- But don't clip: allow wrap
|
|
36
34
|
- Keep some relation to viewport
|
|
@@ -11,6 +11,11 @@ interface SidebarProps {
|
|
|
11
11
|
activeLink?: string;
|
|
12
12
|
version?: string | number;
|
|
13
13
|
hideSearch?: boolean;
|
|
14
|
+
footer?: React.ReactNode;
|
|
15
|
+
resizable?: boolean;
|
|
16
|
+
defaultWidth?: number;
|
|
17
|
+
minWidth?: number;
|
|
18
|
+
storageKey?: string;
|
|
14
19
|
}
|
|
15
|
-
export declare function Sidebar({ items, productLogo, activeLink, version, hideSearch, }: SidebarProps): JSX.Element;
|
|
20
|
+
export declare function Sidebar({ items, productLogo, activeLink, version, hideSearch, footer, resizable, defaultWidth, minWidth, storageKey, }: SidebarProps): JSX.Element;
|
|
16
21
|
export {};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
'use client';
|
|
2
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
2
|
import { SidebarContainer } from './components/sidebar-container/SidebarContainer';
|
|
4
3
|
import { SidebarProvider } from './providers/SidebarProvider';
|
|
5
|
-
export function Sidebar({ items, productLogo, activeLink, version, hideSearch, }) {
|
|
6
|
-
return (_jsx(SidebarProvider, { items: items, children: _jsx(SidebarContainer, { productLogo: productLogo, activeLink: activeLink, version: version, hideSearch: hideSearch }) }));
|
|
4
|
+
export function Sidebar({ items, productLogo, activeLink, version, hideSearch, footer, resizable, defaultWidth, minWidth, storageKey, }) {
|
|
5
|
+
return (_jsx(SidebarProvider, { items: items, children: _jsx(SidebarContainer, { productLogo: productLogo, activeLink: activeLink, version: version, hideSearch: hideSearch, footer: footer, resizable: resizable, defaultWidth: defaultWidth, minWidth: minWidth, storageKey: storageKey }) }));
|
|
7
6
|
}
|
|
@@ -4,6 +4,7 @@ interface SidebarItemProps {
|
|
|
4
4
|
label: string;
|
|
5
5
|
icon: React.ReactNode;
|
|
6
6
|
href?: string;
|
|
7
|
+
truncateLabel?: boolean;
|
|
7
8
|
}
|
|
8
|
-
export declare function SidebarItem({ component: Component, label, icon, href, }: SidebarItemProps): React.ReactNode;
|
|
9
|
+
export declare function SidebarItem({ component: Component, label, icon, href, truncateLabel, }: SidebarItemProps): React.ReactNode;
|
|
9
10
|
export {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { SidebarItemContent } from './sidebar-item-content/SidebarItemContent';
|
|
3
|
-
export function SidebarItem({ component: Component, label, icon, href, }) {
|
|
3
|
+
export function SidebarItem({ component: Component, label, icon, href, truncateLabel, }) {
|
|
4
4
|
if (!Component) {
|
|
5
5
|
return null;
|
|
6
6
|
}
|
|
7
|
-
return (_jsx(Component, { children: _jsx(SidebarItemContent, { icon: icon, label: label, href: href }) }));
|
|
7
|
+
return (_jsx(Component, { children: _jsx(SidebarItemContent, { icon: icon, label: label, href: href, truncateLabel: truncateLabel }) }));
|
|
8
8
|
}
|
package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ type ExpandableSidebarItemProps = {
|
|
|
6
6
|
component: React.ElementType;
|
|
7
7
|
icon: React.ReactNode;
|
|
8
8
|
href: string;
|
|
9
|
+
truncateLabel?: boolean;
|
|
9
10
|
};
|
|
10
|
-
export declare function ExpandableSidebarItem({ items, label, icon, component: Component, href, }: ExpandableSidebarItemProps): React.ReactNode;
|
|
11
|
+
export declare function ExpandableSidebarItem({ items, label, icon, component: Component, href, truncateLabel, }: ExpandableSidebarItemProps): React.ReactNode;
|
|
11
12
|
export {};
|
|
@@ -10,7 +10,7 @@ import { SidebarItemContent } from '../sidebar-item-content/SidebarItemContent';
|
|
|
10
10
|
import { SidebarItem } from '../SidebarItem';
|
|
11
11
|
const isGroup = (item) => item.type === 'group';
|
|
12
12
|
const isExpandable = (item) => item.type === 'expandable';
|
|
13
|
-
export function ExpandableSidebarItem({ items, label, icon, component: Component, href, }) {
|
|
13
|
+
export function ExpandableSidebarItem({ items, label, icon, component: Component, href, truncateLabel, }) {
|
|
14
14
|
const { defaultExpanded, resetExpandAll, isSidebarCollapsed, handleSidebarCollapseChange, expandItem, collapseItem, isExpanded, } = useSidebar();
|
|
15
15
|
// Local-only state for animation coordination
|
|
16
16
|
const [closing, setClosing] = useState(false);
|
|
@@ -60,7 +60,7 @@ export function ExpandableSidebarItem({ items, label, icon, component: Component
|
|
|
60
60
|
if (isExpandable(item)) {
|
|
61
61
|
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));
|
|
62
62
|
}
|
|
63
|
-
return (_jsx(SidebarItem, { component: item.component, label: item.label, icon: item.icon, href: item.href }, key));
|
|
63
|
+
return (_jsx(SidebarItem, { component: item.component, label: item.label, icon: item.icon, href: item.href, truncateLabel: item.truncateLabel }, key));
|
|
64
64
|
};
|
|
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}`)) }))] }));
|
|
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, truncateLabel: truncateLabel, 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
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { JSX, ReactNode } from 'react';
|
|
2
2
|
interface SidebarContainerProps {
|
|
3
3
|
logo?: ReactNode;
|
|
4
4
|
productName?: string;
|
|
@@ -6,6 +6,11 @@ interface SidebarContainerProps {
|
|
|
6
6
|
activeLink?: string;
|
|
7
7
|
version?: string | number;
|
|
8
8
|
hideSearch?: boolean;
|
|
9
|
+
footer?: ReactNode;
|
|
10
|
+
resizable?: boolean;
|
|
11
|
+
defaultWidth?: number;
|
|
12
|
+
minWidth?: number;
|
|
13
|
+
storageKey?: string;
|
|
9
14
|
}
|
|
10
|
-
export declare function SidebarContainer({ logo, productLogo, activeLink, version, hideSearch, }: SidebarContainerProps): JSX.Element;
|
|
15
|
+
export declare function SidebarContainer({ logo, productLogo, activeLink, version, hideSearch, footer, resizable, defaultWidth, minWidth, storageKey, }: SidebarContainerProps): JSX.Element;
|
|
11
16
|
export {};
|
|
@@ -1,12 +1,142 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { ChevronLeft } from 'lucide-react';
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
5
|
import { Logo } from '../../../../assets/logo';
|
|
4
6
|
import { Button } from '../../../../components/button/Button';
|
|
5
7
|
import { SidebarItems } from '../../../../components/sidebar/components/sidebar-items/SidebarItems';
|
|
6
8
|
import SidenavFiltering from '../../../../components/sidebar/components/sidenav-filteirng/SidenavFiltering';
|
|
7
9
|
import { useSidebar } from '../../../../components/sidebar/providers/SidebarProvider';
|
|
8
10
|
import styles from './SidebarContainer.module.css';
|
|
9
|
-
|
|
11
|
+
function clamp(n, min, max) {
|
|
12
|
+
return Math.max(min, Math.min(max, n));
|
|
13
|
+
}
|
|
14
|
+
function readStoredWidth(key) {
|
|
15
|
+
try {
|
|
16
|
+
const raw = localStorage.getItem(key);
|
|
17
|
+
if (!raw)
|
|
18
|
+
return null;
|
|
19
|
+
const num = Number(raw);
|
|
20
|
+
return Number.isFinite(num) ? num : null;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function writeStoredWidth(key, value) {
|
|
27
|
+
try {
|
|
28
|
+
localStorage.setItem(key, String(Math.round(value)));
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// ignore
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function removeStoredWidth(key) {
|
|
35
|
+
try {
|
|
36
|
+
localStorage.removeItem(key);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// ignore
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function SidebarContainer({ logo, productLogo, activeLink, version, hideSearch, footer, resizable, defaultWidth = 240, minWidth = 160, storageKey, }) {
|
|
10
43
|
const { isSidebarCollapsed, handleSidebarCollapseChange } = useSidebar();
|
|
11
|
-
|
|
44
|
+
const initialStoredWidth = typeof window !== 'undefined' && storageKey ? readStoredWidth(storageKey) : null;
|
|
45
|
+
const [sidebarWidth, setSidebarWidth] = useState(() => initialStoredWidth !== null && initialStoredWidth !== void 0 ? initialStoredWidth : defaultWidth);
|
|
46
|
+
const [manualWidth, setManualWidth] = useState(() => initialStoredWidth);
|
|
47
|
+
const [isResizing, setIsResizing] = useState(false);
|
|
48
|
+
const [ariaMaxWidth, setAriaMaxWidth] = useState(undefined);
|
|
49
|
+
const containerRef = useRef(null);
|
|
50
|
+
const draggingRef = useRef(false);
|
|
51
|
+
const pointerIdRef = useRef(null);
|
|
52
|
+
const startXRef = useRef(0);
|
|
53
|
+
const startWidthRef = useRef(0);
|
|
54
|
+
const maxWidthRef = useRef(Infinity);
|
|
55
|
+
// Use the viewport width as the ceiling — avoids the circular-dependency
|
|
56
|
+
// that occurs when the sidebar lives in an `auto`-width grid column whose
|
|
57
|
+
// size is determined by the sidebar itself (e.g. PageLayout vertical).
|
|
58
|
+
const getMaxWidth = useCallback(() => {
|
|
59
|
+
const viewportWidth = typeof window !== 'undefined'
|
|
60
|
+
? window.innerWidth || document.documentElement.clientWidth
|
|
61
|
+
: Infinity;
|
|
62
|
+
return Math.max(minWidth, viewportWidth - minWidth);
|
|
63
|
+
}, [minWidth]);
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (!storageKey)
|
|
66
|
+
return;
|
|
67
|
+
if (manualWidth === null) {
|
|
68
|
+
removeStoredWidth(storageKey);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
writeStoredWidth(storageKey, manualWidth);
|
|
72
|
+
}, [manualWidth, storageKey]);
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
setAriaMaxWidth(getMaxWidth());
|
|
75
|
+
}, [getMaxWidth]);
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
return () => {
|
|
78
|
+
document.body.style.cursor = '';
|
|
79
|
+
document.body.style.userSelect = '';
|
|
80
|
+
};
|
|
81
|
+
}, []);
|
|
82
|
+
const updateWidth = useCallback((nextWidth) => {
|
|
83
|
+
setManualWidth(nextWidth);
|
|
84
|
+
setSidebarWidth(nextWidth);
|
|
85
|
+
}, []);
|
|
86
|
+
const onResizePointerDown = useCallback((e) => {
|
|
87
|
+
const maxWidth = getMaxWidth();
|
|
88
|
+
if (maxWidth === null)
|
|
89
|
+
return;
|
|
90
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
91
|
+
maxWidthRef.current = maxWidth;
|
|
92
|
+
draggingRef.current = true;
|
|
93
|
+
pointerIdRef.current = e.pointerId;
|
|
94
|
+
startXRef.current = e.clientX;
|
|
95
|
+
startWidthRef.current = sidebarWidth;
|
|
96
|
+
setIsResizing(true);
|
|
97
|
+
document.body.style.userSelect = 'none';
|
|
98
|
+
document.body.style.cursor = 'col-resize';
|
|
99
|
+
}, [getMaxWidth, sidebarWidth]);
|
|
100
|
+
const onResizePointerMove = useCallback((e) => {
|
|
101
|
+
if (!draggingRef.current)
|
|
102
|
+
return;
|
|
103
|
+
if (pointerIdRef.current !== null && e.pointerId !== pointerIdRef.current)
|
|
104
|
+
return;
|
|
105
|
+
const next = startWidthRef.current + (e.clientX - startXRef.current);
|
|
106
|
+
updateWidth(clamp(next, minWidth, maxWidthRef.current));
|
|
107
|
+
}, [minWidth, updateWidth]);
|
|
108
|
+
const endResizeDrag = useCallback(() => {
|
|
109
|
+
if (!draggingRef.current)
|
|
110
|
+
return;
|
|
111
|
+
draggingRef.current = false;
|
|
112
|
+
pointerIdRef.current = null;
|
|
113
|
+
setIsResizing(false);
|
|
114
|
+
document.body.style.cursor = '';
|
|
115
|
+
document.body.style.userSelect = '';
|
|
116
|
+
}, []);
|
|
117
|
+
const resetWidth = useCallback(() => {
|
|
118
|
+
setManualWidth(null);
|
|
119
|
+
setSidebarWidth(clamp(defaultWidth, minWidth, getMaxWidth()));
|
|
120
|
+
}, [defaultWidth, getMaxWidth, minWidth]);
|
|
121
|
+
const onKeyDown = useCallback((e) => {
|
|
122
|
+
const maxWidth = getMaxWidth();
|
|
123
|
+
const step = e.shiftKey ? 32 : 8;
|
|
124
|
+
let next = null;
|
|
125
|
+
if (e.key === 'ArrowLeft')
|
|
126
|
+
next = sidebarWidth - step;
|
|
127
|
+
if (e.key === 'ArrowRight')
|
|
128
|
+
next = sidebarWidth + step;
|
|
129
|
+
if (e.key === 'Home')
|
|
130
|
+
next = minWidth;
|
|
131
|
+
if (e.key === 'End')
|
|
132
|
+
next = maxWidth;
|
|
133
|
+
if (next === null)
|
|
134
|
+
return;
|
|
135
|
+
e.preventDefault();
|
|
136
|
+
updateWidth(clamp(next, minWidth, maxWidth));
|
|
137
|
+
}, [getMaxWidth, minWidth, sidebarWidth, updateWidth]);
|
|
138
|
+
const containerStyle = useMemo(() => ({
|
|
139
|
+
'--sidebar-width': `${sidebarWidth}px`,
|
|
140
|
+
}), [sidebarWidth]);
|
|
141
|
+
return (_jsxs("div", { ref: containerRef, role: "complementary", className: `${styles.container} ${isSidebarCollapsed ? styles.collapsed : ''} ${isResizing ? styles.resizing : ''}`, style: containerStyle, 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: [!hideSearch && (_jsx("div", { className: styles.filter, children: _jsx(SidenavFiltering, {}) })), _jsx("div", { className: `${styles.links} hideScrollBar`, children: _jsx(SidebarItems, { activeLink: activeLink }) })] }), footer && _jsx("div", { className: styles.footerSlot, children: footer }), _jsxs("div", { className: styles.footer, children: [_jsx("div", { className: styles.companyLogo, children: logo !== null && logo !== void 0 ? logo : _jsx(Logo, {}) }), version && _jsx("div", { className: `${styles.version} dbc-muted-text dbc-sm-text`, children: version })] }), resizable && (_jsx("div", { className: styles.resizeHandle, role: "separator", "aria-label": "Resize sidebar", "aria-orientation": "vertical", "aria-valuemin": Math.round(minWidth), "aria-valuemax": ariaMaxWidth !== undefined ? Math.round(ariaMaxWidth) : undefined, "aria-valuenow": Math.round(sidebarWidth), tabIndex: isSidebarCollapsed ? -1 : 0, onPointerDown: onResizePointerDown, onPointerMove: onResizePointerMove, onPointerUp: endResizeDrag, onPointerCancel: endResizeDrag, onDoubleClick: resetWidth, onKeyDown: onKeyDown }))] }));
|
|
12
142
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
.container {
|
|
2
|
+
position: relative;
|
|
2
3
|
flex-shrink: 1;
|
|
3
4
|
height: 100%;
|
|
4
5
|
overflow: auto;
|
|
@@ -14,6 +15,10 @@
|
|
|
14
15
|
inline-size var(--transition-fast) var(--ease-standard);
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
.container.resizing {
|
|
19
|
+
transition: none;
|
|
20
|
+
}
|
|
21
|
+
|
|
17
22
|
/* Collapsed state */
|
|
18
23
|
.container.collapsed {
|
|
19
24
|
width: var(--component-size-lg);
|
|
@@ -37,10 +42,12 @@
|
|
|
37
42
|
/* HEADER (product + collapse) */
|
|
38
43
|
.header {
|
|
39
44
|
flex: 0 0 auto;
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
box-sizing: border-box;
|
|
46
|
+
box-shadow: 0 1px 0 var(--color-border-default);
|
|
47
|
+
padding-inline: var(--spacing-xs);
|
|
42
48
|
min-block-size: 60px;
|
|
43
49
|
display: flex;
|
|
50
|
+
align-items: center;
|
|
44
51
|
justify-content: space-between;
|
|
45
52
|
}
|
|
46
53
|
|
|
@@ -57,15 +64,15 @@
|
|
|
57
64
|
display: flex;
|
|
58
65
|
align-items: center;
|
|
59
66
|
max-inline-size: 100%;
|
|
67
|
+
block-size: 25px;
|
|
60
68
|
min-width: 0;
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
/* Keep product logo visible in expanded state */
|
|
64
72
|
.productLogo img,
|
|
65
73
|
.productLogo svg {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
block-size: auto;
|
|
74
|
+
block-size: 100%;
|
|
75
|
+
inline-size: 100%;
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
/* Collapse button */
|
|
@@ -131,6 +138,61 @@
|
|
|
131
138
|
padding: 0;
|
|
132
139
|
}
|
|
133
140
|
|
|
141
|
+
/* RESIZE HANDLE */
|
|
142
|
+
.resizeHandle {
|
|
143
|
+
position: absolute;
|
|
144
|
+
top: 0;
|
|
145
|
+
bottom: 0;
|
|
146
|
+
right: 0;
|
|
147
|
+
width: 8px;
|
|
148
|
+
cursor: col-resize;
|
|
149
|
+
z-index: 1;
|
|
150
|
+
user-select: none;
|
|
151
|
+
touch-action: none;
|
|
152
|
+
outline: none;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.resizeHandle::after {
|
|
156
|
+
content: '';
|
|
157
|
+
position: absolute;
|
|
158
|
+
top: 0;
|
|
159
|
+
bottom: 0;
|
|
160
|
+
right: 0;
|
|
161
|
+
width: var(--border-width-thin);
|
|
162
|
+
background-color: var(--color-border-subtle);
|
|
163
|
+
opacity: 0;
|
|
164
|
+
transition:
|
|
165
|
+
opacity var(--transition-fast) var(--ease-standard),
|
|
166
|
+
background-color var(--transition-fast) var(--ease-standard);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.resizeHandle:hover::after,
|
|
170
|
+
.resizeHandle:active::after {
|
|
171
|
+
opacity: 1;
|
|
172
|
+
background-color: var(--color-border-strong);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.resizeHandle:active::after {
|
|
176
|
+
background-color: var(--color-brand);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.resizeHandle:focus-visible {
|
|
180
|
+
box-shadow: var(--focus-ring);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.container.collapsed .resizeHandle {
|
|
184
|
+
display: none;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* CUSTOM FOOTER SLOT */
|
|
188
|
+
.footerSlot {
|
|
189
|
+
flex: 0 0 auto;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.container:not(.collapsed) .footerSlot {
|
|
193
|
+
padding-inline: var(--spacing-xs);
|
|
194
|
+
}
|
|
195
|
+
|
|
134
196
|
/* FOOTER (company logo) */
|
|
135
197
|
.footer {
|
|
136
198
|
flex: 0 0 auto;
|
|
@@ -7,5 +7,6 @@ export interface SidebarItemContentProps {
|
|
|
7
7
|
href?: string;
|
|
8
8
|
disableActiveStyles?: boolean;
|
|
9
9
|
headerStyle?: boolean;
|
|
10
|
+
truncateLabel?: boolean;
|
|
10
11
|
}
|
|
11
|
-
export declare function SidebarItemContent({ icon, label, suffixIcon, href, disableActiveStyles, headerStyle, }: SidebarItemContentProps): JSX.Element;
|
|
12
|
+
export declare function SidebarItemContent({ icon, label, suffixIcon, href, disableActiveStyles, headerStyle, truncateLabel, }: SidebarItemContentProps): JSX.Element;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import styles from './SidebarItemContent.module.css';
|
|
3
4
|
import { useSidebar } from '../../providers/SidebarProvider';
|
|
4
|
-
export function SidebarItemContent({ icon, label, suffixIcon, href, disableActiveStyles = false, headerStyle, }) {
|
|
5
|
+
export function SidebarItemContent({ icon, label, suffixIcon, href, disableActiveStyles = false, headerStyle, truncateLabel = false, }) {
|
|
5
6
|
const { activeLink, isSidebarCollapsed } = useSidebar();
|
|
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
|
+
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} ${truncateLabel ? styles.truncate : ''}`, title: truncateLabel && typeof label === 'string' ? label : undefined, children: label }))] }), suffixIcon && !isSidebarCollapsed && _jsx("span", { className: styles.suffixIcon, children: suffixIcon })] }));
|
|
7
8
|
}
|
package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.module.css
CHANGED
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
display: flex;
|
|
42
42
|
align-items: center;
|
|
43
43
|
gap: var(--spacing-sm);
|
|
44
|
+
min-inline-size: 0;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
.container:not(.active):hover {
|
|
@@ -83,3 +84,13 @@
|
|
|
83
84
|
letter-spacing: 0.04em;
|
|
84
85
|
transition: 0.15s ease-in-out;
|
|
85
86
|
}
|
|
87
|
+
|
|
88
|
+
.label {
|
|
89
|
+
min-inline-size: 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.truncate {
|
|
93
|
+
overflow: hidden;
|
|
94
|
+
text-overflow: ellipsis;
|
|
95
|
+
white-space: nowrap;
|
|
96
|
+
}
|
|
@@ -16,11 +16,11 @@ export function SidebarItems({ activeLink }) {
|
|
|
16
16
|
return isSidebarCollapsed ? ((_a = item.children) === null || _a === void 0 ? void 0 : _a.map((child, idx) => renderItem(child, `${key}-c${idx}`))) : (_jsxs("div", { className: styles.group, children: [_jsx("div", { className: styles.groupLabel, children: item.label }), (_b = item.children) === null || _b === void 0 ? void 0 : _b.map((child, idx) => renderItem(child, `${key}-c${idx}`))] }, key));
|
|
17
17
|
}
|
|
18
18
|
if (item.type === 'expandable') {
|
|
19
|
-
const { component: Component, label, icon, children, href } = item;
|
|
20
|
-
return (_jsx(ExpandableSidebarItem, { items: children, label: label, icon: icon, href: href, component: Component }, key));
|
|
19
|
+
const { component: Component, label, icon, children, href, truncateLabel } = item;
|
|
20
|
+
return (_jsx(ExpandableSidebarItem, { items: children, label: label, icon: icon, href: href, truncateLabel: truncateLabel, component: Component }, key));
|
|
21
21
|
}
|
|
22
|
-
const { component: Component, label, icon, href } = item;
|
|
23
|
-
return _jsx(SidebarItem, { component: Component, label: label, icon: icon, href: href }, key);
|
|
22
|
+
const { component: Component, label, icon, href, truncateLabel } = item;
|
|
23
|
+
return (_jsx(SidebarItem, { component: Component, label: label, icon: icon, href: href, truncateLabel: truncateLabel }, key));
|
|
24
24
|
};
|
|
25
25
|
return filteredItems === null || filteredItems === void 0 ? void 0 : filteredItems.map((item, idx) => renderItem(item, `nav-${idx}-${item.label}`));
|
|
26
26
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dbcdk/react-components",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.67",
|
|
4
4
|
"description": "Reusable React components for DBC projects",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"build": "npm run clean && npm run build:code && npm run postbuild",
|
|
47
47
|
"build:code": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
48
48
|
"dev": "tsc -p tsconfig.build.json --watch",
|
|
49
|
-
"test": "
|
|
49
|
+
"test": "vitest run",
|
|
50
50
|
"format": "prettier . --write",
|
|
51
51
|
"format:fix": "prettier . --write",
|
|
52
52
|
"lint": "eslint \"src/**/*.{ts,tsx}\" --max-warnings=0",
|
|
@@ -77,11 +77,9 @@
|
|
|
77
77
|
"@storybook/addon-docs": "^10.3.5",
|
|
78
78
|
"@storybook/react-vite": "^10.3.5",
|
|
79
79
|
"@swc/core": "^1.15.11",
|
|
80
|
-
"@swc/jest": "^0.2.39",
|
|
81
80
|
"@tanstack/react-table": "^8.20.0",
|
|
82
81
|
"@testing-library/jest-dom": "^6.9.1",
|
|
83
82
|
"@testing-library/react": "^16.3.2",
|
|
84
|
-
"@types/jest": "^30.0.0",
|
|
85
83
|
"@types/react": "^19.2.14",
|
|
86
84
|
"@types/react-dom": "^19.2.3",
|
|
87
85
|
"@typescript-eslint/eslint-plugin": "^8.41.0",
|
|
@@ -98,8 +96,6 @@
|
|
|
98
96
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
99
97
|
"globals": "^17.3.0",
|
|
100
98
|
"identity-obj-proxy": "^3.0.0",
|
|
101
|
-
"jest": "^30.2.0",
|
|
102
|
-
"jest-environment-jsdom": "^30.2.0",
|
|
103
99
|
"jsdom": "^29.0.2",
|
|
104
100
|
"prettier": "^3.2.4",
|
|
105
101
|
"react": "19.2.4",
|