@camunda/camunda-composite-components 0.23.4 → 0.24.0

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 (26) hide show
  1. package/lib/esm/package.json +1 -1
  2. package/lib/esm/src/components/c3-license-tag/c3-license-tag.d.ts +4 -5
  3. package/lib/esm/src/components/c3-license-tag/c3-license-tag.js +54 -47
  4. package/lib/esm/src/components/c3-navigation-v2/c3-navigation-v2.js +1 -1
  5. package/lib/esm/src/components/c3-navigation-v2/c3-navigation-v2.types.d.ts +14 -0
  6. package/lib/esm/src/components/c3-navigation-v2/c3-sidebar.d.ts +1 -1
  7. package/lib/esm/src/components/c3-navigation-v2/c3-sidebar.js +62 -47
  8. package/lib/esm/src/components/c3-navigation-v2/index.d.ts +5 -3
  9. package/lib/esm/src/components/c3-navigation-v2/index.js +1 -0
  10. package/lib/esm/src/components/c3-navigation-v2/tools/c3-info-panel.d.ts +2 -1
  11. package/lib/esm/src/components/c3-navigation-v2/tools/c3-info-panel.js +1 -1
  12. package/lib/esm/src/components/c3-navigation-v2/tools/c3-notifications-panel.d.ts +11 -0
  13. package/lib/esm/src/components/c3-navigation-v2/tools/c3-notifications-panel.js +9 -5
  14. package/lib/esm/src/components/c3-navigation-v2/tools/c3-theme-selector.d.ts +25 -0
  15. package/lib/esm/src/components/c3-navigation-v2/tools/c3-theme-selector.js +15 -0
  16. package/lib/esm/src/components/c3-navigation-v2/tools/c3-user-panel.d.ts +15 -0
  17. package/lib/esm/src/components/c3-navigation-v2/tools/c3-user-panel.js +10 -17
  18. package/lib/esm/src/components/c3-navigation-v2/use-c3-navigation-v2.d.ts +3 -1
  19. package/lib/esm/src/components/c3-navigation-v2/use-c3-navigation-v2.js +2 -1
  20. package/lib/esm/src/components/c3-navigation-v2/use-camunda-tools.d.ts +7 -1
  21. package/lib/esm/src/components/c3-navigation-v2/use-camunda-tools.js +4 -4
  22. package/lib/esm/src/components/c3-navigation-v2/use-cluster-webapp-breadcrumbs.d.ts +16 -18
  23. package/lib/esm/src/components/c3-navigation-v2/use-cluster-webapp-breadcrumbs.js +146 -36
  24. package/lib/esm/src/index.d.ts +2 -2
  25. package/lib/esm/src/index.js +1 -1
  26. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/camunda-composite-components",
3
- "version": "0.23.4",
3
+ "version": "0.24.0",
4
4
  "description": "Camunda Composite Components",
5
5
  "bugs": {
6
6
  "url": "https://github.com/camunda/camunda-cloud-management-apps/issues"
@@ -8,10 +8,9 @@ export interface C3LicenseTagProps {
8
8
  expiresAt?: number | string;
9
9
  }
10
10
  /**
11
- * Renders a license status tag for the Camunda header. Handles all license
12
- * states: production, non-production (with tooltip), non-commercial,
13
- * non-commercial expiring (with countdown), and non-commercial expired.
14
- *
15
- * Drop into `headerTrailingContent` of C3NavigationV2.
11
+ * Renders license status tags for the Camunda header. Mirrors V1 c3-navigation:
12
+ * always renders a production-status tag, plus a non-commercial tag (or its
13
+ * expiring/expired variant) when isCommercial === false. Drop the element into
14
+ * `headerTrailingContent` of C3NavigationV2.
16
15
  */
17
16
  export declare const C3LicenseTag: (props: C3LicenseTagProps) => JSX.Element | null;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  /*
3
3
  * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
4
4
  * under one or more contributor license agreements. Licensed under a commercial license.
@@ -17,63 +17,70 @@ function resolveExpiresAt(value) {
17
17
  return Date.parse(value);
18
18
  return value;
19
19
  }
20
- function getTagVariant(props) {
20
+ // Matches V1 c3-navigation: production-status tag is always shown, and a
21
+ // non-commercial tag is shown additionally when isCommercial === false. They
22
+ // stack rather than override each other.
23
+ function getTagVariants(props) {
21
24
  const { isProductionLicense, isCommercial, expiresAt: rawExpiresAt } = props;
22
25
  const expiresAt = resolveExpiresAt(rawExpiresAt);
23
26
  const now = Date.now();
24
- // Non-commercial expired
25
- if (isCommercial === false && expiresAt !== undefined && expiresAt < now) {
26
- return {
27
- label: 'Non-commercial license - expired',
28
- color: 'red',
29
- icon: Warning,
30
- tooltip: (_jsx("p", { children: "To continue using all features, please renew your license." })),
31
- };
32
- }
33
- // Non-commercial about to expire (within 30 days)
34
- if (isCommercial === false &&
35
- expiresAt !== undefined &&
36
- expiresAt - LICENSE_EXPIRY_THRESHOLD < now &&
37
- expiresAt > now) {
38
- const daysLeft = Math.floor((expiresAt - now) / DAY);
39
- return {
40
- label: `Non-commercial license - ${daysLeft} ${daysLeft > 1 ? 'days' : 'day'} left`,
41
- color: 'blue',
42
- icon: Time,
43
- tooltip: (_jsx("p", { children: "Please renew and provide new license keys to continue production use of Camunda." })),
44
- };
45
- }
46
- // Non-commercial (not expiring soon or no expiry)
47
- if (isCommercial === false &&
48
- (expiresAt === undefined || expiresAt - LICENSE_EXPIRY_THRESHOLD >= now)) {
49
- return {
50
- label: 'Non-commercial license',
51
- color: 'gray',
52
- };
53
- }
54
- // Production or non-production license
55
- return {
27
+ const variants = [];
28
+ variants.push({
29
+ key: 'license-tag',
56
30
  label: isProductionLicense
57
31
  ? 'Production license'
58
32
  : 'Non-production license',
59
33
  color: 'gray',
60
34
  tooltip: isProductionLicense ? undefined : (_jsxs("p", { children: ["Non-production license. For production usage details, visit our", ' ', _jsx(Link, { href: NON_PRODUCTION_TERMS_LINK, target: '_blank', rel: 'noopener noreferrer', style: { display: 'inline' }, children: "terms & conditions page" }), ' ', "or", ' ', _jsx(Link, { href: SALES_CONTACT_LINK, target: '_blank', rel: 'noopener noreferrer', style: { display: 'inline' }, children: "contact our sales team" }), "."] })),
61
- };
35
+ });
36
+ if (isCommercial === false) {
37
+ if (expiresAt !== undefined && expiresAt < now) {
38
+ variants.push({
39
+ key: 'non-commercial-license-tag-is-expired',
40
+ label: 'Non-commercial license - expired',
41
+ color: 'red',
42
+ icon: Warning,
43
+ tooltip: (_jsx("p", { children: "To continue using all features, please renew your license." })),
44
+ });
45
+ }
46
+ else if (expiresAt !== undefined &&
47
+ expiresAt - LICENSE_EXPIRY_THRESHOLD < now &&
48
+ expiresAt > now) {
49
+ const daysLeft = Math.floor((expiresAt - now) / DAY);
50
+ variants.push({
51
+ key: 'non-commercial-license-tag-about-to-expire',
52
+ label: `Non-commercial license - ${daysLeft} ${daysLeft > 1 ? 'days' : 'day'} left`,
53
+ color: 'blue',
54
+ icon: Time,
55
+ tooltip: (_jsx("p", { children: "Please renew and provide new license keys to continue production use of Camunda." })),
56
+ });
57
+ }
58
+ else {
59
+ variants.push({
60
+ key: 'non-commercial-license-tag',
61
+ label: 'Non-commercial license',
62
+ color: 'gray',
63
+ });
64
+ }
65
+ }
66
+ return variants;
67
+ }
68
+ function renderVariant(variant) {
69
+ const { key, label, color, icon, tooltip } = variant;
70
+ if (tooltip) {
71
+ return (_jsxs(Toggletip, { align: 'bottom', children: [_jsx(ToggletipButton, { as: 'span', label: label, children: _jsx(Tag, { type: color, renderIcon: icon, style: { margin: 0, cursor: 'pointer' }, children: label }) }), _jsx(ToggletipContent, { children: tooltip })] }, key));
72
+ }
73
+ return (_jsx(Tag, { type: color, renderIcon: icon, style: { margin: 0 }, children: label }, key));
62
74
  }
63
75
  /**
64
- * Renders a license status tag for the Camunda header. Handles all license
65
- * states: production, non-production (with tooltip), non-commercial,
66
- * non-commercial expiring (with countdown), and non-commercial expired.
67
- *
68
- * Drop into `headerTrailingContent` of C3NavigationV2.
76
+ * Renders license status tags for the Camunda header. Mirrors V1 c3-navigation:
77
+ * always renders a production-status tag, plus a non-commercial tag (or its
78
+ * expiring/expired variant) when isCommercial === false. Drop the element into
79
+ * `headerTrailingContent` of C3NavigationV2.
69
80
  */
70
81
  export const C3LicenseTag = (props) => {
71
- const variant = getTagVariant(props);
72
- if (!variant)
82
+ const variants = getTagVariants(props);
83
+ if (variants.length === 0)
73
84
  return null;
74
- const { label, color, icon, tooltip } = variant;
75
- if (tooltip) {
76
- return (_jsxs(Toggletip, { align: 'bottom', children: [_jsx(ToggletipButton, { as: 'span', label: label, children: _jsx(Tag, { type: color, renderIcon: icon, style: { margin: 0, cursor: 'pointer' }, children: label }) }), _jsx(ToggletipContent, { children: tooltip })] }));
77
- }
78
- return (_jsx(Tag, { type: color, renderIcon: icon, style: { margin: 0 }, children: label }));
85
+ return _jsx(_Fragment, { children: variants.map(renderVariant) });
79
86
  };
@@ -63,7 +63,7 @@ export const C3NavigationV2 = ({ app, skipToContentTargetId, skipToContentLabel
63
63
  root.style.removeProperty('--c3-sidebar-width');
64
64
  };
65
65
  }, [sidebarWidth]);
66
- 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: {
66
+ return (_jsxs(_Fragment, { children: [_jsxs(StyledHeader, { "aria-label": ariaLabel, children: [_jsx(SkipToContent, { href: `#${skipToContentTargetId}`, children: skipToContentLabel }), _jsx(LogoSection, { as: app.linkProps ? LinkEl : 'div', "aria-label": app.name ? `Camunda ${app.name}` : 'Camunda', ...(app.linkProps ?? {}), children: _jsx(CamundaLogo, {}) }), breadcrumbs && breadcrumbs.length > 0 && (_jsx(C3BreadcrumbBar, { segments: breadcrumbs, linkComponent: LinkEl })), _jsxs(HeaderGlobalBar, { children: [headerTrailingContent && (_jsx("span", { style: {
67
67
  display: 'flex',
68
68
  alignItems: 'center',
69
69
  padding: '0 var(--cds-spacing-03)',
@@ -147,6 +147,19 @@ export interface SidebarSection {
147
147
  compact?: boolean;
148
148
  }
149
149
  export type SidebarNode = SidebarItem | SidebarGroupItem | SidebarGroup | SidebarSection;
150
+ export interface SidebarLabels {
151
+ /** Toggle button text when expanded. Defaults to `'Collapse'`. */
152
+ collapse?: string;
153
+ /** Tooltip on the collapsed toggle button. Defaults to `'Expand'`. */
154
+ expand?: string;
155
+ /** Aria-label on the toggle button. Receives `isExpanded`. Defaults to `'Collapse sidebar'` / `'Expand sidebar'`. */
156
+ toggleAriaLabel?: (isExpanded: boolean) => string;
157
+ /** Aria-label on a group's chevron. Receives `{label, isExpanded}`. Defaults to `'Collapse {label}'` / `'Expand {label}'`. */
158
+ groupToggleAriaLabel?: (args: {
159
+ label: string;
160
+ isExpanded: boolean;
161
+ }) => string;
162
+ }
150
163
  export interface SidebarProps {
151
164
  ariaLabel: string;
152
165
  children: SidebarNode[];
@@ -155,6 +168,7 @@ export interface SidebarProps {
155
168
  expandedWidth?: string;
156
169
  collapsedWidth?: string;
157
170
  linkComponent?: LinkComponent;
171
+ labels?: SidebarLabels;
158
172
  }
159
173
  export interface AppProps {
160
174
  name?: string;
@@ -5,4 +5,4 @@ import type { SidebarProps } from './c3-navigation-v2.types';
5
5
  * Loose items (not wrapped in a section) are valid and behave as an implicit
6
6
  * untitled group at the top.
7
7
  */
8
- export declare const C3Sidebar: ({ ariaLabel, children: nodes, isExpanded, onToggleExpanded, expandedWidth, collapsedWidth, linkComponent, }: SidebarProps) => JSX.Element;
8
+ export declare const C3Sidebar: ({ ariaLabel, children: nodes, isExpanded, onToggleExpanded, expandedWidth, collapsedWidth, linkComponent, labels, }: SidebarProps) => JSX.Element;
@@ -6,8 +6,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  */
7
7
  import { Popover, PopoverContent } from '@carbon/react';
8
8
  import { ChevronDown, ChevronLeft, ChevronRight, ChevronUp, } from '@carbon/react/icons/index.esm.js';
9
- import { useEffect, useRef, useState } from 'react';
9
+ import { createContext, useContext, useEffect, useRef, useState, } from 'react';
10
10
  import styled, { css } from 'styled-components';
11
+ const SidebarLabelsContext = createContext({
12
+ groupToggleAria: ({ label, isExpanded }) => isExpanded ? `Collapse ${label}` : `Expand ${label}`,
13
+ });
14
+ const useSidebarLabels = () => useContext(SidebarLabelsContext);
11
15
  const SidebarNav = styled.nav `
12
16
  position: fixed;
13
17
  top: 3rem;
@@ -36,13 +40,12 @@ const NavButton = styled.button `
36
40
  flex-wrap: nowrap;
37
41
  gap: var(--cds-spacing-04);
38
42
  width: 100%;
39
- min-height: 2.5rem;
43
+ height: var(--cds-spacing-09);
44
+ box-sizing: border-box;
40
45
  padding: ${(p) => {
41
46
  if (p.$depth > 0)
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)';
47
+ return `0 var(--cds-spacing-05) 0 ${0.75 + p.$depth * 1.75}rem`;
48
+ return p.$isExpanded ? '0 var(--cds-spacing-05)' : '0';
46
49
  }};
47
50
  justify-content: ${(p) => (p.$isExpanded ? 'flex-start' : 'center')};
48
51
  background: ${(p) => (p.$isActive ? 'var(--cds-layer-selected)' : 'transparent')};
@@ -86,7 +89,8 @@ const GroupHeader = styled.div `
86
89
  align-items: center;
87
90
  flex-wrap: nowrap;
88
91
  width: 100%;
89
- min-height: 2.5rem;
92
+ height: var(--cds-spacing-09);
93
+ box-sizing: border-box;
90
94
  padding-right: var(--cds-spacing-04);
91
95
  gap: var(--cds-spacing-03);
92
96
  overflow: hidden;
@@ -98,21 +102,20 @@ const GroupHeader = styled.div `
98
102
  transition: background 0.15s, color 0.15s;
99
103
 
100
104
  &:hover:not(:has(button[data-expand]:hover)) {
101
- background: ${p.$isActive
102
- ? 'var(--cds-layer-selected)'
103
- : 'var(--cds-layer-hover)'};
105
+ background: ${p.$isActive ? 'var(--cds-layer-selected)' : 'var(--cds-layer-hover)'};
104
106
  }
105
107
  `}
106
108
  `;
107
109
  const GroupLabelButton = styled.button `
108
110
  display: flex;
109
111
  align-items: center;
112
+ align-self: stretch;
110
113
  flex: 1;
111
114
  min-width: 0;
112
115
  padding: ${(p) => {
113
116
  if (p.$depth > 0)
114
- return `var(--cds-spacing-04) 0 var(--cds-spacing-04) ${0.75 + p.$depth * 1.75}rem`;
115
- return 'var(--cds-spacing-04) 0 var(--cds-spacing-04) var(--cds-spacing-05)';
117
+ return `0 0 0 ${0.75 + p.$depth * 1.75}rem`;
118
+ return '0 0 0 var(--cds-spacing-05)';
116
119
  }};
117
120
  background: transparent;
118
121
  border: none;
@@ -139,7 +142,11 @@ const ExpandButton = styled.button `
139
142
  display: flex;
140
143
  align-items: center;
141
144
  justify-content: center;
142
- padding: var(--cds-spacing-02);
145
+ flex-shrink: 0;
146
+ align-self: center;
147
+ width: var(--cds-spacing-07);
148
+ height: var(--cds-spacing-07);
149
+ padding: 0;
143
150
  background: transparent;
144
151
  border: none;
145
152
  border-radius: 4px;
@@ -159,12 +166,13 @@ const ExpandButton = styled.button `
159
166
  const PlainGroupLabel = styled.span `
160
167
  display: flex;
161
168
  align-items: center;
169
+ align-self: stretch;
162
170
  flex: 1;
163
171
  min-width: 0;
164
172
  padding: ${(p) => {
165
173
  if (p.$depth > 0)
166
- return `var(--cds-spacing-04) 0 var(--cds-spacing-04) ${0.75 + p.$depth * 1.75}rem`;
167
- return 'var(--cds-spacing-04) 0 var(--cds-spacing-04) var(--cds-spacing-05)';
174
+ return `0 0 0 ${0.75 + p.$depth * 1.75}rem`;
175
+ return '0 0 0 var(--cds-spacing-05)';
168
176
  }};
169
177
  gap: var(--cds-spacing-04);
170
178
  color: var(--cds-text-secondary);
@@ -231,10 +239,9 @@ const CollapseButton = styled.button `
231
239
  flex-wrap: nowrap;
232
240
  gap: var(--cds-spacing-04);
233
241
  width: 100%;
234
- min-height: 2.5rem;
235
- padding: ${(p) => p.$isExpanded
236
- ? 'var(--cds-spacing-04) var(--cds-spacing-05)'
237
- : 'var(--cds-spacing-04)'};
242
+ height: var(--cds-spacing-09);
243
+ box-sizing: border-box;
244
+ padding: ${(p) => (p.$isExpanded ? '0 var(--cds-spacing-05)' : '0')};
238
245
  justify-content: ${(p) => (p.$isExpanded ? 'flex-start' : 'center')};
239
246
  background: transparent;
240
247
  border: none;
@@ -316,13 +323,15 @@ const GroupItemNode = ({ node, sidebarExpanded, depth, linkComponent, }) => {
316
323
  };
317
324
  const GroupNode = ({ node, sidebarExpanded, depth, linkComponent, }) => {
318
325
  const Icon = node.icon;
326
+ const { groupToggleAria } = useSidebarLabels();
319
327
  if (!sidebarExpanded) {
320
328
  const collapsed = (_jsx(NavButton, { "$isActive": false, "$isExpanded": false, "$depth": 0, onClick: node.onToggleExpand, children: _jsx(Icon, { size: 20, style: { flexShrink: 0 } }) }));
321
329
  return (_jsx(CollapsedItemTooltip, { label: node.label, children: collapsed }));
322
330
  }
323
- return (_jsxs("div", { children: [_jsxs(GroupHeader, { children: [_jsxs(PlainGroupLabel, { "$depth": depth, children: [_jsx(Icon, { size: 20, style: { flexShrink: 0 } }), _jsx(NavLabel, { children: node.label }), node.trailingElement] }), node.onToggleExpand && (_jsx(ExpandButton, { "data-expand": true, onClick: node.onToggleExpand, "aria-label": node.isExpanded
324
- ? `Collapse ${node.label}`
325
- : `Expand ${node.label}`, "aria-expanded": !!node.isExpanded, children: node.isExpanded ? (_jsx(ChevronUp, { size: 16 })) : (_jsx(ChevronDown, { size: 16 })) }))] }), node.isExpanded &&
331
+ return (_jsxs("div", { children: [_jsxs(GroupHeader, { children: [_jsxs(PlainGroupLabel, { "$depth": depth, children: [_jsx(Icon, { size: 20, style: { flexShrink: 0 } }), _jsx(NavLabel, { children: node.label }), node.trailingElement] }), node.onToggleExpand && (_jsx(ExpandButton, { "data-expand": true, onClick: node.onToggleExpand, "aria-label": groupToggleAria({
332
+ label: node.label,
333
+ isExpanded: !!node.isExpanded,
334
+ }), "aria-expanded": !!node.isExpanded, children: node.isExpanded ? (_jsx(ChevronUp, { size: 16 })) : (_jsx(ChevronDown, { size: 16 })) }))] }), node.isExpanded &&
326
335
  node.children.map((child) => (_jsx(SidebarNodeComponent, { node: child, sidebarExpanded: sidebarExpanded, depth: depth + 1, linkComponent: linkComponent }, child.key)))] }));
327
336
  };
328
337
  const SectionNode = ({ node, sidebarExpanded, linkComponent, hideTopDivider, eatScrollPadding, tight, }) => (_jsxs(SectionDivider, { "$hideTopDivider": hideTopDivider, "$eatScrollPadding": eatScrollPadding, "$tight": tight, children: [sidebarExpanded && node.title && _jsx(SectionTitle, { children: node.title }), node.children.map((child) => (_jsx(SidebarNodeComponent, { node: child, sidebarExpanded: sidebarExpanded, depth: 0, linkComponent: linkComponent }, child.key)))] }));
@@ -347,31 +356,37 @@ const SidebarNodeComponent = ({ node, sidebarExpanded, depth, linkComponent, hid
347
356
  * Loose items (not wrapped in a section) are valid and behave as an implicit
348
357
  * untitled group at the top.
349
358
  */
350
- export const C3Sidebar = ({ ariaLabel, children: nodes, isExpanded = true, onToggleExpanded, expandedWidth = '16rem', collapsedWidth = '3rem', linkComponent, }) => {
359
+ export const C3Sidebar = ({ ariaLabel, children: nodes, isExpanded = true, onToggleExpanded, expandedWidth = '16rem', collapsedWidth = '3rem', linkComponent, labels, }) => {
351
360
  const width = isExpanded ? expandedWidth : collapsedWidth;
352
361
  const prunedNodes = pruneChildren(nodes);
353
- return (_jsxs(SidebarNav, { "$width": width, "aria-label": ariaLabel, children: [_jsx(ScrollArea, { "$sidebarExpanded": isExpanded, children: (() => {
354
- let sectionSeen = false;
355
- let prevSectionCompact = false;
356
- let hasNonSectionNodes = false;
357
- return prunedNodes.map((node) => {
358
- let hideTopDivider = false;
359
- let eatScrollPadding = false;
360
- let tight = false;
361
- if (node.type === 'section') {
362
- const isFirst = !sectionSeen;
363
- hideTopDivider =
364
- (isFirst && !hasNonSectionNodes) || !!node.compact;
365
- eatScrollPadding =
366
- isFirst && !hasNonSectionNodes && !!node.compact;
367
- tight = prevSectionCompact;
368
- sectionSeen = true;
369
- prevSectionCompact = !!node.compact;
370
- }
371
- else {
372
- hasNonSectionNodes = true;
373
- }
374
- return (_jsx(SidebarNodeComponent, { node: node, sidebarExpanded: isExpanded, depth: 0, linkComponent: linkComponent, hideTopDivider: hideTopDivider, eatScrollPadding: eatScrollPadding, tight: tight }, node.key));
375
- });
376
- })() }), onToggleExpanded && (_jsx(CollapseToggleArea, { children: _jsx(CollapsedItemTooltip, { label: 'Expand', enabled: !isExpanded, children: _jsxs(CollapseButton, { "$isExpanded": isExpanded, onClick: onToggleExpanded, "aria-label": isExpanded ? 'Collapse sidebar' : 'Expand sidebar', "aria-expanded": isExpanded, children: [isExpanded ? (_jsx(ChevronLeft, { size: 20, style: { flexShrink: 0 } })) : (_jsx(ChevronRight, { size: 20, style: { flexShrink: 0 } })), isExpanded && _jsx(NavLabel, { children: "Collapse" })] }) }) }))] }));
362
+ const collapseLabel = labels?.collapse ?? 'Collapse';
363
+ const expandLabel = labels?.expand ?? 'Expand';
364
+ const toggleAria = labels?.toggleAriaLabel ??
365
+ ((expanded) => (expanded ? 'Collapse sidebar' : 'Expand sidebar'));
366
+ const groupToggleAria = labels?.groupToggleAriaLabel ??
367
+ (({ label, isExpanded: e }) => e ? `Collapse ${label}` : `Expand ${label}`);
368
+ return (_jsx(SidebarLabelsContext.Provider, { value: { groupToggleAria }, children: _jsxs(SidebarNav, { "$width": width, "aria-label": ariaLabel, children: [_jsx(ScrollArea, { "$sidebarExpanded": isExpanded, children: (() => {
369
+ let sectionSeen = false;
370
+ let prevSectionCompact = false;
371
+ let hasNonSectionNodes = false;
372
+ return prunedNodes.map((node) => {
373
+ let hideTopDivider = false;
374
+ let eatScrollPadding = false;
375
+ let tight = false;
376
+ if (node.type === 'section') {
377
+ const isFirst = !sectionSeen;
378
+ hideTopDivider =
379
+ (isFirst && !hasNonSectionNodes) || !!node.compact;
380
+ eatScrollPadding =
381
+ isFirst && !hasNonSectionNodes && !!node.compact;
382
+ tight = prevSectionCompact;
383
+ sectionSeen = true;
384
+ prevSectionCompact = !!node.compact;
385
+ }
386
+ else {
387
+ hasNonSectionNodes = true;
388
+ }
389
+ return (_jsx(SidebarNodeComponent, { node: node, sidebarExpanded: isExpanded, depth: 0, linkComponent: linkComponent, hideTopDivider: hideTopDivider, eatScrollPadding: eatScrollPadding, tight: tight }, node.key));
390
+ });
391
+ })() }), onToggleExpanded && (_jsx(CollapseToggleArea, { children: _jsx(CollapsedItemTooltip, { label: expandLabel, enabled: !isExpanded, children: _jsxs(CollapseButton, { "$isExpanded": isExpanded, onClick: onToggleExpanded, "aria-label": toggleAria(isExpanded), "aria-expanded": isExpanded, children: [isExpanded ? (_jsx(ChevronLeft, { size: 20, style: { flexShrink: 0 } })) : (_jsx(ChevronRight, { size: 20, style: { flexShrink: 0 } })), isExpanded && _jsx(NavLabel, { children: collapseLabel })] }) }) }))] }) }));
377
392
  };
@@ -1,14 +1,16 @@
1
1
  export type { CamundaApp } from '../../utils/camunda.types';
2
2
  export { C3BreadcrumbBar } from './c3-breadcrumb-bar';
3
3
  export { C3NavigationV2 } from './c3-navigation-v2';
4
- export type { AppProps, BreadcrumbAction, BreadcrumbDropdownItem, BreadcrumbSegment, C3NavigationV2Props, GlobalActionButton, LinkComponent, LinkProps, SidebarGroup, SidebarGroupItem, SidebarItem, SidebarNode, SidebarProps, SidebarSection, ToolDescriptor, } from './c3-navigation-v2.types';
4
+ export type { AppProps, BreadcrumbAction, BreadcrumbDropdownItem, BreadcrumbSegment, C3NavigationV2Props, GlobalActionButton, LinkComponent, LinkProps, SidebarGroup, SidebarGroupItem, SidebarItem, SidebarLabels, SidebarNode, SidebarProps, SidebarSection, ToolDescriptor, } from './c3-navigation-v2.types';
5
5
  export { C3Sidebar } from './c3-sidebar';
6
6
  export { C3ToolsArea } from './c3-tools-area';
7
7
  export type { C3InfoPanelProps, InfoPanelElement } from './tools/c3-info-panel';
8
8
  export { C3InfoPanel } from './tools/c3-info-panel';
9
- export type { C3NotificationsPanelProps } from './tools/c3-notifications-panel';
9
+ export type { C3NotificationsPanelLabels, C3NotificationsPanelProps, } from './tools/c3-notifications-panel';
10
10
  export { C3NotificationsPanel } from './tools/c3-notifications-panel';
11
- export type { C3UserPanelProps, UserPanelElement, } from './tools/c3-user-panel';
11
+ export type { C3ThemeSelectorLabels, C3ThemeSelectorProps, } from './tools/c3-theme-selector';
12
+ export { C3ThemeSelector } from './tools/c3-theme-selector';
13
+ export type { C3UserPanelLabels, C3UserPanelProps, UserPanelElement, } from './tools/c3-user-panel';
12
14
  export { C3UserPanel } from './tools/c3-user-panel';
13
15
  export type { BreadcrumbDescriptor, BreadcrumbDropdownItemDescriptor, GroupDescriptor, GroupItemDescriptor, ItemDescriptor, SectionDescriptor, SidebarNodeDescriptor, UseC3NavigationV2Options, UseC3NavigationV2Return, } from './use-c3-navigation-v2';
14
16
  export { useC3NavigationV2 } from './use-c3-navigation-v2';
@@ -9,6 +9,7 @@ export { C3Sidebar } from './c3-sidebar.js';
9
9
  export { C3ToolsArea } from './c3-tools-area.js';
10
10
  export { C3InfoPanel } from './tools/c3-info-panel.js';
11
11
  export { C3NotificationsPanel } from './tools/c3-notifications-panel.js';
12
+ export { C3ThemeSelector } from './tools/c3-theme-selector.js';
12
13
  export { C3UserPanel } from './tools/c3-user-panel.js';
13
14
  export { useC3NavigationV2 } from './use-c3-navigation-v2.js';
14
15
  export { useCamundaTools } from './use-camunda-tools.js';
@@ -6,6 +6,7 @@ export interface InfoPanelElement {
6
6
  }
7
7
  export interface C3InfoPanelProps {
8
8
  elements: InfoPanelElement[];
9
- title?: string;
9
+ /** Defaults to `'Info'`. */
10
+ title?: string | null;
10
11
  }
11
12
  export declare const C3InfoPanel: FC<C3InfoPanelProps>;
@@ -30,4 +30,4 @@ const LinkButton = styled.button `
30
30
  outline-offset: -2px;
31
31
  }
32
32
  `;
33
- export const C3InfoPanel = ({ elements, title = 'Help & Resources', }) => (_jsxs(_Fragment, { children: [_jsx(PanelHeader, { children: _jsx(PanelTitle, { children: title }) }), _jsx(LinkList, { children: elements.map((el) => (_jsx(LinkItem, { children: _jsx(LinkButton, { onClick: el.onClick, children: el.label }) }, el.key))) })] }));
33
+ export const C3InfoPanel = ({ elements, title = 'Info', }) => (_jsxs(_Fragment, { children: [title && (_jsx(PanelHeader, { children: _jsx(PanelTitle, { children: title }) })), _jsx(LinkList, { children: elements.map((el) => (_jsx(LinkItem, { children: _jsx(LinkButton, { onClick: el.onClick, children: el.label }) }, el.key))) })] }));
@@ -1,6 +1,17 @@
1
1
  import { type FC } from 'react';
2
2
  import type { Notification } from '../../../api/notifications';
3
+ export interface C3NotificationsPanelLabels {
4
+ /** Defaults to `'Dismiss all'`. */
5
+ dismissAll?: string;
6
+ /** Defaults to `'No notifications'`. */
7
+ emptyTitle?: string;
8
+ /** Defaults to `'New updates regarding your processes, clusters and more will appear here.'`. */
9
+ emptyDescription?: string;
10
+ }
3
11
  export interface C3NotificationsPanelProps {
4
12
  onLinkClick?: (meta: Notification['meta']) => void;
13
+ /** Defaults to `'Notifications'`. */
14
+ title?: string | null;
15
+ labels?: C3NotificationsPanelLabels;
5
16
  }
6
17
  export declare const C3NotificationsPanel: FC<C3NotificationsPanelProps>;
@@ -28,7 +28,7 @@ const EmptyStateDescription = styled(NotificationDescription) `
28
28
  margin-top: var(--cds-spacing-03);
29
29
  `;
30
30
  const sortDescending = (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
31
- export const C3NotificationsPanel = ({ onLinkClick, }) => {
31
+ export const C3NotificationsPanel = ({ onLinkClick, title = 'Notifications', labels, }) => {
32
32
  const { enabled, notifications, markAllAsRead, dismiss, dismissAll, analytics, } = useContext(C3NotificationContext);
33
33
  // Snapshot uuids that were unread when the panel opened so "new" dots
34
34
  // persist until the panel closes, even after mark-as-read runs.
@@ -41,17 +41,21 @@ export const C3NotificationsPanel = ({ onLinkClick, }) => {
41
41
  if (enabled)
42
42
  analytics('notification-panel-opened');
43
43
  }, []);
44
- return (_jsxs(_Fragment, { children: [_jsxs(PanelHeader, { style: {
44
+ return (_jsxs(_Fragment, { children: [(title || notifications.length > 0) && (_jsxs(PanelHeader, { style: {
45
45
  width: '100%',
46
46
  height: 60,
47
47
  display: 'flex',
48
48
  flexDirection: 'row',
49
- justifyContent: 'space-between',
49
+ // Without a title, the dismiss-all button is the sole row item;
50
+ // flex-end keeps it pinned right instead of drifting left under
51
+ // `space-between`.
52
+ justifyContent: title ? 'space-between' : 'flex-end',
50
53
  alignItems: 'center',
51
- }, children: [_jsx(PanelTitle, { children: "Notifications" }), notifications.length > 0 && (_jsx(DismissAllButton, { kind: 'ghost', size: 'sm', onClick: () => dismissAll(notifications), children: "Dismiss all" }))] }), notifications.length > 0 ? ([...notifications].sort(sortDescending).map((notification) => (_jsx(C3NotificationContainer, { onRead: () => undefined, onDismiss: () => dismiss(notification), originalOnLinkClick: onLinkClick, onLinkClick: () => {
54
+ }, children: [title && _jsx(PanelTitle, { children: title }), notifications.length > 0 && (_jsx(DismissAllButton, { kind: 'ghost', size: 'sm', onClick: () => dismissAll(notifications), children: labels?.dismissAll ?? 'Dismiss all' }))] })), notifications.length > 0 ? ([...notifications].sort(sortDescending).map((notification) => (_jsx(C3NotificationContainer, { onRead: () => undefined, onDismiss: () => dismiss(notification), originalOnLinkClick: onLinkClick, onLinkClick: () => {
52
55
  if (enabled) {
53
56
  analytics('notification-clicked-cta', notification.meta?.identifier);
54
57
  }
55
58
  onLinkClick?.(notification.meta);
56
- }, unread: unreadAtOpen.has(notification.uuid), ...notification }, notification.uuid)))) : (_jsxs(EmptyState, { children: [_jsx(C3BellIcon, { size: 56 }), _jsx(EmptyStateTitle, { children: "No notifications" }), _jsx(EmptyStateDescription, { children: "New updates regarding your processes, clusters and more will appear here." })] }))] }));
59
+ }, unread: unreadAtOpen.has(notification.uuid), ...notification }, notification.uuid)))) : (_jsxs(EmptyState, { children: [_jsx(C3BellIcon, { size: 56 }), _jsx(EmptyStateTitle, { children: labels?.emptyTitle ?? 'No notifications' }), _jsx(EmptyStateDescription, { children: labels?.emptyDescription ??
60
+ 'New updates regarding your processes, clusters and more will appear here.' })] }))] }));
57
61
  };
@@ -0,0 +1,25 @@
1
+ import type { FC } from 'react';
2
+ import type { Theme } from '../../c3-user-configuration/c3-profile-provider/c3-profile-provider';
3
+ export interface C3ThemeSelectorLabels {
4
+ /** Defaults to `'Theme'`. */
5
+ legend?: string;
6
+ /** Defaults to `'Light'`. */
7
+ light?: string;
8
+ /** Defaults to `'System'`. */
9
+ system?: string;
10
+ /** Defaults to `'Dark'`. */
11
+ dark?: string;
12
+ }
13
+ export interface C3ThemeSelectorProps {
14
+ currentTheme: Theme;
15
+ onChange: (theme: Theme) => void;
16
+ labels?: C3ThemeSelectorLabels;
17
+ }
18
+ /**
19
+ * Light / System / Dark radio group, matching the V1 user sidebar's
20
+ * built-in theme switcher. Consumers wire it into V2's
21
+ * `useCamundaTools.user.customSection` (or anywhere else) and pass
22
+ * their own state. SaaS consumers can read `theme` and
23
+ * `onThemeChange` from `useC3Profile()`; SM consumers pass local state.
24
+ */
25
+ export declare const C3ThemeSelector: FC<C3ThemeSelectorProps>;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
4
+ * under one or more contributor license agreements. Licensed under a commercial license.
5
+ * You may not use this file except in compliance with the commercial license.
6
+ */
7
+ import { Layer, RadioButton, RadioButtonGroup } from '@carbon/react';
8
+ /**
9
+ * Light / System / Dark radio group, matching the V1 user sidebar's
10
+ * built-in theme switcher. Consumers wire it into V2's
11
+ * `useCamundaTools.user.customSection` (or anywhere else) and pass
12
+ * their own state. SaaS consumers can read `theme` and
13
+ * `onThemeChange` from `useC3Profile()`; SM consumers pass local state.
14
+ */
15
+ export const C3ThemeSelector = ({ currentTheme, onChange, labels, }) => (_jsx(Layer, { children: _jsx("div", { style: { padding: '0.5rem 1rem' }, children: _jsxs(RadioButtonGroup, { name: 'theme-radio-group', legendText: labels?.legend ?? 'Theme', orientation: 'vertical', valueSelected: currentTheme, onChange: (value) => onChange(value), children: [_jsx(RadioButton, { id: 'theme-light', labelText: labels?.light ?? 'Light', value: 'light' }), _jsx(RadioButton, { id: 'theme-system', labelText: labels?.system ?? 'System', value: 'system' }), _jsx(RadioButton, { id: 'theme-dark', labelText: labels?.dark ?? 'Dark', value: 'dark' })] }) }) }));
@@ -26,5 +26,20 @@ export interface C3UserPanelProps {
26
26
  * a built-in link (`terms`, `privacy`, `imprint`) replace the default.
27
27
  */
28
28
  elements?: UserPanelElement[];
29
+ /** Defaults to `'Account'`. */
30
+ title?: string | null;
31
+ labels?: C3UserPanelLabels;
32
+ }
33
+ export interface C3UserPanelLabels {
34
+ /** Defaults to `'Terms of use'`. */
35
+ termsOfUse?: string;
36
+ /** Defaults to `'Privacy policy'`. */
37
+ privacyPolicy?: string;
38
+ /** Defaults to `'Imprint'`. */
39
+ imprint?: string;
40
+ /** Defaults to `'Log out'`. */
41
+ logOut?: string;
42
+ /** Footer copyright. Defaults to `'© Camunda Services GmbH {year}'`. Receives the current year. */
43
+ copyright?: (year: number) => string;
29
44
  }
30
45
  export declare const C3UserPanel: FC<C3UserPanelProps>;
@@ -4,11 +4,9 @@ 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 { Button, FormLabel, RadioButton, RadioButtonGroup, Stack, Toggle, } from '@carbon/react';
7
+ import { Button, Toggle } from '@carbon/react';
8
8
  import { ArrowRight } from '@carbon/react/icons/index.esm.js';
9
9
  import styled from 'styled-components';
10
- import { useC3Profile, } from '../../c3-user-configuration/c3-profile-provider/c3-profile-provider.js';
11
- import { useC3UserConfiguration } from '../../c3-user-configuration/c3-user-configuration-provider.js';
12
10
  import { PanelHeader, PanelTitle } from './panel-primitives.js';
13
11
  const ProfileSection = styled.div `
14
12
  padding: var(--cds-spacing-05);
@@ -26,10 +24,6 @@ const ProfileEmail = styled.span `
26
24
  font-size: var(--cds-label-01-font-size);
27
25
  color: var(--cds-text-secondary);
28
26
  `;
29
- const ThemeSection = styled.div `
30
- padding: var(--cds-spacing-05);
31
- border-bottom: 1px solid var(--cds-border-subtle-01);
32
- `;
33
27
  const CustomSection = styled.div `
34
28
  border-bottom: 1px solid var(--cds-border-subtle-01);
35
29
  `;
@@ -95,31 +89,30 @@ const Copyright = styled.p `
95
89
  line-height: var(--cds-label-01-line-height);
96
90
  letter-spacing: var(--cds-label-01-letter-spacing);
97
91
  `;
98
- const LEGAL_LINKS = [
92
+ const buildLegalLinks = (labels) => [
99
93
  {
100
94
  key: 'terms',
101
- label: 'Terms of use',
95
+ label: labels?.termsOfUse ?? 'Terms of use',
102
96
  onClick: () => window.open('https://camunda.com/legal/terms/camunda-platform/camunda-platform-8-saas-trial/', '_blank'),
103
97
  },
104
98
  {
105
99
  key: 'privacy',
106
- label: 'Privacy policy',
100
+ label: labels?.privacyPolicy ?? 'Privacy policy',
107
101
  onClick: () => window.open('https://camunda.com/legal/privacy/', '_blank'),
108
102
  },
109
103
  {
110
104
  key: 'imprint',
111
- label: 'Imprint',
105
+ label: labels?.imprint ?? 'Imprint',
112
106
  onClick: () => window.open('https://camunda.com/legal/imprint/', '_blank'),
113
107
  },
114
108
  ];
115
- export const C3UserPanel = ({ name, email, version, onLogout, stageToggle, customSection, elements, }) => {
116
- const { handleTheme } = useC3UserConfiguration();
117
- const { theme, onThemeChange } = useC3Profile();
109
+ export const C3UserPanel = ({ name, email, version, onLogout, stageToggle, customSection, elements, title = 'Account', labels, }) => {
118
110
  const consumerKeys = new Set((elements ?? []).map((e) => e.key));
119
- const defaults = LEGAL_LINKS.filter((l) => !consumerKeys.has(l.key));
111
+ const defaults = buildLegalLinks(labels).filter((l) => !consumerKeys.has(l.key));
120
112
  const allElements = [...(elements ?? []), ...defaults];
121
- return (_jsxs(_Fragment, { children: [_jsx(PanelHeader, { children: _jsx(PanelTitle, { children: "Account" }) }), (name || email) && (_jsxs(ProfileSection, { children: [name && _jsx(ProfileName, { children: name }), email && _jsx(ProfileEmail, { children: email })] })), customSection && _jsx(CustomSection, { children: customSection }), handleTheme && (_jsx(ThemeSection, { children: _jsxs(Stack, { gap: 3, children: [_jsx(FormLabel, { children: "Theme" }), _jsxs(RadioButtonGroup, { name: 'theme', valueSelected: theme, onChange: (value) => onThemeChange(value), orientation: 'vertical', children: [_jsx(RadioButton, { value: 'light', labelText: 'Light', id: 'theme-light' }), _jsx(RadioButton, { value: 'dark', labelText: 'Dark', id: 'theme-dark' }), _jsx(RadioButton, { value: 'system', labelText: 'System', id: 'theme-system' })] })] }) })), stageToggle && (_jsx("div", { style: {
113
+ return (_jsxs(_Fragment, { children: [title && (_jsx(PanelHeader, { children: _jsx(PanelTitle, { children: title }) })), (name || email) && (_jsxs(ProfileSection, { children: [name && _jsx(ProfileName, { children: name }), email && _jsx(ProfileEmail, { children: email })] })), customSection && _jsx(CustomSection, { children: customSection }), stageToggle && (_jsx("div", { style: {
122
114
  padding: 'var(--cds-spacing-05)',
123
115
  borderBottom: '1px solid var(--cds-border-subtle-01)',
124
- }, children: _jsx(Toggle, { id: 'stage-toggle', labelText: 'Production features', toggled: stageToggle.prodFeaturesEnabled, onToggle: stageToggle.toggle, size: 'sm' }) })), allElements.length > 0 && (_jsx(ElementList, { children: allElements.map((element) => (_jsx(ElementItem, { children: _jsx(ElementButton, { type: 'button', onClick: element.onClick, children: element.label }) }, element.key))) })), _jsxs(BottomPinned, { children: [version && (_jsxs(VersionWrapper, { children: [_jsx(VersionText, { children: version }), _jsxs(Copyright, { children: ["\u00A9 Camunda Services GmbH ", new Date().getFullYear()] })] })), onLogout && (_jsx(BottomActions, { children: _jsx(Button, { kind: 'ghost', size: 'lg', onClick: onLogout, renderIcon: ArrowRight, children: "Log out" }) }))] })] }));
116
+ }, children: _jsx(Toggle, { id: 'stage-toggle', labelText: 'Production features', toggled: stageToggle.prodFeaturesEnabled, onToggle: stageToggle.toggle, size: 'sm' }) })), allElements.length > 0 && (_jsx(ElementList, { children: allElements.map((element) => (_jsx(ElementItem, { children: _jsx(ElementButton, { type: 'button', onClick: element.onClick, children: element.label }) }, element.key))) })), _jsxs(BottomPinned, { children: [version && (_jsxs(VersionWrapper, { children: [_jsx(VersionText, { children: version }), _jsx(Copyright, { children: labels?.copyright?.(new Date().getFullYear()) ??
117
+ `© Camunda Services GmbH ${new Date().getFullYear()}` })] })), onLogout && (_jsx(BottomActions, { children: _jsx(Button, { kind: 'ghost', size: 'lg', onClick: onLogout, renderIcon: ArrowRight, children: labels?.logOut ?? 'Log out' }) }))] })] }));
125
118
  };
@@ -1,4 +1,4 @@
1
- import type { BreadcrumbAction, C3NavigationV2Props, GlobalActionButton, LinkComponent, LinkProps, ToolDescriptor } from './c3-navigation-v2.types';
1
+ import type { BreadcrumbAction, C3NavigationV2Props, GlobalActionButton, LinkComponent, LinkProps, SidebarLabels, ToolDescriptor } from './c3-navigation-v2.types';
2
2
  type ActiveMatcher = boolean | string | ((activeItemKey: string) => boolean);
3
3
  export interface ItemDescriptor {
4
4
  type: 'item';
@@ -82,6 +82,8 @@ export interface UseC3NavigationV2Options {
82
82
  skipToContentLabel?: string;
83
83
  headerAriaLabel?: string;
84
84
  sidebarAriaLabel?: string;
85
+ /** Forwarded to `C3Sidebar` `labels`. */
86
+ sidebarLabels?: SidebarLabels;
85
87
  /** Used to resolve `isActive` on sidebar items and `isSelected` on breadcrumb dropdown items. */
86
88
  activeItemKey: string;
87
89
  sidebarChildren?: SidebarNodeDescriptor[];
@@ -30,7 +30,7 @@ function collectDefaultExpanded(nodes, out) {
30
30
  * descriptors (isActive, label functions) into concrete props.
31
31
  */
32
32
  export function useC3NavigationV2(options) {
33
- const { app, skipToContentTargetId, skipToContentLabel, headerAriaLabel, sidebarAriaLabel = 'Main navigation', activeItemKey, sidebarChildren = [], breadcrumbs = [], tools, globalActions, sidebarExpandedWidth, sidebarCollapsedWidth, defaultSidebarExpanded = true, onSidebarToggle, linkComponent, headerTrailingContent, } = options;
33
+ const { app, skipToContentTargetId, skipToContentLabel, headerAriaLabel, sidebarAriaLabel = 'Main navigation', sidebarLabels, activeItemKey, sidebarChildren = [], breadcrumbs = [], tools, globalActions, sidebarExpandedWidth, sidebarCollapsedWidth, defaultSidebarExpanded = true, onSidebarToggle, linkComponent, headerTrailingContent, } = options;
34
34
  const [isSidebarExpanded, setIsSidebarExpanded] = useState(defaultSidebarExpanded);
35
35
  const setSidebarExpanded = useCallback((expanded) => {
36
36
  setIsSidebarExpanded(expanded);
@@ -173,6 +173,7 @@ export function useC3NavigationV2(options) {
173
173
  expandedWidth: sidebarExpandedWidth,
174
174
  collapsedWidth: sidebarCollapsedWidth,
175
175
  linkComponent,
176
+ labels: sidebarLabels,
176
177
  }
177
178
  : undefined;
178
179
  const navProps = useMemo(() => ({
@@ -2,10 +2,15 @@ import { type FC, type ReactNode } from 'react';
2
2
  import type { Notification } from '../../api/notifications';
3
3
  import type { ToolDescriptor } from './c3-navigation-v2.types';
4
4
  import { type InfoPanelElement } from './tools/c3-info-panel';
5
+ import { type C3NotificationsPanelLabels } from './tools/c3-notifications-panel';
5
6
  import { type C3UserPanelProps } from './tools/c3-user-panel';
6
7
  export interface NotificationsToolOptions {
7
8
  onLinkClick?: (meta: Notification['meta']) => void;
8
9
  ariaLabel?: string;
10
+ /** Forwarded to `C3NotificationsPanel`. */
11
+ title?: string | null;
12
+ /** Forwarded to `C3NotificationsPanel`. */
13
+ labels?: C3NotificationsPanelLabels;
9
14
  }
10
15
  export type UserToolOptions = C3UserPanelProps & {
11
16
  ariaLabel?: string;
@@ -16,7 +21,8 @@ export interface HelpToolOptions {
16
21
  }
17
22
  export interface InfoToolOptions {
18
23
  elements: InfoPanelElement[];
19
- title?: string;
24
+ /** Forwarded to `C3InfoPanel`. */
25
+ title?: string | null;
20
26
  ariaLabel?: string;
21
27
  }
22
28
  export interface UseCamundaToolsOptions {
@@ -5,13 +5,13 @@ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
5
5
  * You may not use this file except in compliance with the commercial license.
6
6
  */
7
7
  import { HeaderGlobalAction as HeaderGlobalActionBase } from '@carbon/react';
8
- import { Help as HelpIcon, Information, Notification as NotificationIcon, UserAvatar, } from '@carbon/react/icons/index.esm.js';
8
+ import { Help as HelpIcon, Notification as NotificationIcon, UserAvatar, } from '@carbon/react/icons/index.esm.js';
9
9
  import { useContext, useMemo } from 'react';
10
10
  import { C3NotificationsUnreadIcon } from '../../assets/c3-icons.js';
11
11
  import { useC3HelpCenter } from '../c3-help-center/c3-help-center-provider.js';
12
12
  import C3NotificationProvider, { C3NotificationContext, } from '../c3-navigation/c3-notification-provider/c3-notification-provider.js';
13
13
  import { C3InfoPanel } from './tools/c3-info-panel.js';
14
- import { C3NotificationsPanel } from './tools/c3-notifications-panel.js';
14
+ import { C3NotificationsPanel, } from './tools/c3-notifications-panel.js';
15
15
  import { C3UserPanel } from './tools/c3-user-panel.js';
16
16
  /**
17
17
  * Carbon's `HeaderGlobalAction` is typed as `React.FC` and omits both `ref`
@@ -45,7 +45,7 @@ export const useCamundaTools = (options) => {
45
45
  const hasUnread = !isFetching && notifications.some((n) => n.state === 'new');
46
46
  return (_jsx(HeaderGlobalAction, { "aria-label": label, onClick: onClick, isActive: isActive, tooltipAlignment: 'center', leaveDelayMs: 100, children: hasUnread ? (_jsx(C3NotificationsUnreadIcon, { size: 20 })) : (_jsx(NotificationIcon, { size: 20 })) }));
47
47
  },
48
- panel: (_jsx(C3NotificationsPanel, { onLinkClick: notifOptions.onLinkClick })),
48
+ panel: (_jsx(C3NotificationsPanel, { onLinkClick: notifOptions.onLinkClick, title: notifOptions.title, labels: notifOptions.labels })),
49
49
  });
50
50
  }
51
51
  if (options.info !== undefined) {
@@ -54,7 +54,7 @@ export const useCamundaTools = (options) => {
54
54
  result.push({
55
55
  key: 'info',
56
56
  label,
57
- renderButton: ({ onClick, isActive }) => (_jsx(HeaderGlobalAction, { "aria-label": label, onClick: onClick, isActive: isActive, tooltipAlignment: 'center', leaveDelayMs: 100, children: _jsx(Information, { size: 20 }) })),
57
+ renderButton: ({ onClick, isActive }) => (_jsx(HeaderGlobalAction, { "aria-label": label, onClick: onClick, isActive: isActive, tooltipAlignment: 'center', leaveDelayMs: 100, children: _jsx(HelpIcon, { size: 20 }) })),
58
58
  panel: (_jsx(C3InfoPanel, { elements: elements, title: title })),
59
59
  });
60
60
  }
@@ -3,30 +3,28 @@ import type { BreadcrumbDescriptor } from './use-c3-navigation-v2';
3
3
  export interface UseClusterWebappBreadcrumbsOptions {
4
4
  /** The app currently being viewed. */
5
5
  currentApp: CamundaApp;
6
- /**
7
- * Optional resolver to convert an endpoint URL into linkProps.
8
- * Defaults to `(url) => ({ href: url })`. Override to support
9
- * client-side routing (e.g. returning `{ to: path }` for internal links).
10
- */
11
- resolveLinkProps?: (url: string, app: string) => {
12
- href?: string;
13
- to?: string;
14
- [key: string]: unknown;
15
- };
16
6
  }
17
7
  /**
18
8
  * Builds the standard breadcrumb chain for a cluster-scoped Camunda webapp:
19
9
  *
20
- * [Org] > [Cluster] > [App ▾]
10
+ * [Org] > [Cluster] > [App ▾]
21
11
  *
22
- * - **Org crumb**: shown when org data is available (SaaS). Links back to
23
- * web modeler at the org level.
24
- * - **Cluster crumb**: shown when cluster data is available. Links back to
25
- * web modeler / console at the cluster level.
26
- * - **App crumb**: always shown. Includes a dropdown to switch between
27
- * sibling apps on the same cluster (filtered to apps with endpoints).
12
+ * - **Org crumb**: shown when org data is available (SaaS). Label links back
13
+ * to web modeler for the current org. Dropdown switches orgs and lands on
14
+ * web modeler scoped to the selected org. Dropdown only renders when there
15
+ * is at least one sibling org and the current cluster exposes a modeler
16
+ * endpoint to derive sibling URLs from.
17
+ * - **Cluster crumb**: shown when cluster data is available. Label links back
18
+ * to web modeler / console at the cluster level. Dropdown switches clusters
19
+ * while staying on `currentApp`, filtered to clusters that expose an
20
+ * endpoint for that app (alias-aware via `getEndpointForApp`).
21
+ * - **App crumb**: always shown. Dropdown switches between sibling apps on
22
+ * the same cluster (filtered to apps with endpoints).
28
23
  *
29
- * Crumbs are omitted when the underlying data is unavailable, so the same
24
+ * Each dropdown follows the breadcrumb convention: clicking the label
25
+ * navigates, clicking the chevron opens the dropdown. Items are listed in
26
+ * source order with the current entry marked `isSelected`. Crumbs and
27
+ * dropdowns are omitted when the underlying data is unavailable, so the same
30
28
  * hook works for both SaaS (full chain) and Self-Managed (partial or single
31
29
  * crumb) without any environment flag.
32
30
  */
@@ -1,3 +1,4 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
1
2
  /*
2
3
  * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
3
4
  * under one or more contributor license agreements. Licensed under a commercial license.
@@ -5,7 +6,8 @@
5
6
  */
6
7
  import { Building, CloudApp } from '@carbon/react/icons/index.esm.js';
7
8
  import { useMemo } from 'react';
8
- import { APP_ICONS, getReadableAppName } from '../../utils/camunda.js';
9
+ import { APP_ICONS, getEndpointForApp, getReadableAppName, isAdminApp, resolveAdminAppName, } from '../../utils/camunda.js';
10
+ import { C3ClusterTag } from '../c3-cluster-tag/c3-cluster-tag.js';
9
11
  import { useC3Profile } from '../c3-user-configuration/c3-profile-provider/c3-profile-provider.js';
10
12
  import { useC3UserConfiguration } from '../c3-user-configuration/c3-user-configuration-provider.js';
11
13
  /**
@@ -20,77 +22,185 @@ const SWITCHABLE_APPS = [
20
22
  'optimize',
21
23
  'admin',
22
24
  ];
25
+ /**
26
+ * Replaces the path of `modelerEndpoint` with `/org/{orgId}`. Used to derive
27
+ * the modeler URL for sibling orgs from the current cluster's modeler
28
+ * endpoint. Assumes all modeler URLs share the same host across orgs (SaaS
29
+ * pattern).
30
+ */
31
+ function buildModelerOrgUrl(modelerEndpoint, orgId) {
32
+ try {
33
+ const url = new URL(modelerEndpoint);
34
+ url.pathname = `/org/${orgId}`;
35
+ url.search = '';
36
+ return url.toString();
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
23
42
  /**
24
43
  * Builds the standard breadcrumb chain for a cluster-scoped Camunda webapp:
25
44
  *
26
- * [Org] > [Cluster] > [App ▾]
45
+ * [Org] > [Cluster] > [App ▾]
27
46
  *
28
- * - **Org crumb**: shown when org data is available (SaaS). Links back to
29
- * web modeler at the org level.
30
- * - **Cluster crumb**: shown when cluster data is available. Links back to
31
- * web modeler / console at the cluster level.
32
- * - **App crumb**: always shown. Includes a dropdown to switch between
33
- * sibling apps on the same cluster (filtered to apps with endpoints).
47
+ * - **Org crumb**: shown when org data is available (SaaS). Label links back
48
+ * to web modeler for the current org. Dropdown switches orgs and lands on
49
+ * web modeler scoped to the selected org. Dropdown only renders when there
50
+ * is at least one sibling org and the current cluster exposes a modeler
51
+ * endpoint to derive sibling URLs from.
52
+ * - **Cluster crumb**: shown when cluster data is available. Label links back
53
+ * to web modeler / console at the cluster level. Dropdown switches clusters
54
+ * while staying on `currentApp`, filtered to clusters that expose an
55
+ * endpoint for that app (alias-aware via `getEndpointForApp`).
56
+ * - **App crumb**: always shown. Dropdown switches between sibling apps on
57
+ * the same cluster (filtered to apps with endpoints).
34
58
  *
35
- * Crumbs are omitted when the underlying data is unavailable, so the same
59
+ * Each dropdown follows the breadcrumb convention: clicking the label
60
+ * navigates, clicking the chevron opens the dropdown. Items are listed in
61
+ * source order with the current entry marked `isSelected`. Crumbs and
62
+ * dropdowns are omitted when the underlying data is unavailable, so the same
36
63
  * hook works for both SaaS (full chain) and Self-Managed (partial or single
37
64
  * crumb) without any environment flag.
38
65
  */
39
66
  export function useClusterWebappBreadcrumbs(options) {
40
- const { currentApp, resolveLinkProps = (url) => ({ href: url }) } = options;
41
- const { clusters, activeOrg } = useC3Profile();
67
+ const { currentApp } = options;
68
+ const { clusters, activeOrg, orgs } = useC3Profile();
42
69
  const config = useC3UserConfiguration();
43
70
  const currentClusterUuid = config.currentClusterUuid;
44
71
  const currentCluster = useMemo(() => clusters?.find((c) => c.uuid === currentClusterUuid) ?? null, [clusters, currentClusterUuid]);
45
72
  return useMemo(() => {
46
73
  const crumbs = [];
74
+ const modelerUrl = currentCluster?.endpoints?.modeler;
47
75
  // ── Org crumb ──────────────────────────────────────────────────────────
48
76
  if (activeOrg) {
49
- const modelerUrl = currentCluster?.endpoints?.modeler;
77
+ const allOrgs = orgs ?? [];
78
+ const hasSiblings = allOrgs.filter((o) => o.uuid !== activeOrg.uuid).length > 0;
79
+ const orgDropdownItems = hasSiblings && modelerUrl
80
+ ? allOrgs.flatMap((o) => {
81
+ const isCurrent = o.uuid === activeOrg.uuid;
82
+ if (isCurrent) {
83
+ return [
84
+ {
85
+ key: o.uuid,
86
+ label: o.name,
87
+ icon: Building,
88
+ isSelected: true,
89
+ },
90
+ ];
91
+ }
92
+ const href = buildModelerOrgUrl(modelerUrl, o.uuid);
93
+ if (!href)
94
+ return [];
95
+ return [
96
+ {
97
+ key: o.uuid,
98
+ label: o.name,
99
+ icon: Building,
100
+ isSelected: false,
101
+ linkProps: { href },
102
+ },
103
+ ];
104
+ })
105
+ : undefined;
50
106
  crumbs.push({
51
107
  key: 'org',
52
108
  label: activeOrg.name,
53
109
  icon: Building,
54
- ...(modelerUrl
55
- ? { linkProps: resolveLinkProps(modelerUrl, 'org') }
110
+ ...(modelerUrl ? { linkProps: { href: modelerUrl } } : {}),
111
+ ...(orgDropdownItems
112
+ ? {
113
+ dropdownTitle: 'Switch organization',
114
+ dropdownAriaLabel: 'Switch organization',
115
+ dropdownItems: orgDropdownItems,
116
+ }
56
117
  : {}),
57
118
  });
58
119
  }
59
120
  // ── Cluster crumb ──────────────────────────────────────────────────────
60
121
  if (currentCluster) {
61
- const modelerUrl = currentCluster.endpoints?.modeler;
62
122
  const clusterUrl = modelerUrl
63
123
  ? `${modelerUrl}?fromCluster=${currentCluster.uuid}`
64
124
  : undefined;
125
+ const reachableClusters = (clusters ?? []).filter((c) => c.uuid === currentCluster.uuid ||
126
+ getEndpointForApp(currentApp, c.endpoints));
127
+ const hasSiblings = reachableClusters.filter((c) => c.uuid !== currentCluster.uuid).length >
128
+ 0;
129
+ const clusterDropdownItems = hasSiblings
130
+ ? reachableClusters.map((c) => {
131
+ const isCurrent = c.uuid === currentCluster.uuid;
132
+ const stage = c.labels?.camunda?.[0];
133
+ return {
134
+ key: c.uuid,
135
+ label: c.name,
136
+ icon: CloudApp,
137
+ isSelected: isCurrent,
138
+ ...(isCurrent
139
+ ? {}
140
+ : {
141
+ linkProps: {
142
+ href: getEndpointForApp(currentApp, c.endpoints) ?? '',
143
+ },
144
+ }),
145
+ ...(stage
146
+ ? { trailingElement: _jsx(C3ClusterTag, { stage: stage, subtle: true }) }
147
+ : {}),
148
+ };
149
+ })
150
+ : undefined;
65
151
  crumbs.push({
66
152
  key: 'cluster',
67
153
  label: currentCluster.name,
68
154
  icon: CloudApp,
69
- ...(clusterUrl
70
- ? { linkProps: resolveLinkProps(clusterUrl, 'cluster') }
155
+ ...(clusterUrl ? { linkProps: { href: clusterUrl } } : {}),
156
+ ...(clusterDropdownItems
157
+ ? {
158
+ dropdownTitle: 'Switch cluster',
159
+ dropdownAriaLabel: 'Switch cluster',
160
+ dropdownItems: clusterDropdownItems,
161
+ }
71
162
  : {}),
72
163
  });
73
164
  }
74
165
  // ── App crumb ──────────────────────────────────────────────────────────
75
- const appLabel = getReadableAppName(currentApp);
76
- const appIcon = APP_ICONS[currentApp];
77
- const siblingApps = SWITCHABLE_APPS.filter((app) => app !== currentApp && currentCluster?.endpoints?.[app]);
78
- const dropdownItems = siblingApps.length > 0
79
- ? [
80
- {
81
- key: currentApp,
82
- label: appLabel,
83
- icon: appIcon,
84
- isSelected: true,
85
- },
86
- ...siblingApps.map((app) => ({
166
+ // Resolve label/icon via the same admin↔identity aliasing the sidebar
167
+ // entries use (`useClusterSidebarEntries`), so older clusters that expose
168
+ // only `identity` render as "Identity" instead of disappearing.
169
+ // `SWITCHABLE_APPS` carries only the canonical `admin` key; consumers
170
+ // that pass `currentApp: 'identity'` (legacy code path) still match the
171
+ // switcher row via this normalisation, otherwise the current entry
172
+ // would be missing from the dropdown and nothing would be selected.
173
+ const switcherKey = isAdminApp(currentApp) ? 'admin' : currentApp;
174
+ const resolvedCurrentApp = isAdminApp(currentApp) && currentCluster
175
+ ? resolveAdminAppName(currentCluster)
176
+ : currentApp;
177
+ const appLabel = getReadableAppName(resolvedCurrentApp);
178
+ const appIcon = APP_ICONS[resolvedCurrentApp];
179
+ const reachableApps = currentCluster
180
+ ? SWITCHABLE_APPS.filter((app) => app === switcherKey ||
181
+ getEndpointForApp(app, currentCluster.endpoints))
182
+ : [switcherKey];
183
+ const hasSiblingApps = reachableApps.filter((app) => app !== switcherKey).length > 0;
184
+ const dropdownItems = hasSiblingApps && currentCluster
185
+ ? reachableApps.map((app) => {
186
+ const isCurrent = app === switcherKey;
187
+ const resolvedApp = isAdminApp(app)
188
+ ? resolveAdminAppName(currentCluster)
189
+ : app;
190
+ return {
87
191
  key: app,
88
- label: getReadableAppName(app),
89
- icon: APP_ICONS[app],
90
- isSelected: false,
91
- linkProps: resolveLinkProps(currentCluster?.endpoints[app] ?? '', app),
92
- })),
93
- ]
192
+ label: getReadableAppName(resolvedApp),
193
+ icon: APP_ICONS[resolvedApp],
194
+ isSelected: isCurrent,
195
+ ...(isCurrent
196
+ ? {}
197
+ : {
198
+ linkProps: {
199
+ href: getEndpointForApp(app, currentCluster.endpoints) ?? '',
200
+ },
201
+ }),
202
+ };
203
+ })
94
204
  : undefined;
95
205
  crumbs.push({
96
206
  key: 'app',
@@ -105,5 +215,5 @@ export function useClusterWebappBreadcrumbs(options) {
105
215
  : {}),
106
216
  });
107
217
  return crumbs;
108
- }, [activeOrg, currentCluster, currentApp, resolveLinkProps]);
218
+ }, [activeOrg, orgs, clusters, currentCluster, currentApp]);
109
219
  }
@@ -15,8 +15,8 @@ export { C3LicenseTag, type C3LicenseTagProps, } from './components/c3-license-t
15
15
  export { default as C3Navigation } from './components/c3-navigation';
16
16
  export { C3NavigationAppProps, C3NavigationElementProps, C3NavigationNavBarElement, C3NavigationNavBarProps, C3NavigationNavBarSubElement, C3NavigationProps, } from './components/c3-navigation/c3-navigation.types';
17
17
  export { C3NavigationSideBarBaseProps } from './components/c3-navigation/c3-navigation-sidebar/c3-navigation-sidebar.types';
18
- export type { AppProps as C3NavV2AppProps, BreadcrumbDescriptor, BreadcrumbDropdownItem, BreadcrumbSegment, C3NavigationV2Props, C3NotificationsPanelProps, C3UserPanelProps, GlobalActionButton, GroupDescriptor, GroupItemDescriptor, HelpToolOptions, ItemDescriptor, LinkComponent, LinkProps, NotificationsToolOptions, SectionDescriptor, SidebarGroup, SidebarGroupItem, SidebarItem, SidebarNode, SidebarNodeDescriptor, SidebarProps, SidebarSection, ToolDescriptor, UseC3NavigationV2Options, UseC3NavigationV2Return, UseCamundaToolsOptions, UseClusterSidebarEntriesOptions, UserToolOptions, } from './components/c3-navigation-v2';
19
- export { C3BreadcrumbBar as preview_C3BreadcrumbBar, C3NavigationV2 as preview_C3NavigationV2, C3NotificationsPanel as preview_C3NotificationsPanel, C3Sidebar as preview_C3Sidebar, C3ToolsArea as preview_C3ToolsArea, C3UserPanel as preview_C3UserPanel, useC3NavigationV2 as preview_useC3NavigationV2, useCamundaTools as preview_useCamundaTools, useClusterSidebarEntries as preview_useClusterSidebarEntries, useClusterWebappBreadcrumbs as preview_useClusterWebappBreadcrumbs, } from './components/c3-navigation-v2';
18
+ export type { AppProps as C3NavV2AppProps, BreadcrumbDescriptor, BreadcrumbDropdownItem, BreadcrumbSegment, C3NavigationV2Props, C3NotificationsPanelLabels, C3NotificationsPanelProps, C3ThemeSelectorLabels, C3ThemeSelectorProps, C3UserPanelLabels, C3UserPanelProps, GlobalActionButton, GroupDescriptor, GroupItemDescriptor, HelpToolOptions, ItemDescriptor, LinkComponent, LinkProps, NotificationsToolOptions, SectionDescriptor, SidebarGroup, SidebarGroupItem, SidebarItem, SidebarLabels, SidebarNode, SidebarNodeDescriptor, SidebarProps, SidebarSection, ToolDescriptor, UseC3NavigationV2Options, UseC3NavigationV2Return, UseCamundaToolsOptions, UseClusterSidebarEntriesOptions, UserToolOptions, } from './components/c3-navigation-v2';
19
+ export { C3BreadcrumbBar as preview_C3BreadcrumbBar, C3NavigationV2 as preview_C3NavigationV2, C3NotificationsPanel as preview_C3NotificationsPanel, C3Sidebar as preview_C3Sidebar, C3ThemeSelector, C3ToolsArea as preview_C3ToolsArea, C3UserPanel as preview_C3UserPanel, useC3NavigationV2 as preview_useC3NavigationV2, useCamundaTools as preview_useCamundaTools, useClusterSidebarEntries as preview_useClusterSidebarEntries, useClusterWebappBreadcrumbs as preview_useClusterWebappBreadcrumbs, } from './components/c3-navigation-v2';
20
20
  export { C3BreadcrumbProps } from './components/c3-page/c3-breadcrumb/c3-breadcrumb.types';
21
21
  export { C3Page } from './components/c3-page/c3-page';
22
22
  export { C3PageProps } from './components/c3-page/c3-page.types';
@@ -13,7 +13,7 @@ export { C3HelpCenter } from './components/c3-help-center/c3-help-center.js';
13
13
  export { useC3HelpCenter } from './components/c3-help-center/c3-help-center-provider.js';
14
14
  export { C3LicenseTag, } from './components/c3-license-tag/index.js';
15
15
  export { default as C3Navigation } from './components/c3-navigation/index.js';
16
- export { C3BreadcrumbBar as preview_C3BreadcrumbBar, C3NavigationV2 as preview_C3NavigationV2, C3NotificationsPanel as preview_C3NotificationsPanel, C3Sidebar as preview_C3Sidebar, C3ToolsArea as preview_C3ToolsArea, C3UserPanel as preview_C3UserPanel, useC3NavigationV2 as preview_useC3NavigationV2, useCamundaTools as preview_useCamundaTools, useClusterSidebarEntries as preview_useClusterSidebarEntries, useClusterWebappBreadcrumbs as preview_useClusterWebappBreadcrumbs, } from './components/c3-navigation-v2/index.js';
16
+ export { C3BreadcrumbBar as preview_C3BreadcrumbBar, C3NavigationV2 as preview_C3NavigationV2, C3NotificationsPanel as preview_C3NotificationsPanel, C3Sidebar as preview_C3Sidebar, C3ThemeSelector, C3ToolsArea as preview_C3ToolsArea, C3UserPanel as preview_C3UserPanel, useC3NavigationV2 as preview_useC3NavigationV2, useCamundaTools as preview_useCamundaTools, useClusterSidebarEntries as preview_useClusterSidebarEntries, useClusterWebappBreadcrumbs as preview_useClusterWebappBreadcrumbs, } from './components/c3-navigation-v2/index.js';
17
17
  export { C3Page } from './components/c3-page/c3-page.js';
18
18
  export { C3ResponsiveStack } from './components/c3-responsive-stack/c3-responsive-stack.js';
19
19
  export { useC3Profile } from './components/c3-user-configuration/c3-profile-provider/c3-profile-provider.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/camunda-composite-components",
3
- "version": "0.23.4",
3
+ "version": "0.24.0",
4
4
  "description": "Camunda Composite Components",
5
5
  "bugs": {
6
6
  "url": "https://github.com/camunda/camunda-cloud-management-apps/issues"
@@ -60,7 +60,7 @@
60
60
  "styled-components": "6.4.2",
61
61
  "typescript-eslint": "8.59.4",
62
62
  "wait-on": "9.0.10",
63
- "@camunda/ccma-shared-types": "0.1.0"
63
+ "@camunda/ccma-shared-types": "0.1.1"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "@carbon/react": "1.x",