@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.
- package/README.md +46 -0
- package/package.json +68 -0
- package/src/app-shell.js +106 -0
- package/src/baselines/app-shell/vite/_gitignore +30 -0
- package/src/baselines/app-shell/vite/_oxlintrc.json +5 -0
- package/src/baselines/app-shell/vite/_package.json +55 -0
- package/src/baselines/app-shell/vite/public/locales/en/example.json +8 -0
- package/src/baselines/app-shell/vite/src/lib/data/config.ts +15 -0
- package/src/baselines/app-shell/vite/src/lib/i18n.ts +44 -0
- package/src/baselines/app-shell/vite/src/pages/Example/index.tsx +25 -0
- package/src/baselines/app-shell/vite/src/providers/Provider.tsx +31 -0
- package/src/baselines/app-shell/vite/src/tests/mocks.ts +1 -0
- package/src/baselines/app-shell/vite/src/tests/providers.tsx +13 -0
- package/src/baselines/app-shell/vite/src/tests/setupTests.ts +24 -0
- package/src/baselines/app-shell/vite/src/types/theme.d.ts +8 -0
- package/src/baselines/app-shell/vite/src/types/vite-env.d.ts +1 -0
- package/src/baselines/app-shell/vite/tsconfig.json +10 -0
- package/src/baselines/app-shell/vite/tsconfig.node.json +9 -0
- package/src/baselines/app-shell/vite/uno.config.ts +6 -0
- package/src/baselines/app-shell/vite/vite.config.ts +45 -0
- package/src/baselines/vite/_gitignore +30 -0
- package/src/baselines/vite/_oxlintrc.json +5 -0
- package/src/baselines/vite/_package.json +53 -0
- package/src/baselines/vite/index.html +18 -0
- package/src/baselines/vite/public/favicon.ico +0 -0
- package/src/baselines/vite/public/locales/en/common.json +16 -0
- package/src/baselines/vite/public/locales/en/home.json +4 -0
- package/src/baselines/vite/public/logo192.png +0 -0
- package/src/baselines/vite/src/App.tsx +31 -0
- package/src/baselines/vite/src/assets/HitachiLogo.tsx +27 -0
- package/src/baselines/vite/src/components/common/Loading/Loading.test.tsx +18 -0
- package/src/baselines/vite/src/components/common/Loading/Loading.tsx +15 -0
- package/src/baselines/vite/src/components/common/Loading/index.ts +1 -0
- package/src/baselines/vite/src/context/NavigationContext.tsx +67 -0
- package/src/baselines/vite/src/lib/i18n.ts +29 -0
- package/src/baselines/vite/src/main.tsx +12 -0
- package/src/baselines/vite/src/pages/Home/index.tsx +13 -0
- package/src/baselines/vite/src/pages/NotFound/NotFound.tsx +39 -0
- package/src/baselines/vite/src/pages/NotFound/index.tsx +1 -0
- package/src/baselines/vite/src/pages/layout/navigation.tsx +82 -0
- package/src/baselines/vite/src/routes.tsx +14 -0
- package/src/baselines/vite/src/tests/mocks.ts +1 -0
- package/src/baselines/vite/src/tests/providers.tsx +13 -0
- package/src/baselines/vite/src/tests/setupTests.ts +24 -0
- package/src/baselines/vite/src/types/theme.d.ts +8 -0
- package/src/baselines/vite/src/vite-env.d.ts +1 -0
- package/src/baselines/vite/tsconfig.json +10 -0
- package/src/baselines/vite/tsconfig.node.json +9 -0
- package/src/baselines/vite/uno.config.ts +6 -0
- package/src/baselines/vite/vite.config.ts +31 -0
- package/src/contents.js +63 -0
- package/src/create.js +172 -0
- package/src/index.js +22 -0
- package/src/navigation.js +21 -0
- package/src/package.js +37 -0
- package/src/plop-templates/README.md.hbs +10 -0
- package/src/plop-templates/app-shell/app-shell.config.ts.hbs +54 -0
- package/src/plop-templates/app-shell/index.html.hbs +15 -0
- package/src/plopfile.js +61 -0
- package/src/templates/AssetInventory/CardView.tsx +167 -0
- package/src/templates/AssetInventory/ListView.tsx +56 -0
- package/src/templates/AssetInventory/data.tsx +255 -0
- package/src/templates/AssetInventory/index.tsx +198 -0
- package/src/templates/AssetInventory/usePaginationData.ts +158 -0
- package/src/templates/Canvas/Context.tsx +49 -0
- package/src/templates/Canvas/ListView.tsx +189 -0
- package/src/templates/Canvas/Node.tsx +203 -0
- package/src/templates/Canvas/Sidebar.tsx +51 -0
- package/src/templates/Canvas/StatusEdge.tsx +75 -0
- package/src/templates/Canvas/StickyNode.tsx +475 -0
- package/src/templates/Canvas/Table.tsx +202 -0
- package/src/templates/Canvas/TreeView.tsx +211 -0
- package/src/templates/Canvas/dependencies.json +7 -0
- package/src/templates/Canvas/index.tsx +363 -0
- package/src/templates/Canvas/styles.tsx +41 -0
- package/src/templates/Canvas/utils.tsx +70 -0
- package/src/templates/Dashboard/GridPanel.tsx +33 -0
- package/src/templates/Dashboard/Kpi.tsx +107 -0
- package/src/templates/Dashboard/Map.styles.ts +681 -0
- package/src/templates/Dashboard/Map.tsx +71 -0
- package/src/templates/Dashboard/data.ts +67 -0
- package/src/templates/Dashboard/dependencies.json +11 -0
- package/src/templates/Dashboard/index.tsx +173 -0
- package/src/templates/DetailsView/KPIs.tsx +70 -0
- package/src/templates/DetailsView/MetadataItem.tsx +35 -0
- package/src/templates/DetailsView/Properties.tsx +127 -0
- package/src/templates/DetailsView/Table.tsx +104 -0
- package/src/templates/DetailsView/data.ts +67 -0
- package/src/templates/DetailsView/index.tsx +102 -0
- package/src/templates/DetailsView/usePaginationData.ts +155 -0
- package/src/templates/DetailsView/utils.ts +51 -0
- package/src/templates/Form/index.tsx +107 -0
- package/src/templates/KanbanBoard/ColumnContainer.tsx +89 -0
- package/src/templates/KanbanBoard/TaskCard.tsx +130 -0
- package/src/templates/KanbanBoard/data.tsx +140 -0
- package/src/templates/KanbanBoard/dependencies.json +6 -0
- package/src/templates/KanbanBoard/index.tsx +179 -0
- package/src/templates/KanbanBoard/styles.tsx +76 -0
- package/src/templates/KanbanBoard/types.ts +21 -0
- package/src/templates/ListView/Indicator.tsx +42 -0
- package/src/templates/ListView/Kpi.tsx +120 -0
- package/src/templates/ListView/Table.tsx +55 -0
- package/src/templates/ListView/data.tsx +179 -0
- package/src/templates/ListView/dependencies.json +5 -0
- package/src/templates/ListView/index.tsx +245 -0
- package/src/templates/ListView/usePaginationData.ts +158 -0
- package/src/templates/Welcome/index.tsx +101 -0
- package/src/templates/package.json +30 -0
- 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,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
|
+
};
|