@camunda/camunda-composite-components 0.23.4 → 0.25.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.
- package/lib/esm/package.json +22 -22
- package/lib/esm/src/assets/c3-icons.d.ts +4 -4
- package/lib/esm/src/components/c3-app-teaser/app-teaser-cards.d.ts +1 -1
- package/lib/esm/src/components/c3-help-center/help-center-hint.d.ts +1 -1
- package/lib/esm/src/components/c3-license-tag/c3-license-tag.d.ts +4 -5
- package/lib/esm/src/components/c3-license-tag/c3-license-tag.js +57 -46
- package/lib/esm/src/components/c3-navigation-v2/c3-breadcrumb-bar.js +2 -2
- package/lib/esm/src/components/c3-navigation-v2/c3-navigation-v2.js +6 -2
- package/lib/esm/src/components/c3-navigation-v2/c3-navigation-v2.types.d.ts +14 -0
- package/lib/esm/src/components/c3-navigation-v2/c3-sidebar.d.ts +1 -1
- package/lib/esm/src/components/c3-navigation-v2/c3-sidebar.js +62 -47
- package/lib/esm/src/components/c3-navigation-v2/index.d.ts +5 -3
- package/lib/esm/src/components/c3-navigation-v2/index.js +1 -0
- package/lib/esm/src/components/c3-navigation-v2/tools/c3-info-panel.d.ts +2 -1
- package/lib/esm/src/components/c3-navigation-v2/tools/c3-info-panel.js +1 -1
- package/lib/esm/src/components/c3-navigation-v2/tools/c3-notifications-panel.d.ts +11 -0
- package/lib/esm/src/components/c3-navigation-v2/tools/c3-notifications-panel.js +9 -5
- package/lib/esm/src/components/c3-navigation-v2/tools/c3-theme-selector.d.ts +25 -0
- package/lib/esm/src/components/c3-navigation-v2/tools/c3-theme-selector.js +15 -0
- package/lib/esm/src/components/c3-navigation-v2/tools/c3-user-panel.d.ts +15 -0
- package/lib/esm/src/components/c3-navigation-v2/tools/c3-user-panel.js +10 -17
- package/lib/esm/src/components/c3-navigation-v2/use-c3-navigation-v2.d.ts +3 -1
- package/lib/esm/src/components/c3-navigation-v2/use-c3-navigation-v2.js +2 -1
- package/lib/esm/src/components/c3-navigation-v2/use-camunda-tools.d.ts +7 -1
- package/lib/esm/src/components/c3-navigation-v2/use-camunda-tools.js +4 -4
- package/lib/esm/src/components/c3-navigation-v2/use-cluster-webapp-breadcrumbs.d.ts +20 -16
- package/lib/esm/src/components/c3-navigation-v2/use-cluster-webapp-breadcrumbs.js +156 -36
- package/lib/esm/src/components/c3-user-configuration/c3-profile-provider/c3-profile-provider.d.ts +1 -1
- package/lib/esm/src/contexts/c3-cluster-update-manager.d.ts +1 -1
- package/lib/esm/src/index.d.ts +2 -2
- package/lib/esm/src/index.js +1 -1
- package/lib/esm/src/utils/camunda.d.ts +1 -1
- package/package.json +23 -23
package/lib/esm/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camunda/camunda-composite-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Camunda Composite Components",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/camunda/camunda-cloud-management-apps/issues"
|
|
@@ -50,42 +50,42 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"jwt-decode": "4.0.0",
|
|
53
|
-
"react-error-boundary": "6.1.
|
|
53
|
+
"react-error-boundary": "6.1.2",
|
|
54
54
|
"react-markdown": "10.1.0",
|
|
55
|
-
"semver": "7.8.
|
|
55
|
+
"semver": "7.8.4"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@auth0/auth0-spa-js": "2.
|
|
58
|
+
"@auth0/auth0-spa-js": "2.21.1",
|
|
59
59
|
"@camunda/ccma-shared-types": "workspace:*",
|
|
60
|
-
"@carbon/react": "1.
|
|
60
|
+
"@carbon/react": "1.109.0",
|
|
61
61
|
"@chromatic-com/storybook": "5.2.1",
|
|
62
62
|
"@mdx-js/react": "3.1.1",
|
|
63
63
|
"@playwright/test": "1.60.0",
|
|
64
|
-
"@storybook/addon-a11y": "10.4.
|
|
65
|
-
"@storybook/addon-docs": "10.4.
|
|
66
|
-
"@storybook/addon-links": "10.4.
|
|
67
|
-
"@storybook/addon-vitest": "10.4.
|
|
68
|
-
"@storybook/react": "10.4.
|
|
69
|
-
"@storybook/react-vite": "10.4.
|
|
70
|
-
"@vitest/browser": "4.1.
|
|
71
|
-
"@vitest/browser-playwright": "4.1.
|
|
72
|
-
"vitest": "4.1.
|
|
64
|
+
"@storybook/addon-a11y": "10.4.3",
|
|
65
|
+
"@storybook/addon-docs": "10.4.3",
|
|
66
|
+
"@storybook/addon-links": "10.4.3",
|
|
67
|
+
"@storybook/addon-vitest": "10.4.3",
|
|
68
|
+
"@storybook/react": "10.4.3",
|
|
69
|
+
"@storybook/react-vite": "10.4.3",
|
|
70
|
+
"@vitest/browser": "4.1.8",
|
|
71
|
+
"@vitest/browser-playwright": "4.1.8",
|
|
72
|
+
"vitest": "4.1.8",
|
|
73
73
|
"conventional-changelog-conventionalcommits": "9.3.1",
|
|
74
|
-
"eslint-import-resolver-typescript": "4.4.
|
|
74
|
+
"eslint-import-resolver-typescript": "4.4.5",
|
|
75
75
|
"eslint-plugin-react": "7.37.5",
|
|
76
76
|
"eslint-plugin-react-hooks": "7.1.1",
|
|
77
|
-
"eslint-plugin-storybook": "10.4.
|
|
77
|
+
"eslint-plugin-storybook": "10.4.3",
|
|
78
78
|
"event-source-polyfill": "1.0.31",
|
|
79
|
-
"mixpanel-browser": "2.
|
|
79
|
+
"mixpanel-browser": "2.80.0",
|
|
80
80
|
"playwright": "1.60.0",
|
|
81
|
-
"react": "19.2.
|
|
82
|
-
"react-dom": "19.2.
|
|
83
|
-
"react-is": "19.2.
|
|
81
|
+
"react": "19.2.7",
|
|
82
|
+
"react-dom": "19.2.7",
|
|
83
|
+
"react-is": "19.2.7",
|
|
84
84
|
"rimraf": "6.1.3",
|
|
85
85
|
"serve": "14.2.6",
|
|
86
|
-
"storybook": "10.4.
|
|
86
|
+
"storybook": "10.4.3",
|
|
87
87
|
"styled-components": "6.4.2",
|
|
88
|
-
"typescript-eslint": "8.
|
|
88
|
+
"typescript-eslint": "8.61.0",
|
|
89
89
|
"wait-on": "9.0.10"
|
|
90
90
|
},
|
|
91
91
|
"peerDependencies": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { C3IconProps } from './c3-icons.types';
|
|
2
|
-
export declare const C3AppMenuIcon: ({ size }: C3IconProps) => import("react
|
|
3
|
-
export declare const C3NotificationsUnreadIcon: ({ size }: C3IconProps) => import("react
|
|
4
|
-
export declare const C3BellIcon: ({ size }: C3IconProps) => import("react
|
|
5
|
-
export declare const CamundaLogo: () => import("react
|
|
2
|
+
export declare const C3AppMenuIcon: ({ size }: C3IconProps) => import("react").JSX.Element;
|
|
3
|
+
export declare const C3NotificationsUnreadIcon: ({ size }: C3IconProps) => import("react").JSX.Element;
|
|
4
|
+
export declare const C3BellIcon: ({ size }: C3IconProps) => import("react").JSX.Element;
|
|
5
|
+
export declare const CamundaLogo: () => import("react").JSX.Element;
|
|
@@ -13,5 +13,5 @@ type AppTeaserCardsProps = {
|
|
|
13
13
|
};
|
|
14
14
|
subtext: string;
|
|
15
15
|
};
|
|
16
|
-
export declare const AppTeaserCards: ({ title, subtitle, cards, cta, subtext, }: AppTeaserCardsProps) =>
|
|
16
|
+
export declare const AppTeaserCards: ({ title, subtitle, cards, cta, subtext, }: AppTeaserCardsProps) => JSX.Element;
|
|
17
17
|
export {};
|
|
@@ -8,10 +8,9 @@ export interface C3LicenseTagProps {
|
|
|
8
8
|
expiresAt?: number | string;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
* Renders
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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;
|
|
@@ -17,63 +17,74 @@ function resolveExpiresAt(value) {
|
|
|
17
17
|
return Date.parse(value);
|
|
18
18
|
return value;
|
|
19
19
|
}
|
|
20
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
65
|
-
*
|
|
66
|
-
*
|
|
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
|
|
72
|
-
if (
|
|
82
|
+
const variants = getTagVariants(props);
|
|
83
|
+
if (variants.length === 0)
|
|
73
84
|
return null;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
return (_jsx("span", { style: {
|
|
86
|
+
display: 'inline-flex',
|
|
87
|
+
alignItems: 'center',
|
|
88
|
+
gap: 'var(--cds-spacing-02)',
|
|
89
|
+
}, children: variants.map(renderVariant) }));
|
|
79
90
|
};
|
|
@@ -173,7 +173,7 @@ const Separator = styled.span `
|
|
|
173
173
|
const DropdownEntry = ({ item, onSelect, linkComponent, }) => {
|
|
174
174
|
const Icon = item.icon;
|
|
175
175
|
return (_jsxs(DropdownItem, { as: item.linkProps?.href !== undefined
|
|
176
|
-
?
|
|
176
|
+
? 'a'
|
|
177
177
|
: item.linkProps
|
|
178
178
|
? linkComponent
|
|
179
179
|
: 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 } }))] }));
|
|
@@ -345,7 +345,7 @@ const BreadcrumbSegmentComponent = ({ segment, isLast, linkComponent, }) => {
|
|
|
345
345
|
}, [close]);
|
|
346
346
|
const handleDropdownKeyDown = usePanelKeyboard(dropdownPanelRef, close, chevronRef, isOpen, '[role="option"]');
|
|
347
347
|
return (_jsxs(_Fragment, { children: [_jsxs(SegmentWrapper, { ref: wrapperRef, children: [_jsxs(SegmentButton, { ref: segmentButtonRef, as: segment.linkProps?.href !== undefined
|
|
348
|
-
?
|
|
348
|
+
? 'a'
|
|
349
349
|
: segment.linkProps
|
|
350
350
|
? linkComponent
|
|
351
351
|
: undefined, ...(segment.linkProps ?? {}), "$isInteractive": !!(segment.onClick || segment.linkProps), "$isLast": isLast, onClick: segment.onClick, "aria-label": segment.label, title: segment.label, children: [Icon && _jsx(Icon, { size: 16, style: { flexShrink: 0 } }), _jsx(SegmentLabel, { children: segment.label }), segment.trailingElement] }), segment.menuElement, segment.actions && segment.actions.length > 0 && (_jsx(ActionsMenu, { actions: segment.actions, ariaLabel: `${segment.label} actions` })), hasDropdown && (_jsx(ChevronButton, { ref: chevronRef, "$isOpen": isOpen, onClick: toggle, onKeyDown: (e) => {
|
|
@@ -18,9 +18,13 @@ const StyledHeader = styled(Header) `
|
|
|
18
18
|
z-index: 8001 !important;
|
|
19
19
|
|
|
20
20
|
// Carbon defaults the tools area to flex: 1, which splits the row
|
|
21
|
-
// 50/50 with breadcrumbs. Make it intrinsic
|
|
21
|
+
// 50/50 with breadcrumbs. Make it intrinsic and pin it to the right
|
|
22
|
+
// (margin-inline-start: auto) so the tools stay flush right even
|
|
23
|
+
// when the breadcrumb bar is absent — loading state, or routes that
|
|
24
|
+
// don't publish breadcrumbs.
|
|
22
25
|
.cds--header__global {
|
|
23
26
|
flex: 0 0 auto;
|
|
27
|
+
margin-inline-start: auto;
|
|
24
28
|
}
|
|
25
29
|
`;
|
|
26
30
|
const LogoSection = styled.a `
|
|
@@ -63,7 +67,7 @@ export const C3NavigationV2 = ({ app, skipToContentTargetId, skipToContentLabel
|
|
|
63
67
|
root.style.removeProperty('--c3-sidebar-width');
|
|
64
68
|
};
|
|
65
69
|
}, [sidebarWidth]);
|
|
66
|
-
return (_jsxs(_Fragment, { children: [_jsxs(StyledHeader, { "aria-label": ariaLabel, children: [_jsx(SkipToContent, { href: `#${skipToContentTargetId}`, children: skipToContentLabel }),
|
|
70
|
+
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
71
|
display: 'flex',
|
|
68
72
|
alignItems: 'center',
|
|
69
73
|
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
|
-
|
|
43
|
+
height: var(--cds-spacing-09);
|
|
44
|
+
box-sizing: border-box;
|
|
40
45
|
padding: ${(p) => {
|
|
41
46
|
if (p.$depth > 0)
|
|
42
|
-
return `0
|
|
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
|
-
|
|
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 `
|
|
115
|
-
return '
|
|
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
|
-
|
|
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 `
|
|
167
|
-
return '
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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":
|
|
324
|
-
|
|
325
|
-
:
|
|
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
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
let
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
eatScrollPadding =
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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 {
|
|
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';
|
|
@@ -30,4 +30,4 @@ const LinkButton = styled.button `
|
|
|
30
30
|
outline-offset: -2px;
|
|
31
31
|
}
|
|
32
32
|
`;
|
|
33
|
-
export const C3InfoPanel = ({ elements, title = '
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
};
|