@camunda/camunda-composite-components 0.23.1 → 0.23.3

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 (28) hide show
  1. package/lib/esm/package.json +1 -1
  2. package/lib/esm/src/api/endpoints.const.js +2 -2
  3. package/lib/esm/src/components/c3-navigation/c3-navigation-appbar/c3-navigation-appbar.js +8 -4
  4. package/lib/esm/src/components/c3-navigation/c3-navigation-sidebar/c3-navigation-sidebar.js +8 -4
  5. package/lib/esm/src/components/c3-navigation/c3-notification-provider/c3-notification-container.d.ts +1 -1
  6. package/lib/esm/src/components/c3-navigation/c3-notification-provider/c3-notification-container.js +16 -11
  7. package/lib/esm/src/components/c3-navigation-v2/c3-breadcrumb-bar.js +10 -10
  8. package/lib/esm/src/components/c3-navigation-v2/c3-navigation-v2.js +10 -6
  9. package/lib/esm/src/components/c3-navigation-v2/c3-navigation-v2.types.d.ts +5 -1
  10. package/lib/esm/src/components/c3-navigation-v2/c3-sidebar.js +62 -25
  11. package/lib/esm/src/components/c3-navigation-v2/c3-tools-area.js +8 -2
  12. package/lib/esm/src/components/c3-navigation-v2/index.d.ts +2 -0
  13. package/lib/esm/src/components/c3-navigation-v2/index.js +1 -0
  14. package/lib/esm/src/components/c3-navigation-v2/stories/story-templates.d.ts +3 -0
  15. package/lib/esm/src/components/c3-navigation-v2/stories/story-templates.js +587 -2
  16. package/lib/esm/src/components/c3-navigation-v2/tools/c3-info-panel.js +2 -2
  17. package/lib/esm/src/components/c3-navigation-v2/tools/c3-notifications-panel.js +16 -11
  18. package/lib/esm/src/components/c3-navigation-v2/tools/c3-user-panel.js +12 -12
  19. package/lib/esm/src/components/c3-navigation-v2/tools/panel-primitives.js +1 -1
  20. package/lib/esm/src/components/c3-navigation-v2/use-camunda-tools.js +2 -2
  21. package/lib/esm/src/components/c3-navigation-v2/use-cluster-sidebar-entries.d.ts +58 -0
  22. package/lib/esm/src/components/c3-navigation-v2/use-cluster-sidebar-entries.js +84 -0
  23. package/lib/esm/src/components/c3-navigation-v2/use-cluster-webapp-breadcrumbs.js +7 -24
  24. package/lib/esm/src/index.d.ts +4 -2
  25. package/lib/esm/src/index.js +2 -1
  26. package/lib/esm/src/utils/camunda.d.ts +16 -0
  27. package/lib/esm/src/utils/camunda.js +29 -6
  28. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/camunda-composite-components",
3
- "version": "0.23.1",
3
+ "version": "0.23.3",
4
4
  "description": "Camunda Composite Components",
5
5
  "bugs": {
6
6
  "url": "https://github.com/camunda/camunda-cloud-management-apps/issues"
@@ -23,8 +23,8 @@ export const CONSOLE = {
23
23
  };
24
24
  export const STATUS = {
25
25
  id: 'status',
26
- dev: 'https://camundaultrawombat.statuspage.io/',
27
- int: 'https://camundaultrawombat.statuspage.io/',
26
+ dev: 'https://camundaultrawombat.statuspage.io',
27
+ int: 'https://camundaultrawombat.statuspage.io',
28
28
  prod: 'https://status.camunda.io',
29
29
  };
30
30
  export function getEndpoint(stage, endpoint) {
@@ -4,7 +4,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
4
4
  * under one or more contributor license agreements. Licensed under a commercial license.
5
5
  * You may not use this file except in compliance with the commercial license.
6
6
  */
7
- import { FormLabel, HeaderGlobalAction, HeaderMenuItem, HeaderSideNavItems, Modal, SideNavItems, SideNavLink, SideNavMenu, SideNavMenuItem, } from '@carbon/react';
7
+ import { FormLabel, HeaderGlobalAction as HeaderGlobalActionBase, HeaderMenuItem, HeaderSideNavItems, Modal, SideNavItems, SideNavLink, SideNavMenu, SideNavMenuItem, } from '@carbon/react';
8
8
  import { Close } from '@carbon/react/icons/index.esm.js';
9
9
  import { Fragment, useCallback, useEffect, useState } from 'react';
10
10
  import { styled } from 'styled-components';
@@ -17,6 +17,12 @@ import { useC3UserConfiguration } from '../../c3-user-configuration/c3-user-conf
17
17
  import { ActiveOrgName } from '../c3-org-sidebar/components.js';
18
18
  import { useOnClickOutside } from '../helpers.js';
19
19
  import { NavWrapper, SideNav } from './components.js';
20
+ /**
21
+ * Carbon's `HeaderGlobalAction` is typed as `React.FC` and omits both `ref`
22
+ * (the component uses `forwardRef` internally) and `leaveDelayMs`. Both work
23
+ * at runtime, so we widen the type at the import site.
24
+ */
25
+ const HeaderGlobalAction = HeaderGlobalActionBase;
20
26
  const getOrgLink = (app, orgId, domain) => {
21
27
  switch (app) {
22
28
  case 'console': {
@@ -346,9 +352,7 @@ export const C3NavigationAppBar = ({ app: appProps, appBar, forwardRef, navbar,
346
352
  const orgName = activeOrg?.name || navbar.orgName;
347
353
  return (_jsxs(_Fragment, { children: [_jsx(HeaderGlobalAction, { ref: iconRef, "aria-label": 'Camunda components', isActive: appBar.isOpen, onClick: () => {
348
354
  toggleAppbar(!appBar.isOpen);
349
- }, tooltipAlignment: 'start',
350
- // @ts-expect-error
351
- leaveDelayMs: 100, children: appBar.isOpen ? _jsx(Close, { size: 20 }) : _jsx(C3AppMenuIcon, { size: 20 }) }), _jsx(OrgPickerModal, { activeOrg: activeOrg, appToNavigateTo: appToNavigateTo, isOpen: isOrgPickerModalOpen, orgs: clustersByOrgId, onCancel: () => {
355
+ }, tooltipAlignment: 'start', leaveDelayMs: 100, children: appBar.isOpen ? _jsx(Close, { size: 20 }) : _jsx(C3AppMenuIcon, { size: 20 }) }), _jsx(OrgPickerModal, { activeOrg: activeOrg, appToNavigateTo: appToNavigateTo, isOpen: isOrgPickerModalOpen, orgs: clustersByOrgId, onCancel: () => {
352
356
  setIsOrgPickerModalOpen(false);
353
357
  }, loadingStatus: loadingStatus, domain: domain }), _jsx(NavWrapper, { children: _jsx(SideNav, { ref: panelRef, "aria-label": appBar.ariaLabel || 'Side Navigation', expanded: appBar.isOpen, isPersistent: false, children: _jsxs(SideNavItems, { children: [orgName && (_jsx("li", { children: _jsxs(OrgNameWrapper, { children: [_jsx(FormLabel, { children: "Organization" }), _jsx(ActiveOrgName, { className: 'textPrimary', title: orgName, children: orgName })] }) })), _jsx("li", { children: navbar.elements.length > 0 && (_jsx(HeaderSideNavItems, { hasDivider: true, children: navbar.elements.map((element) => (_jsx(HeaderMenuItem, { as: element.routeProps && forwardRef, isActive: element.isCurrentPage, ...element.routeProps, onClick: () => {
354
358
  if (element.routeProps.onClick) {
@@ -4,12 +4,18 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
4
  * under one or more contributor license agreements. Licensed under a commercial license.
5
5
  * You may not use this file except in compliance with the commercial license.
6
6
  */
7
- import { Button, HeaderPanel as CarbonHeaderPanel, HeaderGlobalAction, Stack, SwitcherDivider, } from '@carbon/react';
7
+ import { Button, HeaderPanel as CarbonHeaderPanel, HeaderGlobalAction as HeaderGlobalActionBase, Stack, SwitcherDivider, } from '@carbon/react';
8
8
  import { useEffect } from 'react';
9
9
  import styled from 'styled-components';
10
10
  import { useOnClickOutside } from '../helpers.js';
11
11
  import C3NavigationSidebarElement from './c3-navigation-sidebar-element.js';
12
12
  import { useSidebarState } from './c3-sidebar-state-provider.js';
13
+ /**
14
+ * Carbon's `HeaderGlobalAction` is typed as `React.FC` and omits both `ref`
15
+ * (the component uses `forwardRef` internally) and `leaveDelayMs`. Both work
16
+ * at runtime, so we widen the type at the import site.
17
+ */
18
+ const HeaderGlobalAction = HeaderGlobalActionBase;
13
19
  const Wrapper = styled.div `
14
20
  .cds--popover * {
15
21
  z-index: 9000;
@@ -67,9 +73,7 @@ const C3NavigationSideBar = (props) => {
67
73
  }, [isOpen]);
68
74
  return (_jsxs(Wrapper, { children: [_jsx(HeaderGlobalAction, { ref: setIconRef, "aria-label": sideBar.tooltip ?? `Open ${sideBar.ariaLabel}`, "aria-expanded": isOpen, "aria-controls": id, onClick: () => {
69
75
  setIsOpen(!isOpen);
70
- }, isActive: isOpen, tooltipAlignment: sideBar.type === 'user' ? 'end' : 'center',
71
- /* @ts-expect-error */
72
- leaveDelayMs: 100, children: icon }), _jsxs(HeaderPanel, { ref: setPanelRef, expanded: isOpen, "data-floating-menu-container": true, children: [_jsxs(Stack, { children: [children, sideBar.elements &&
76
+ }, isActive: isOpen, tooltipAlignment: sideBar.type === 'user' ? 'end' : 'center', leaveDelayMs: 100, children: icon }), _jsxs(HeaderPanel, { ref: setPanelRef, expanded: isOpen, "data-floating-menu-container": true, children: [_jsxs(Stack, { children: [children, sideBar.elements &&
73
77
  sideBar.elements.length > 0 &&
74
78
  'customElements' in sideBar &&
75
79
  sideBar.customElements &&
@@ -1,4 +1,4 @@
1
- import { type FC } from 'react';
1
+ import type { FC } from 'react';
2
2
  import type { Notification as NotificationProps } from '../../../api/notifications';
3
3
  export declare const NotificationTitle: any;
4
4
  export declare const NotificationDescription: any;
@@ -6,14 +6,24 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  */
7
7
  import { Button, Link, Tile } from '@carbon/react';
8
8
  import { Close } from '@carbon/react/icons/index.esm.js';
9
- import { useState } from 'react';
10
9
  import styled from 'styled-components';
10
+ const CloseButtonWrapper = styled.div `
11
+ position: absolute;
12
+ top: var(--cds-spacing-02);
13
+ right: var(--cds-spacing-02);
14
+ opacity: 0;
15
+
16
+ &:focus-within {
17
+ opacity: 1;
18
+ }
19
+ `;
11
20
  const NotificationTile = styled(Tile) `
21
+ position: relative;
12
22
  box-shadow: inset 0px -1px 0px var(--cds-border-subtle-01);
13
- `;
14
- const CloseButton = styled(Button) `
15
- margin-right: -1rem;
16
- visibility: ${({ $show }) => ($show ? 'visible' : 'hidden')};
23
+
24
+ &:hover ${CloseButtonWrapper} {
25
+ opacity: 1;
26
+ }
17
27
  `;
18
28
  const UnreadNotificationHeader = styled.div `
19
29
  display: flex;
@@ -78,7 +88,6 @@ const getReadableTimestamp = (timestamp) => {
78
88
  return date.toDateString();
79
89
  };
80
90
  const C3NotificationContainer = ({ description, state, timestamp, title, onDismiss, meta, onLinkClick, originalOnLinkClick, unread = false, }) => {
81
- const [isHovering, setIsHovering] = useState(false);
82
91
  const dismissNotification = (e) => {
83
92
  e.stopPropagation();
84
93
  onDismiss();
@@ -91,10 +100,6 @@ const C3NotificationContainer = ({ description, state, timestamp, title, onDismi
91
100
  }
92
101
  return true;
93
102
  };
94
- return (_jsxs(NotificationTile, { onMouseEnter: () => {
95
- setIsHovering(true);
96
- }, onMouseLeave: () => {
97
- setIsHovering(false);
98
- }, children: [_jsxs(UnreadNotificationHeader, { children: [(state === 'new' || unread) && _jsx(Dot, {}), _jsxs(NotificationHeader, { children: [_jsx(Timestamp, { children: getReadableTimestamp(timestamp) }), _jsx(CloseButton, { kind: 'ghost', hasIconOnly: true, iconDescription: 'Dismiss', align: 'bottom-right', renderIcon: Close, onClick: dismissNotification, size: 'sm', "$show": isHovering })] })] }), _jsx(NotificationTitle, { children: title }), _jsx(NotificationDescription, { children: description }), meta && (_jsx(LinkContainer, { children: _jsx(Link, { href: meta.href, onClick: handleLinkClick, visited: true, style: { fontSize: 'var(--cds-helper-text-01-font-size)' }, children: meta.label }) }))] }));
103
+ return (_jsxs(NotificationTile, { children: [_jsxs(UnreadNotificationHeader, { children: [(state === 'new' || unread) && _jsx(Dot, {}), _jsx(NotificationHeader, { children: _jsx(Timestamp, { children: getReadableTimestamp(timestamp) }) })] }), _jsx(NotificationTitle, { children: title }), _jsx(NotificationDescription, { children: description }), meta && (_jsx(LinkContainer, { children: _jsx(Link, { href: meta.href, onClick: handleLinkClick, visited: true, style: { fontSize: 'var(--cds-helper-text-01-font-size)' }, children: meta.label }) })), _jsx(CloseButtonWrapper, { children: _jsx(Button, { kind: 'ghost', hasIconOnly: true, iconDescription: 'Dismiss', tooltipPosition: 'top', tooltipAlignment: 'end', renderIcon: Close, onClick: dismissNotification, size: 'sm' }) })] }));
99
104
  };
100
105
  export default C3NotificationContainer;
@@ -17,8 +17,8 @@ const SegmentWrapper = styled.div `
17
17
  const SegmentButton = styled.button `
18
18
  display: flex;
19
19
  align-items: center;
20
- gap: 0.5rem;
21
- padding: 0.25rem 0.5rem;
20
+ gap: var(--cds-spacing-03);
21
+ padding: var(--cds-spacing-02) var(--cds-spacing-03);
22
22
  background: transparent;
23
23
  border: none;
24
24
  border-radius: 4px;
@@ -45,7 +45,7 @@ const ChevronButton = styled.button `
45
45
  display: flex;
46
46
  align-items: center;
47
47
  justify-content: center;
48
- padding: 0.25rem;
48
+ padding: var(--cds-spacing-02);
49
49
  background: ${(p) => (p.$isOpen ? 'var(--cds-layer-01)' : 'transparent')};
50
50
  border: none;
51
51
  border-radius: 4px;
@@ -71,7 +71,7 @@ const ActionMenuButton = styled.button `
71
71
  display: flex;
72
72
  align-items: center;
73
73
  justify-content: center;
74
- padding: 0.25rem;
74
+ padding: var(--cds-spacing-02);
75
75
  background: ${(p) => (p.$isOpen ? 'var(--cds-layer-01)' : 'transparent')};
76
76
  border: none;
77
77
  border-radius: 4px;
@@ -92,7 +92,7 @@ const ActionMenuItem = styled.button `
92
92
  width: 100%;
93
93
  display: flex;
94
94
  align-items: center;
95
- padding: 0.625rem 1rem;
95
+ padding: 0.625rem var(--cds-spacing-05);
96
96
  background: transparent;
97
97
  border: none;
98
98
  border-top: ${(p) => (p.$hasDivider ? '1px solid var(--cds-border-subtle)' : 'none')};
@@ -133,8 +133,8 @@ const DropdownItem = styled.button `
133
133
  width: 100%;
134
134
  display: flex;
135
135
  align-items: center;
136
- gap: 0.75rem;
137
- padding: 0.625rem 1rem;
136
+ gap: var(--cds-spacing-04);
137
+ padding: 0.625rem var(--cds-spacing-05);
138
138
  background: ${(p) => (p.$isSelected ? 'var(--cds-layer-selected)' : 'transparent')};
139
139
  border: none;
140
140
  cursor: pointer;
@@ -163,7 +163,7 @@ const DropdownItemLabel = styled.span `
163
163
  const Separator = styled.span `
164
164
  display: flex;
165
165
  align-items: center;
166
- padding: 0 0.25rem;
166
+ padding: 0 var(--cds-spacing-02);
167
167
  color: var(--cds-text-secondary);
168
168
  font-size: 0.875rem;
169
169
  user-select: none;
@@ -181,7 +181,7 @@ const DropdownEntry = ({ item, onSelect, linkComponent, }) => {
181
181
  : undefined, ...(item.linkProps ?? {}), "$isSelected": !!item.isSelected, onClick: () => onSelect(item), role: 'option', tabIndex: -1, "aria-selected": !!item.isSelected, children: [Icon && (_jsx(Icon, { size: 16, style: { color: 'var(--cds-icon-secondary)', flexShrink: 0 } })), _jsx(DropdownItemLabel, { children: item.label }), item.trailingElement, item.isSelected && (_jsx(Checkmark, { size: 16, style: { color: 'var(--cds-icon-primary)', flexShrink: 0 } }))] }));
182
182
  };
183
183
  const DropdownTitle = styled.div `
184
- padding: 0.75rem 1rem 0.5rem;
184
+ padding: var(--cds-spacing-04) var(--cds-spacing-05) var(--cds-spacing-03);
185
185
  font-size: 0.6875rem;
186
186
  font-weight: 600;
187
187
  color: var(--cds-text-secondary);
@@ -366,6 +366,6 @@ const BreadcrumbBarWrapper = styled.div `
366
366
  flex: 1;
367
367
  min-width: 0;
368
368
  overflow-x: auto;
369
- padding-left: 0.5rem;
369
+ padding-left: var(--cds-spacing-03);
370
370
  `;
371
371
  export const C3BreadcrumbBar = ({ segments, linkComponent, }) => (_jsx(BreadcrumbBarWrapper, { role: 'navigation', "aria-label": 'Breadcrumb', children: segments.map((segment, index) => (_jsx(BreadcrumbSegmentComponent, { segment: segment, isLast: index === segments.length - 1, linkComponent: linkComponent }, segment.key))) }));
@@ -5,7 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
5
5
  * You may not use this file except in compliance with the commercial license.
6
6
  */
7
7
  import { Header, HeaderGlobalAction, HeaderGlobalBar, SkipToContent, } from '@carbon/react';
8
- import { useEffect } from 'react';
8
+ import { Fragment, useEffect } from 'react';
9
9
  import styled from 'styled-components';
10
10
  import { CamundaLogo } from '../../assets/c3-icons.js';
11
11
  import { C3BreadcrumbBar } from './c3-breadcrumb-bar.js';
@@ -23,7 +23,7 @@ const LogoSection = styled.a `
23
23
  justify-content: center;
24
24
  width: 3rem;
25
25
  flex-shrink: 0;
26
- gap: 0.5rem;
26
+ gap: var(--cds-spacing-03);
27
27
  border-right: 1px solid var(--cds-border-subtle);
28
28
  height: 100%;
29
29
  text-decoration: none;
@@ -60,13 +60,17 @@ export const C3NavigationV2 = ({ app, skipToContentTargetId, skipToContentLabel
60
60
  return (_jsxs(_Fragment, { children: [_jsxs(StyledHeader, { "aria-label": ariaLabel, children: [_jsx(SkipToContent, { href: `#${skipToContentTargetId}`, children: skipToContentLabel }), _jsxs(LogoSection, { as: app.linkProps ? LinkEl : 'div', ...(app.linkProps ?? {}), children: [_jsx(CamundaLogo, { "aria-label": 'Camunda' }), app.name && _jsx("span", { children: app.name })] }), breadcrumbs && breadcrumbs.length > 0 && (_jsx(C3BreadcrumbBar, { segments: breadcrumbs, linkComponent: LinkEl })), _jsxs(HeaderGlobalBar, { children: [headerTrailingContent && (_jsx("span", { style: {
61
61
  display: 'flex',
62
62
  alignItems: 'center',
63
- padding: '0 0.5rem',
64
- }, children: headerTrailingContent })), tools && tools.length > 0 && (_jsx(C3ToolsArea, { tools: tools, activeToolKey: activeToolKey, onActiveToolChange: onActiveToolChange })), globalActions?.map((action, index) => {
63
+ padding: '0 var(--cds-spacing-03)',
64
+ }, children: headerTrailingContent })), tools && tools.length > 0 && (_jsx(C3ToolsArea, { tools: tools, activeToolKey: activeToolKey, onActiveToolChange: onActiveToolChange })), globalActions
65
+ ?.filter((action) => action.element || action.icon)
66
+ .map((action, index, arr) => {
65
67
  if (action.element) {
66
- return _jsx("span", { children: action.element }, action.key);
68
+ // Direct flex child of HeaderGlobalBar (no wrapper); preserves
69
+ // the layout context custom elements like C4Search rely on.
70
+ return _jsx(Fragment, { children: action.element }, action.key);
67
71
  }
68
72
  const Icon = action.icon;
69
- const isLast = index === (globalActions?.length ?? 0) - 1;
73
+ const isLast = index === arr.length - 1;
70
74
  return (_jsx(HeaderGlobalAction, { "aria-label": action.label, onClick: action.onClick, tooltipAlignment: isLast ? 'end' : undefined, children: _jsx(Icon, { size: 20 }) }, action.key));
71
75
  })] })] }), sidebar && _jsx(C3Sidebar, { ...sidebar })] }));
72
76
  };
@@ -74,7 +74,11 @@ export interface BreadcrumbSegment {
74
74
  export interface GlobalActionButton {
75
75
  key: string;
76
76
  label: string;
77
- icon: React.ComponentType<{
77
+ /**
78
+ * Required when `element` is omitted. Ignored when `element` is supplied
79
+ * (the custom element renders instead of the default icon button).
80
+ */
81
+ icon?: React.ComponentType<{
78
82
  size?: number;
79
83
  }>;
80
84
  onClick?: () => void;
@@ -27,20 +27,22 @@ const ScrollArea = styled.div `
27
27
  flex-direction: column;
28
28
  overflow-y: ${(p) => (p.$sidebarExpanded ? 'auto' : 'visible')};
29
29
  overflow-x: ${(p) => (p.$sidebarExpanded ? 'hidden' : 'visible')};
30
- padding-top: 0.5rem;
31
- padding-bottom: 1rem;
30
+ padding-top: var(--cds-spacing-03);
31
+ padding-bottom: var(--cds-spacing-05);
32
32
  `;
33
33
  const NavButton = styled.button `
34
34
  display: flex;
35
35
  align-items: center;
36
36
  flex-wrap: nowrap;
37
- gap: 0.75rem;
37
+ gap: var(--cds-spacing-04);
38
38
  width: 100%;
39
39
  min-height: 2.5rem;
40
40
  padding: ${(p) => {
41
41
  if (p.$depth > 0)
42
- return `0.625rem 1rem 0.625rem ${0.75 + p.$depth * 1.75}rem`;
43
- return p.$isExpanded ? '0.75rem 1rem' : '0.75rem';
42
+ return `0.625rem var(--cds-spacing-05) 0.625rem ${0.75 + p.$depth * 1.75}rem`;
43
+ return p.$isExpanded
44
+ ? 'var(--cds-spacing-04) var(--cds-spacing-05)'
45
+ : 'var(--cds-spacing-04)';
44
46
  }};
45
47
  justify-content: ${(p) => (p.$isExpanded ? 'flex-start' : 'center')};
46
48
  background: ${(p) => (p.$isActive ? 'var(--cds-layer-selected)' : 'transparent')};
@@ -68,6 +70,8 @@ const NavButton = styled.button `
68
70
  }
69
71
  `;
70
72
  const NavLabel = styled.span `
73
+ flex: 1;
74
+ min-width: 0;
71
75
  overflow: hidden;
72
76
  text-overflow: ellipsis;
73
77
  white-space: nowrap;
@@ -91,10 +95,11 @@ const GroupLabelButton = styled.button `
91
95
  display: flex;
92
96
  align-items: center;
93
97
  flex: 1;
98
+ min-width: 0;
94
99
  padding: ${(p) => {
95
100
  if (p.$depth > 0)
96
- return `0.75rem 0 0.75rem ${0.75 + p.$depth * 1.75}rem`;
97
- return '0.75rem 0 0.75rem 1rem';
101
+ return `var(--cds-spacing-04) 0 var(--cds-spacing-04) ${0.75 + p.$depth * 1.75}rem`;
102
+ return 'var(--cds-spacing-04) 0 var(--cds-spacing-04) var(--cds-spacing-05)';
98
103
  }};
99
104
  background: transparent;
100
105
  border: none;
@@ -105,7 +110,7 @@ const GroupLabelButton = styled.button `
105
110
  text-align: left;
106
111
  text-decoration: none;
107
112
  transition: color 0.15s;
108
- gap: 0.75rem;
113
+ gap: var(--cds-spacing-04);
109
114
 
110
115
  &:hover {
111
116
  color: var(--cds-text-primary);
@@ -121,8 +126,8 @@ const ExpandButton = styled.button `
121
126
  display: flex;
122
127
  align-items: center;
123
128
  justify-content: center;
124
- padding: 0.25rem;
125
- margin-right: 0.75rem;
129
+ padding: var(--cds-spacing-02);
130
+ margin-right: var(--cds-spacing-04);
126
131
  background: transparent;
127
132
  border: none;
128
133
  border-radius: 4px;
@@ -152,12 +157,13 @@ const PlainGroupLabel = styled.span `
152
157
  display: flex;
153
158
  align-items: center;
154
159
  flex: 1;
160
+ min-width: 0;
155
161
  padding: ${(p) => {
156
162
  if (p.$depth > 0)
157
- return `0.75rem 0 0.75rem ${0.75 + p.$depth * 1.75}rem`;
158
- return '0.75rem 0 0.75rem 1rem';
163
+ return `var(--cds-spacing-04) 0 var(--cds-spacing-04) ${0.75 + p.$depth * 1.75}rem`;
164
+ return 'var(--cds-spacing-04) 0 var(--cds-spacing-04) var(--cds-spacing-05)';
159
165
  }};
160
- gap: 0.75rem;
166
+ gap: var(--cds-spacing-04);
161
167
  color: var(--cds-text-secondary);
162
168
  font-size: 0.875rem;
163
169
  font-weight: 400;
@@ -166,8 +172,8 @@ const PlainGroupExpandButton = styled.button `
166
172
  display: flex;
167
173
  align-items: center;
168
174
  justify-content: center;
169
- padding: 0.25rem;
170
- margin-right: 0.75rem;
175
+ padding: var(--cds-spacing-02);
176
+ margin-right: var(--cds-spacing-04);
171
177
  background: transparent;
172
178
  border: none;
173
179
  border-radius: 4px;
@@ -189,7 +195,7 @@ const StyledPopover = styled(Popover) `
189
195
 
190
196
  .cds--popover-content {
191
197
  pointer-events: none;
192
- padding: 0.1875rem 0.5rem;
198
+ padding: 0.1875rem var(--cds-spacing-03);
193
199
  font-size: var(--cds-body-compact-01-font-size);
194
200
  white-space: nowrap;
195
201
  }
@@ -214,18 +220,18 @@ const CollapsedItemTooltip = ({ label, children, }) => {
214
220
  return (_jsxs(StyledPopover, { open: open, align: 'right', highContrast: true, dropShadow: false, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [children, _jsx(PopoverContent, { children: label })] }));
215
221
  };
216
222
  const SectionDivider = styled.div `
217
- border-top: ${(p) => (p.$hideTopDivider ? 'none' : '1px solid var(--cds-border-subtle)')};
223
+ border-top: ${(p) => (p.$hideTopDivider ? 'none' : '1px solid var(--cds-border-subtle-01)')};
218
224
  margin-top: ${(p) => {
219
225
  if (p.$eatScrollPadding)
220
- return '-0.5rem';
226
+ return 'calc(-1 * var(--cds-spacing-03))';
221
227
  if (p.$tight)
222
228
  return '0';
223
- return p.$hideTopDivider ? '0' : '0.5rem';
229
+ return p.$hideTopDivider ? '0' : 'var(--cds-spacing-03)';
224
230
  }};
225
- padding-top: ${(p) => (p.$hideTopDivider ? '0' : '0.5rem')};
231
+ padding-top: ${(p) => (p.$hideTopDivider ? '0' : 'var(--cds-spacing-03)')};
226
232
  `;
227
233
  const SectionTitle = styled.div `
228
- padding: 0.5rem 1rem;
234
+ padding: var(--cds-spacing-03) var(--cds-spacing-05);
229
235
  font-size: 0.6875rem;
230
236
  font-weight: 600;
231
237
  color: var(--cds-text-secondary);
@@ -240,10 +246,12 @@ const CollapseButton = styled.button `
240
246
  display: flex;
241
247
  align-items: center;
242
248
  flex-wrap: nowrap;
243
- gap: 0.75rem;
249
+ gap: var(--cds-spacing-04);
244
250
  width: 100%;
245
251
  min-height: 2.5rem;
246
- padding: ${(p) => (p.$isExpanded ? '0.75rem 1rem' : '0.75rem')};
252
+ padding: ${(p) => p.$isExpanded
253
+ ? 'var(--cds-spacing-04) var(--cds-spacing-05)'
254
+ : 'var(--cds-spacing-04)'};
247
255
  justify-content: ${(p) => (p.$isExpanded ? 'flex-start' : 'center')};
248
256
  background: transparent;
249
257
  border: none;
@@ -272,6 +280,34 @@ const resolveLinkAs = (linkProps, linkComponent) => {
272
280
  return 'a';
273
281
  return linkComponent;
274
282
  };
283
+ const pruneNode = (node) => {
284
+ if (node.type === 'item') {
285
+ if (!node.onClick && !node.linkProps)
286
+ return null;
287
+ return node;
288
+ }
289
+ if (node.type === 'group') {
290
+ const children = pruneChildren(node.children);
291
+ if (children.length === 0)
292
+ return null;
293
+ return { ...node, children };
294
+ }
295
+ if (node.type === 'group-item') {
296
+ const children = pruneChildren(node.children);
297
+ const isClickable = !!(node.onClick || node.linkProps);
298
+ if (children.length === 0 && !isClickable)
299
+ return null;
300
+ return { ...node, children };
301
+ }
302
+ const children = pruneChildren(node.children);
303
+ if (children.length === 0)
304
+ return null;
305
+ return { ...node, children };
306
+ };
307
+ const pruneChildren = (nodes) => nodes.flatMap((n) => {
308
+ const pruned = pruneNode(n);
309
+ return pruned ? [pruned] : [];
310
+ });
275
311
  const ItemNode = ({ node, sidebarExpanded, depth, linkComponent, }) => {
276
312
  const Icon = node.icon;
277
313
  const content = (_jsxs(NavButton, { as: resolveLinkAs(node.linkProps, linkComponent), ...(node.linkProps ?? {}), "$isActive": !!node.isActive, "$isExpanded": sidebarExpanded, "$depth": depth, onClick: node.onClick, "aria-current": node.isActive ? 'page' : undefined, children: [_jsx(Icon, { size: 20, style: { flexShrink: 0 } }), sidebarExpanded && _jsx(NavLabel, { children: node.label }), sidebarExpanded && node.trailingElement] }));
@@ -287,7 +323,7 @@ const GroupItemNode = ({ node, sidebarExpanded, depth, linkComponent, }) => {
287
323
  const collapsed = (_jsx(NavButton, { as: resolveLinkAs(node.linkProps, linkComponent), ...(node.linkProps ?? {}), "$isActive": isActive, "$isExpanded": false, "$depth": 0, onClick: node.onClick ?? node.onToggleExpand, "aria-current": isActive ? 'page' : undefined, children: _jsx(Icon, { size: 20, style: { flexShrink: 0 } }) }));
288
324
  return (_jsx(CollapsedItemTooltip, { label: node.label, children: collapsed }));
289
325
  }
290
- return (_jsxs("div", { children: [_jsxs(GroupHeader, { "$isActive": isActive, "$depth": depth, children: [_jsxs(GroupLabelButton, { as: resolveLinkAs(node.linkProps, linkComponent), ...(node.linkProps ?? {}), "$isActive": isActive, "$isClickable": true, "$depth": depth, onClick: node.onClick, "aria-current": isActive ? 'page' : undefined, children: [_jsx(Icon, { size: 20, style: { flexShrink: 0 } }), _jsx(NavLabel, { children: node.label }), node.trailingElement] }), node.onToggleExpand && (_jsx(ExpandButton, { "data-expand": true, onClick: (e) => {
326
+ return (_jsxs("div", { children: [_jsxs(GroupHeader, { "$isActive": isActive, "$depth": depth, children: [_jsxs(GroupLabelButton, { as: resolveLinkAs(node.linkProps, linkComponent), ...(node.linkProps ?? {}), "$isActive": isActive, "$isClickable": true, "$depth": depth, onClick: node.onClick, "aria-current": isActive ? 'page' : undefined, children: [_jsx(Icon, { size: 20, style: { flexShrink: 0 } }), _jsx(NavLabel, { children: node.label }), node.trailingElement] }), node.onToggleExpand && node.children.length > 0 && (_jsx(ExpandButton, { "data-expand": true, onClick: (e) => {
291
327
  e.stopPropagation();
292
328
  node.onToggleExpand?.();
293
329
  }, "aria-label": node.isExpanded
@@ -330,11 +366,12 @@ const SidebarNodeComponent = ({ node, sidebarExpanded, depth, linkComponent, hid
330
366
  */
331
367
  export const C3Sidebar = ({ ariaLabel, children: nodes, isExpanded = true, onToggleExpanded, expandedWidth = '16rem', collapsedWidth = '3rem', linkComponent, }) => {
332
368
  const width = isExpanded ? expandedWidth : collapsedWidth;
369
+ const prunedNodes = pruneChildren(nodes);
333
370
  return (_jsxs(SidebarNav, { "$width": width, "aria-label": ariaLabel, children: [_jsx(ScrollArea, { "$sidebarExpanded": isExpanded, children: (() => {
334
371
  let sectionSeen = false;
335
372
  let prevSectionCompact = false;
336
373
  let hasNonSectionNodes = false;
337
- return nodes.map((node) => {
374
+ return prunedNodes.map((node) => {
338
375
  let hideTopDivider = false;
339
376
  let eatScrollPadding = false;
340
377
  let tight = false;
@@ -20,6 +20,10 @@ const ToolsPanel = styled.div `
20
20
  z-index: 8000;
21
21
  display: flex;
22
22
  flex-direction: column;
23
+
24
+ & > * {
25
+ flex-shrink: 0;
26
+ }
23
27
  `;
24
28
  /**
25
29
  * Renders tool buttons inline (for HeaderGlobalBar) and manages a single shared
@@ -85,9 +89,11 @@ export const C3ToolsArea = ({ tools, activeToolKey: controlledKey, onActiveToolC
85
89
  }, [activeKey, setActive]);
86
90
  const handlePanelBlur = useCallback((event) => {
87
91
  const next = event.relatedTarget;
88
- if (next && panelRef.current?.contains(next))
92
+ if (!next)
89
93
  return;
90
- if (next?.closest?.('.cds--header'))
94
+ if (panelRef.current?.contains(next))
95
+ return;
96
+ if (next.closest?.('.cds--header'))
91
97
  return;
92
98
  setActive(null);
93
99
  }, [setActive]);
@@ -14,5 +14,7 @@ export type { BreadcrumbDescriptor, BreadcrumbDropdownItemDescriptor, GroupDescr
14
14
  export { useC3NavigationV2 } from './use-c3-navigation-v2';
15
15
  export type { HelpToolOptions, InfoToolOptions, NotificationsToolOptions, UseCamundaToolsOptions, UserToolOptions, } from './use-camunda-tools';
16
16
  export { useCamundaTools } from './use-camunda-tools';
17
+ export type { UseClusterSidebarEntriesOptions } from './use-cluster-sidebar-entries';
18
+ export { useClusterSidebarEntries } from './use-cluster-sidebar-entries';
17
19
  export type { UseClusterWebappBreadcrumbsOptions } from './use-cluster-webapp-breadcrumbs';
18
20
  export { useClusterWebappBreadcrumbs } from './use-cluster-webapp-breadcrumbs';
@@ -12,4 +12,5 @@ export { C3NotificationsPanel } from './tools/c3-notifications-panel.js';
12
12
  export { C3UserPanel } from './tools/c3-user-panel.js';
13
13
  export { useC3NavigationV2 } from './use-c3-navigation-v2.js';
14
14
  export { useCamundaTools } from './use-camunda-tools.js';
15
+ export { useClusterSidebarEntries } from './use-cluster-sidebar-entries.js';
15
16
  export { useClusterWebappBreadcrumbs } from './use-cluster-webapp-breadcrumbs.js';
@@ -8,4 +8,7 @@ export declare const ToolPanelsTemplate: FC;
8
8
  export declare const BreadcrumbActionsTemplate: FC;
9
9
  export declare const ClusterWebappTemplate: FC;
10
10
  export declare const HeaderTrailingContentTemplate: FC;
11
+ export declare const PruningTemplate: FC;
12
+ export declare const BuildClusterSidebarEntriesTemplate: FC;
11
13
  export declare const CompactSectionTemplate: FC;
14
+ export declare const GlobalActionWithCustomElementTemplate: FC;