@camunda/camunda-composite-components 0.17.1 → 0.18.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/README.md +48 -0
- package/lib/esm/package.json +1 -1
- package/lib/esm/src/api/clusters.d.ts +10 -0
- package/lib/esm/src/api/clusters.js +27 -0
- package/lib/esm/src/api/endpoints.const.d.ts +2 -0
- package/lib/esm/src/api/endpoints.const.js +17 -4
- package/lib/esm/src/components/c3-navigation/c3-navigation-appbar/c3-navigation-appbar.js +95 -15
- package/lib/esm/src/components/c3-navigation/c3-navigation.js +69 -68
- package/lib/esm/src/components/c3-navigation/c3-navigation.types.d.ts +1 -0
- package/lib/esm/src/components/c3-navigation/helpers.js +14 -16
- package/lib/esm/src/components/c3-navigation/stories/story-templates.js +5 -1
- package/lib/esm/src/components/c3-user-configuration/c3-profile-provider/c3-profile-provider.d.ts +1 -0
- package/lib/esm/src/components/c3-user-configuration/c3-profile-provider/c3-profile-provider.js +3 -1
- package/lib/esm/src/contexts/c3-cluster-update-manager.d.ts +12 -0
- package/lib/esm/src/contexts/c3-cluster-update-manager.js +27 -0
- package/lib/esm/src/index.d.ts +1 -0
- package/lib/esm/src/index.js +1 -0
- package/lib/esm/src/utils/camunda.types.d.ts +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -67,6 +67,54 @@ return (
|
|
|
67
67
|
)
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
Opt into SSE by using the `C3ClusterUpdateManager` Provider
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
// App.tsx
|
|
74
|
+
import { C3ClusterUpdateManager } from "@camunda/camunda-composite-components"
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<C3ClusterUpdateManager stage={getEnv()} userToken={auth.token!}>
|
|
78
|
+
<App />
|
|
79
|
+
</C3ClusterUpdateManager>
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Listen for SSE events from anywhere in your app
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
// ClusterPage.tsx
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
const handler = (event) => {
|
|
90
|
+
const data = event.detail
|
|
91
|
+
|
|
92
|
+
if (data && data.org && data.org === currentOrgId) {
|
|
93
|
+
switch (data.entity) {
|
|
94
|
+
case "cluster": {
|
|
95
|
+
}
|
|
96
|
+
case "cluster-client": {
|
|
97
|
+
}
|
|
98
|
+
case "alert-subscription": {
|
|
99
|
+
}
|
|
100
|
+
case "connector-secrets": {
|
|
101
|
+
}
|
|
102
|
+
case "console-api-client": {
|
|
103
|
+
}
|
|
104
|
+
case "backup": {
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
window.addEventListener("clusterchange", handler)
|
|
111
|
+
|
|
112
|
+
return () => {
|
|
113
|
+
window.removeEventListener("clusterchange", handler)
|
|
114
|
+
}
|
|
115
|
+
}, [])
|
|
116
|
+
```
|
|
117
|
+
|
|
70
118
|
## <a name="c4list"></a> (incomplete) List of adopters of C3+C4
|
|
71
119
|
|
|
72
120
|
- [Console team](https://confluence.camunda.com/display/HAN/Console+Team)
|
package/lib/esm/package.json
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { EventSourcePolyfill } from "event-source-polyfill";
|
|
2
|
+
import { Endpoints, Stage } from "./endpoints.const";
|
|
3
|
+
export interface C3ClustersProps {
|
|
4
|
+
stage?: Stage;
|
|
5
|
+
endpoints?: Endpoints;
|
|
6
|
+
userToken: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class ClustersService {
|
|
9
|
+
static clustersStream(options: C3ClustersProps): EventSourcePolyfill;
|
|
10
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { EventSourcePolyfill } from "event-source-polyfill";
|
|
2
|
+
import { CONSOLE, getEndpointByOptions, } from "./endpoints.const";
|
|
3
|
+
export class ClustersService {
|
|
4
|
+
static clustersStream(options) {
|
|
5
|
+
const baseUrl = getEndpointByOptions({
|
|
6
|
+
...options,
|
|
7
|
+
endpoint: CONSOLE,
|
|
8
|
+
});
|
|
9
|
+
let source = new EventSourcePolyfill(`${baseUrl}/external/events`, {
|
|
10
|
+
headers: {
|
|
11
|
+
authorization: `Bearer ${options.userToken}`,
|
|
12
|
+
},
|
|
13
|
+
heartbeatTimeout: 2 * 60 * 1000,
|
|
14
|
+
});
|
|
15
|
+
source.onmessage = function (event) {
|
|
16
|
+
const data = JSON.parse(event.data);
|
|
17
|
+
const clusterChangeEvent = new CustomEvent("clusterchange", {
|
|
18
|
+
detail: data,
|
|
19
|
+
});
|
|
20
|
+
window.dispatchEvent(clusterChangeEvent);
|
|
21
|
+
};
|
|
22
|
+
source.onerror = (_error) => {
|
|
23
|
+
console.log({ _error });
|
|
24
|
+
};
|
|
25
|
+
return source;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -7,9 +7,11 @@ export interface Endpoint {
|
|
|
7
7
|
export interface Endpoints {
|
|
8
8
|
notifications?: string;
|
|
9
9
|
accounts?: string;
|
|
10
|
+
console?: string;
|
|
10
11
|
}
|
|
11
12
|
export declare const NOTIFICATIONS: Endpoint;
|
|
12
13
|
export declare const ACCOUNTS: Endpoint;
|
|
14
|
+
export declare const CONSOLE: Endpoint;
|
|
13
15
|
export type Stage = "dev" | "int" | "prod";
|
|
14
16
|
export declare function getEndpoint(stage: Stage, endpoint: Endpoint): string;
|
|
15
17
|
export declare function getEndpointByOptions(options: {
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
export const NOTIFICATIONS = {
|
|
2
2
|
id: "notifications",
|
|
3
3
|
dev: "https://notifications.cloud.dev.ultrawombat.com",
|
|
4
|
-
int: "https://notifications.
|
|
5
|
-
prod: "https://notifications.
|
|
4
|
+
int: "https://notifications.ultrawombat.com",
|
|
5
|
+
prod: "https://notifications.camunda.io",
|
|
6
6
|
};
|
|
7
7
|
export const ACCOUNTS = {
|
|
8
8
|
id: "accounts",
|
|
9
9
|
dev: "https://accounts.cloud.dev.ultrawombat.com",
|
|
10
|
-
int: "https://accounts.
|
|
11
|
-
prod: "https://accounts.
|
|
10
|
+
int: "https://accounts.ultrawombat.com",
|
|
11
|
+
prod: "https://accounts.camunda.io",
|
|
12
|
+
};
|
|
13
|
+
export const CONSOLE = {
|
|
14
|
+
id: "console",
|
|
15
|
+
dev: "https://console.cloud.dev.ultrawombat.com",
|
|
16
|
+
int: "https://console.ultrawombat.com",
|
|
17
|
+
prod: "https://console.camunda.io",
|
|
12
18
|
};
|
|
13
19
|
export function getEndpoint(stage, endpoint) {
|
|
14
20
|
switch (stage) {
|
|
@@ -39,6 +45,13 @@ export function getEndpointByOptions(options) {
|
|
|
39
45
|
else if (options.stage) {
|
|
40
46
|
return getEndpoint(options.stage, options.endpoint);
|
|
41
47
|
}
|
|
48
|
+
case "console":
|
|
49
|
+
if (options.endpoints?.console) {
|
|
50
|
+
return options.endpoints.console;
|
|
51
|
+
}
|
|
52
|
+
else if (options.stage) {
|
|
53
|
+
return getEndpoint(options.stage, options.endpoint);
|
|
54
|
+
}
|
|
42
55
|
}
|
|
43
56
|
throw new Error(`Missing stage or notifications endpoint`);
|
|
44
57
|
}
|
|
@@ -8,6 +8,7 @@ import { useC3Profile } from "../../c3-user-configuration/c3-profile-provider/c3
|
|
|
8
8
|
import { useC3UserConfiguration } from "../../c3-user-configuration/c3-user-configuration-provider";
|
|
9
9
|
import { APPS } from "../../../utils/camunda";
|
|
10
10
|
import { NavWrapper, SideNav } from "./components";
|
|
11
|
+
import { useClusterUpdate } from "../../../contexts/c3-cluster-update-manager";
|
|
11
12
|
const getOrgLink = (app, orgId, domain) => {
|
|
12
13
|
switch (app) {
|
|
13
14
|
case "console": {
|
|
@@ -27,7 +28,7 @@ const getClustersToRender = (clusters, activeOrg, appToNavigateTo) => {
|
|
|
27
28
|
// either the user has appreadpermissions on this app
|
|
28
29
|
(activeOrg?.permissions?.cluster?.[appToNavigateTo]?.read ||
|
|
29
30
|
// OR this is a 8.8+ cluster - in this case we also allow rendering this item
|
|
30
|
-
[1, 2, 3, 4, 5, 6, 7].every((pre8Minor) => !generation.
|
|
31
|
+
[1, 2, 3, 4, 5, 6, 7].every((pre8Minor) => !generation.versions?.zeebe?.includes(`8.${pre8Minor}`))));
|
|
31
32
|
};
|
|
32
33
|
const OrgPickerModal = ({ isOpen, onCancel, orgs, activeOrg, loadingStatus, appToNavigateTo, domain, }) => {
|
|
33
34
|
const hasNoTeams = Object.values(orgs ?? {})?.every(({ org }) => {
|
|
@@ -73,15 +74,94 @@ const SubElementWrapper = styled.ul `
|
|
|
73
74
|
visibility: inherit;
|
|
74
75
|
}
|
|
75
76
|
`;
|
|
76
|
-
export const C3NavigationAppBar = ({ app: appProps, appBar, forwardRef, navbar, }) => {
|
|
77
|
+
export const C3NavigationAppBar = ({ app: appProps, appBar, forwardRef, navbar, toggleAppbar, }) => {
|
|
77
78
|
const { currentApp, domain, analyticsTrack, currentClusterUuid } = useC3UserConfiguration();
|
|
78
|
-
const { orgs, clusters, activeOrg, isEnabled, loadClustersById } = useC3Profile();
|
|
79
|
-
const
|
|
80
|
-
const { setPanelRef: panelRef, setIconRef: iconRef } = useOnClickOutside(() => setAppBarOpen(false));
|
|
79
|
+
const { orgs, clusters, setClusters, activeOrg, isEnabled, loadClustersById, } = useC3Profile();
|
|
80
|
+
const { setPanelRef: panelRef, setIconRef: iconRef } = useOnClickOutside(() => toggleAppbar(false));
|
|
81
81
|
const [appElements, setAppElements] = useState([]);
|
|
82
82
|
const [loadingStatus, setLoadingStatus] = useState("inactive");
|
|
83
83
|
const [clustersByOrgId, setClustersByOrgId] = useState(null);
|
|
84
84
|
const [appToNavigateTo, setAppToNavigateTo] = useState(APPS[0]);
|
|
85
|
+
useClusterUpdate({
|
|
86
|
+
onUpdate: (data) => {
|
|
87
|
+
if (data && data.org && data.org === activeOrg?.uuid) {
|
|
88
|
+
switch (data.entity) {
|
|
89
|
+
case "cluster": {
|
|
90
|
+
if (data.payload.uuid) {
|
|
91
|
+
const clusterDto = data.payload;
|
|
92
|
+
const status = clusterDto.status;
|
|
93
|
+
const endpoints = {
|
|
94
|
+
tasklist: status?.tasklistUrl,
|
|
95
|
+
operate: status?.operateUrl,
|
|
96
|
+
optimize: status?.optimizeUrl,
|
|
97
|
+
console: "",
|
|
98
|
+
modeler: "",
|
|
99
|
+
identity: "",
|
|
100
|
+
};
|
|
101
|
+
const expectedStatus = {
|
|
102
|
+
tasklist: status?.tasklistStatus,
|
|
103
|
+
operate: status?.operateStatus,
|
|
104
|
+
optimize: status?.optimizeStatus,
|
|
105
|
+
console: "",
|
|
106
|
+
modeler: "",
|
|
107
|
+
identity: "",
|
|
108
|
+
};
|
|
109
|
+
const affectedCluster = {
|
|
110
|
+
uuid: clusterDto.uuid,
|
|
111
|
+
name: clusterDto.name,
|
|
112
|
+
status: expectedStatus,
|
|
113
|
+
generation: clusterDto.generation,
|
|
114
|
+
endpoints,
|
|
115
|
+
};
|
|
116
|
+
switch (data.type) {
|
|
117
|
+
case "deleted": {
|
|
118
|
+
setClusters((clusters) => clusters?.filter((cluster) => cluster.uuid !== affectedCluster.uuid) ?? []);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
case "updated": {
|
|
122
|
+
setClusters((clusters) => {
|
|
123
|
+
if (!clusters) {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
const clustersClone = [...clusters];
|
|
127
|
+
const clusterToUpdateIndex = clustersClone?.findIndex((cluster) => cluster.uuid === affectedCluster.uuid);
|
|
128
|
+
if (clusterToUpdateIndex > -1) {
|
|
129
|
+
clustersClone[clusterToUpdateIndex] = affectedCluster;
|
|
130
|
+
return clustersClone;
|
|
131
|
+
}
|
|
132
|
+
return clusters;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
case "created": {
|
|
136
|
+
setClusters((clusters) => {
|
|
137
|
+
if (!clusters) {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
const clustersClone = [...clusters];
|
|
141
|
+
const clusterToUpdateIndex = clustersClone?.findIndex((cluster) => cluster.uuid === affectedCluster.uuid);
|
|
142
|
+
if (clusterToUpdateIndex > -1) {
|
|
143
|
+
clustersClone[clusterToUpdateIndex] = affectedCluster;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
clustersClone.push(affectedCluster);
|
|
147
|
+
}
|
|
148
|
+
return clustersClone;
|
|
149
|
+
});
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
default:
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
default: {
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
});
|
|
85
165
|
if (!appBar.elements && !isEnabled)
|
|
86
166
|
console.warn("No app elements and user config provided. Please provide at least one of them.");
|
|
87
167
|
const isMemberOfTeam = useCallback((orgUuid) => orgs?.some(({ uuid }) => uuid === orgUuid) || false, [orgs]);
|
|
@@ -165,7 +245,7 @@ export const C3NavigationAppBar = ({ app: appProps, appBar, forwardRef, navbar,
|
|
|
165
245
|
label: cluster.name,
|
|
166
246
|
href: cluster.endpoints[app],
|
|
167
247
|
onClick: () => {
|
|
168
|
-
|
|
248
|
+
toggleAppbar(false);
|
|
169
249
|
analyticsTrack?.(`${app}:open`, { currentApp: app });
|
|
170
250
|
},
|
|
171
251
|
isActive: currentApp === app && currentClusterUuid === cluster.uuid,
|
|
@@ -199,7 +279,7 @@ export const C3NavigationAppBar = ({ app: appProps, appBar, forwardRef, navbar,
|
|
|
199
279
|
}
|
|
200
280
|
}
|
|
201
281
|
setAppElements(defaultElements);
|
|
202
|
-
}, [clusters, currentApp, domain, handleSuperOrg
|
|
282
|
+
}, [clusters, currentApp, domain, handleSuperOrg]);
|
|
203
283
|
const appBarElements = appBar.elements || (clusters || clustersByOrgId ? appElements : null);
|
|
204
284
|
const [isOrgPickerModalOpen, setIsOrgPickerModalOpen] = useState(false);
|
|
205
285
|
return (React.createElement(React.Fragment, null,
|
|
@@ -209,28 +289,28 @@ export const C3NavigationAppBar = ({ app: appProps, appBar, forwardRef, navbar,
|
|
|
209
289
|
, {
|
|
210
290
|
// eslint-disable-next-line
|
|
211
291
|
// @ts-ignore
|
|
212
|
-
ref: iconRef, "aria-label": "Camunda components", isActive:
|
|
213
|
-
|
|
292
|
+
ref: iconRef, "aria-label": "Camunda components", isActive: appBar.isOpen, onClick: () => {
|
|
293
|
+
toggleAppbar(!appBar.isOpen);
|
|
214
294
|
}, tooltipAlignment: "start",
|
|
215
295
|
/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */
|
|
216
296
|
/* @ts-ignore */
|
|
217
|
-
leaveDelayMs: 100 },
|
|
297
|
+
leaveDelayMs: 100 }, appBar.isOpen ? React.createElement(Close, { size: 20 }) : React.createElement(C3AppMenuIcon, { size: 20 })),
|
|
218
298
|
React.createElement(OrgPickerModal, { activeOrg: activeOrg, appToNavigateTo: appToNavigateTo, isOpen: isOrgPickerModalOpen, orgs: clustersByOrgId, onCancel: () => {
|
|
219
299
|
setIsOrgPickerModalOpen(false);
|
|
220
300
|
}, loadingStatus: loadingStatus, domain: domain }),
|
|
221
301
|
React.createElement(NavWrapper, null,
|
|
222
|
-
React.createElement(SideNav, { ref: panelRef, "aria-label": appBar.ariaLabel || "Side Navigation", expanded:
|
|
302
|
+
React.createElement(SideNav, { ref: panelRef, "aria-label": appBar.ariaLabel || "Side Navigation", expanded: appBar.isOpen, isPersistent: false },
|
|
223
303
|
React.createElement(SideNavItems, null,
|
|
224
304
|
React.createElement("li", null, navbar.elements.length > 0 && (React.createElement(HeaderSideNavItems, { hasDivider: true }, navbar.elements.map((element) => (React.createElement(HeaderMenuItem, { key: element.key, as: element.routeProps && forwardRef, isActive: element.isCurrentPage, ...element.routeProps, onClick: () => {
|
|
225
305
|
if (element.routeProps.onClick) {
|
|
226
306
|
element.routeProps.onClick();
|
|
227
307
|
}
|
|
228
308
|
if (appBar.closeOnClick !== false) {
|
|
229
|
-
|
|
309
|
+
toggleAppbar(false);
|
|
230
310
|
}
|
|
231
311
|
} }, element.label)))))),
|
|
232
312
|
React.createElement("li", null,
|
|
233
|
-
React.createElement(SubElementWrapper, { "$expanded":
|
|
313
|
+
React.createElement(SubElementWrapper, { "$expanded": appBar.isOpen }, appBarElements &&
|
|
234
314
|
appBarElements.map((element) => {
|
|
235
315
|
if (element.subElements && element.subElements.length > 0) {
|
|
236
316
|
return (React.createElement(SideNavMenu, { large: true, title: element.label, key: element.key }, element.subElements.map((subElement) => (React.createElement(SideNavMenuItem, { as: subElement.routeProps && forwardRef, key: subElement.key, href: subElement.href, target: subElement.href ? subElement.target : undefined, isActive: subElement.isActive || subElement.active, ...subElement.routeProps, onClick: () => {
|
|
@@ -241,7 +321,7 @@ export const C3NavigationAppBar = ({ app: appProps, appBar, forwardRef, navbar,
|
|
|
241
321
|
subElement.routeProps.onClick();
|
|
242
322
|
}
|
|
243
323
|
if (appBar.closeOnClick !== false) {
|
|
244
|
-
|
|
324
|
+
toggleAppbar(false);
|
|
245
325
|
}
|
|
246
326
|
if (appBar.elementClicked) {
|
|
247
327
|
appBar.elementClicked(subElement.key);
|
|
@@ -257,7 +337,7 @@ export const C3NavigationAppBar = ({ app: appProps, appBar, forwardRef, navbar,
|
|
|
257
337
|
element.routeProps.onClick();
|
|
258
338
|
}
|
|
259
339
|
if (appBar.closeOnClick !== false) {
|
|
260
|
-
|
|
340
|
+
toggleAppbar(false);
|
|
261
341
|
}
|
|
262
342
|
if (appBar.elementClicked) {
|
|
263
343
|
appBar.elementClicked(element.key);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Header,
|
|
2
|
-
import React from "react";
|
|
1
|
+
import { Header, HeaderGlobalBar, HeaderMenuItem, HeaderName, HeaderNavigation, SkipToContent, Tag, Toggletip, ToggletipButton, ToggletipContent, Stack as CarbonStack, Link, } from "@carbon/react";
|
|
2
|
+
import React, { useState } from "react";
|
|
3
3
|
import C3InfoSidebar from "./c3-navigation-sidebar/c3-info-sidebar";
|
|
4
4
|
import { C3NotificationSidebar } from "./c3-navigation-sidebar/c3-notification-sidebar";
|
|
5
5
|
import C3OrgSidebar from "./c3-org-sidebar/c3-org-sidebar";
|
|
@@ -56,9 +56,10 @@ export const C3Navigation = ({ app, appBar, forwardRef, navbar, orgSideBar, info
|
|
|
56
56
|
const { activeOrg } = useC3Profile();
|
|
57
57
|
const isLargeScreen = useMediaQuery(`(min-width: ${BREAKPOINT_LG_WIDTH}`);
|
|
58
58
|
const appBarElementsLength = appBar.elements?.length ?? 0;
|
|
59
|
-
const displayAppBar = appBarElementsLength > 0 ||
|
|
59
|
+
const displayAppBar = !!(appBarElementsLength > 0 ||
|
|
60
60
|
appBar.appTeaserRouteProps ||
|
|
61
|
-
(!isLargeScreen && navbar.elements.length > 0);
|
|
61
|
+
(!isLargeScreen && navbar.elements.length > 0));
|
|
62
|
+
const [appBarOpen, setAppBarOpen] = useState(appBar.isOpen || false);
|
|
62
63
|
const orgName = activeOrg?.name || navbar.orgName;
|
|
63
64
|
let expiresAt;
|
|
64
65
|
if (navbar.licenseTag?.expiresAt !== undefined) {
|
|
@@ -146,69 +147,69 @@ export const C3Navigation = ({ app, appBar, forwardRef, navbar, orgSideBar, info
|
|
|
146
147
|
if (app.prefix)
|
|
147
148
|
console.warn("The `prefix` prop is deprecated and will be removed in a future release. It has been replaced with a Camunda icon.");
|
|
148
149
|
return (React.createElement(C3SidebarStateProvider, { isNotificationSidebarOpen: notificationSideBar?.isOpen, isOrgSidebarOpen: orgSideBar?.isOpen, isInfoSidebarOpen: infoSideBar?.isOpen, isUserSidebarOpen: userSideBar?.isOpen },
|
|
149
|
-
React.createElement(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
React.createElement(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
150
|
+
React.createElement(Header, { "aria-label": app.ariaLabel },
|
|
151
|
+
React.createElement(SkipToContent, null),
|
|
152
|
+
displayAppBar && (React.createElement(C3NavigationAppBar, { app: app, appBar: { ...appBar, isOpen: appBarOpen }, forwardRef: forwardRef, navbar: navbar, toggleAppbar: (value) => {
|
|
153
|
+
setAppBarOpen(value);
|
|
154
|
+
} })),
|
|
155
|
+
React.createElement(HeaderName, { as: forwardRef, prefix: "", ...app.routeProps },
|
|
156
|
+
React.createElement(Stack, { gap: 3, orientation: "horizontal" },
|
|
157
|
+
React.createElement(CamundaLogo, { "aria-label": "Camunda" }),
|
|
158
|
+
React.createElement("span", null, app.name))),
|
|
159
|
+
React.createElement(HeaderNavigation, { "aria-label": app.ariaLabel }, navbar.elements.map((element) => (React.createElement(HeaderMenuItem, { key: element.key, as: element.routeProps && forwardRef, isActive: element.isCurrentPage, ...element.routeProps },
|
|
160
|
+
React.createElement("span", null, element.label))))),
|
|
161
|
+
React.createElement(HeaderGlobalBar, null,
|
|
162
|
+
React.createElement("div", { style: {
|
|
163
|
+
display: "grid",
|
|
164
|
+
gridAutoFlow: "column",
|
|
165
|
+
gap: ".5rem",
|
|
166
|
+
paddingRight: ".5rem",
|
|
167
|
+
} },
|
|
168
|
+
tags &&
|
|
169
|
+
tags.length > 0 &&
|
|
170
|
+
tags.map((tag) => {
|
|
171
|
+
if (tag?.tooltip !== undefined) {
|
|
172
|
+
const { content, buttonLabel } = tag.tooltip;
|
|
173
|
+
return (React.createElement("div", { key: tag.key, style: {
|
|
174
|
+
height: "1.5rem",
|
|
175
|
+
marginTop: "0.75rem",
|
|
176
|
+
} },
|
|
177
|
+
React.createElement(Toggletip, null,
|
|
178
|
+
React.createElement(ToggletipButton, { as: "span", label: buttonLabel },
|
|
179
|
+
React.createElement(Tag, { type: tag.color, renderIcon: tag.renderIcon, style: {
|
|
180
|
+
margin: "0",
|
|
181
|
+
cursor: "pointer",
|
|
182
|
+
overflow: "hidden",
|
|
183
|
+
whiteSpace: "nowrap",
|
|
184
|
+
textOverflow: "ellipsis",
|
|
185
|
+
} }, tag.label)),
|
|
186
|
+
React.createElement(StyledToggletipContent, null, content))));
|
|
187
|
+
}
|
|
188
|
+
return (React.createElement(Tag, { key: tag.key, renderIcon: tag.renderIcon, style: {
|
|
189
|
+
height: "1.5rem",
|
|
190
|
+
marginTop: "0.75rem",
|
|
191
|
+
overflow: "hidden",
|
|
192
|
+
whiteSpace: "nowrap",
|
|
193
|
+
textOverflow: "ellipsis",
|
|
194
|
+
}, type: tag.color }, tag.label));
|
|
195
|
+
}),
|
|
196
|
+
(clusterUuid || currentClusterUuid) && (React.createElement(ClusterTagWrapper, null,
|
|
197
|
+
React.createElement(C3ClusterTagWithClusterName, { clusterUuid: clusterUuid || currentClusterUuid || "", conditionalRendering: options?.conditionalTagRendering }))),
|
|
198
|
+
orgName && (React.createElement(OrgName, { orgName: orgName, isSuperOrg: activeOrg?.isSuperOrg, activeSuperOrg: activeOrg?.superOrganization?.name }))),
|
|
199
|
+
actionButtons && React.createElement(C3ActionButtons, { elements: actionButtons }),
|
|
200
|
+
notificationSideBar && (React.createElement(C3NotificationSidebar, { sideBar: {
|
|
201
|
+
...notificationSideBar,
|
|
202
|
+
type: "notifications",
|
|
203
|
+
} })),
|
|
204
|
+
orgSideBar && (React.createElement(C3OrgSidebar, { sideBar: {
|
|
205
|
+
...orgSideBar,
|
|
206
|
+
type: "org",
|
|
207
|
+
} })),
|
|
208
|
+
infoButton || helpCenter ? (React.createElement(HelpCenterHint, null,
|
|
209
|
+
React.createElement(InfoButton, { onClick: infoButton ? infoButton.onClick : () => openHelpCenter() }))) : (infoSideBar && (React.createElement(C3InfoSidebar, { sideBar: { ...infoSideBar, type: "info" } }))),
|
|
210
|
+
userSideBar && (React.createElement(C3UserSidebar, { sideBar: {
|
|
211
|
+
...userSideBar,
|
|
212
|
+
type: "user",
|
|
213
|
+
} })))),
|
|
213
214
|
helpCenter && React.createElement(C3HelpCenter, { ...helpCenter })));
|
|
214
215
|
};
|
|
@@ -2,36 +2,34 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
2
2
|
export function useOnClickOutside(handler) {
|
|
3
3
|
const panelRef = useRef(null);
|
|
4
4
|
const iconRef = useRef(null);
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
const handlerRef = useRef(handler);
|
|
6
|
+
handlerRef.current = handler;
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const listener = (event) => {
|
|
9
|
+
if (!panelRef.current ||
|
|
10
|
+
!iconRef.current ||
|
|
11
|
+
(event.target instanceof Node &&
|
|
12
|
+
(panelRef.current.contains(event.target) ||
|
|
13
|
+
iconRef.current.contains(event.target)))) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
handlerRef.current(event);
|
|
17
|
+
};
|
|
16
18
|
document.addEventListener("mousedown", listener);
|
|
17
19
|
document.addEventListener("touchstart", listener);
|
|
18
|
-
};
|
|
19
|
-
useEffect(() => {
|
|
20
20
|
return () => {
|
|
21
21
|
document.removeEventListener("mousedown", listener);
|
|
22
22
|
document.removeEventListener("touchstart", listener);
|
|
23
23
|
};
|
|
24
|
-
}, []);
|
|
24
|
+
}, [handlerRef]);
|
|
25
25
|
const setPanelRef = useCallback((node) => {
|
|
26
26
|
if (node && node instanceof HTMLElement) {
|
|
27
27
|
panelRef.current = node;
|
|
28
|
-
setListener();
|
|
29
28
|
}
|
|
30
29
|
}, []);
|
|
31
30
|
const setIconRef = useCallback((node) => {
|
|
32
31
|
if (node && node instanceof HTMLElement) {
|
|
33
32
|
iconRef.current = node;
|
|
34
|
-
setListener();
|
|
35
33
|
}
|
|
36
34
|
}, []);
|
|
37
35
|
return { panelRef, setPanelRef, iconRef, setIconRef };
|
|
@@ -13,8 +13,12 @@ export const SuperUserToggle = ({ isActive, onChange }) => {
|
|
|
13
13
|
};
|
|
14
14
|
export const NavbarWithCustomSection = () => {
|
|
15
15
|
const [isSuperAdminModeActive, setIsSuperAdminModeActive] = useState(true);
|
|
16
|
+
const [appbarOpen, setAppBarOpen] = useState(false);
|
|
17
|
+
const appbarProps = createAppBarProps();
|
|
16
18
|
return (React.createElement(React.Fragment, null,
|
|
17
|
-
React.createElement(C3Navigation, { app: createAppProps(), appBar:
|
|
19
|
+
React.createElement(C3Navigation, { app: createAppProps(), appBar: { ...appbarProps, isOpen: appbarProps.isOpen ?? appbarOpen }, toggleAppbar: (value) => {
|
|
20
|
+
setAppBarOpen(value);
|
|
21
|
+
}, navbar: {
|
|
18
22
|
...createNavBarBarProps(),
|
|
19
23
|
tags: isSuperAdminModeActive
|
|
20
24
|
? [
|
package/lib/esm/src/components/c3-user-configuration/c3-profile-provider/c3-profile-provider.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export type C3ProfileContextValue = {
|
|
|
12
12
|
reloadOrgs: () => void;
|
|
13
13
|
removeOrg: (orgId: string) => void;
|
|
14
14
|
clusters: Cluster[] | null;
|
|
15
|
+
setClusters: React.Dispatch<React.SetStateAction<Cluster[] | null>>;
|
|
15
16
|
reloadClusters: () => void;
|
|
16
17
|
resolvedTheme: ResolvedTheme;
|
|
17
18
|
onThemeChange: (newTheme: Theme) => void;
|
package/lib/esm/src/components/c3-user-configuration/c3-profile-provider/c3-profile-provider.js
CHANGED
|
@@ -16,6 +16,7 @@ export const C3ProfileContext = createContext({
|
|
|
16
16
|
resolvedTheme: defaultTheme,
|
|
17
17
|
onThemeChange: () => undefined,
|
|
18
18
|
loadClustersById: () => undefined,
|
|
19
|
+
setClusters: () => undefined,
|
|
19
20
|
});
|
|
20
21
|
export const C3ProfileProvider = ({ children }) => {
|
|
21
22
|
const { decodedAudience, analyticsTrack, ...config } = useContext(C3UserConfigurationContext);
|
|
@@ -37,7 +38,7 @@ export const C3ProfileProvider = ({ children }) => {
|
|
|
37
38
|
queryKey: [decodedAudience, config.userToken],
|
|
38
39
|
enabled: isConfigValid,
|
|
39
40
|
});
|
|
40
|
-
const { data: clusters, refetch: loadClusters } = useApi({
|
|
41
|
+
const { data: clusters, setData: setClusters, refetch: loadClusters, } = useApi({
|
|
41
42
|
queryFn: () => {
|
|
42
43
|
if (!isConfigValid)
|
|
43
44
|
return;
|
|
@@ -99,6 +100,7 @@ export const C3ProfileProvider = ({ children }) => {
|
|
|
99
100
|
activeOrg,
|
|
100
101
|
reloadOrgs: loadOrgs,
|
|
101
102
|
clusters,
|
|
103
|
+
setClusters,
|
|
102
104
|
reloadClusters: loadClusters,
|
|
103
105
|
removeOrg,
|
|
104
106
|
onThemeChange,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Stage } from "../api/endpoints.const";
|
|
3
|
+
type C3ClusterUpdateManagerProps = {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
stage: Stage;
|
|
6
|
+
userToken: string;
|
|
7
|
+
};
|
|
8
|
+
declare const C3ClusterUpdateManager: ({ children, stage, userToken, }: C3ClusterUpdateManagerProps) => JSX.Element;
|
|
9
|
+
declare const useClusterUpdate: ({ onUpdate }: {
|
|
10
|
+
onUpdate: (data: any) => void;
|
|
11
|
+
}) => void;
|
|
12
|
+
export { C3ClusterUpdateManager, useClusterUpdate };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React, { useRef } from "react";
|
|
2
|
+
import { useEffect, createContext } from "react";
|
|
3
|
+
import { ClustersService } from "../api/clusters";
|
|
4
|
+
const C3ClusterUpdateManagerContext = createContext(null);
|
|
5
|
+
const C3ClusterUpdateManager = ({ children, stage, userToken, }) => {
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const eventSource = ClustersService.clustersStream({ stage, userToken });
|
|
8
|
+
return () => {
|
|
9
|
+
eventSource.close();
|
|
10
|
+
};
|
|
11
|
+
}, [stage, userToken]);
|
|
12
|
+
return (React.createElement(C3ClusterUpdateManagerContext.Provider, { value: null }, children));
|
|
13
|
+
};
|
|
14
|
+
const useClusterUpdate = ({ onUpdate }) => {
|
|
15
|
+
const onUpdateRef = useRef(onUpdate);
|
|
16
|
+
onUpdateRef.current = onUpdate;
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const handler = (event) => {
|
|
19
|
+
onUpdateRef.current(event.detail);
|
|
20
|
+
};
|
|
21
|
+
window.addEventListener("clusterchange", handler);
|
|
22
|
+
return () => {
|
|
23
|
+
window.removeEventListener("clusterchange", handler);
|
|
24
|
+
};
|
|
25
|
+
}, [onUpdateRef]);
|
|
26
|
+
};
|
|
27
|
+
export { C3ClusterUpdateManager, useClusterUpdate };
|
package/lib/esm/src/index.d.ts
CHANGED
|
@@ -28,3 +28,4 @@ export { C3DataTable } from "./components/c3-data-table/c3-data-table";
|
|
|
28
28
|
export { C3DataTableActionProps, C3DataTableHeaderContentType, C3DataTableHeadersProps, C3DataTableProps, C3DataTableToolbarButtonItemProps, C3DataTableToolbarMultiItemButtonProps, C3DataTableToolbarMultiSelectItemProps, C3DataTableToolbarProps, C3DataTableToolbarSearchItemProps, Filter, RowBaseProps, } from "./components/c3-data-table/c3-data-table.types";
|
|
29
29
|
export { RequestResponse } from "./api/api";
|
|
30
30
|
export { useApi } from "./hooks/useApi";
|
|
31
|
+
export { C3ClusterUpdateManager } from "./contexts/c3-cluster-update-manager";
|
package/lib/esm/src/index.js
CHANGED
|
@@ -19,3 +19,4 @@ export * from "./assets/app-teaser-images/tasklist-3-api";
|
|
|
19
19
|
export { C3Page } from "./components/c3-page/c3-page";
|
|
20
20
|
export { C3DataTable } from "./components/c3-data-table/c3-data-table";
|
|
21
21
|
export { useApi } from "./hooks/useApi";
|
|
22
|
+
export { C3ClusterUpdateManager } from "./contexts/c3-cluster-update-manager";
|