@camunda/camunda-composite-components 0.22.7 → 0.23.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 +23 -23
- package/lib/esm/src/components/c3-cluster-tag/c3-cluster-tag.d.ts +1 -0
- package/lib/esm/src/components/c3-cluster-tag/c3-cluster-tag.js +24 -3
- package/lib/esm/src/components/c3-cluster-tag/c3-cluster-tag.types.d.ts +1 -0
- package/lib/esm/src/components/c3-license-tag/c3-license-tag.d.ts +17 -0
- package/lib/esm/src/components/c3-license-tag/c3-license-tag.js +79 -0
- package/lib/esm/src/components/c3-license-tag/index.d.ts +1 -0
- package/lib/esm/src/components/c3-license-tag/index.js +6 -0
- package/lib/esm/src/components/c3-navigation/c3-org-sidebar/components.d.ts +3 -4
- package/lib/esm/src/components/c3-navigation-v2/c3-breadcrumb-bar.d.ts +7 -0
- package/lib/esm/src/components/c3-navigation-v2/c3-breadcrumb-bar.js +371 -0
- package/lib/esm/src/components/c3-navigation-v2/c3-navigation-v2.d.ts +9 -0
- package/lib/esm/src/components/c3-navigation-v2/c3-navigation-v2.js +72 -0
- package/lib/esm/src/components/c3-navigation-v2/c3-navigation-v2.types.d.ts +180 -0
- package/lib/esm/src/components/c3-navigation-v2/c3-navigation-v2.types.js +6 -0
- package/lib/esm/src/components/c3-navigation-v2/c3-sidebar.d.ts +8 -0
- package/lib/esm/src/components/c3-navigation-v2/c3-sidebar.js +357 -0
- package/lib/esm/src/components/c3-navigation-v2/c3-tools-area.d.ts +16 -0
- package/lib/esm/src/components/c3-navigation-v2/c3-tools-area.js +99 -0
- package/lib/esm/src/components/c3-navigation-v2/index.d.ts +18 -0
- package/lib/esm/src/components/c3-navigation-v2/index.js +15 -0
- package/lib/esm/src/components/c3-navigation-v2/stories/story-helpers.d.ts +10 -0
- package/lib/esm/src/components/c3-navigation-v2/stories/story-helpers.js +231 -0
- package/lib/esm/src/components/c3-navigation-v2/stories/story-templates.d.ts +11 -0
- package/lib/esm/src/components/c3-navigation-v2/stories/story-templates.js +796 -0
- package/lib/esm/src/components/c3-navigation-v2/tools/c3-info-panel.d.ts +11 -0
- package/lib/esm/src/components/c3-navigation-v2/tools/c3-info-panel.js +33 -0
- package/lib/esm/src/components/c3-navigation-v2/tools/c3-notifications-panel.d.ts +6 -0
- package/lib/esm/src/components/c3-navigation-v2/tools/c3-notifications-panel.js +52 -0
- package/lib/esm/src/components/c3-navigation-v2/tools/c3-user-panel.d.ts +30 -0
- package/lib/esm/src/components/c3-navigation-v2/tools/c3-user-panel.js +125 -0
- package/lib/esm/src/components/c3-navigation-v2/tools/panel-primitives.d.ts +7 -0
- package/lib/esm/src/components/c3-navigation-v2/tools/panel-primitives.js +16 -0
- package/lib/esm/src/components/c3-navigation-v2/use-c3-navigation-v2.d.ts +115 -0
- package/lib/esm/src/components/c3-navigation-v2/use-c3-navigation-v2.js +216 -0
- package/lib/esm/src/components/c3-navigation-v2/use-camunda-tools.d.ts +49 -0
- package/lib/esm/src/components/c3-navigation-v2/use-camunda-tools.js +75 -0
- package/lib/esm/src/components/c3-navigation-v2/use-cluster-webapp-breadcrumbs.d.ts +33 -0
- package/lib/esm/src/components/c3-navigation-v2/use-cluster-webapp-breadcrumbs.js +126 -0
- package/lib/esm/src/index.d.ts +5 -0
- package/lib/esm/src/index.js +3 -0
- package/package.json +23 -23
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
export interface InfoPanelElement {
|
|
3
|
+
key: string;
|
|
4
|
+
label: string;
|
|
5
|
+
onClick: () => void;
|
|
6
|
+
}
|
|
7
|
+
export interface C3InfoPanelProps {
|
|
8
|
+
elements: InfoPanelElement[];
|
|
9
|
+
title?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const C3InfoPanel: FC<C3InfoPanelProps>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { PanelHeader, PanelTitle } from './panel-primitives.js';
|
|
4
|
+
const LinkList = styled.ul `
|
|
5
|
+
list-style: none;
|
|
6
|
+
margin: 0;
|
|
7
|
+
padding: 0.5rem 0;
|
|
8
|
+
`;
|
|
9
|
+
const LinkItem = styled.li ``;
|
|
10
|
+
const LinkButton = styled.button `
|
|
11
|
+
width: 100%;
|
|
12
|
+
padding: 0.625rem 1rem;
|
|
13
|
+
background: transparent;
|
|
14
|
+
border: none;
|
|
15
|
+
cursor: pointer;
|
|
16
|
+
text-align: left;
|
|
17
|
+
color: var(--cds-link-primary);
|
|
18
|
+
font-size: var(--cds-body-01-font-size);
|
|
19
|
+
text-decoration: none;
|
|
20
|
+
transition: background 0.15s;
|
|
21
|
+
|
|
22
|
+
&:hover {
|
|
23
|
+
background: var(--cds-layer-hover);
|
|
24
|
+
color: var(--cds-link-primary-hover);
|
|
25
|
+
text-decoration: underline;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&:focus-visible {
|
|
29
|
+
outline: 2px solid var(--cds-focus);
|
|
30
|
+
outline-offset: -2px;
|
|
31
|
+
}
|
|
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))) })] }));
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type FC } from 'react';
|
|
2
|
+
import type { Notification } from '../../../api/notifications';
|
|
3
|
+
export interface C3NotificationsPanelProps {
|
|
4
|
+
onLinkClick?: (meta: Notification['meta']) => void;
|
|
5
|
+
}
|
|
6
|
+
export declare const C3NotificationsPanel: FC<C3NotificationsPanelProps>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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 { Button } from '@carbon/react';
|
|
8
|
+
import { useContext, useEffect } from 'react';
|
|
9
|
+
import styled from 'styled-components';
|
|
10
|
+
import { C3BellIcon } from '../../../assets/c3-icons.js';
|
|
11
|
+
import C3NotificationContainer, { NotificationDescription, NotificationTitle, } from '../../c3-navigation/c3-notification-provider/c3-notification-container.js';
|
|
12
|
+
import { C3NotificationContext } from '../../c3-navigation/c3-notification-provider/c3-notification-provider.js';
|
|
13
|
+
import { PanelHeader, PanelTitle } from './panel-primitives.js';
|
|
14
|
+
const DismissAllButton = styled(Button) `
|
|
15
|
+
font-size: var(--cds-helper-text-01-font-size);
|
|
16
|
+
font-weight: var(--cds-helper-text-01-font-weight);
|
|
17
|
+
line-height: var(--cds-helper-text-01-line-height);
|
|
18
|
+
letter-spacing: var(--cds-helper-text-01-letter-spacing);
|
|
19
|
+
`;
|
|
20
|
+
const EmptyState = styled.div `
|
|
21
|
+
padding: 16px;
|
|
22
|
+
`;
|
|
23
|
+
const EmptyStateTitle = styled(NotificationTitle) `
|
|
24
|
+
margin-top: 24px;
|
|
25
|
+
`;
|
|
26
|
+
const EmptyStateDescription = styled(NotificationDescription) `
|
|
27
|
+
color: var(--cds-text-secondary);
|
|
28
|
+
margin-top: 8px;
|
|
29
|
+
`;
|
|
30
|
+
const sortDescending = (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
|
|
31
|
+
export const C3NotificationsPanel = ({ onLinkClick, }) => {
|
|
32
|
+
const { notifications, markAllAsRead, dismiss, dismissAll } = useContext(C3NotificationContext);
|
|
33
|
+
// C3NotificationContainer ignores its onRead prop; read-state is driven by
|
|
34
|
+
// panel open/close instead. Since the panel only mounts while its tool is
|
|
35
|
+
// active, one mount-time pass is enough.
|
|
36
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-only
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const unread = notifications.filter((n) => n.state === 'new');
|
|
39
|
+
if (unread.length)
|
|
40
|
+
markAllAsRead(unread);
|
|
41
|
+
}, []);
|
|
42
|
+
return (_jsxs(_Fragment, { children: [_jsxs(PanelHeader, { style: {
|
|
43
|
+
width: '100%',
|
|
44
|
+
height: 60,
|
|
45
|
+
display: 'flex',
|
|
46
|
+
flexDirection: 'row',
|
|
47
|
+
justifyContent: 'space-between',
|
|
48
|
+
alignItems: 'center',
|
|
49
|
+
}, children: [_jsx(PanelTitle, { children: "Notifications" }), notifications.length > 0 && (_jsx(DismissAllButton, { kind: 'ghost', size: 'sm', onClick: () => dismissAll(notifications), children: "Dismiss all" }))] }), notifications.length > 0 ? ([...notifications]
|
|
50
|
+
.sort(sortDescending)
|
|
51
|
+
.map((notification) => (_jsx(C3NotificationContainer, { onRead: () => undefined, onDismiss: () => dismiss(notification), originalOnLinkClick: onLinkClick, onLinkClick: () => onLinkClick?.(notification.meta), unread: false, ...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." })] }))] }));
|
|
52
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { FC, ReactNode } from 'react';
|
|
2
|
+
export interface UserPanelElement {
|
|
3
|
+
key: string;
|
|
4
|
+
label: string;
|
|
5
|
+
onClick: () => void;
|
|
6
|
+
}
|
|
7
|
+
export interface C3UserPanelProps {
|
|
8
|
+
/** User's display name. */
|
|
9
|
+
name?: string;
|
|
10
|
+
/** User's email address. */
|
|
11
|
+
email?: string;
|
|
12
|
+
/** App version string shown at the bottom of the panel. */
|
|
13
|
+
version?: string;
|
|
14
|
+
/** Called when the user clicks "Log out". Omit to hide the button. */
|
|
15
|
+
onLogout?: () => void;
|
|
16
|
+
/** Internal toggle for production features (development use). */
|
|
17
|
+
stageToggle?: {
|
|
18
|
+
prodFeaturesEnabled: boolean;
|
|
19
|
+
toggle: () => void;
|
|
20
|
+
};
|
|
21
|
+
/** Arbitrary content rendered between the profile section and the link list. */
|
|
22
|
+
customSection?: ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* Additional link elements rendered above the built-in legal links
|
|
25
|
+
* (Terms of use, Privacy policy, Imprint). Elements whose `key` matches
|
|
26
|
+
* a built-in link (`terms`, `privacy`, `imprint`) replace the default.
|
|
27
|
+
*/
|
|
28
|
+
elements?: UserPanelElement[];
|
|
29
|
+
}
|
|
30
|
+
export declare const C3UserPanel: FC<C3UserPanelProps>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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 { Button, FormLabel, RadioButton, RadioButtonGroup, Stack, Toggle, } from '@carbon/react';
|
|
8
|
+
import { ArrowRight } from '@carbon/react/icons/index.esm.js';
|
|
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
|
+
import { PanelHeader, PanelTitle } from './panel-primitives.js';
|
|
13
|
+
const ProfileSection = styled.div `
|
|
14
|
+
padding: 1rem;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
gap: 2px;
|
|
18
|
+
border-bottom: 1px solid var(--cds-border-subtle);
|
|
19
|
+
`;
|
|
20
|
+
const ProfileName = styled.span `
|
|
21
|
+
font-size: var(--cds-body-01-font-size);
|
|
22
|
+
font-weight: 600;
|
|
23
|
+
color: var(--cds-text-primary);
|
|
24
|
+
`;
|
|
25
|
+
const ProfileEmail = styled.span `
|
|
26
|
+
font-size: var(--cds-label-01-font-size);
|
|
27
|
+
color: var(--cds-text-secondary);
|
|
28
|
+
`;
|
|
29
|
+
const ThemeSection = styled.div `
|
|
30
|
+
padding: 1rem;
|
|
31
|
+
border-bottom: 1px solid var(--cds-border-subtle);
|
|
32
|
+
`;
|
|
33
|
+
const CustomSection = styled.div `
|
|
34
|
+
border-bottom: 1px solid var(--cds-border-subtle);
|
|
35
|
+
`;
|
|
36
|
+
const ElementList = styled.ul `
|
|
37
|
+
list-style: none;
|
|
38
|
+
margin: 0;
|
|
39
|
+
padding: 0.5rem 0;
|
|
40
|
+
`;
|
|
41
|
+
const ElementItem = styled.li ``;
|
|
42
|
+
const ElementButton = styled.button `
|
|
43
|
+
width: 100%;
|
|
44
|
+
padding: 0.625rem 1rem;
|
|
45
|
+
background: transparent;
|
|
46
|
+
border: none;
|
|
47
|
+
cursor: pointer;
|
|
48
|
+
text-align: left;
|
|
49
|
+
color: var(--cds-link-primary);
|
|
50
|
+
font-size: var(--cds-body-01-font-size);
|
|
51
|
+
text-decoration: none;
|
|
52
|
+
transition: background 0.15s;
|
|
53
|
+
|
|
54
|
+
&:hover {
|
|
55
|
+
background: var(--cds-layer-hover);
|
|
56
|
+
color: var(--cds-link-primary-hover);
|
|
57
|
+
text-decoration: underline;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
&:focus-visible {
|
|
61
|
+
outline: 2px solid var(--cds-focus);
|
|
62
|
+
outline-offset: -2px;
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
const BottomPinned = styled.div `
|
|
66
|
+
margin-top: auto;
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
`;
|
|
70
|
+
const BottomActions = styled.div `
|
|
71
|
+
border-top: 1px solid var(--cds-border-subtle);
|
|
72
|
+
display: flex;
|
|
73
|
+
flex-direction: column;
|
|
74
|
+
|
|
75
|
+
.cds--btn {
|
|
76
|
+
max-width: none;
|
|
77
|
+
width: 100%;
|
|
78
|
+
justify-content: space-between;
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
81
|
+
const VersionWrapper = styled.div `
|
|
82
|
+
padding: 0.5rem 1rem;
|
|
83
|
+
`;
|
|
84
|
+
const VersionText = styled.p `
|
|
85
|
+
color: var(--cds-text-primary);
|
|
86
|
+
font-size: var(--cds-label-02-font-size);
|
|
87
|
+
font-weight: var(--cds-label-02-font-weight);
|
|
88
|
+
line-height: var(--cds-label-02-line-height);
|
|
89
|
+
letter-spacing: var(--cds-label-02-letter-spacing);
|
|
90
|
+
`;
|
|
91
|
+
const Copyright = styled.p `
|
|
92
|
+
color: var(--cds-text-secondary);
|
|
93
|
+
font-size: var(--cds-label-01-font-size);
|
|
94
|
+
font-weight: var(--cds-label-01-font-weight);
|
|
95
|
+
line-height: var(--cds-label-01-line-height);
|
|
96
|
+
letter-spacing: var(--cds-label-01-letter-spacing);
|
|
97
|
+
`;
|
|
98
|
+
const LEGAL_LINKS = [
|
|
99
|
+
{
|
|
100
|
+
key: 'terms',
|
|
101
|
+
label: 'Terms of use',
|
|
102
|
+
onClick: () => window.open('https://camunda.com/legal/terms/camunda-platform/camunda-platform-8-saas-trial/', '_blank'),
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
key: 'privacy',
|
|
106
|
+
label: 'Privacy policy',
|
|
107
|
+
onClick: () => window.open('https://camunda.com/legal/privacy/', '_blank'),
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
key: 'imprint',
|
|
111
|
+
label: 'Imprint',
|
|
112
|
+
onClick: () => window.open('https://camunda.com/legal/imprint/', '_blank'),
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
export const C3UserPanel = ({ name, email, version, onLogout, stageToggle, customSection, elements, }) => {
|
|
116
|
+
const { handleTheme } = useC3UserConfiguration();
|
|
117
|
+
const { theme, onThemeChange } = useC3Profile();
|
|
118
|
+
const consumerKeys = new Set((elements ?? []).map((e) => e.key));
|
|
119
|
+
const defaults = LEGAL_LINKS.filter((l) => !consumerKeys.has(l.key));
|
|
120
|
+
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: {
|
|
122
|
+
padding: '1rem',
|
|
123
|
+
borderBottom: '1px solid var(--cds-border-subtle)',
|
|
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" }) }))] })] }));
|
|
125
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
const headerStyle = {
|
|
3
|
+
background: 'var(--cds-layer-01)',
|
|
4
|
+
boxShadow: 'inset 0 -1px 0 var(--cds-border-subtle-01)',
|
|
5
|
+
padding: '24px 16px 13px',
|
|
6
|
+
flexShrink: 0,
|
|
7
|
+
};
|
|
8
|
+
const titleStyle = {
|
|
9
|
+
color: 'var(--cds-text-primary)',
|
|
10
|
+
fontSize: 'var(--cds-body-01-font-size)',
|
|
11
|
+
fontWeight: 'var(--cds-body-01-font-weight)',
|
|
12
|
+
lineHeight: 'var(--cds-body-01-line-height)',
|
|
13
|
+
letterSpacing: 'var(--cds-body-01-letter-spacing)',
|
|
14
|
+
};
|
|
15
|
+
export const PanelHeader = ({ style, ...props }) => (_jsx("div", { style: { ...headerStyle, ...style }, ...props }));
|
|
16
|
+
export const PanelTitle = ({ style, ...props }) => (_jsx("span", { style: { ...titleStyle, ...style }, ...props }));
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { BreadcrumbAction, C3NavigationV2Props, GlobalActionButton, LinkComponent, LinkProps, ToolDescriptor } from './c3-navigation-v2.types';
|
|
2
|
+
type ActiveMatcher = boolean | string | ((activeItemKey: string) => boolean);
|
|
3
|
+
export interface ItemDescriptor {
|
|
4
|
+
type: 'item';
|
|
5
|
+
key: string;
|
|
6
|
+
label: string;
|
|
7
|
+
icon: React.ComponentType<{
|
|
8
|
+
size?: number;
|
|
9
|
+
style?: React.CSSProperties;
|
|
10
|
+
}>;
|
|
11
|
+
linkProps?: LinkProps;
|
|
12
|
+
onClick?: () => void;
|
|
13
|
+
isActive?: ActiveMatcher;
|
|
14
|
+
trailingElement?: React.ReactNode;
|
|
15
|
+
}
|
|
16
|
+
export interface GroupItemDescriptor {
|
|
17
|
+
type: 'group-item';
|
|
18
|
+
key: string;
|
|
19
|
+
label: string;
|
|
20
|
+
icon: React.ComponentType<{
|
|
21
|
+
size?: number;
|
|
22
|
+
style?: React.CSSProperties;
|
|
23
|
+
}>;
|
|
24
|
+
linkProps?: LinkProps;
|
|
25
|
+
onClick?: () => void;
|
|
26
|
+
trailingElement?: React.ReactNode;
|
|
27
|
+
defaultExpanded?: boolean;
|
|
28
|
+
isActive?: ActiveMatcher;
|
|
29
|
+
children: SidebarNodeDescriptor[];
|
|
30
|
+
}
|
|
31
|
+
export interface GroupDescriptor {
|
|
32
|
+
type: 'group';
|
|
33
|
+
key: string;
|
|
34
|
+
label: string;
|
|
35
|
+
icon: React.ComponentType<{
|
|
36
|
+
size?: number;
|
|
37
|
+
style?: React.CSSProperties;
|
|
38
|
+
}>;
|
|
39
|
+
trailingElement?: React.ReactNode;
|
|
40
|
+
defaultExpanded?: boolean;
|
|
41
|
+
children: SidebarNodeDescriptor[];
|
|
42
|
+
}
|
|
43
|
+
export interface SectionDescriptor {
|
|
44
|
+
type: 'section';
|
|
45
|
+
key: string;
|
|
46
|
+
title?: string;
|
|
47
|
+
children: SidebarNodeDescriptor[];
|
|
48
|
+
compact?: boolean;
|
|
49
|
+
}
|
|
50
|
+
export type SidebarNodeDescriptor = ItemDescriptor | GroupItemDescriptor | GroupDescriptor | SectionDescriptor;
|
|
51
|
+
export interface BreadcrumbDropdownItemDescriptor {
|
|
52
|
+
key: string;
|
|
53
|
+
label: string;
|
|
54
|
+
icon?: React.ComponentType<{
|
|
55
|
+
size?: number;
|
|
56
|
+
style?: React.CSSProperties;
|
|
57
|
+
}>;
|
|
58
|
+
isSelected?: ActiveMatcher;
|
|
59
|
+
onClick?: () => void;
|
|
60
|
+
linkProps?: LinkProps;
|
|
61
|
+
trailingElement?: React.ReactNode;
|
|
62
|
+
}
|
|
63
|
+
export interface BreadcrumbDescriptor {
|
|
64
|
+
key: string;
|
|
65
|
+
label: string | ((activeItemKey: string) => string);
|
|
66
|
+
icon?: React.ComponentType<{
|
|
67
|
+
size?: number;
|
|
68
|
+
style?: React.CSSProperties;
|
|
69
|
+
}>;
|
|
70
|
+
onClick?: () => void;
|
|
71
|
+
linkProps?: LinkProps;
|
|
72
|
+
trailingElement?: React.ReactNode;
|
|
73
|
+
menuElement?: React.ReactNode;
|
|
74
|
+
actions?: BreadcrumbAction[];
|
|
75
|
+
dropdownTitle?: string;
|
|
76
|
+
dropdownAriaLabel?: string;
|
|
77
|
+
dropdownItems?: BreadcrumbDropdownItemDescriptor[];
|
|
78
|
+
}
|
|
79
|
+
export interface UseC3NavigationV2Options {
|
|
80
|
+
app: C3NavigationV2Props['app'];
|
|
81
|
+
skipToContentTargetId: string;
|
|
82
|
+
skipToContentLabel?: string;
|
|
83
|
+
headerAriaLabel?: string;
|
|
84
|
+
sidebarAriaLabel?: string;
|
|
85
|
+
/** Used to resolve `isActive` on sidebar items and `isSelected` on breadcrumb dropdown items. */
|
|
86
|
+
activeItemKey: string;
|
|
87
|
+
sidebarChildren?: SidebarNodeDescriptor[];
|
|
88
|
+
breadcrumbs?: BreadcrumbDescriptor[];
|
|
89
|
+
tools?: ToolDescriptor[];
|
|
90
|
+
globalActions?: GlobalActionButton[];
|
|
91
|
+
sidebarExpandedWidth?: string;
|
|
92
|
+
sidebarCollapsedWidth?: string;
|
|
93
|
+
defaultSidebarExpanded?: boolean;
|
|
94
|
+
onSidebarToggle?: (isExpanded: boolean) => void;
|
|
95
|
+
linkComponent?: LinkComponent;
|
|
96
|
+
headerTrailingContent?: React.ReactNode;
|
|
97
|
+
}
|
|
98
|
+
export interface UseC3NavigationV2Return {
|
|
99
|
+
navProps: C3NavigationV2Props;
|
|
100
|
+
isSidebarExpanded: boolean;
|
|
101
|
+
setSidebarExpanded: (expanded: boolean) => void;
|
|
102
|
+
expandedGroups: Set<string>;
|
|
103
|
+
toggleGroup: (groupKey: string) => void;
|
|
104
|
+
expandGroup: (groupKey: string) => void;
|
|
105
|
+
collapseGroup: (groupKey: string) => void;
|
|
106
|
+
activeToolKey: string | null;
|
|
107
|
+
setActiveToolKey: (key: string | null) => void;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* State container and props assembler for `C3NavigationV2`. Manages sidebar
|
|
111
|
+
* expand/collapse, group expand state, tool panel state, and resolves
|
|
112
|
+
* descriptors (isActive, label functions) into concrete props.
|
|
113
|
+
*/
|
|
114
|
+
export declare function useC3NavigationV2(options: UseC3NavigationV2Options): UseC3NavigationV2Return;
|
|
115
|
+
export {};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
|
|
3
|
+
* under one or more contributor license agreements. Licensed under a commercial license.
|
|
4
|
+
* You may not use this file except in compliance with the commercial license.
|
|
5
|
+
*/
|
|
6
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
7
|
+
function matchesActive(descriptor, activeItemKey) {
|
|
8
|
+
if (descriptor.isActive === undefined)
|
|
9
|
+
return descriptor.key === activeItemKey;
|
|
10
|
+
if (typeof descriptor.isActive === 'boolean')
|
|
11
|
+
return descriptor.isActive;
|
|
12
|
+
if (typeof descriptor.isActive === 'string')
|
|
13
|
+
return descriptor.isActive === activeItemKey;
|
|
14
|
+
return descriptor.isActive(activeItemKey);
|
|
15
|
+
}
|
|
16
|
+
function collectDefaultExpanded(nodes, out) {
|
|
17
|
+
for (const node of nodes) {
|
|
18
|
+
if ((node.type === 'group-item' || node.type === 'group') &&
|
|
19
|
+
node.defaultExpanded) {
|
|
20
|
+
out.add(node.key);
|
|
21
|
+
}
|
|
22
|
+
if ('children' in node) {
|
|
23
|
+
collectDefaultExpanded(node.children, out);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* State container and props assembler for `C3NavigationV2`. Manages sidebar
|
|
29
|
+
* expand/collapse, group expand state, tool panel state, and resolves
|
|
30
|
+
* descriptors (isActive, label functions) into concrete props.
|
|
31
|
+
*/
|
|
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;
|
|
34
|
+
const [isSidebarExpanded, setIsSidebarExpanded] = useState(defaultSidebarExpanded);
|
|
35
|
+
const setSidebarExpanded = useCallback((expanded) => {
|
|
36
|
+
setIsSidebarExpanded(expanded);
|
|
37
|
+
onSidebarToggle?.(expanded);
|
|
38
|
+
}, [onSidebarToggle]);
|
|
39
|
+
const handleToggleSidebar = useCallback(() => {
|
|
40
|
+
setSidebarExpanded(!isSidebarExpanded);
|
|
41
|
+
}, [isSidebarExpanded, setSidebarExpanded]);
|
|
42
|
+
const [expandedGroups, setExpandedGroups] = useState(() => {
|
|
43
|
+
const initial = new Set();
|
|
44
|
+
collectDefaultExpanded(sidebarChildren, initial);
|
|
45
|
+
return initial;
|
|
46
|
+
});
|
|
47
|
+
const toggleGroup = useCallback((groupKey) => {
|
|
48
|
+
setExpandedGroups((prev) => {
|
|
49
|
+
const next = new Set(prev);
|
|
50
|
+
if (next.has(groupKey))
|
|
51
|
+
next.delete(groupKey);
|
|
52
|
+
else
|
|
53
|
+
next.add(groupKey);
|
|
54
|
+
return next;
|
|
55
|
+
});
|
|
56
|
+
}, []);
|
|
57
|
+
const expandGroup = useCallback((groupKey) => {
|
|
58
|
+
setExpandedGroups((prev) => {
|
|
59
|
+
if (prev.has(groupKey))
|
|
60
|
+
return prev;
|
|
61
|
+
const next = new Set(prev);
|
|
62
|
+
next.add(groupKey);
|
|
63
|
+
return next;
|
|
64
|
+
});
|
|
65
|
+
}, []);
|
|
66
|
+
const collapseGroup = useCallback((groupKey) => {
|
|
67
|
+
setExpandedGroups((prev) => {
|
|
68
|
+
if (!prev.has(groupKey))
|
|
69
|
+
return prev;
|
|
70
|
+
const next = new Set(prev);
|
|
71
|
+
next.delete(groupKey);
|
|
72
|
+
return next;
|
|
73
|
+
});
|
|
74
|
+
}, []);
|
|
75
|
+
const [activeToolKey, setActiveToolKey] = useState(null);
|
|
76
|
+
const handleActiveToolChange = useCallback((key) => setActiveToolKey(key), []);
|
|
77
|
+
const resolveNodes = useCallback((descriptors) => descriptors.map((desc) => {
|
|
78
|
+
switch (desc.type) {
|
|
79
|
+
case 'item':
|
|
80
|
+
return {
|
|
81
|
+
type: 'item',
|
|
82
|
+
key: desc.key,
|
|
83
|
+
label: desc.label,
|
|
84
|
+
icon: desc.icon,
|
|
85
|
+
isActive: matchesActive(desc, activeItemKey),
|
|
86
|
+
onClick: desc.onClick,
|
|
87
|
+
linkProps: desc.linkProps,
|
|
88
|
+
trailingElement: desc.trailingElement,
|
|
89
|
+
};
|
|
90
|
+
case 'group-item':
|
|
91
|
+
return {
|
|
92
|
+
type: 'group-item',
|
|
93
|
+
key: desc.key,
|
|
94
|
+
label: desc.label,
|
|
95
|
+
icon: desc.icon,
|
|
96
|
+
isActive: matchesActive(desc, activeItemKey),
|
|
97
|
+
onClick: desc.onClick,
|
|
98
|
+
linkProps: desc.linkProps,
|
|
99
|
+
trailingElement: desc.trailingElement,
|
|
100
|
+
isExpanded: expandedGroups.has(desc.key),
|
|
101
|
+
onToggleExpand: () => toggleGroup(desc.key),
|
|
102
|
+
children: resolveNodes(desc.children),
|
|
103
|
+
};
|
|
104
|
+
case 'group':
|
|
105
|
+
return {
|
|
106
|
+
type: 'group',
|
|
107
|
+
key: desc.key,
|
|
108
|
+
label: desc.label,
|
|
109
|
+
icon: desc.icon,
|
|
110
|
+
trailingElement: desc.trailingElement,
|
|
111
|
+
isExpanded: expandedGroups.has(desc.key),
|
|
112
|
+
onToggleExpand: () => {
|
|
113
|
+
if (!isSidebarExpanded) {
|
|
114
|
+
setSidebarExpanded(true);
|
|
115
|
+
expandGroup(desc.key);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
toggleGroup(desc.key);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
children: resolveNodes(desc.children),
|
|
122
|
+
};
|
|
123
|
+
case 'section':
|
|
124
|
+
return {
|
|
125
|
+
type: 'section',
|
|
126
|
+
key: desc.key,
|
|
127
|
+
title: desc.title,
|
|
128
|
+
compact: desc.compact,
|
|
129
|
+
children: resolveNodes(desc.children),
|
|
130
|
+
};
|
|
131
|
+
default:
|
|
132
|
+
return desc;
|
|
133
|
+
}
|
|
134
|
+
}), [
|
|
135
|
+
activeItemKey,
|
|
136
|
+
expandedGroups,
|
|
137
|
+
toggleGroup,
|
|
138
|
+
expandGroup,
|
|
139
|
+
isSidebarExpanded,
|
|
140
|
+
setSidebarExpanded,
|
|
141
|
+
]);
|
|
142
|
+
const resolvedChildren = useMemo(() => resolveNodes(sidebarChildren), [resolveNodes, sidebarChildren]);
|
|
143
|
+
const resolvedBreadcrumbs = useMemo(() => breadcrumbs.map((desc) => ({
|
|
144
|
+
key: desc.key,
|
|
145
|
+
label: typeof desc.label === 'function'
|
|
146
|
+
? desc.label(activeItemKey)
|
|
147
|
+
: desc.label,
|
|
148
|
+
icon: desc.icon,
|
|
149
|
+
onClick: desc.onClick,
|
|
150
|
+
linkProps: desc.linkProps,
|
|
151
|
+
trailingElement: desc.trailingElement,
|
|
152
|
+
menuElement: desc.menuElement,
|
|
153
|
+
actions: desc.actions,
|
|
154
|
+
dropdownTitle: desc.dropdownTitle,
|
|
155
|
+
dropdownAriaLabel: desc.dropdownAriaLabel,
|
|
156
|
+
dropdownItems: desc.dropdownItems?.map((item) => ({
|
|
157
|
+
key: item.key,
|
|
158
|
+
label: item.label,
|
|
159
|
+
icon: item.icon,
|
|
160
|
+
isSelected: matchesActive({ key: item.key, isActive: item.isSelected }, activeItemKey),
|
|
161
|
+
onClick: item.onClick,
|
|
162
|
+
linkProps: item.linkProps,
|
|
163
|
+
trailingElement: item.trailingElement,
|
|
164
|
+
})),
|
|
165
|
+
})), [breadcrumbs, activeItemKey]);
|
|
166
|
+
const hasSidebar = sidebarChildren.length > 0;
|
|
167
|
+
const sidebarProps = hasSidebar
|
|
168
|
+
? {
|
|
169
|
+
ariaLabel: sidebarAriaLabel,
|
|
170
|
+
children: resolvedChildren,
|
|
171
|
+
isExpanded: isSidebarExpanded,
|
|
172
|
+
onToggleExpanded: handleToggleSidebar,
|
|
173
|
+
expandedWidth: sidebarExpandedWidth,
|
|
174
|
+
collapsedWidth: sidebarCollapsedWidth,
|
|
175
|
+
linkComponent,
|
|
176
|
+
}
|
|
177
|
+
: undefined;
|
|
178
|
+
const navProps = useMemo(() => ({
|
|
179
|
+
app,
|
|
180
|
+
skipToContentTargetId,
|
|
181
|
+
skipToContentLabel,
|
|
182
|
+
headerAriaLabel,
|
|
183
|
+
breadcrumbs: resolvedBreadcrumbs,
|
|
184
|
+
tools,
|
|
185
|
+
globalActions,
|
|
186
|
+
sidebar: sidebarProps,
|
|
187
|
+
linkComponent,
|
|
188
|
+
headerTrailingContent,
|
|
189
|
+
activeToolKey,
|
|
190
|
+
onActiveToolChange: handleActiveToolChange,
|
|
191
|
+
}), [
|
|
192
|
+
app,
|
|
193
|
+
skipToContentTargetId,
|
|
194
|
+
skipToContentLabel,
|
|
195
|
+
headerAriaLabel,
|
|
196
|
+
resolvedBreadcrumbs,
|
|
197
|
+
tools,
|
|
198
|
+
globalActions,
|
|
199
|
+
sidebarProps,
|
|
200
|
+
linkComponent,
|
|
201
|
+
headerTrailingContent,
|
|
202
|
+
activeToolKey,
|
|
203
|
+
handleActiveToolChange,
|
|
204
|
+
]);
|
|
205
|
+
return {
|
|
206
|
+
navProps,
|
|
207
|
+
isSidebarExpanded,
|
|
208
|
+
setSidebarExpanded,
|
|
209
|
+
expandedGroups,
|
|
210
|
+
toggleGroup,
|
|
211
|
+
expandGroup,
|
|
212
|
+
collapseGroup,
|
|
213
|
+
activeToolKey,
|
|
214
|
+
setActiveToolKey,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Notification } from '../../api/notifications';
|
|
2
|
+
import type { ToolDescriptor } from './c3-navigation-v2.types';
|
|
3
|
+
import { type InfoPanelElement } from './tools/c3-info-panel';
|
|
4
|
+
import { type C3UserPanelProps } from './tools/c3-user-panel';
|
|
5
|
+
export interface NotificationsToolOptions {
|
|
6
|
+
onLinkClick?: (meta: Notification['meta']) => void;
|
|
7
|
+
ariaLabel?: string;
|
|
8
|
+
}
|
|
9
|
+
export type UserToolOptions = C3UserPanelProps & {
|
|
10
|
+
ariaLabel?: string;
|
|
11
|
+
};
|
|
12
|
+
export interface HelpToolOptions {
|
|
13
|
+
onClick: () => void;
|
|
14
|
+
ariaLabel?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface InfoToolOptions {
|
|
17
|
+
elements: InfoPanelElement[];
|
|
18
|
+
title?: string;
|
|
19
|
+
ariaLabel?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface UseCamundaToolsOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Notifications tool with a right-side panel. Omit to hide; pass `{}` for
|
|
24
|
+
* defaults, or an options object to customise behaviour.
|
|
25
|
+
*/
|
|
26
|
+
notifications?: NotificationsToolOptions;
|
|
27
|
+
/**
|
|
28
|
+
* User/account tool with a right-side panel. Omit to hide; pass `{}` for
|
|
29
|
+
* defaults, or options with profile/version/stageToggle/customSection.
|
|
30
|
+
*/
|
|
31
|
+
user?: UserToolOptions;
|
|
32
|
+
/**
|
|
33
|
+
* Info/help panel with a list of link elements (docs, academy, support…).
|
|
34
|
+
* Consumer provides the elements; C3 renders the panel.
|
|
35
|
+
*/
|
|
36
|
+
info?: InfoToolOptions;
|
|
37
|
+
/**
|
|
38
|
+
* Help button with no panel. Consumer provides the onClick action.
|
|
39
|
+
*/
|
|
40
|
+
help?: HelpToolOptions;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Single helper entry point for the standard Camunda tool set.
|
|
44
|
+
* Returns a ToolDescriptor[] ready to pass into C3NavigationV2's `tools` prop.
|
|
45
|
+
* Custom tools can be appended to the returned array.
|
|
46
|
+
*
|
|
47
|
+
* Must be called within a C3UserConfigurationProvider tree (SaaS).
|
|
48
|
+
*/
|
|
49
|
+
export declare const useCamundaTools: (options: UseCamundaToolsOptions) => ToolDescriptor[];
|