@hitachivantara/uikit-cli 6.0.1

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 (109) hide show
  1. package/README.md +46 -0
  2. package/package.json +68 -0
  3. package/src/app-shell.js +106 -0
  4. package/src/baselines/app-shell/vite/_gitignore +30 -0
  5. package/src/baselines/app-shell/vite/_oxlintrc.json +5 -0
  6. package/src/baselines/app-shell/vite/_package.json +55 -0
  7. package/src/baselines/app-shell/vite/public/locales/en/example.json +8 -0
  8. package/src/baselines/app-shell/vite/src/lib/data/config.ts +15 -0
  9. package/src/baselines/app-shell/vite/src/lib/i18n.ts +44 -0
  10. package/src/baselines/app-shell/vite/src/pages/Example/index.tsx +25 -0
  11. package/src/baselines/app-shell/vite/src/providers/Provider.tsx +31 -0
  12. package/src/baselines/app-shell/vite/src/tests/mocks.ts +1 -0
  13. package/src/baselines/app-shell/vite/src/tests/providers.tsx +13 -0
  14. package/src/baselines/app-shell/vite/src/tests/setupTests.ts +24 -0
  15. package/src/baselines/app-shell/vite/src/types/theme.d.ts +8 -0
  16. package/src/baselines/app-shell/vite/src/types/vite-env.d.ts +1 -0
  17. package/src/baselines/app-shell/vite/tsconfig.json +10 -0
  18. package/src/baselines/app-shell/vite/tsconfig.node.json +9 -0
  19. package/src/baselines/app-shell/vite/uno.config.ts +6 -0
  20. package/src/baselines/app-shell/vite/vite.config.ts +45 -0
  21. package/src/baselines/vite/_gitignore +30 -0
  22. package/src/baselines/vite/_oxlintrc.json +5 -0
  23. package/src/baselines/vite/_package.json +53 -0
  24. package/src/baselines/vite/index.html +18 -0
  25. package/src/baselines/vite/public/favicon.ico +0 -0
  26. package/src/baselines/vite/public/locales/en/common.json +16 -0
  27. package/src/baselines/vite/public/locales/en/home.json +4 -0
  28. package/src/baselines/vite/public/logo192.png +0 -0
  29. package/src/baselines/vite/src/App.tsx +31 -0
  30. package/src/baselines/vite/src/assets/HitachiLogo.tsx +27 -0
  31. package/src/baselines/vite/src/components/common/Loading/Loading.test.tsx +18 -0
  32. package/src/baselines/vite/src/components/common/Loading/Loading.tsx +15 -0
  33. package/src/baselines/vite/src/components/common/Loading/index.ts +1 -0
  34. package/src/baselines/vite/src/context/NavigationContext.tsx +67 -0
  35. package/src/baselines/vite/src/lib/i18n.ts +29 -0
  36. package/src/baselines/vite/src/main.tsx +12 -0
  37. package/src/baselines/vite/src/pages/Home/index.tsx +13 -0
  38. package/src/baselines/vite/src/pages/NotFound/NotFound.tsx +39 -0
  39. package/src/baselines/vite/src/pages/NotFound/index.tsx +1 -0
  40. package/src/baselines/vite/src/pages/layout/navigation.tsx +82 -0
  41. package/src/baselines/vite/src/routes.tsx +14 -0
  42. package/src/baselines/vite/src/tests/mocks.ts +1 -0
  43. package/src/baselines/vite/src/tests/providers.tsx +13 -0
  44. package/src/baselines/vite/src/tests/setupTests.ts +24 -0
  45. package/src/baselines/vite/src/types/theme.d.ts +8 -0
  46. package/src/baselines/vite/src/vite-env.d.ts +1 -0
  47. package/src/baselines/vite/tsconfig.json +10 -0
  48. package/src/baselines/vite/tsconfig.node.json +9 -0
  49. package/src/baselines/vite/uno.config.ts +6 -0
  50. package/src/baselines/vite/vite.config.ts +31 -0
  51. package/src/contents.js +63 -0
  52. package/src/create.js +172 -0
  53. package/src/index.js +22 -0
  54. package/src/navigation.js +21 -0
  55. package/src/package.js +37 -0
  56. package/src/plop-templates/README.md.hbs +10 -0
  57. package/src/plop-templates/app-shell/app-shell.config.ts.hbs +54 -0
  58. package/src/plop-templates/app-shell/index.html.hbs +15 -0
  59. package/src/plopfile.js +61 -0
  60. package/src/templates/AssetInventory/CardView.tsx +167 -0
  61. package/src/templates/AssetInventory/ListView.tsx +56 -0
  62. package/src/templates/AssetInventory/data.tsx +255 -0
  63. package/src/templates/AssetInventory/index.tsx +198 -0
  64. package/src/templates/AssetInventory/usePaginationData.ts +158 -0
  65. package/src/templates/Canvas/Context.tsx +49 -0
  66. package/src/templates/Canvas/ListView.tsx +189 -0
  67. package/src/templates/Canvas/Node.tsx +203 -0
  68. package/src/templates/Canvas/Sidebar.tsx +51 -0
  69. package/src/templates/Canvas/StatusEdge.tsx +75 -0
  70. package/src/templates/Canvas/StickyNode.tsx +475 -0
  71. package/src/templates/Canvas/Table.tsx +202 -0
  72. package/src/templates/Canvas/TreeView.tsx +211 -0
  73. package/src/templates/Canvas/dependencies.json +7 -0
  74. package/src/templates/Canvas/index.tsx +363 -0
  75. package/src/templates/Canvas/styles.tsx +41 -0
  76. package/src/templates/Canvas/utils.tsx +70 -0
  77. package/src/templates/Dashboard/GridPanel.tsx +33 -0
  78. package/src/templates/Dashboard/Kpi.tsx +107 -0
  79. package/src/templates/Dashboard/Map.styles.ts +681 -0
  80. package/src/templates/Dashboard/Map.tsx +71 -0
  81. package/src/templates/Dashboard/data.ts +67 -0
  82. package/src/templates/Dashboard/dependencies.json +11 -0
  83. package/src/templates/Dashboard/index.tsx +173 -0
  84. package/src/templates/DetailsView/KPIs.tsx +70 -0
  85. package/src/templates/DetailsView/MetadataItem.tsx +35 -0
  86. package/src/templates/DetailsView/Properties.tsx +127 -0
  87. package/src/templates/DetailsView/Table.tsx +104 -0
  88. package/src/templates/DetailsView/data.ts +67 -0
  89. package/src/templates/DetailsView/index.tsx +102 -0
  90. package/src/templates/DetailsView/usePaginationData.ts +155 -0
  91. package/src/templates/DetailsView/utils.ts +51 -0
  92. package/src/templates/Form/index.tsx +107 -0
  93. package/src/templates/KanbanBoard/ColumnContainer.tsx +89 -0
  94. package/src/templates/KanbanBoard/TaskCard.tsx +130 -0
  95. package/src/templates/KanbanBoard/data.tsx +140 -0
  96. package/src/templates/KanbanBoard/dependencies.json +6 -0
  97. package/src/templates/KanbanBoard/index.tsx +179 -0
  98. package/src/templates/KanbanBoard/styles.tsx +76 -0
  99. package/src/templates/KanbanBoard/types.ts +21 -0
  100. package/src/templates/ListView/Indicator.tsx +42 -0
  101. package/src/templates/ListView/Kpi.tsx +120 -0
  102. package/src/templates/ListView/Table.tsx +55 -0
  103. package/src/templates/ListView/data.tsx +179 -0
  104. package/src/templates/ListView/dependencies.json +5 -0
  105. package/src/templates/ListView/index.tsx +245 -0
  106. package/src/templates/ListView/usePaginationData.ts +158 -0
  107. package/src/templates/Welcome/index.tsx +101 -0
  108. package/src/templates/package.json +30 -0
  109. package/src/utils.js +37 -0
@@ -0,0 +1,211 @@
1
+ import { forwardRef, useRef, useState } from "react";
2
+ import { useDndMonitor, useDraggable } from "@dnd-kit/core";
3
+ import { css, cx } from "@emotion/css";
4
+ import {
5
+ HvOverflowTooltip,
6
+ HvTreeItem,
7
+ HvTreeItemProps,
8
+ HvTreeView,
9
+ theme,
10
+ useForkRef,
11
+ useHvTreeItem,
12
+ } from "@hitachivantara/uikit-react-core";
13
+ import { DataSource, Drag, Table } from "@hitachivantara/uikit-react-icons";
14
+
15
+ import { NodeData } from "./Node";
16
+
17
+ const classes = {
18
+ dragging: css({
19
+ border: `2px solid ${theme.colors.primaryStrong}`,
20
+ }),
21
+ contentDragging: css({
22
+ "&&:hover": {
23
+ backgroundColor: theme.colors.bgContainer,
24
+ },
25
+ }),
26
+ labelRoot: css({
27
+ display: "flex",
28
+ alignItems: "center",
29
+ width: "100%",
30
+ gap: theme.space.xs,
31
+ }),
32
+ dragIcon: css({ flex: 1, display: "flex", justifyContent: "flex-end" }),
33
+ };
34
+
35
+ interface Data {
36
+ id: string;
37
+ label: string;
38
+ subtitle?: string;
39
+ children?: Data[];
40
+ }
41
+
42
+ interface TreeItemProps extends HvTreeItemProps {
43
+ isDragging?: boolean;
44
+ }
45
+
46
+ interface DraggableTreeItemProps extends HvTreeItemProps {
47
+ subtitle?: string;
48
+ }
49
+
50
+ const data = [
51
+ {
52
+ id: "db1",
53
+ label: "Database 1",
54
+ children: [
55
+ { id: "table1", label: "Table 1", subtitle: "Table from Database 1" },
56
+ { id: "table2", label: "Table 2", subtitle: "Table from Database 1" },
57
+ { id: "table3", label: "Table 3", subtitle: "Table from Database 1" },
58
+ ],
59
+ },
60
+ {
61
+ id: "db2",
62
+ label: "Database 2",
63
+ children: [
64
+ { id: "table4", label: "Table 4", subtitle: "Table from Database 2" },
65
+ { id: "table5", label: "Table 5", subtitle: "Table from Database 2" },
66
+ { id: "table6", label: "Table 6", subtitle: "Table from Database 2" },
67
+ ],
68
+ },
69
+ ] satisfies Data[];
70
+
71
+ export const TreeItem = forwardRef<HTMLLIElement, TreeItemProps>(
72
+ function TreeItem(props, ref) {
73
+ const {
74
+ className,
75
+ isDragging,
76
+ children,
77
+ nodeId,
78
+ label,
79
+ onKeyDown,
80
+ ...others
81
+ } = props;
82
+ const Icon = children ? DataSource : Table;
83
+
84
+ const { handleExpansion } = useHvTreeItem(nodeId);
85
+
86
+ // Whether an item is being dragged or not
87
+ const [draggingActive, setDraggingActive] = useState<React.ReactNode>();
88
+ useDndMonitor({
89
+ onDragEnd: () => setDraggingActive(false),
90
+ onDragStart: () => setDraggingActive(true),
91
+ });
92
+
93
+ return (
94
+ <HvTreeItem
95
+ ref={ref}
96
+ nodeId={nodeId}
97
+ className={cx(className, { [classes.dragging]: isDragging })}
98
+ classes={{
99
+ content: cx({ [classes.contentDragging]: isDragging }),
100
+ }}
101
+ label={
102
+ <div className={classes.labelRoot}>
103
+ <Icon />
104
+ <HvOverflowTooltip data={label} />
105
+ {!children && (
106
+ <span className={classes.dragIcon}>
107
+ <Drag />
108
+ </span>
109
+ )}
110
+ </div>
111
+ }
112
+ // The following props enable drag and drop with keyboard
113
+ disableTreeFocus
114
+ tabIndex={0}
115
+ onKeyDown={(event) => {
116
+ if (onKeyDown) {
117
+ onKeyDown?.(event);
118
+ if (!isDragging) event.stopPropagation();
119
+ return;
120
+ }
121
+
122
+ // Expands item if possible when navigating with tab key
123
+ if (event.key === "Enter" && !draggingActive) {
124
+ handleExpansion(event as any);
125
+ }
126
+ }}
127
+ {...others}
128
+ >
129
+ {children}
130
+ </HvTreeItem>
131
+ );
132
+ },
133
+ );
134
+
135
+ const DraggableTreeItem = (props: DraggableTreeItemProps) => {
136
+ const { subtitle, label, children, nodeId, ...others } = props;
137
+
138
+ const itemRef = useRef<HTMLLIElement>(null);
139
+
140
+ const { attributes, listeners, setNodeRef, isDragging, transform } =
141
+ useDraggable({
142
+ id: nodeId,
143
+ data: {
144
+ // Data needed to be dropped in HvFlow
145
+ hvFlow: {
146
+ // HvFlow will use this value to populate the node's data.nodeLabel
147
+ label,
148
+ // Node type from nodeTypes property provided to HvFlow
149
+ type: "node",
150
+ // Item position: used by HvFlow to position the node when dropped
151
+ x: itemRef.current?.getBoundingClientRect().x,
152
+ y: itemRef.current?.getBoundingClientRect().y,
153
+ // Values to be added to the node's data
154
+ data: {
155
+ tableId: nodeId,
156
+ subtitle,
157
+ color: "cat3",
158
+ icon: "table",
159
+ output: {
160
+ id: "data",
161
+ label: "Data",
162
+ },
163
+ } satisfies NodeData,
164
+ },
165
+ // Data needed for the DragOverlay component
166
+ dragOverlay: {
167
+ component: <TreeItem nodeId={nodeId} label={label} isDragging />,
168
+ },
169
+ },
170
+ });
171
+
172
+ const style = transform
173
+ ? {
174
+ transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
175
+ }
176
+ : undefined;
177
+
178
+ const forkedRef = useForkRef(itemRef, setNodeRef);
179
+
180
+ return (
181
+ <TreeItem
182
+ nodeId={nodeId}
183
+ ref={forkedRef}
184
+ style={style}
185
+ isDragging={isDragging}
186
+ label={label}
187
+ {...attributes}
188
+ {...listeners}
189
+ {...others}
190
+ >
191
+ {children}
192
+ </TreeItem>
193
+ );
194
+ };
195
+
196
+ const renderTreeItem = ({ id, label, children, subtitle }: Data) =>
197
+ children ? (
198
+ <TreeItem key={id} nodeId={id} label={label}>
199
+ {children?.map(renderTreeItem)}
200
+ </TreeItem>
201
+ ) : (
202
+ <DraggableTreeItem key={id} nodeId={id} label={label} subtitle={subtitle}>
203
+ {children}
204
+ </DraggableTreeItem>
205
+ );
206
+
207
+ export const TreeView = () => (
208
+ <HvTreeView aria-label="Database navigator" disableSelection>
209
+ {data.map((db) => renderTreeItem(db))}
210
+ </HvTreeView>
211
+ );
@@ -0,0 +1,7 @@
1
+ {
2
+ "dependencies": {
3
+ "@dnd-kit/sortable": "^7.0.2",
4
+ "@dnd-kit/utilities": "^3.2.2",
5
+ "reactflow": "^11.10.1"
6
+ }
7
+ }
@@ -0,0 +1,363 @@
1
+ import { useCallback, useMemo, useState } from "react";
2
+ import { applyNodeChanges, ReactFlowInstance } from "reactflow";
3
+ import {
4
+ HvButton,
5
+ HvDialog,
6
+ HvDialogContent,
7
+ HvDialogTitle,
8
+ HvIconButton,
9
+ HvInlineEditor,
10
+ HvOverflowTooltip,
11
+ HvTypography,
12
+ } from "@hitachivantara/uikit-react-core";
13
+ import {
14
+ Add,
15
+ Close,
16
+ DataSource,
17
+ DropUpXS,
18
+ Fullscreen,
19
+ Report,
20
+ Table,
21
+ } from "@hitachivantara/uikit-react-icons";
22
+ import {
23
+ HvFlow,
24
+ HvFlowBackground,
25
+ HvFlowControls,
26
+ HvFlowEmpty,
27
+ HvFlowProps,
28
+ StickyNode,
29
+ StickyNodeData,
30
+ } from "@hitachivantara/uikit-react-lab";
31
+ import {
32
+ HvCanvasBottomPanel,
33
+ HvCanvasBottomPanelProps,
34
+ HvCanvasProvider,
35
+ HvCanvasToolbar,
36
+ } from "@hitachivantara/uikit-react-pentaho";
37
+
38
+ import { CanvasProvider, useCanvasContext } from "./Context";
39
+ import { ListView } from "./ListView";
40
+ import { Node, NodeData } from "./Node";
41
+ import { CanvasSidebar } from "./Sidebar";
42
+ import { StatusEdge } from "./StatusEdge";
43
+ import { classes } from "./styles";
44
+ import { DataTable } from "./Table";
45
+ import { TreeView } from "./TreeView";
46
+ import { flowStatuses } from "./utils";
47
+
48
+ const nodeTypes = {
49
+ node: Node,
50
+ sticky: StickyNode,
51
+ };
52
+ const edgeTypes = {
53
+ status: StatusEdge,
54
+ };
55
+ const initialNodes: HvFlowProps["nodes"] = [];
56
+ const initialEdges: HvFlowProps["edges"] = [];
57
+
58
+ const sidePanelTabs = [
59
+ {
60
+ id: 0,
61
+ content: (
62
+ <div className={classes.tabLabel}>
63
+ <DataSource />
64
+ Data
65
+ </div>
66
+ ),
67
+ },
68
+ {
69
+ id: 2,
70
+ content: (
71
+ <div className={classes.tabLabel}>
72
+ <Add />
73
+ Nodes
74
+ </div>
75
+ ),
76
+ },
77
+ ];
78
+ const sidePanelContent = {
79
+ [sidePanelTabs[0].id]: <TreeView />,
80
+ [sidePanelTabs[1].id]: <ListView />,
81
+ };
82
+
83
+ const Page = () => {
84
+ const [sidePanelOpen, setSidePanelOpen] = useState(true);
85
+ const [sidePanelTab, setSidePanelTab] = useState(sidePanelTabs[0].id);
86
+ const [minimize, setMinimize] = useState(false);
87
+ const [fullscreen, setFullscreen] = useState(false);
88
+ const [nodesHidden, setNodesHidden] = useState(false);
89
+ const [flowInstance, setFlowInstance] =
90
+ useState<ReactFlowInstance<NodeData | StickyNodeData>>();
91
+ const [nodes, setNodes] = useState(initialNodes);
92
+
93
+ const { selectedTable, openedTables, setOpenedTables, setSelectedTable } =
94
+ useCanvasContext();
95
+
96
+ const bottomTabs = useMemo(() => {
97
+ return (openedTables || []).map((table) => ({
98
+ id: table.id,
99
+ title: (overflowing) => (
100
+ <div className={classes.titleRoot}>
101
+ {!overflowing && <Table />}
102
+ <HvOverflowTooltip data={table.label} />
103
+ </div>
104
+ ),
105
+ })) satisfies HvCanvasBottomPanelProps["tabs"];
106
+ }, [openedTables]);
107
+
108
+ const handleCloseTab = (value: string | number) => {
109
+ const newOpenedTables = openedTables?.filter((x) => x.id !== value) ?? [];
110
+ if (newOpenedTables.length !== 0) {
111
+ setOpenedTables?.(newOpenedTables);
112
+ setSelectedTable?.(newOpenedTables[0].id as string);
113
+ } else {
114
+ setOpenedTables?.(undefined);
115
+ }
116
+ };
117
+
118
+ const handleChangeTab: HvCanvasBottomPanelProps["onTabChange"] = (
119
+ event,
120
+ value,
121
+ ) => {
122
+ setSelectedTable?.(value as string);
123
+ };
124
+
125
+ const handleAction: HvCanvasBottomPanelProps["onAction"] = (
126
+ event,
127
+ action,
128
+ tabId,
129
+ ) => {
130
+ switch (action.id) {
131
+ case "close":
132
+ event.stopPropagation();
133
+ handleCloseTab(tabId);
134
+ break;
135
+ case "toggle":
136
+ if (minimize && selectedTable !== tabId) handleChangeTab(null, tabId);
137
+ setMinimize((prev) => !prev);
138
+ break;
139
+ case "fullscreen":
140
+ if (minimize && selectedTable !== tabId) handleChangeTab(null, tabId);
141
+ setFullscreen((prev) => !prev);
142
+ break;
143
+ default:
144
+ break;
145
+ }
146
+ };
147
+
148
+ // Simulating an execution of the flow and updating the statuses of nodes and edges
149
+ const handleExecute = () => {
150
+ flowInstance?.setNodes((nodes) =>
151
+ nodes.map((node) => {
152
+ if (node.type === "sticky") return node;
153
+
154
+ const random = Math.floor(Math.random() * flowStatuses.length);
155
+
156
+ return {
157
+ ...node,
158
+ data: {
159
+ ...node.data,
160
+ status: flowStatuses[random],
161
+ },
162
+ };
163
+ }),
164
+ );
165
+ flowInstance?.setEdges((edges) =>
166
+ edges.map((edge) => {
167
+ const random = Math.floor(Math.random() * flowStatuses.length);
168
+ return {
169
+ ...edge,
170
+ type: Object.keys(edgeTypes)[0],
171
+ data: {
172
+ ...edge.data,
173
+ status: flowStatuses[random],
174
+ },
175
+ };
176
+ }),
177
+ );
178
+ };
179
+
180
+ const handleAddSticky = () => {
181
+ const id = Date.now().toString();
182
+ const newNode = {
183
+ id,
184
+ type: "sticky",
185
+ position: {
186
+ x: Math.random() * 250 + 350,
187
+ y: Math.random() * 250 + 200,
188
+ },
189
+ style: {
190
+ width: 150,
191
+ height: 125,
192
+ },
193
+ data: {},
194
+ };
195
+ flowInstance?.addNodes(newNode);
196
+ };
197
+
198
+ const handleHideNotes = () => {
199
+ setNodesHidden(!nodesHidden);
200
+ flowInstance?.setNodes((nds) => {
201
+ if (!nds.some((node) => node.type === "sticky")) return nds;
202
+
203
+ return nds.map((node) =>
204
+ node.type === "sticky"
205
+ ? {
206
+ ...node,
207
+ data: {
208
+ ...node.data,
209
+ visible: !((node.data as StickyNodeData)?.visible ?? true),
210
+ },
211
+ }
212
+ : node,
213
+ );
214
+ });
215
+ };
216
+
217
+ const bottomPanelOpen = useMemo(
218
+ () =>
219
+ !!openedTables &&
220
+ openedTables.length > 0 &&
221
+ bottomTabs &&
222
+ bottomTabs.length > 0,
223
+ [bottomTabs, openedTables],
224
+ );
225
+
226
+ const leftActions = [
227
+ {
228
+ id: "toggle",
229
+ label: minimize ? "Maximize" : "Minimize",
230
+ icon: <DropUpXS size="XS" rotate={!minimize} />,
231
+ },
232
+ ];
233
+
234
+ const rightActions = [
235
+ {
236
+ id: "fullscreen",
237
+ label: "Fullscreen",
238
+ icon: <Fullscreen iconSize="XS" />,
239
+ },
240
+ {
241
+ id: "close",
242
+ label: "Close",
243
+ icon: <Close iconSize="XS" />,
244
+ },
245
+ ];
246
+
247
+ const onNodesChange = useCallback((changes: any) => {
248
+ return setNodes((nds) => applyNodeChanges(changes, nds));
249
+ }, []);
250
+
251
+ return (
252
+ <div className={classes.root}>
253
+ <HvCanvasProvider>
254
+ <HvFlow
255
+ className={classes.flow}
256
+ nodes={nodes}
257
+ edges={initialEdges}
258
+ nodeTypes={nodeTypes}
259
+ onNodesChange={onNodesChange}
260
+ edgeTypes={edgeTypes}
261
+ onInit={setFlowInstance}
262
+ /** Flow sidebar passed as prop to access the flow's Dnd context inside CanvasSidePanel */
263
+ sidebar={
264
+ <CanvasSidebar
265
+ tabs={sidePanelTabs}
266
+ open={sidePanelOpen}
267
+ tab={sidePanelTab}
268
+ onToggle={(event, value) => setSidePanelOpen(value)}
269
+ onTabChange={(event, value) => setSidePanelTab(value as number)}
270
+ >
271
+ {sidePanelContent[sidePanelTab]}
272
+ </CanvasSidebar>
273
+ }
274
+ >
275
+ <HvFlowEmpty
276
+ className={classes.flowEmpty}
277
+ title={
278
+ <HvTypography variant="title3" component="p">
279
+ Drag and Drop your Nodes
280
+ </HvTypography>
281
+ }
282
+ message={
283
+ <HvTypography
284
+ className={classes.flowEmptyMessage}
285
+ variant="label"
286
+ component="p"
287
+ >
288
+ Then you can start configuring your flow.
289
+ </HvTypography>
290
+ }
291
+ icon={null}
292
+ />
293
+ <HvFlowBackground />
294
+ <HvFlowControls>
295
+ <HvIconButton
296
+ variant="secondarySubtle"
297
+ onClick={handleHideNotes}
298
+ aria-label={nodesHidden ? "Show notes" : "Hide notes"}
299
+ title={nodesHidden ? "Show notes" : "Hide notes"}
300
+ >
301
+ <Report />
302
+ </HvIconButton>
303
+ </HvFlowControls>
304
+ </HvFlow>
305
+ <HvCanvasToolbar
306
+ title={<HvInlineEditor defaultValue="My Canvas" variant="title4" />}
307
+ >
308
+ <div className="flex gap-sm">
309
+ <HvButton variant="secondarySubtle" onClick={handleAddSticky}>
310
+ Add note
311
+ </HvButton>
312
+ <HvButton variant="primary" onClick={handleExecute}>
313
+ Execute
314
+ </HvButton>
315
+ </div>
316
+ </HvCanvasToolbar>
317
+ {bottomTabs.length > 0 && bottomPanelOpen && (
318
+ <HvCanvasBottomPanel
319
+ classes={{
320
+ rightActions: classes.rightActions,
321
+ }}
322
+ open={bottomPanelOpen}
323
+ minimize={minimize}
324
+ tabs={bottomTabs}
325
+ selectedTabId={selectedTable}
326
+ leftActions={leftActions}
327
+ rightActions={rightActions}
328
+ overflowActions={[...leftActions, ...rightActions]}
329
+ onTabChange={handleChangeTab}
330
+ onAction={handleAction}
331
+ >
332
+ <DataTable id={selectedTable} />
333
+ </HvCanvasBottomPanel>
334
+ )}
335
+ {bottomPanelOpen && (
336
+ <HvDialog
337
+ fullWidth
338
+ maxWidth="lg"
339
+ open={fullscreen}
340
+ onClose={() => setFullscreen((prev) => !prev)}
341
+ >
342
+ <HvDialogTitle className={classes.dialogTitle}>
343
+ {bottomTabs.find((x) => x.id === selectedTable)?.title(false)}
344
+ </HvDialogTitle>
345
+ <HvDialogContent>
346
+ <DataTable id={selectedTable} />
347
+ </HvDialogContent>
348
+ </HvDialog>
349
+ )}
350
+ </HvCanvasProvider>
351
+ </div>
352
+ );
353
+ };
354
+
355
+ const Canvas = () => (
356
+ <CanvasProvider>
357
+ <Page />
358
+ </CanvasProvider>
359
+ );
360
+
361
+ export { Canvas as Component };
362
+
363
+ export default Canvas;
@@ -0,0 +1,41 @@
1
+ import { css } from "@emotion/css";
2
+ import { actionsGenericClasses, theme } from "@hitachivantara/uikit-react-core";
3
+
4
+ export const classes = {
5
+ flow: css({
6
+ width: "100%",
7
+ height: "100%",
8
+ // border style when a node is selected
9
+ [`& .selected > div`]: {
10
+ borderWidth: "2px",
11
+ borderColor: theme.colors.primaryStrong,
12
+ },
13
+ " .react-flow": {
14
+ minHeight: 700,
15
+ },
16
+ }),
17
+ flowEmpty: css({ backgroundColor: "transparent" }),
18
+ flowEmptyMessage: css({ color: theme.colors.textSubtle }),
19
+ tabLabel: css({ display: "flex", alignItems: "center" }),
20
+ root: css({
21
+ minHeight: 700,
22
+ height: "100%",
23
+ display: "flex",
24
+ width: "100%",
25
+ position: "relative",
26
+ }),
27
+ titleRoot: css({
28
+ display: "flex",
29
+ width: "100%",
30
+ alignItems: "center",
31
+ }),
32
+ dialogTitle: css({
33
+ ...theme.typography.label,
34
+ "& div > div": { margin: 0, padding: 0 },
35
+ }),
36
+ rightActions: css({
37
+ [`& .${actionsGenericClasses.button}:not(:last-child)`]: {
38
+ marginRight: 0,
39
+ },
40
+ }),
41
+ };
@@ -0,0 +1,70 @@
1
+ import { theme } from "@hitachivantara/uikit-react-core";
2
+ import {
3
+ Battery,
4
+ Cloud,
5
+ Edit,
6
+ Favorite,
7
+ Fire,
8
+ Ghost,
9
+ Heart,
10
+ Level0Good,
11
+ Level2Average,
12
+ Level3Bad,
13
+ Palette,
14
+ Table,
15
+ } from "@hitachivantara/uikit-react-icons";
16
+
17
+ export const delay = (ms: number) =>
18
+ new Promise((resolve) => {
19
+ setTimeout(resolve, ms);
20
+ });
21
+
22
+ export const iconsMapping = {
23
+ table: <Table color="textDark" />,
24
+ ghost: <Ghost color="textDark" />,
25
+ cloud: <Cloud color="textDark" />,
26
+ battery: <Battery color="textDark" />,
27
+ fire: <Fire color="textDark" />,
28
+ palette: <Palette color="textDark" />,
29
+ edit: <Edit color="textDark" />,
30
+ heart: <Heart color="textDark" />,
31
+ favorite: <Favorite color="textDark" />,
32
+ };
33
+
34
+ export const iconsMappingKeys = Object.keys(iconsMapping);
35
+
36
+ const iconWrapper = (icon: React.ReactNode) => (
37
+ <div
38
+ style={{
39
+ height: 24,
40
+ width: 24,
41
+ display: "flex",
42
+ justifyContent: "center",
43
+ alignItems: "center",
44
+ border: `1px solid ${theme.colors.bgContainer}`,
45
+ borderRadius: theme.radii.full,
46
+ }}
47
+ >
48
+ {icon}
49
+ </div>
50
+ );
51
+
52
+ export const flowStatuses = ["warning", "error", "success"] as const;
53
+ export type FlowStatus = (typeof flowStatuses)[number];
54
+ export const flowStatusesSpecs = {
55
+ [flowStatuses[0]]: {
56
+ icon: iconWrapper(<Level2Average color={["warning", "textLight"]} />),
57
+ color: theme.colors.warning,
58
+ description: "Warning",
59
+ },
60
+ [flowStatuses[1]]: {
61
+ icon: iconWrapper(<Level3Bad color={["negativeStrong", "textLight"]} />),
62
+ color: theme.colors.negativeStrong,
63
+ description: "Error",
64
+ },
65
+ [flowStatuses[2]]: {
66
+ icon: iconWrapper(<Level0Good color={["positive", "textLight"]} />),
67
+ color: theme.colors.positive,
68
+ description: "Success",
69
+ },
70
+ };