@camunda/camunda-composite-components 0.0.30 → 0.0.31-rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/lib/esm/api/api.d.ts +22 -0
  2. package/lib/esm/api/api.js +66 -0
  3. package/lib/esm/api/endpoints.const.d.ts +9 -0
  4. package/lib/esm/api/endpoints.const.js +18 -0
  5. package/lib/esm/api/jwt.utils.d.ts +5 -0
  6. package/lib/esm/api/jwt.utils.js +23 -0
  7. package/lib/esm/api/notifications.d.ts +39 -0
  8. package/lib/esm/api/notifications.js +153 -0
  9. package/lib/esm/components/c3-navigation/c3-navigation-sidebar/c3-info-sidebar.d.ts +6 -0
  10. package/lib/esm/components/c3-navigation/c3-navigation-sidebar/c3-info-sidebar.js +40 -0
  11. package/lib/esm/components/c3-navigation/c3-navigation-sidebar/c3-navigation-sidebar-element.d.ts +11 -0
  12. package/lib/esm/components/c3-navigation/c3-navigation-sidebar/c3-navigation-sidebar-element.js +16 -0
  13. package/lib/esm/components/c3-navigation/c3-navigation-sidebar/c3-navigation-sidebar.d.ts +9 -0
  14. package/lib/esm/components/c3-navigation/c3-navigation-sidebar/c3-navigation-sidebar.js +38 -0
  15. package/lib/esm/components/c3-navigation/c3-navigation-sidebar/c3-navigation-sidebar.types.d.ts +58 -0
  16. package/lib/esm/components/c3-navigation/c3-navigation-sidebar/c3-navigation-sidebar.types.js +1 -0
  17. package/lib/esm/components/c3-navigation/c3-navigation-sidebar/c3-notification-sidebar.d.ts +5 -0
  18. package/lib/esm/components/c3-navigation/c3-navigation-sidebar/c3-notification-sidebar.js +110 -0
  19. package/lib/esm/components/c3-navigation/c3-navigation-sidebar/c3-org-sidebar.d.ts +6 -0
  20. package/lib/esm/components/c3-navigation/c3-navigation-sidebar/c3-org-sidebar.js +52 -0
  21. package/lib/esm/components/c3-navigation/c3-navigation-sidebar/c3-user-sidebar.d.ts +6 -0
  22. package/lib/esm/components/c3-navigation/c3-navigation-sidebar/c3-user-sidebar.js +44 -0
  23. package/lib/esm/components/c3-navigation/c3-navigation.d.ts +1 -1
  24. package/lib/esm/components/c3-navigation/c3-navigation.js +31 -214
  25. package/lib/esm/components/c3-navigation/c3-navigation.types.d.ts +15 -40
  26. package/lib/esm/components/c3-navigation/c3-notification-provider/c3-notification-container.d.ts +12 -0
  27. package/lib/esm/components/c3-navigation/c3-notification-provider/c3-notification-container.js +106 -0
  28. package/lib/esm/components/c3-navigation/c3-notification-provider/c3-notification-provider.d.ts +30 -0
  29. package/lib/esm/components/c3-navigation/c3-notification-provider/c3-notification-provider.js +91 -0
  30. package/lib/esm/components/c3-navigation/helpers.d.ts +4 -0
  31. package/lib/esm/components/c3-navigation/helpers.js +62 -0
  32. package/lib/esm/components/c3-navigation/index.d.ts +3 -0
  33. package/lib/esm/components/c3-navigation/index.js +3 -0
  34. package/lib/esm/components/c3-navigation/story-helpers.d.ts +23 -0
  35. package/lib/esm/components/c3-navigation/story-helpers.js +179 -0
  36. package/lib/esm/components/c3-user-configuration/c3-user-configuration-provider.d.ts +13 -0
  37. package/lib/esm/components/c3-user-configuration/c3-user-configuration-provider.js +4 -0
  38. package/lib/esm/icons/c3-icons.d.ts +2 -0
  39. package/lib/esm/icons/c3-icons.js +6 -1
  40. package/lib/esm/index.d.ts +5 -2
  41. package/lib/esm/index.js +2 -1
  42. package/package.json +8 -2
@@ -0,0 +1,52 @@
1
+ import React, { useState } from "react";
2
+ import { Button, FormLabel, SwitcherDivider } from "@carbon/react";
3
+ import { Enterprise } from "@carbon/react/icons";
4
+ import C3NavigationSideBar from "./c3-navigation-sidebar";
5
+ const C3OrgSidebar = ({ sideBar }) => {
6
+ const { customElements, isOpen, ...sideBarProps } = sideBar;
7
+ const activeOrganization = sideBar.customElements?.activeOrganization;
8
+ const [isSidebarOpen, setIsSidebarOpen] = useState(isOpen);
9
+ const itemTabIndex = isSidebarOpen ? undefined : -1;
10
+ return (React.createElement(C3NavigationSideBar, { sideBar: {
11
+ ...sideBarProps,
12
+ ariaLabel: sideBarProps.ariaLabel || "Organization Sidebars",
13
+ isOpen: isSidebarOpen,
14
+ setIsOpen: setIsSidebarOpen,
15
+ }, icon: React.createElement(Enterprise, { size: 20 }) }, activeOrganization && (React.createElement(React.Fragment, null,
16
+ React.createElement("div", { style: {
17
+ padding: "1rem",
18
+ paddingTop: "1.5rem",
19
+ paddingBottom: ".5rem",
20
+ display: "grid",
21
+ gridAutoFlow: "column",
22
+ gap: ".25rem",
23
+ } },
24
+ React.createElement("div", { style: {
25
+ overflow: "hidden",
26
+ display: "grid",
27
+ gap: "4px",
28
+ } },
29
+ React.createElement(FormLabel, null, activeOrganization.activeLabel),
30
+ React.createElement("div", { className: "textPrimary", style: {
31
+ height: "20px",
32
+ lineHeight: "20px",
33
+ fontSize: "14px",
34
+ textOverflow: "ellipsis",
35
+ overflow: "hidden",
36
+ whiteSpace: "nowrap",
37
+ }, title: activeOrganization.orgName }, activeOrganization.orgName)),
38
+ React.createElement(Button, { size: "md", kind: "ghost", key: "org-management", onClick: () => {
39
+ activeOrganization.action.onClick();
40
+ if (sideBar.closeOnClick !== false) {
41
+ setIsSidebarOpen(false);
42
+ }
43
+ }, tabIndex: itemTabIndex }, activeOrganization.action.label)),
44
+ sideBar.elements && sideBar.elements.length > 0 && (React.createElement(React.Fragment, null,
45
+ React.createElement(SwitcherDivider, null),
46
+ React.createElement(FormLabel, { style: {
47
+ paddingTop: ".5rem",
48
+ paddingLeft: "1rem",
49
+ paddingBottom: ".25rem",
50
+ } }, activeOrganization.otherLabel)))))));
51
+ };
52
+ export default C3OrgSidebar;
@@ -0,0 +1,6 @@
1
+ import { FC } from "react";
2
+ import { C3NavigationUserSideBarProps } from "./c3-navigation-sidebar.types";
3
+ declare const C3UserSidebar: FC<{
4
+ sideBar: C3NavigationUserSideBarProps;
5
+ }>;
6
+ export default C3UserSidebar;
@@ -0,0 +1,44 @@
1
+ import React, { useState } from "react";
2
+ import C3NavigationSideBar from "./c3-navigation-sidebar";
3
+ import { FormLabel, RadioButton, RadioButtonGroup, Stack, SwitcherDivider, Toggle, } from "@carbon/react";
4
+ import { UserAvatar } from "@carbon/react/icons";
5
+ const C3UserSidebar = ({ sideBar }) => {
6
+ const { customElements, isOpen, ...sideBarProps } = sideBar;
7
+ const profile = sideBar.customElements?.profile;
8
+ const themeSelector = sideBar.customElements?.themeSelector;
9
+ const stageToggle = sideBar.customElements?.stageToggle;
10
+ const [isSidebarOpen, setIsSidebarOpen] = useState(isOpen);
11
+ const itemTabIndex = isSidebarOpen ? undefined : -1;
12
+ return (React.createElement(C3NavigationSideBar, { sideBar: {
13
+ ...sideBarProps,
14
+ ariaLabel: sideBarProps.ariaLabel || "User Sidebar",
15
+ isOpen: isSidebarOpen,
16
+ setIsOpen: setIsSidebarOpen,
17
+ }, icon: React.createElement(UserAvatar, { size: 20 }) },
18
+ profile && (React.createElement("div", { style: {
19
+ padding: "1rem",
20
+ paddingTop: "1.5rem",
21
+ paddingBottom: ".5rem",
22
+ } },
23
+ React.createElement(Stack, { gap: 2 },
24
+ React.createElement(FormLabel, null, profile.label),
25
+ React.createElement(Stack, null,
26
+ React.createElement("div", { className: "textPrimary", style: { fontSize: "14px" } }, profile.user.name),
27
+ React.createElement("div", { className: "textPrimary", style: { fontSize: "12px" } }, profile.user.email))))),
28
+ themeSelector && (React.createElement(React.Fragment, null,
29
+ React.createElement(SwitcherDivider, null),
30
+ React.createElement("div", { style: {
31
+ padding: ".5rem 1rem",
32
+ } },
33
+ React.createElement(RadioButtonGroup, { name: "theme-radio-group", defaultSelected: themeSelector.currentTheme, legendText: "Theme", orientation: "vertical", onChange: (newValue) => {
34
+ themeSelector.onChange(newValue);
35
+ } },
36
+ React.createElement(RadioButton, { id: "light", labelText: "Light", value: "light", tabIndex: itemTabIndex }),
37
+ React.createElement(RadioButton, { id: "system", labelText: "System", value: "system", tabIndex: itemTabIndex }),
38
+ React.createElement(RadioButton, { id: "dark", labelText: "Dark", value: "dark", tabIndex: itemTabIndex }))))),
39
+ stageToggle && (React.createElement(React.Fragment, null,
40
+ React.createElement(SwitcherDivider, null),
41
+ React.createElement("div", { style: { padding: ".5rem 1rem" } },
42
+ React.createElement(Toggle, { size: "sm", id: "toggle-productionfeatures", defaultToggled: stageToggle.prodFeaturesEnabled, onClick: stageToggle.toggle, labelText: "Simulate Production Features", tabIndex: itemTabIndex }))))));
43
+ };
44
+ export default C3UserSidebar;
@@ -1,3 +1,3 @@
1
1
  /// <reference types="react" />
2
2
  import { C3NavigationProps } from "./c3-navigation.types";
3
- export declare const C3Navigation: ({ app, appBar, forwardRef, navbar, orgSideBar, infoSideBar, userSideBar, }: C3NavigationProps) => JSX.Element;
3
+ export declare const C3Navigation: ({ app, appBar, forwardRef, navbar, orgSideBar, infoSideBar, userSideBar, notificationSideBar, }: C3NavigationProps) => JSX.Element;
@@ -1,19 +1,22 @@
1
- import { Button, FormLabel, Header, HeaderContainer, HeaderGlobalAction, HeaderGlobalBar, HeaderMenuItem, HeaderName, HeaderNavigation, HeaderPanel, HeaderSideNavItems, RadioButton, RadioButtonGroup, SideNav, SideNavItems, SideNavLink, SideNavMenu, SideNavMenuItem, SkipToContent, Stack, SwitcherDivider, Tag, Toggle, Toggletip, ToggletipButton, ToggletipContent, usePrefix, useTheme, } from "@carbon/react";
2
- import { Close, Enterprise, Help, UserAvatar } from "@carbon/react/icons";
3
- import React, { useEffect, useState, } from "react";
1
+ import { Header, HeaderContainer, HeaderGlobalAction, HeaderGlobalBar, HeaderMenuItem, HeaderName, HeaderNavigation, HeaderSideNavItems, SideNav, SideNavItems, SideNavLink, SideNavMenu, SideNavMenuItem, SkipToContent, Tag, Toggletip, ToggletipButton, ToggletipContent, } from "@carbon/react";
2
+ import { Close } from "@carbon/react/icons";
3
+ import React, { useState } from "react";
4
4
  import { C3AppMenuIcon } from "../../icons/c3-icons";
5
+ import C3InfoSidebar from "./c3-navigation-sidebar/c3-info-sidebar";
6
+ import { C3NotificationSidebar } from "./c3-navigation-sidebar/c3-notification-sidebar";
7
+ import C3OrgSidebar from "./c3-navigation-sidebar/c3-org-sidebar";
8
+ import C3UserSidebar from "./c3-navigation-sidebar/c3-user-sidebar";
9
+ import { useInverseThemeClassName, useMediaQuery, useOnClickOutside, } from "./helpers";
5
10
  const BREAKPOINT_LG_WIDTH = "66rem"; // This is Carbon's breakpoint (lg) width https://github.com/carbon-design-system/carbon/blob/main/packages/layout/src/index.js#L56
6
11
  const C3NavigationExternalLink = ({ label }) => React.createElement(React.Fragment, null, label);
7
12
  const C3NavigationAppBar = ({ appBar, forwardRef, navbar, }) => {
8
13
  const [appBarOpen, setAppBarOpen] = useState(appBar.isOpen);
9
- const refPanel = React.createRef();
10
- const refIcon = React.createRef();
11
- useOnClickOutside(refPanel, refIcon, () => setAppBarOpen(false));
14
+ const [panelRef, iconRef] = useOnClickOutside(() => setAppBarOpen(false));
12
15
  return (React.createElement(React.Fragment, null,
13
- React.createElement(HeaderGlobalAction, { ref: refIcon, "aria-label": "App Switcher", isActive: appBarOpen, onClick: () => {
16
+ React.createElement(HeaderGlobalAction, { ref: iconRef, "aria-label": "App Switcher", isActive: appBarOpen, onClick: () => {
14
17
  setAppBarOpen(!appBarOpen);
15
18
  }, tooltipAlignment: "start" }, appBarOpen ? React.createElement(Close, { size: 20 }) : React.createElement(C3AppMenuIcon, { size: 20 })),
16
- React.createElement(SideNav, { ref: refPanel, "aria-label": appBar.ariaLabel || "Side Navigation", expanded: appBarOpen, isPersistent: false, style: {
19
+ React.createElement(SideNav, { ref: panelRef, "aria-label": appBar.ariaLabel || "Side Navigation", expanded: appBarOpen, isPersistent: false, style: {
17
20
  display: "grid",
18
21
  gridAutoFlow: "row",
19
22
  gridAutoRows: "max-content 1fr",
@@ -32,201 +35,59 @@ const C3NavigationAppBar = ({ appBar, forwardRef, navbar, }) => {
32
35
  appBar.elements.map((element) => {
33
36
  if (element.subElements && element.subElements.length > 0) {
34
37
  return (React.createElement(SideNavMenu, { large: true, title: element.label, key: element.key }, element.subElements.map((subElement) => (React.createElement(SideNavMenuItem, { key: subElement.key, href: subElement.href, target: subElement.href ? subElement.target : undefined, onClick: () => {
38
+ let handlerAvailable = false;
35
39
  if (subElement.onClick) {
40
+ handlerAvailable = true;
36
41
  subElement.onClick();
37
42
  }
38
43
  if (appBar.closeOnClick !== false) {
44
+ handlerAvailable = true;
39
45
  setAppBarOpen(false);
40
46
  }
41
47
  if (appBar.elementClicked) {
48
+ handlerAvailable = true;
42
49
  appBar.elementClicked(subElement.key);
43
50
  }
51
+ return handlerAvailable;
44
52
  } },
45
53
  React.createElement(C3NavigationExternalLink, { label: subElement.label }))))));
46
54
  }
47
55
  else {
48
56
  return element.routeProps ? (React.createElement(SideNavLink, { element: forwardRef, key: element.key, large: true, isActive: element.active, ...element.routeProps, onClick: () => {
57
+ let handlerAvailable = false;
49
58
  if (element.onClick) {
59
+ handlerAvailable = true;
50
60
  element.onClick();
51
61
  }
52
62
  if (appBar.closeOnClick !== false) {
63
+ handlerAvailable = true;
53
64
  setAppBarOpen(false);
54
65
  }
55
66
  if (appBar.elementClicked) {
67
+ handlerAvailable = true;
56
68
  appBar.elementClicked(element.key);
57
69
  }
70
+ return handlerAvailable;
58
71
  } }, element.label)) : (React.createElement(SideNavLink, { key: element.key, large: true, onClick: () => {
72
+ let handlerAvailable = false;
59
73
  if (element.onClick) {
74
+ handlerAvailable = true;
60
75
  element.onClick();
61
76
  }
62
77
  if (appBar.closeOnClick !== false) {
78
+ handlerAvailable = true;
63
79
  setAppBarOpen(false);
64
80
  }
65
81
  if (appBar.elementClicked) {
82
+ handlerAvailable = true;
66
83
  appBar.elementClicked(element.key);
67
84
  }
85
+ return handlerAvailable;
68
86
  }, href: element.href, target: element.href ? element.target : undefined }, element.label));
69
87
  }
70
88
  })))));
71
89
  };
72
- const C3NavigationSideBar = (props) => {
73
- const { sideBar } = props;
74
- if (sideBar) {
75
- const activeOrganization = sideBar.customElements?.activeOrganization;
76
- const profile = sideBar.customElements?.profile;
77
- const themeSelector = sideBar.customElements?.themeSelector;
78
- const stageToggle = sideBar.customElements?.stageToggle;
79
- let icon;
80
- let ariaLabel;
81
- switch (sideBar.type) {
82
- case "org":
83
- icon = React.createElement(Enterprise, { size: 20 });
84
- ariaLabel = "Organization Sidebar";
85
- break;
86
- case "info":
87
- icon = React.createElement(Help, { size: 20 });
88
- ariaLabel = "Info Sidebar";
89
- break;
90
- case "user":
91
- icon = React.createElement(UserAvatar, { size: 20 });
92
- ariaLabel = "User Sidebar";
93
- break;
94
- }
95
- const [sideBarOpen, setSideBarOpen] = useState(sideBar.isOpen);
96
- const refPanel = React.createRef();
97
- const refIcon = React.createRef();
98
- useOnClickOutside(refPanel, refIcon, () => setSideBarOpen(false));
99
- const itemTabIndex = sideBarOpen ? undefined : -1;
100
- return (React.createElement(React.Fragment, null,
101
- React.createElement(HeaderGlobalAction, { ref: refIcon, "aria-label": sideBar.ariaLabel || `Open ${ariaLabel}`, onClick: () => {
102
- setSideBarOpen(!sideBarOpen);
103
- }, isActive: sideBarOpen, tooltipAlignment: sideBar.type === "user" ? "end" : "center" }, icon),
104
- React.createElement(HeaderPanel, { ref: refPanel, "aria-label": sideBar.ariaLabel || ariaLabel, expanded: sideBarOpen, style: {
105
- display: "grid",
106
- gridAutoFlow: "row",
107
- gridAutoRows: "max-content 1fr",
108
- } },
109
- React.createElement(Stack, null,
110
- profile && (React.createElement("div", { style: {
111
- padding: "1rem",
112
- paddingTop: "1.5rem",
113
- paddingBottom: ".5rem",
114
- } },
115
- React.createElement(Stack, { gap: 2 },
116
- React.createElement(FormLabel, null, profile.label),
117
- React.createElement(Stack, null,
118
- React.createElement("div", { className: "textPrimary", style: { fontSize: "14px" } }, profile.user.name),
119
- React.createElement("div", { className: "textPrimary", style: { fontSize: "12px" } }, profile.user.email))))),
120
- themeSelector && (React.createElement(React.Fragment, null,
121
- React.createElement(SwitcherDivider, null),
122
- React.createElement("div", { style: {
123
- padding: ".5rem 1rem",
124
- } },
125
- React.createElement(RadioButtonGroup, { name: "theme-radio-group", defaultSelected: themeSelector.currentTheme, legendText: "Theme", orientation: "vertical", onChange: (newValue) => {
126
- themeSelector.onChange(newValue);
127
- } },
128
- React.createElement(RadioButton, { id: "light", labelText: "Light", value: "light", tabIndex: itemTabIndex }),
129
- React.createElement(RadioButton, { id: "system", labelText: "System", value: "system", tabIndex: itemTabIndex }),
130
- React.createElement(RadioButton, { id: "dark", labelText: "Dark", value: "dark", tabIndex: itemTabIndex }))))),
131
- stageToggle && (React.createElement(React.Fragment, null,
132
- React.createElement(SwitcherDivider, null),
133
- React.createElement("div", { style: { padding: ".5rem 1rem" } },
134
- React.createElement(Toggle, { size: "sm", id: "toggle-productionfeatures", defaultToggled: stageToggle.prodFeaturesEnabled, onClick: stageToggle.toggle, labelText: "Simulate Production Features", tabIndex: itemTabIndex })))),
135
- activeOrganization && (React.createElement(React.Fragment, null,
136
- React.createElement("div", { style: {
137
- padding: "1rem",
138
- paddingTop: "1.5rem",
139
- paddingBottom: ".5rem",
140
- display: "grid",
141
- gridAutoFlow: "column",
142
- gap: ".25rem",
143
- } },
144
- React.createElement("div", { style: {
145
- overflow: "hidden",
146
- display: "grid",
147
- gap: "4px",
148
- } },
149
- React.createElement(FormLabel, null, activeOrganization.activeLabel),
150
- React.createElement("div", { className: "textPrimary", style: {
151
- height: "20px",
152
- lineHeight: "20px",
153
- fontSize: "14px",
154
- textOverflow: "ellipsis",
155
- overflow: "hidden",
156
- whiteSpace: "nowrap",
157
- }, title: activeOrganization.orgName }, activeOrganization.orgName)),
158
- React.createElement(Button, { size: "md", kind: "ghost", key: "org-management", onClick: () => {
159
- activeOrganization.action.onClick();
160
- if (sideBar.closeOnClick !== false) {
161
- setSideBarOpen(false);
162
- }
163
- }, tabIndex: itemTabIndex }, activeOrganization.action.label)),
164
- sideBar.elements && sideBar.elements.length > 0 && (React.createElement(React.Fragment, null,
165
- React.createElement(SwitcherDivider, null),
166
- React.createElement(FormLabel, { style: {
167
- paddingTop: ".5rem",
168
- paddingLeft: "1rem",
169
- paddingBottom: ".25rem",
170
- } }, activeOrganization.otherLabel))))),
171
- sideBar.elements &&
172
- sideBar.elements.length > 0 &&
173
- sideBar.customElements &&
174
- !sideBar.customElements?.activeOrganization && (React.createElement(SwitcherDivider, null)),
175
- sideBar.elements &&
176
- sideBar.elements.map((element, index) => (React.createElement(C3NavigationSideBarElement, { key: element.key, element: element, index: index, sideBar: sideBar, setSideBarOpen: setSideBarOpen, itemTabIndex: itemTabIndex }))),
177
- sideBar.type === "info" && sideBar.version !== undefined && (React.createElement(React.Fragment, null,
178
- React.createElement(SwitcherDivider, null),
179
- React.createElement("span", { className: "cds--switcher__item", style: {
180
- padding: "var(--cds-spacing-05)",
181
- paddingTop: "var(--cds-spacing-03)",
182
- paddingBottom: 0,
183
- color: "var(--cds-text-primary)",
184
- fontSize: "var(--cds-body-01-font-size)",
185
- fontWeight: "var(--cds-body-01-font-weight)",
186
- lineHeight: "var(--cds-body-01-line-height)",
187
- letterSpacing: "var(--cds-body-01-letter-spacing)",
188
- } },
189
- "Version ",
190
- sideBar.version),
191
- React.createElement("span", { className: "cds--switcher__item", style: {
192
- paddingRight: "var(--cds-spacing-05)",
193
- paddingLeft: "var(--cds-spacing-05)",
194
- color: "var(--cds-text-secondary)",
195
- fontSize: "var(--cds-label-01-font-size)",
196
- fontWeight: "var(--cds-label-01-font-weight)",
197
- lineHeight: "var(--cds-label-01-line-height)",
198
- letterSpacing: "var(--cds-label-01-letter-spacing)",
199
- } },
200
- `© Camunda Services GmbH ${new Date().getFullYear()}`,
201
- React.createElement("br", null),
202
- " All rights reserved.")))),
203
- sideBar.bottomElements &&
204
- sideBar.bottomElements.map((element) => (React.createElement(Button, { kind: element.kind, key: element.key, className: "cds--switcher__item", renderIcon: element.renderIcon, onClick: () => {
205
- if (element.onClick) {
206
- element.onClick();
207
- }
208
- if (sideBar.closeOnClick !== false) {
209
- setSideBarOpen(false);
210
- }
211
- }, style: { alignSelf: "end" }, tabIndex: itemTabIndex }, element.label))))));
212
- }
213
- return null;
214
- };
215
- const C3NavigationSideBarElement = (props) => {
216
- return (React.createElement(React.Fragment, null,
217
- props.element.preceedingDivider && React.createElement(SwitcherDivider, null),
218
- React.createElement(Button, { style: props.index === 0 && !props.sideBar.customElements
219
- ? { marginTop: "1.5rem", whiteSpace: "nowrap" }
220
- : { whiteSpace: "nowrap" }, size: "sm", kind: props.element.kind ?? "ghost", className: "cds--switcher__item", onClick: () => {
221
- if (props.element.onClick) {
222
- props.element.onClick();
223
- }
224
- if (props.sideBar.closeOnClick !== false) {
225
- props.setSideBarOpen(false);
226
- }
227
- }, tabIndex: props.itemTabIndex }, props.element.label)));
228
- };
229
- export const C3Navigation = ({ app, appBar, forwardRef, navbar, orgSideBar, infoSideBar, userSideBar, }) => {
90
+ export const C3Navigation = ({ app, appBar, forwardRef, navbar, orgSideBar, infoSideBar, userSideBar, notificationSideBar, }) => {
230
91
  const isLargeScreen = useMediaQuery(`(min-width: ${BREAKPOINT_LG_WIDTH}`);
231
92
  const appBarElementsLength = appBar.elements?.length ?? 0;
232
93
  const displayAppBar = appBarElementsLength > 0 || (!isLargeScreen && navbar.elements.length > 0);
@@ -279,53 +140,9 @@ export const C3Navigation = ({ app, appBar, forwardRef, navbar, orgSideBar, info
279
140
  overflow: "hidden",
280
141
  maxWidth: "150px",
281
142
  } }, navbar.orgName))),
282
- React.createElement(C3NavigationSideBar, { sideBar: orgSideBar }),
283
- React.createElement(C3NavigationSideBar, { sideBar: infoSideBar }),
284
- React.createElement(C3NavigationSideBar, { sideBar: userSideBar }))));
143
+ notificationSideBar && (React.createElement(C3NotificationSidebar, { sideBar: { ...notificationSideBar, type: "notifications" } })),
144
+ orgSideBar && (React.createElement(C3OrgSidebar, { sideBar: { ...orgSideBar, type: "org" } })),
145
+ infoSideBar && (React.createElement(C3InfoSidebar, { sideBar: { ...infoSideBar, type: "info" } })),
146
+ userSideBar && (React.createElement(C3UserSidebar, { sideBar: { ...userSideBar, type: "user" } })))));
285
147
  } }));
286
148
  };
287
- function useOnClickOutside(refPanel, refIcon, handler) {
288
- useEffect(() => {
289
- const listener = (event) => {
290
- if (!refPanel.current ||
291
- !refIcon.current ||
292
- (event.target instanceof HTMLElement &&
293
- (refPanel.current.contains(event.target) ||
294
- refIcon.current.contains(event.target)))) {
295
- return;
296
- }
297
- handler(event);
298
- };
299
- document.addEventListener("mousedown", listener);
300
- document.addEventListener("touchstart", listener);
301
- return () => {
302
- document.removeEventListener("mousedown", listener);
303
- document.removeEventListener("touchstart", listener);
304
- };
305
- }, [refPanel, handler]);
306
- }
307
- function executeMediaQuery(mediaQuery) {
308
- return window.matchMedia(mediaQuery);
309
- }
310
- function useMediaQuery(mediaQuery) {
311
- const [isMatched, setIsMatched] = useState(executeMediaQuery(mediaQuery).matches);
312
- useEffect(() => {
313
- const query = executeMediaQuery(mediaQuery);
314
- const listener = (event) => {
315
- setIsMatched(event.matches);
316
- };
317
- query.addEventListener("change", listener);
318
- return () => {
319
- query.removeEventListener("change", listener);
320
- };
321
- }, []);
322
- return isMatched;
323
- }
324
- function useInverseThemeClassName() {
325
- const { theme } = useTheme();
326
- const prefix = usePrefix();
327
- if (!["g10", "g100"].includes(theme)) {
328
- return "";
329
- }
330
- return theme === "g10" ? `${prefix}--g100` : `${prefix}--g10`;
331
- }
@@ -1,5 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  import { Tag } from "@carbon/react";
3
+ import { Stage } from "../../api/endpoints.const";
4
+ import { C3NavigationAppBarProps, C3NavigationInfoSideBarProps, C3NavigationNotificationsSideBarProps, C3NavigationOrgSideBarProps, C3NavigationUserSideBarProps } from "./c3-navigation-sidebar/c3-navigation-sidebar.types";
3
5
  export interface C3NavigationAppProps {
4
6
  prefix: string;
5
7
  name: string;
@@ -20,42 +22,6 @@ export interface C3NavigationElementProps {
20
22
  subElements?: C3NavigationElementProps[];
21
23
  preceedingDivider?: boolean;
22
24
  }
23
- export interface C3NavigationSideBarBaseProps {
24
- ariaLabel?: string;
25
- isOpen?: boolean;
26
- type: "org" | "info" | "user" | "app";
27
- closeOnClick?: boolean;
28
- elementClicked?: (key: string) => void;
29
- customElements?: {
30
- activeOrganization?: {
31
- activeLabel: string;
32
- otherLabel: string;
33
- orgName: string;
34
- action: {
35
- label: string;
36
- onClick: () => void;
37
- };
38
- };
39
- profile?: {
40
- label: string;
41
- user: {
42
- name: string;
43
- email: string;
44
- };
45
- };
46
- themeSelector?: {
47
- currentTheme: string;
48
- onChange: (newValue: string) => void;
49
- };
50
- stageToggle?: {
51
- prodFeaturesEnabled: boolean;
52
- toggle: () => void;
53
- };
54
- };
55
- elements?: C3NavigationElementProps[];
56
- bottomElements?: C3NavigationElementProps[];
57
- version?: string;
58
- }
59
25
  export interface C3NavigationNavBarProps {
60
26
  elements: Array<{
61
27
  label: string;
@@ -74,12 +40,21 @@ export interface C3NavigationNavBarProps {
74
40
  }>;
75
41
  orgName?: string;
76
42
  }
43
+ export interface C3NotificationsProps {
44
+ stage: Stage;
45
+ activeOrganizationId: string;
46
+ userToken: string;
47
+ getNewUserToken: () => Promise<string>;
48
+ }
49
+ declare type WithoutType<P> = Omit<P, "type">;
77
50
  export interface C3NavigationProps {
78
51
  app: C3NavigationAppProps;
79
- appBar: C3NavigationSideBarBaseProps;
80
- orgSideBar?: C3NavigationSideBarBaseProps;
81
- infoSideBar?: C3NavigationSideBarBaseProps;
82
- userSideBar?: C3NavigationSideBarBaseProps;
52
+ appBar: WithoutType<C3NavigationAppBarProps>;
53
+ orgSideBar?: WithoutType<C3NavigationOrgSideBarProps>;
54
+ infoSideBar?: WithoutType<C3NavigationInfoSideBarProps>;
55
+ userSideBar?: WithoutType<C3NavigationUserSideBarProps>;
56
+ notificationSideBar?: WithoutType<C3NavigationNotificationsSideBarProps>;
83
57
  navbar: C3NavigationNavBarProps;
84
58
  forwardRef?: React.ForwardRefExoticComponent<any>;
85
59
  }
60
+ export {};
@@ -0,0 +1,12 @@
1
+ import { FC } from "react";
2
+ import { Notification as NotificationProps } from "../../../api/notifications";
3
+ export declare const NotificationTitle: import("styled-components").StyledComponent<"p", any, {}, never>;
4
+ export declare const NotificationDescription: import("styled-components").StyledComponent<"p", any, {}, never>;
5
+ export declare const LinkContainer: import("styled-components").StyledComponent<"div", any, {}, never>;
6
+ declare const C3NotificationContainer: FC<NotificationProps & {
7
+ onRead: () => void;
8
+ onDismiss: () => void;
9
+ onLinkClick?: (meta: NotificationProps["meta"]) => void;
10
+ unread?: boolean;
11
+ }>;
12
+ export default C3NotificationContainer;
@@ -0,0 +1,106 @@
1
+ import React, { useState } from "react";
2
+ import styled from "styled-components";
3
+ import { Button, Link, Tile } from "@carbon/react";
4
+ import { Close } from "@carbon/react/icons";
5
+ const NotificationTile = styled(Tile) `
6
+ box-shadow: inset 0px -1px 0px var(--cds-border-subtle-01);
7
+ `;
8
+ const CloseButton = styled(Button) `
9
+ margin-right: -1rem;
10
+ visibility: ${({ $show }) => ($show ? "visible" : "hidden")};
11
+ `;
12
+ const UnreadNotificationHeader = styled.div `
13
+ display: flex;
14
+ align-items: center;
15
+ width: 100%;
16
+ `;
17
+ const Dot = styled.div `
18
+ background: var(--cds-interactive);
19
+ width: 5px;
20
+ height: 5px;
21
+ border-radius: 50%;
22
+ margin-left: -9px;
23
+ margin-right: 4px;
24
+ `;
25
+ const Timestamp = styled.p `
26
+ color: var(--cds-text-helper);
27
+ font-size: var(--cds-label-01-font-size);
28
+ font-weight: var(--cds-label-01-font-weight);
29
+ line-height: var(--cds-label-01-line-height);
30
+ letter-spacing: var(--cds-label-01-letter-spacing);
31
+ `;
32
+ const NotificationHeader = styled.div `
33
+ width: 100%;
34
+ min-height: 2rem;
35
+ display: flex;
36
+ align-items: flex-end;
37
+ justify-content: space-between;
38
+ margin-top: -1rem;
39
+ `;
40
+ export const NotificationTitle = styled.p `
41
+ color: var(--cds-text-primary);
42
+ font-size: var(--cds-heading-compact-01-font-size);
43
+ font-weight: var(--cds-heading-compact-01-font-weight);
44
+ line-height: var(--cds-heading-compact-01-line-height);
45
+ letter-spacing: var(--cds-heading-compact-01-letter-spacing);
46
+ margin-top: var(--cds-spacing-01);
47
+ `;
48
+ export const NotificationDescription = styled.p `
49
+ color: var(--cds-text-primary);
50
+ font-size: var(--cds-helper-text-01-font-size);
51
+ font-weight: var(--cds-helper-text-01-font-weight);
52
+ line-height: var(--cds-helper-text-01-line-height);
53
+ letter-spacing: var(--cds-helper-text-01-letter-spacing);
54
+ margin-top: var(--cds-spacing-01);
55
+ `;
56
+ export const LinkContainer = styled.div `
57
+ margin-top: 0.5rem;
58
+ `;
59
+ const getReadableTimestamp = (timestamp) => {
60
+ const date = new Date(timestamp);
61
+ // eslint-disable-next-line
62
+ // @ts-ignore
63
+ const minutesAgo = Math.floor(Math.abs(new Date() - date) / 1000 / 60);
64
+ if (minutesAgo === 0) {
65
+ return "New";
66
+ }
67
+ if (minutesAgo < 60) {
68
+ return `${minutesAgo}min ago`;
69
+ }
70
+ const hoursAgo = Math.floor(minutesAgo / 60);
71
+ if (hoursAgo < 24) {
72
+ return `${hoursAgo}hr ago`;
73
+ }
74
+ return date.toDateString();
75
+ };
76
+ const C3NotificationContainer = ({ description, state, timestamp, title, onDismiss, meta, onLinkClick, unread = false, }) => {
77
+ const [isHovering, setIsHovering] = useState(false);
78
+ const dismissNotification = (e) => {
79
+ e.stopPropagation();
80
+ onDismiss();
81
+ };
82
+ const handleLinkClick = () => {
83
+ if (onLinkClick) {
84
+ onLinkClick(meta);
85
+ return false;
86
+ }
87
+ return true;
88
+ };
89
+ return (React.createElement(NotificationTile, { onMouseEnter: () => {
90
+ setIsHovering(true);
91
+ }, onMouseLeave: () => {
92
+ setIsHovering(false);
93
+ } },
94
+ React.createElement(UnreadNotificationHeader, null,
95
+ (state === "new" || unread) && React.createElement(Dot, null),
96
+ React.createElement(NotificationHeader, null,
97
+ React.createElement(Timestamp, null, getReadableTimestamp(timestamp)),
98
+ // eslint-disable-next-line
99
+ // @ts-ignore
100
+ React.createElement(CloseButton, { kind: "ghost", hasIconOnly: true, iconDescription: "Dismiss", align: "bottom-right", renderIcon: Close, onClick: dismissNotification, size: "sm", "$show": isHovering }))),
101
+ React.createElement(NotificationTitle, null, title),
102
+ React.createElement(NotificationDescription, null, description),
103
+ meta && (React.createElement(LinkContainer, null,
104
+ React.createElement(Link, { href: meta.href, onClick: handleLinkClick, visited: true, style: { fontSize: "var(--cds-helper-text-01-font-size)" } }, meta.label)))));
105
+ };
106
+ export default C3NotificationContainer;
@@ -0,0 +1,30 @@
1
+ import React, { ComponentType, FC, ReactNode } from "react";
2
+ import { AnalyticsEvent, Notification } from "../../../api/notifications";
3
+ export declare type LinkProps = {
4
+ label?: string;
5
+ href?: string;
6
+ entity?: {
7
+ id: string;
8
+ type: string;
9
+ };
10
+ parentEntity?: {
11
+ id: string;
12
+ type: string;
13
+ };
14
+ };
15
+ export declare type C3NotificationContextValue = {
16
+ enabled: boolean;
17
+ notifications: Notification[];
18
+ markAsRead: (notification: Notification) => void;
19
+ markAllAsRead: (notifications: Notification[]) => void;
20
+ dismiss: (notification: Notification) => void;
21
+ dismissAll: (notifications: Notification[]) => void;
22
+ analytics: (event: AnalyticsEvent, id?: string) => void;
23
+ };
24
+ export declare const C3NotificationContext: React.Context<C3NotificationContextValue>;
25
+ export declare type C3NotificationProviderProps = {
26
+ children?: ReactNode;
27
+ };
28
+ declare const C3NotificationProvider: FC<C3NotificationProviderProps>;
29
+ export declare function withNotifications<P>(Component: ComponentType<P>): ComponentType<P>;
30
+ export default C3NotificationProvider;