@dbcdk/react-components 0.0.210 → 0.0.211

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/dist/components/nav-bar/NavBar.d.ts +2 -1
  2. package/dist/components/sidebar/Sidebar.cjs +2 -1
  3. package/dist/components/sidebar/Sidebar.js +2 -1
  4. package/dist/components/sidebar/components/SidebarItem.cjs +34 -13
  5. package/dist/components/sidebar/components/SidebarItem.d.ts +4 -2
  6. package/dist/components/sidebar/components/SidebarItem.js +30 -13
  7. package/dist/components/sidebar/components/SidebarItem.module.css +16 -0
  8. package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.cjs +41 -116
  9. package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.d.ts +4 -5
  10. package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.js +42 -117
  11. package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.module.css +17 -25
  12. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +0 -1
  13. package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.cjs +1 -2
  14. package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.d.ts +1 -2
  15. package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.js +1 -2
  16. package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.module.css +4 -10
  17. package/dist/components/sidebar/components/sidebar-items/SidebarItems.cjs +11 -19
  18. package/dist/components/sidebar/components/sidebar-items/SidebarItems.js +11 -19
  19. package/dist/components/sidebar/components/sidebar-items/SidebarItems.module.css +0 -1
  20. package/dist/components/sidebar/providers/SidebarProvider.cjs +9 -7
  21. package/dist/components/sidebar/providers/SidebarProvider.d.ts +2 -1
  22. package/dist/components/sidebar/providers/SidebarProvider.js +9 -7
  23. package/package.json +1 -1
@@ -18,7 +18,8 @@ export type NavBarLinkItem = NavBarBase & {
18
18
  active?: boolean;
19
19
  external?: boolean;
20
20
  };
21
- /** Clickable + expandable item (has href AND children) */
21
+ /** Expandable container. In Sidebar, href is a stable expansion key, not a destination.
22
+ * Legacy component/active/external props are accepted but ignored by Sidebar groups. */
22
23
  export type NavBarExpandableItem = NavBarBase & {
23
24
  type: 'expandable';
24
25
  href: string;
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
3
4
  var jsxRuntime = require('react/jsx-runtime');
@@ -19,7 +20,7 @@ function Sidebar({
19
20
  minWidth,
20
21
  storageKey
21
22
  }) {
22
- return /* @__PURE__ */ jsxRuntime.jsx(SidebarProvider.SidebarProvider, { items, children: /* @__PURE__ */ jsxRuntime.jsx(
23
+ return /* @__PURE__ */ jsxRuntime.jsx(SidebarProvider.SidebarProvider, { items, initialActiveLink: activeLink, children: /* @__PURE__ */ jsxRuntime.jsx(
23
24
  SidebarContainer.SidebarContainer,
24
25
  {
25
26
  productName,
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { jsx } from 'react/jsx-runtime';
2
3
  import { SidebarContainer } from './components/sidebar-container/SidebarContainer';
3
4
  import { SidebarProvider } from './providers/SidebarProvider';
@@ -17,7 +18,7 @@ function Sidebar({
17
18
  minWidth,
18
19
  storageKey
19
20
  }) {
20
- return /* @__PURE__ */ jsx(SidebarProvider, { items, children: /* @__PURE__ */ jsx(
21
+ return /* @__PURE__ */ jsx(SidebarProvider, { items, initialActiveLink: activeLink, children: /* @__PURE__ */ jsx(
21
22
  SidebarContainer,
22
23
  {
23
24
  productName,
@@ -1,31 +1,52 @@
1
+ 'use client';
1
2
  'use strict';
2
3
 
3
4
  var jsxRuntime = require('react/jsx-runtime');
5
+ var styles = require('./SidebarItem.module.css');
6
+ var SidebarProvider = require('../providers/SidebarProvider');
4
7
  var SidebarItemContent = require('./sidebar-item-content/SidebarItemContent');
5
8
 
9
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
+
11
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
12
+
6
13
  function SidebarItem({
7
- component: Component,
14
+ component: Component = "a",
8
15
  label,
9
16
  icon,
10
17
  badge,
11
18
  href,
12
19
  iconWidth,
13
- truncateLabel
20
+ truncateLabel,
21
+ enabled = true,
22
+ external
14
23
  }) {
15
- if (!Component) {
16
- return null;
17
- }
18
- return /* @__PURE__ */ jsxRuntime.jsx(Component, { children: /* @__PURE__ */ jsxRuntime.jsx(
19
- SidebarItemContent.SidebarItemContent,
24
+ const { activeLink, isSidebarCollapsed } = SidebarProvider.useSidebar();
25
+ return /* @__PURE__ */ jsxRuntime.jsx(
26
+ Component,
20
27
  {
21
- icon,
22
- badge,
23
- iconWidth,
24
- label,
25
28
  href,
26
- truncateLabel
29
+ className: styles__default.default.link,
30
+ "aria-label": isSidebarCollapsed ? label : void 0,
31
+ "aria-current": enabled && activeLink === href ? "page" : void 0,
32
+ "aria-disabled": !enabled || void 0,
33
+ tabIndex: enabled ? void 0 : -1,
34
+ onClick: enabled ? void 0 : (event) => event.preventDefault(),
35
+ ...external ? { target: "_blank", rel: "noopener noreferrer" } : {},
36
+ children: /* @__PURE__ */ jsxRuntime.jsx(
37
+ SidebarItemContent.SidebarItemContent,
38
+ {
39
+ icon,
40
+ badge,
41
+ iconWidth,
42
+ label,
43
+ href,
44
+ disableActiveStyles: !enabled,
45
+ truncateLabel
46
+ }
47
+ )
27
48
  }
28
- ) });
49
+ );
29
50
  }
30
51
 
31
52
  exports.SidebarItem = SidebarItem;
@@ -2,11 +2,13 @@ import React from 'react';
2
2
  interface SidebarItemProps {
3
3
  component?: React.ElementType;
4
4
  label: string;
5
- icon: React.ReactNode;
5
+ icon?: React.ReactNode;
6
6
  badge?: number | string;
7
7
  href?: string;
8
8
  iconWidth?: string | number;
9
+ enabled?: boolean;
10
+ external?: boolean;
9
11
  truncateLabel?: boolean;
10
12
  }
11
- export declare function SidebarItem({ component: Component, label, icon, badge, href, iconWidth, truncateLabel, }: SidebarItemProps): React.ReactNode;
13
+ export declare function SidebarItem({ component: Component, label, icon, badge, href, iconWidth, truncateLabel, enabled, external, }: SidebarItemProps): React.ReactNode;
12
14
  export {};
@@ -1,29 +1,46 @@
1
+ 'use client';
1
2
  import { jsx } from 'react/jsx-runtime';
3
+ import styles from './SidebarItem.module.css';
4
+ import { useSidebar } from '../providers/SidebarProvider';
2
5
  import { SidebarItemContent } from './sidebar-item-content/SidebarItemContent';
3
6
 
4
7
  function SidebarItem({
5
- component: Component,
8
+ component: Component = "a",
6
9
  label,
7
10
  icon,
8
11
  badge,
9
12
  href,
10
13
  iconWidth,
11
- truncateLabel
14
+ truncateLabel,
15
+ enabled = true,
16
+ external
12
17
  }) {
13
- if (!Component) {
14
- return null;
15
- }
16
- return /* @__PURE__ */ jsx(Component, { children: /* @__PURE__ */ jsx(
17
- SidebarItemContent,
18
+ const { activeLink, isSidebarCollapsed } = useSidebar();
19
+ return /* @__PURE__ */ jsx(
20
+ Component,
18
21
  {
19
- icon,
20
- badge,
21
- iconWidth,
22
- label,
23
22
  href,
24
- truncateLabel
23
+ className: styles.link,
24
+ "aria-label": isSidebarCollapsed ? label : void 0,
25
+ "aria-current": enabled && activeLink === href ? "page" : void 0,
26
+ "aria-disabled": !enabled || void 0,
27
+ tabIndex: enabled ? void 0 : -1,
28
+ onClick: enabled ? void 0 : (event) => event.preventDefault(),
29
+ ...external ? { target: "_blank", rel: "noopener noreferrer" } : {},
30
+ children: /* @__PURE__ */ jsx(
31
+ SidebarItemContent,
32
+ {
33
+ icon,
34
+ badge,
35
+ iconWidth,
36
+ label,
37
+ href,
38
+ disableActiveStyles: !enabled,
39
+ truncateLabel
40
+ }
41
+ )
25
42
  }
26
- ) });
43
+ );
27
44
  }
28
45
 
29
46
  export { SidebarItem };
@@ -0,0 +1,16 @@
1
+ .link {
2
+ display: block;
3
+ color: inherit;
4
+ text-decoration: none;
5
+ border-radius: var(--border-radius-md);
6
+ }
7
+
8
+ .link:focus-visible {
9
+ outline: none;
10
+ box-shadow: var(--focus-ring);
11
+ }
12
+
13
+ .link[aria-disabled='true'] {
14
+ color: var(--color-fg-subtle);
15
+ cursor: default;
16
+ }
@@ -5,27 +5,22 @@ var jsxRuntime = require('react/jsx-runtime');
5
5
  var lucideReact = require('lucide-react');
6
6
  var react = require('react');
7
7
  var styles = require('./ExpandableSidebarItem.module.css');
8
- var Button = require('../../../button/Button');
9
8
  var SidebarProvider = require('../../providers/SidebarProvider');
10
- var ExpandableSidebarItem$1 = require('../expandable-sidebar-item/ExpandableSidebarItem');
11
9
  var SidebarItemContent = require('../sidebar-item-content/SidebarItemContent');
12
- var SidebarItem = require('../SidebarItem');
13
10
 
14
11
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
12
 
16
13
  var styles__default = /*#__PURE__*/_interopDefault(styles);
17
14
 
18
- const isGroup = (item) => item.type === "group";
19
- const isExpandable = (item) => item.type === "expandable";
20
15
  function ExpandableSidebarItem({
21
- items,
16
+ children,
22
17
  label,
23
18
  icon,
24
19
  badge,
25
- component: Component,
26
20
  href,
27
21
  iconWidth,
28
- truncateLabel
22
+ truncateLabel,
23
+ enabled = true
29
24
  }) {
30
25
  const {
31
26
  defaultExpanded,
@@ -36,121 +31,51 @@ function ExpandableSidebarItem({
36
31
  collapseItem,
37
32
  isExpanded
38
33
  } = SidebarProvider.useSidebar();
39
- const [closing, setClosing] = react.useState(false);
40
- const expanded = react.useMemo(() => isExpanded(href), [href, isExpanded]);
34
+ const childrenId = react.useId();
35
+ const expanded = isExpanded(href);
36
+ const visible = expanded && !isSidebarCollapsed;
41
37
  react.useEffect(() => {
42
38
  if (defaultExpanded === null) return;
43
39
  if (defaultExpanded) expandItem(href);
44
40
  else collapseItem(href);
45
41
  }, [defaultExpanded, expandItem, collapseItem, href]);
46
- const commitCollapse = react.useCallback(() => {
47
- collapseItem(href);
48
- setClosing(false);
49
- }, [collapseItem, href]);
50
- const handleAnimationEnd = react.useCallback(() => {
51
- if (closing) commitCollapse();
52
- }, [closing, commitCollapse]);
53
- react.useEffect(() => {
54
- if (!closing) return;
55
- const timer = setTimeout(commitCollapse, 250);
56
- return () => clearTimeout(timer);
57
- }, [closing, commitCollapse]);
58
- const toggleAccordion = react.useCallback(
59
- (e, onlyExpand = false) => {
60
- e == null ? void 0 : e.preventDefault();
61
- e == null ? void 0 : e.stopPropagation();
62
- resetExpandAll();
63
- handleSidebarCollapseChange(false);
64
- if (!expanded) {
65
- expandItem(href);
66
- return;
67
- }
68
- if (!isSidebarCollapsed && !onlyExpand) {
69
- setClosing(true);
70
- }
71
- },
72
- [expanded, expandItem, href, handleSidebarCollapseChange, isSidebarCollapsed, resetExpandAll]
73
- );
74
- const renderNavItem = (item, key) => {
75
- var _a, _b;
76
- if (isGroup(item)) {
77
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.group, children: [
78
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.groupLabel, children: item.label }),
79
- (_a = item.children) == null ? void 0 : _a.map((child, idx) => renderNavItem(child, `${key}-${idx}`))
80
- ] }, key);
81
- }
82
- if (isExpandable(item)) {
83
- return /* @__PURE__ */ jsxRuntime.jsx(
84
- ExpandableSidebarItem$1.ExpandableSidebarItem,
85
- {
86
- items: (_b = item.children) != null ? _b : [],
87
- label: item.label,
88
- icon: item.icon,
89
- badge: item.badge,
90
- href: item.href,
91
- iconWidth: item.iconWidth,
92
- component: item.component
93
- },
94
- key
95
- );
96
- }
97
- return /* @__PURE__ */ jsxRuntime.jsx(
98
- SidebarItem.SidebarItem,
99
- {
100
- component: item.component,
101
- label: item.label,
102
- icon: item.icon,
103
- badge: item.badge,
104
- href: item.href,
105
- iconWidth: item.iconWidth,
106
- truncateLabel: item.truncateLabel
107
- },
108
- key
109
- );
110
- };
111
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${styles__default.default.container}`, children: [
112
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${styles__default.default.itemRow} ${expanded ? styles__default.default.itemRowExpanded : ""}`, children: [
113
- /* @__PURE__ */ jsxRuntime.jsx(Component, { onClick: () => toggleAccordion(void 0, true), children: /* @__PURE__ */ jsxRuntime.jsx(
114
- SidebarItemContent.SidebarItemContent,
115
- {
116
- headerStyle: expanded,
117
- icon,
118
- badge,
119
- iconWidth,
120
- label,
121
- href,
122
- disableActiveStyles: expanded,
123
- suffixIcon: !isSidebarCollapsed ? /* @__PURE__ */ jsxRuntime.jsx(
124
- lucideReact.ChevronDown,
125
- {
126
- className: `${styles__default.default.chevron} ${expanded ? styles__default.default.chevronExpanded : ""}`
127
- }
128
- ) : void 0,
129
- truncateLabel
130
- }
131
- ) }),
132
- !isSidebarCollapsed && /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.toggleButton, children: /* @__PURE__ */ jsxRuntime.jsx(
133
- Button.Button,
134
- {
135
- variant: "inline",
136
- shape: "round",
137
- className: styles__default.default.toggleButtonControl,
138
- "aria-label": expanded ? `Collapse ${label}` : `Expand ${label}`,
139
- "aria-expanded": expanded,
140
- "aria-controls": `${href}-children`,
141
- onClick: toggleAccordion
142
- }
143
- ) })
144
- ] }),
145
- expanded && !isSidebarCollapsed && /* @__PURE__ */ jsxRuntime.jsx(
146
- "div",
42
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.container, children: [
43
+ /* @__PURE__ */ jsxRuntime.jsx(
44
+ "button",
147
45
  {
148
- id: `${href}-children`,
149
- onAnimationEnd: handleAnimationEnd,
150
- className: `${styles__default.default.childrenContainer} ${closing ? "animate--collapse" : ""}`,
151
- children: items.map((item, idx) => renderNavItem(item, `${href}-${idx}`))
46
+ type: "button",
47
+ className: styles__default.default.toggleButton,
48
+ "aria-label": isSidebarCollapsed ? label : void 0,
49
+ "aria-expanded": visible,
50
+ "aria-controls": childrenId,
51
+ disabled: !enabled,
52
+ onClick: () => {
53
+ resetExpandAll();
54
+ handleSidebarCollapseChange(false);
55
+ if (visible) collapseItem(href);
56
+ else expandItem(href);
57
+ },
58
+ children: /* @__PURE__ */ jsxRuntime.jsx(
59
+ SidebarItemContent.SidebarItemContent,
60
+ {
61
+ icon,
62
+ badge,
63
+ iconWidth,
64
+ label,
65
+ disableActiveStyles: true,
66
+ suffixIcon: /* @__PURE__ */ jsxRuntime.jsx(
67
+ lucideReact.ChevronDown,
68
+ {
69
+ "aria-hidden": "true",
70
+ className: `${styles__default.default.chevron} ${visible ? styles__default.default.chevronExpanded : ""}`
71
+ }
72
+ ),
73
+ truncateLabel
74
+ }
75
+ )
152
76
  }
153
- )
77
+ ),
78
+ /* @__PURE__ */ jsxRuntime.jsx("div", { id: childrenId, hidden: !visible, className: styles__default.default.childrenContainer, children: visible ? children : null })
154
79
  ] });
155
80
  }
156
81
 
@@ -1,14 +1,13 @@
1
1
  import React from 'react';
2
- import type { NavBarItem } from '../../../../components/nav-bar/NavBar';
3
2
  type ExpandableSidebarItemProps = {
4
- items: NavBarItem[];
3
+ children: React.ReactNode;
5
4
  label: string;
6
- component: React.ElementType;
7
- icon: React.ReactNode;
5
+ icon?: React.ReactNode;
8
6
  badge?: number | string;
9
7
  href: string;
10
8
  iconWidth?: string | number;
11
9
  truncateLabel?: boolean;
10
+ enabled?: boolean;
12
11
  };
13
- export declare function ExpandableSidebarItem({ items, label, icon, badge, component: Component, href, iconWidth, truncateLabel, }: ExpandableSidebarItemProps): React.ReactNode;
12
+ export declare function ExpandableSidebarItem({ children, label, icon, badge, href, iconWidth, truncateLabel, enabled, }: ExpandableSidebarItemProps): React.ReactNode;
14
13
  export {};
@@ -1,25 +1,20 @@
1
1
  'use client';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { ChevronDown } from 'lucide-react';
4
- import { useState, useMemo, useEffect, useCallback } from 'react';
4
+ import { useId, useEffect } from 'react';
5
5
  import styles from './ExpandableSidebarItem.module.css';
6
- import { Button } from '../../../button/Button';
7
6
  import { useSidebar } from '../../providers/SidebarProvider';
8
- import { ExpandableSidebarItem as ExpandableSidebarItem$1 } from '../expandable-sidebar-item/ExpandableSidebarItem';
9
7
  import { SidebarItemContent } from '../sidebar-item-content/SidebarItemContent';
10
- import { SidebarItem } from '../SidebarItem';
11
8
 
12
- const isGroup = (item) => item.type === "group";
13
- const isExpandable = (item) => item.type === "expandable";
14
9
  function ExpandableSidebarItem({
15
- items,
10
+ children,
16
11
  label,
17
12
  icon,
18
13
  badge,
19
- component: Component,
20
14
  href,
21
15
  iconWidth,
22
- truncateLabel
16
+ truncateLabel,
17
+ enabled = true
23
18
  }) {
24
19
  const {
25
20
  defaultExpanded,
@@ -30,121 +25,51 @@ function ExpandableSidebarItem({
30
25
  collapseItem,
31
26
  isExpanded
32
27
  } = useSidebar();
33
- const [closing, setClosing] = useState(false);
34
- const expanded = useMemo(() => isExpanded(href), [href, isExpanded]);
28
+ const childrenId = useId();
29
+ const expanded = isExpanded(href);
30
+ const visible = expanded && !isSidebarCollapsed;
35
31
  useEffect(() => {
36
32
  if (defaultExpanded === null) return;
37
33
  if (defaultExpanded) expandItem(href);
38
34
  else collapseItem(href);
39
35
  }, [defaultExpanded, expandItem, collapseItem, href]);
40
- const commitCollapse = useCallback(() => {
41
- collapseItem(href);
42
- setClosing(false);
43
- }, [collapseItem, href]);
44
- const handleAnimationEnd = useCallback(() => {
45
- if (closing) commitCollapse();
46
- }, [closing, commitCollapse]);
47
- useEffect(() => {
48
- if (!closing) return;
49
- const timer = setTimeout(commitCollapse, 250);
50
- return () => clearTimeout(timer);
51
- }, [closing, commitCollapse]);
52
- const toggleAccordion = useCallback(
53
- (e, onlyExpand = false) => {
54
- e == null ? void 0 : e.preventDefault();
55
- e == null ? void 0 : e.stopPropagation();
56
- resetExpandAll();
57
- handleSidebarCollapseChange(false);
58
- if (!expanded) {
59
- expandItem(href);
60
- return;
61
- }
62
- if (!isSidebarCollapsed && !onlyExpand) {
63
- setClosing(true);
64
- }
65
- },
66
- [expanded, expandItem, href, handleSidebarCollapseChange, isSidebarCollapsed, resetExpandAll]
67
- );
68
- const renderNavItem = (item, key) => {
69
- var _a, _b;
70
- if (isGroup(item)) {
71
- return /* @__PURE__ */ jsxs("div", { className: styles.group, children: [
72
- /* @__PURE__ */ jsx("div", { className: styles.groupLabel, children: item.label }),
73
- (_a = item.children) == null ? void 0 : _a.map((child, idx) => renderNavItem(child, `${key}-${idx}`))
74
- ] }, key);
75
- }
76
- if (isExpandable(item)) {
77
- return /* @__PURE__ */ jsx(
78
- ExpandableSidebarItem$1,
79
- {
80
- items: (_b = item.children) != null ? _b : [],
81
- label: item.label,
82
- icon: item.icon,
83
- badge: item.badge,
84
- href: item.href,
85
- iconWidth: item.iconWidth,
86
- component: item.component
87
- },
88
- key
89
- );
90
- }
91
- return /* @__PURE__ */ jsx(
92
- SidebarItem,
93
- {
94
- component: item.component,
95
- label: item.label,
96
- icon: item.icon,
97
- badge: item.badge,
98
- href: item.href,
99
- iconWidth: item.iconWidth,
100
- truncateLabel: item.truncateLabel
101
- },
102
- key
103
- );
104
- };
105
- return /* @__PURE__ */ jsxs("div", { className: `${styles.container}`, children: [
106
- /* @__PURE__ */ jsxs("div", { className: `${styles.itemRow} ${expanded ? styles.itemRowExpanded : ""}`, children: [
107
- /* @__PURE__ */ jsx(Component, { onClick: () => toggleAccordion(void 0, true), children: /* @__PURE__ */ jsx(
108
- SidebarItemContent,
109
- {
110
- headerStyle: expanded,
111
- icon,
112
- badge,
113
- iconWidth,
114
- label,
115
- href,
116
- disableActiveStyles: expanded,
117
- suffixIcon: !isSidebarCollapsed ? /* @__PURE__ */ jsx(
118
- ChevronDown,
119
- {
120
- className: `${styles.chevron} ${expanded ? styles.chevronExpanded : ""}`
121
- }
122
- ) : void 0,
123
- truncateLabel
124
- }
125
- ) }),
126
- !isSidebarCollapsed && /* @__PURE__ */ jsx("div", { className: styles.toggleButton, children: /* @__PURE__ */ jsx(
127
- Button,
128
- {
129
- variant: "inline",
130
- shape: "round",
131
- className: styles.toggleButtonControl,
132
- "aria-label": expanded ? `Collapse ${label}` : `Expand ${label}`,
133
- "aria-expanded": expanded,
134
- "aria-controls": `${href}-children`,
135
- onClick: toggleAccordion
136
- }
137
- ) })
138
- ] }),
139
- expanded && !isSidebarCollapsed && /* @__PURE__ */ jsx(
140
- "div",
36
+ return /* @__PURE__ */ jsxs("div", { className: styles.container, children: [
37
+ /* @__PURE__ */ jsx(
38
+ "button",
141
39
  {
142
- id: `${href}-children`,
143
- onAnimationEnd: handleAnimationEnd,
144
- className: `${styles.childrenContainer} ${closing ? "animate--collapse" : ""}`,
145
- children: items.map((item, idx) => renderNavItem(item, `${href}-${idx}`))
40
+ type: "button",
41
+ className: styles.toggleButton,
42
+ "aria-label": isSidebarCollapsed ? label : void 0,
43
+ "aria-expanded": visible,
44
+ "aria-controls": childrenId,
45
+ disabled: !enabled,
46
+ onClick: () => {
47
+ resetExpandAll();
48
+ handleSidebarCollapseChange(false);
49
+ if (visible) collapseItem(href);
50
+ else expandItem(href);
51
+ },
52
+ children: /* @__PURE__ */ jsx(
53
+ SidebarItemContent,
54
+ {
55
+ icon,
56
+ badge,
57
+ iconWidth,
58
+ label,
59
+ disableActiveStyles: true,
60
+ suffixIcon: /* @__PURE__ */ jsx(
61
+ ChevronDown,
62
+ {
63
+ "aria-hidden": "true",
64
+ className: `${styles.chevron} ${visible ? styles.chevronExpanded : ""}`
65
+ }
66
+ ),
67
+ truncateLabel
68
+ }
69
+ )
146
70
  }
147
- )
71
+ ),
72
+ /* @__PURE__ */ jsx("div", { id: childrenId, hidden: !visible, className: styles.childrenContainer, children: visible ? children : null })
148
73
  ] });
149
74
  }
150
75
 
@@ -1,45 +1,37 @@
1
1
  .container {
2
- position: relative;
3
2
  display: flex;
4
3
  flex-direction: column;
5
- border-radius: var(--border-radius-default);
6
4
  }
7
5
 
8
- .itemRow {
9
- position: relative;
6
+ .toggleButton {
10
7
  display: block;
8
+ inline-size: 100%;
9
+ padding: 0;
10
+ border: 0;
11
11
  border-radius: var(--border-radius-md);
12
+ background: transparent;
13
+ color: inherit;
14
+ font: inherit;
15
+ text-align: start;
16
+ cursor: pointer;
12
17
  }
13
18
 
14
- .toggleButton {
15
- position: absolute;
16
- inset-inline-end: var(--spacing-xs);
17
- inset-block-start: 50%;
18
- transform: translateY(-50%);
19
- display: inline-flex;
20
- align-items: center;
21
- justify-content: center;
22
- }
23
-
24
- .container button {
25
- min-block-size: 28px !important;
26
- min-inline-size: 28px !important;
27
- max-block-size: 28px !important;
28
- max-inline-size: 28px !important;
29
- border-color: transparent !important;
30
- background-color: transparent !important;
19
+ .toggleButton:focus-visible {
20
+ outline: none;
21
+ box-shadow: var(--focus-ring);
31
22
  }
32
23
 
33
- .toggleButtonControl {
34
- opacity: 0;
24
+ .toggleButton:disabled {
25
+ color: var(--color-fg-subtle);
26
+ cursor: default;
35
27
  }
36
28
 
37
29
  .childrenContainer {
38
- overflow: hidden;
30
+ padding-inline-start: var(--spacing-md);
39
31
  }
40
32
 
41
33
  .chevron {
42
- transition: transform 0.3s ease;
34
+ transition: transform var(--transition-fast) var(--ease-standard);
43
35
  }
44
36
 
45
37
  .chevronExpanded {
@@ -164,7 +164,6 @@
164
164
  display: flex;
165
165
  flex-grow: 1;
166
166
  flex-direction: column;
167
- gap: 2px;
168
167
  font-size: var(--font-size-sm);
169
168
  }
170
169
 
@@ -21,7 +21,6 @@ function SidebarItemContent({
21
21
  href,
22
22
  iconWidth,
23
23
  disableActiveStyles = false,
24
- headerStyle,
25
24
  truncateLabel = false
26
25
  }) {
27
26
  const { activeLink, activeQuery, isSidebarCollapsed, wrapItemText } = SidebarProvider.useSidebar();
@@ -34,7 +33,7 @@ function SidebarItemContent({
34
33
  return /* @__PURE__ */ jsxRuntime.jsxs(
35
34
  "span",
36
35
  {
37
- className: `${styles__default.default.container} ${!disableActiveStyles && activeLink === href ? styles__default.default.active : ""} ${isSidebarCollapsed ? styles__default.default.collapsed : ""} ${headerStyle ? styles__default.default.headerStyle : ""}`,
36
+ className: `${styles__default.default.container} ${!disableActiveStyles && href && activeLink === href ? styles__default.default.active : ""} ${isSidebarCollapsed ? styles__default.default.collapsed : ""}`,
38
37
  children: [
39
38
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: styles__default.default.iconLabel, children: [
40
39
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.icon, style: iconStyle, children: icon }),
@@ -8,7 +8,6 @@ export interface SidebarItemContentProps {
8
8
  href?: string;
9
9
  iconWidth?: string | number;
10
10
  disableActiveStyles?: boolean;
11
- headerStyle?: boolean;
12
11
  truncateLabel?: boolean;
13
12
  }
14
- export declare function SidebarItemContent({ icon, label, badge, suffixIcon, href, iconWidth, disableActiveStyles, headerStyle, truncateLabel, }: SidebarItemContentProps): JSX.Element;
13
+ export declare function SidebarItemContent({ icon, label, badge, suffixIcon, href, iconWidth, disableActiveStyles, truncateLabel, }: SidebarItemContentProps): JSX.Element;
@@ -14,7 +14,6 @@ function SidebarItemContent({
14
14
  href,
15
15
  iconWidth,
16
16
  disableActiveStyles = false,
17
- headerStyle,
18
17
  truncateLabel = false
19
18
  }) {
20
19
  const { activeLink, activeQuery, isSidebarCollapsed, wrapItemText } = useSidebar();
@@ -27,7 +26,7 @@ function SidebarItemContent({
27
26
  return /* @__PURE__ */ jsxs(
28
27
  "span",
29
28
  {
30
- className: `${styles.container} ${!disableActiveStyles && activeLink === href ? styles.active : ""} ${isSidebarCollapsed ? styles.collapsed : ""} ${headerStyle ? styles.headerStyle : ""}`,
29
+ className: `${styles.container} ${!disableActiveStyles && href && activeLink === href ? styles.active : ""} ${isSidebarCollapsed ? styles.collapsed : ""}`,
31
30
  children: [
32
31
  /* @__PURE__ */ jsxs("span", { className: styles.iconLabel, children: [
33
32
  /* @__PURE__ */ jsx("span", { className: styles.icon, style: iconStyle, children: icon }),
@@ -4,6 +4,9 @@
4
4
  align-items: center;
5
5
  justify-content: space-between;
6
6
  gap: var(--spacing-sm);
7
+ /* Transparent borders separate the backgrounds while remaining part of the hit area. */
8
+ border-block: calc(var(--spacing-xxs) / 4) solid transparent;
9
+ background-clip: padding-box;
7
10
  padding-block: var(--spacing-xs);
8
11
  padding-inline: var(--spacing-xs);
9
12
  text-decoration: none;
@@ -19,12 +22,11 @@
19
22
 
20
23
  .container.collapsed {
21
24
  justify-content: center;
22
- padding: var(--spacing-xs);
23
25
  border-radius: 0;
24
26
  }
25
27
 
26
28
  .active {
27
- background: var(--color-bg-selected);
29
+ background-color: var(--color-bg-selected);
28
30
  }
29
31
 
30
32
  .active::before {
@@ -111,14 +113,6 @@
111
113
  box-shadow: var(--focus-ring);
112
114
  }
113
115
 
114
- .headerStyle .label {
115
- font-weight: 500;
116
- color: var(--color-fg-subtle);
117
- font-size: var(--font-size-xs);
118
- text-transform: uppercase;
119
- letter-spacing: 0.04em;
120
- }
121
-
122
116
  .label {
123
117
  min-inline-size: 0;
124
118
  flex: 1 1 auto;
@@ -26,27 +26,17 @@ function SidebarItems({ activeLink }) {
26
26
  ] }, key);
27
27
  }
28
28
  if (item.type === "expandable") {
29
- const {
30
- component: Component2,
31
- label: label2,
32
- icon: icon2,
33
- badge: badge2,
34
- children,
35
- href: href2,
36
- iconWidth: iconWidth2,
37
- truncateLabel: truncateLabel2
38
- } = item;
39
29
  return /* @__PURE__ */ jsxRuntime.jsx(
40
30
  ExpandableSidebarItem.ExpandableSidebarItem,
41
31
  {
42
- items: children,
43
- label: label2,
44
- icon: icon2,
45
- badge: badge2,
46
- href: href2,
47
- iconWidth: iconWidth2,
48
- truncateLabel: truncateLabel2,
49
- component: Component2
32
+ label: item.label,
33
+ icon: item.icon,
34
+ badge: item.badge,
35
+ href: item.href,
36
+ iconWidth: item.iconWidth,
37
+ truncateLabel: item.truncateLabel,
38
+ enabled: item.enabled,
39
+ children: item.children.map((child, idx) => renderItem(child, `${key}-c${idx}`))
50
40
  },
51
41
  key
52
42
  );
@@ -61,7 +51,9 @@ function SidebarItems({ activeLink }) {
61
51
  badge,
62
52
  href,
63
53
  iconWidth,
64
- truncateLabel
54
+ truncateLabel,
55
+ enabled: item.enabled,
56
+ external: item.external
65
57
  },
66
58
  key
67
59
  );
@@ -20,27 +20,17 @@ function SidebarItems({ activeLink }) {
20
20
  ] }, key);
21
21
  }
22
22
  if (item.type === "expandable") {
23
- const {
24
- component: Component2,
25
- label: label2,
26
- icon: icon2,
27
- badge: badge2,
28
- children,
29
- href: href2,
30
- iconWidth: iconWidth2,
31
- truncateLabel: truncateLabel2
32
- } = item;
33
23
  return /* @__PURE__ */ jsx(
34
24
  ExpandableSidebarItem,
35
25
  {
36
- items: children,
37
- label: label2,
38
- icon: icon2,
39
- badge: badge2,
40
- href: href2,
41
- iconWidth: iconWidth2,
42
- truncateLabel: truncateLabel2,
43
- component: Component2
26
+ label: item.label,
27
+ icon: item.icon,
28
+ badge: item.badge,
29
+ href: item.href,
30
+ iconWidth: item.iconWidth,
31
+ truncateLabel: item.truncateLabel,
32
+ enabled: item.enabled,
33
+ children: item.children.map((child, idx) => renderItem(child, `${key}-c${idx}`))
44
34
  },
45
35
  key
46
36
  );
@@ -55,7 +45,9 @@ function SidebarItems({ activeLink }) {
55
45
  badge,
56
46
  href,
57
47
  iconWidth,
58
- truncateLabel
48
+ truncateLabel,
49
+ enabled: item.enabled,
50
+ external: item.external
59
51
  },
60
52
  key
61
53
  );
@@ -1,7 +1,6 @@
1
1
  .group {
2
2
  display: flex;
3
3
  flex-direction: column;
4
- gap: var(--spacing-xxs);
5
4
  margin-top: var(--spacing-xs);
6
5
  }
7
6
 
@@ -17,10 +17,10 @@ const findExpandableParentChain = (targetHref, items) => {
17
17
  const dfs = (nodes, parentExpandables) => {
18
18
  for (const node of nodes) {
19
19
  const nodeHref = hasHref(node) ? normalizeHref(node.href) : null;
20
- const isExpandableParent = hasChildren(node) && hasHref(node);
20
+ const isExpandableParent = node.type === "expandable";
21
21
  const nextParents = isExpandableParent && nodeHref ? [...parentExpandables, nodeHref] : parentExpandables;
22
- if (nodeHref === target) {
23
- return isExpandableParent ? nextParents : parentExpandables;
22
+ if (node.type !== "expandable" && nodeHref === target) {
23
+ return parentExpandables;
24
24
  }
25
25
  if (hasChildren(node)) {
26
26
  const found = dfs(node.children, nextParents);
@@ -66,6 +66,7 @@ function SidebarProvider({
66
66
  children,
67
67
  items,
68
68
  initialQuery,
69
+ initialActiveLink = "",
69
70
  initialCollapsed = false,
70
71
  initialSidebarCollapsed
71
72
  }) {
@@ -73,8 +74,10 @@ function SidebarProvider({
73
74
  const [activeQuery, setActiveQuery] = react.useState(initialQuery != null ? initialQuery : "");
74
75
  const [wrapItemText, setWrapItemText] = react.useState(false);
75
76
  const [areItemsCollapsed, setItemsCollapsed] = react.useState(initialCollapsed);
76
- const [activeHref, setActiveHref] = react.useState("");
77
- const [expandedItems, setExpandedItems] = react.useState(/* @__PURE__ */ new Set());
77
+ const [activeHref, setActiveHref] = react.useState(initialActiveLink);
78
+ const [expandedItems, setExpandedItems] = react.useState(
79
+ () => new Set(findExpandableParentChain(initialActiveLink, items))
80
+ );
78
81
  const itemsRef = react.useRef(items);
79
82
  react.useEffect(() => {
80
83
  itemsRef.current = items;
@@ -121,8 +124,7 @@ function SidebarProvider({
121
124
  );
122
125
  react.useEffect(() => {
123
126
  if (!activeHref) return;
124
- const currentItems = itemsRef.current;
125
- const parents = findExpandableParentChain(activeHref, currentItems);
127
+ const parents = findExpandableParentChain(activeHref, itemsRef.current);
126
128
  if (parents.length === 0) return;
127
129
  setExpandedItems((prev) => {
128
130
  let changed = false;
@@ -24,11 +24,12 @@ export type SidebarContextValue = {
24
24
  type SidebarProviderProps = {
25
25
  children: React.ReactNode;
26
26
  items: NavBarItem[];
27
+ initialActiveLink?: string;
27
28
  initialQuery?: string | null;
28
29
  initialCollapsed?: boolean;
29
30
  initialCollapseChildren?: boolean;
30
31
  initialSidebarCollapsed?: boolean;
31
32
  };
32
- export declare function SidebarProvider({ children, items, initialQuery, initialCollapsed, initialSidebarCollapsed, }: SidebarProviderProps): JSX.Element;
33
+ export declare function SidebarProvider({ children, items, initialQuery, initialActiveLink, initialCollapsed, initialSidebarCollapsed, }: SidebarProviderProps): JSX.Element;
33
34
  export declare function useSidebar(): SidebarContextValue;
34
35
  export {};
@@ -15,10 +15,10 @@ const findExpandableParentChain = (targetHref, items) => {
15
15
  const dfs = (nodes, parentExpandables) => {
16
16
  for (const node of nodes) {
17
17
  const nodeHref = hasHref(node) ? normalizeHref(node.href) : null;
18
- const isExpandableParent = hasChildren(node) && hasHref(node);
18
+ const isExpandableParent = node.type === "expandable";
19
19
  const nextParents = isExpandableParent && nodeHref ? [...parentExpandables, nodeHref] : parentExpandables;
20
- if (nodeHref === target) {
21
- return isExpandableParent ? nextParents : parentExpandables;
20
+ if (node.type !== "expandable" && nodeHref === target) {
21
+ return parentExpandables;
22
22
  }
23
23
  if (hasChildren(node)) {
24
24
  const found = dfs(node.children, nextParents);
@@ -64,6 +64,7 @@ function SidebarProvider({
64
64
  children,
65
65
  items,
66
66
  initialQuery,
67
+ initialActiveLink = "",
67
68
  initialCollapsed = false,
68
69
  initialSidebarCollapsed
69
70
  }) {
@@ -71,8 +72,10 @@ function SidebarProvider({
71
72
  const [activeQuery, setActiveQuery] = useState(initialQuery != null ? initialQuery : "");
72
73
  const [wrapItemText, setWrapItemText] = useState(false);
73
74
  const [areItemsCollapsed, setItemsCollapsed] = useState(initialCollapsed);
74
- const [activeHref, setActiveHref] = useState("");
75
- const [expandedItems, setExpandedItems] = useState(/* @__PURE__ */ new Set());
75
+ const [activeHref, setActiveHref] = useState(initialActiveLink);
76
+ const [expandedItems, setExpandedItems] = useState(
77
+ () => new Set(findExpandableParentChain(initialActiveLink, items))
78
+ );
76
79
  const itemsRef = useRef(items);
77
80
  useEffect(() => {
78
81
  itemsRef.current = items;
@@ -119,8 +122,7 @@ function SidebarProvider({
119
122
  );
120
123
  useEffect(() => {
121
124
  if (!activeHref) return;
122
- const currentItems = itemsRef.current;
123
- const parents = findExpandableParentChain(activeHref, currentItems);
125
+ const parents = findExpandableParentChain(activeHref, itemsRef.current);
124
126
  if (parents.length === 0) return;
125
127
  setExpandedItems((prev) => {
126
128
  let changed = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dbcdk/react-components",
3
- "version": "0.0.210",
3
+ "version": "0.0.211",
4
4
  "description": "Reusable React components for DBC projects",
5
5
  "license": "ISC",
6
6
  "author": "",