sample-ui-component-library 0.0.33-dev → 0.0.35-dev

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sample-ui-component-library",
3
- "version": "0.0.33-dev",
3
+ "version": "0.0.35-dev",
4
4
  "description": "A library which contains sample UI elements that can be used for populating layouts.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -0,0 +1,159 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+ import {
3
+ ReactFlow,
4
+ ReactFlowProvider,
5
+ useNodesState,
6
+ useEdgesState,
7
+ Background,
8
+ Controls,
9
+ useReactFlow,
10
+ addEdge,
11
+ applyNodeChanges,
12
+ applyEdgeChanges
13
+ } from "@xyflow/react";
14
+ import "@xyflow/react/dist/style.css";
15
+
16
+ const initialNodes = [
17
+ {
18
+ id: "1",
19
+ position: { x: 250, y: 150 },
20
+ data: { label: "Behavior 1" },
21
+ draggable: true,
22
+ selectable: true,
23
+ },
24
+ {
25
+ id: "2",
26
+ position: { x: 250, y: 250 },
27
+ data: { label: "Behavior 2" },
28
+ draggable: true,
29
+ selectable: true,
30
+ },
31
+ ];
32
+
33
+ const NODE_WIDTH = 150;
34
+ const NODE_HEIGHT = 40;
35
+ const Flow = ({ activeTool }) => {
36
+ const { screenToFlowPosition } = useReactFlow();
37
+ const [nodes, setNodes] = useState(initialNodes);
38
+ const [edges, setEdges] = useState([]);
39
+ const [ghostNode, setGhostNode] = useState(null);
40
+
41
+ // Callbacks to apply node changes
42
+ const onNodesChange = useCallback((changes) => {
43
+ setNodes((nds) => applyNodeChanges(changes, nds));
44
+ }, []);
45
+ const onEdgesChange = useCallback((changes) => {
46
+ setEdges((eds) => applyEdgeChanges(changes, eds));
47
+ }, []);
48
+
49
+ // Console messages for debugging
50
+ useEffect(() => {
51
+ console.log("Active Tool:", activeTool);
52
+ }, [activeTool]);
53
+
54
+ // Callback for mouse pane move
55
+ const onPaneMouseMove = useCallback( (event) => {
56
+ const pos = screenToFlowPosition({
57
+ x: event.clientX,
58
+ y: event.clientY,
59
+ });
60
+
61
+ if (activeTool === "drop") {
62
+ setGhostNode({
63
+ id: "ghost",
64
+ position: {
65
+ x: pos.x - NODE_WIDTH / 2,
66
+ y: pos.y - NODE_HEIGHT / 2,
67
+ },
68
+ data: { label: "Behavior" },
69
+ draggable: false,
70
+ selectable: false,
71
+ style: {
72
+ opacity: 0.5,
73
+ pointerEvents: "none",
74
+ width: NODE_WIDTH,
75
+ height: NODE_HEIGHT,
76
+ },
77
+ });
78
+ }
79
+ }, [activeTool]);
80
+
81
+ // Callback for when mouse leaves pane
82
+ const onPaneMouseLeave = useCallback(() => {
83
+ setGhostNode(null);
84
+ }, []);
85
+
86
+ // Callback for pane click
87
+ const onPaneClick = useCallback((event, node) => {
88
+ if (activeTool === "drop") {
89
+ if (!ghostNode) return;
90
+ setNodes((nds) => [
91
+ ...nds,
92
+ {
93
+ ...ghostNode,
94
+ draggable: true,
95
+ selectable: true,
96
+ id: crypto.randomUUID(),
97
+ style: {},
98
+ },
99
+ ]);
100
+ }
101
+ }, [ghostNode, activeTool]);
102
+
103
+ // Callback for node click to delete it (if tool is active)
104
+ const onNodeClick = useCallback((event, node) => {
105
+ if (activeTool === "delete") {
106
+ setNodes((nds) => nds.filter((n) => n.id !== node.id));
107
+ setEdges((eds) =>
108
+ eds.filter((e) => e.source !== node.id && e.target !== node.id)
109
+ );
110
+ }
111
+ }, [activeTool]);
112
+
113
+ // Callback for edge click to delete it (if tool is active)
114
+ const onEdgeClick = useCallback((event, edge) => {
115
+ if (activeTool === "delete") {
116
+ setEdges((eds) => eds.filter((e) => e.id !== edge.id));
117
+ }
118
+ }, [activeTool]);
119
+
120
+ // Callback for when edge is connected, nodesConnectable={activeTool === "connect"}
121
+ // is used to determine connectability
122
+ const onConnect = useCallback((connection) => {
123
+ const edge = {
124
+ ...connection,
125
+ id: crypto.randomUUID(),
126
+ type: "smoothstep",
127
+ animated: true,
128
+ };
129
+ setEdges((eds) => [...eds, edge]);
130
+ }, [activeTool]);
131
+
132
+ return (
133
+ <ReactFlow
134
+ nodes={ghostNode ? [...nodes, ghostNode] : nodes}
135
+ edges={edges}
136
+ onPaneMouseMove={onPaneMouseMove}
137
+ onPaneMouseLeave={onPaneMouseLeave}
138
+ onPaneClick={onPaneClick}
139
+ onConnect={onConnect}
140
+ onNodesChange={onNodesChange}
141
+ onEdgesChange={onEdgesChange}
142
+ onNodeClick={onNodeClick}
143
+ onEdgeClick={onEdgeClick}
144
+ colorMode={"dark"}
145
+ fitView
146
+ >
147
+ <Background />
148
+ <Controls />
149
+ </ReactFlow>
150
+ );
151
+ };
152
+
153
+ export const BehavioralGraphBuilder = ({ activeTool }) => {
154
+ return (
155
+ <ReactFlowProvider>
156
+ <Flow activeTool={activeTool} />
157
+ </ReactFlowProvider>
158
+ );
159
+ };
@@ -0,0 +1 @@
1
+ export * from "./BehavioralGraphBuilder.jsx"
@@ -41,21 +41,20 @@ export const Editor = forwardRef(({ }, ref) => {
41
41
  dispatch({ type: "ADD_TAB", payload: { tab, index } });
42
42
  }, []);
43
43
 
44
- const setTabGroupId = useCallback((id) => {
45
- dispatch({ type: "RESET_STATE"});
46
- dispatch({ type: "SET_PARENT_TAB_GROUP_ID", payload: id });
47
- }, []);
44
+ const getGroupUid = useCallback((id) => {
45
+ return state.uid;
46
+ }, [state]);
48
47
 
49
48
  const api = useMemo(() => {
50
49
  return {
51
50
  state,
52
51
  addTab,
53
- setTabGroupId,
54
52
  selectTab,
55
53
  closeTab,
56
- moveTab
54
+ moveTab,
55
+ getGroupUid
57
56
  };
58
- }, [state, addTab, selectTab, closeTab, moveTab, setTabGroupId]);
57
+ }, [state, addTab, selectTab, closeTab, moveTab, getGroupUid]);
59
58
 
60
59
  useImperativeHandle(ref, () => api, [api]);
61
60
 
@@ -37,14 +37,19 @@ export const FileBrowser = forwardRef(({onSelectFile}, ref) => {
37
37
  const selectNode = useCallback((node) => {
38
38
  dispatch({ type: "SELECT_NODE", payload: node });
39
39
  }, []);
40
+
41
+ const getGroupUid = useCallback((id) => {
42
+ return state.uid;
43
+ }, [state]);
40
44
 
41
45
  const api = useMemo(() => {
42
46
  return {
43
47
  state,
44
48
  addFileTree,
45
- selectNode
49
+ selectNode,
50
+ getGroupUid
46
51
  };
47
- }, [state, addFileTree, selectNode]);
52
+ }, [state, addFileTree, selectNode, getGroupUid]);
48
53
 
49
54
  useImperativeHandle(ref, () => api, [api]);
50
55
 
package/src/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./components/StackList";
2
2
  export * from "./components/FlowDiagram";
3
3
  export * from "./components/FileBrowser";
4
- export * from "./components/Editor";
4
+ export * from "./components/Editor";
5
+ export * from "./components/BehavioralGraphBuilder";
@@ -0,0 +1,42 @@
1
+ import { useCallback, useState, useEffect } from "react";
2
+ import { BehavioralGraphBuilder } from "../components/BehavioralGraphBuilder";
3
+ import "./BehavioralGraphBuilderStories.scss"
4
+ import { ToolBar } from "./components/ToolBar/ToolBar";
5
+ import { useArgs } from "@storybook/preview-api";
6
+
7
+ export default {
8
+ title: 'BehavioralGraphBuilder',
9
+ component: BehavioralGraphBuilder,
10
+ argTypes: {
11
+ }
12
+ };
13
+
14
+ const Template = (args) => {
15
+ const [, updateArgs] = useArgs();
16
+
17
+ const [activeTool, setActiveTool] = useState("select");
18
+
19
+ const selectTool = useCallback((tool) => {
20
+ updateArgs({activeTool : tool});
21
+ }, [activeTool, setActiveTool]);
22
+
23
+ useEffect(() => {
24
+ updateArgs({activeTool : activeTool});
25
+ }, []);
26
+
27
+ return (
28
+ <div className="graphBuilderRootContainer">
29
+ <div className="toolbar">
30
+ <ToolBar onSelectTool={selectTool}/>
31
+ </div>
32
+ <div className="flow">
33
+ <BehavioralGraphBuilder {...args}/>
34
+ </div>
35
+ </div>
36
+ )
37
+ }
38
+
39
+ export const Default = Template.bind({})
40
+
41
+ Default.args = {
42
+ }
@@ -0,0 +1,17 @@
1
+ .graphBuilderRootContainer {
2
+ position: absolute;
3
+ top:0;
4
+ bottom:0;
5
+ left:0;
6
+ right:0;
7
+ display:flex;
8
+ flex-direction: row;
9
+ }
10
+
11
+ .toolbar {
12
+ width:40px;
13
+ }
14
+
15
+ .flow {
16
+ flex-grow: 1;
17
+ }
@@ -0,0 +1,57 @@
1
+ import React, { useState } from "react";
2
+
3
+ import {
4
+ Cursor,
5
+ Square,
6
+ NodePlus,
7
+ Trash
8
+ } from "react-bootstrap-icons";
9
+
10
+ import "./ToolBar.scss";
11
+
12
+ ToolBar.propTypes = {};
13
+
14
+ /**
15
+ * Toolbar Component
16
+ * @return {JSX.Element}
17
+ */
18
+ export function ToolBar({ onSelectTool }) {
19
+ const [selectedTool, setSelectedTool] = useState("select");
20
+
21
+ const selectTool = (tool) => {
22
+ setSelectedTool(tool);
23
+ onSelectTool(tool);
24
+ };
25
+
26
+ return (
27
+ <div className="toolbarWrapper">
28
+ <div className="toolbarContainer">
29
+ <Cursor
30
+ onClick={(e) => selectTool("select")}
31
+ style={{color: selectedTool === "select" ? "white": "grey"}}
32
+ title="Select"
33
+ className="icon"
34
+ />
35
+ <Square
36
+ onClick={(e) => selectTool("drop")}
37
+ style={{color: selectedTool === "drop" ? "white": "grey"}}
38
+ title="Add Node"
39
+ className="icon"
40
+ />
41
+ <NodePlus
42
+ onClick={(e) => selectTool("connect")}
43
+ style={{color: selectedTool === "connect" ? "white": "grey"}}
44
+ title="Connect Node"
45
+ className="icon"
46
+ />
47
+ <Trash
48
+ onClick={(e) => selectTool("delete")}
49
+ style={{color: selectedTool === "delete" ? "white": "grey"}}
50
+ title="Delete Node"
51
+ className="icon"
52
+ />
53
+ </div>
54
+ <div className="toolbarContainer bottom"></div>
55
+ </div>
56
+ );
57
+ }
@@ -0,0 +1,29 @@
1
+ .toolbarWrapper {
2
+ display:flex;
3
+ flex-direction: column;
4
+ justify-content:space-around;
5
+ height: 100%;
6
+ background-color: #333333;
7
+ }
8
+
9
+ .toolbarContainer {
10
+ display: flex;
11
+ flex-direction: column;
12
+ gap: 15px;
13
+ width:100%;
14
+ align-items: center;
15
+ padding: 10px 0;
16
+ }
17
+
18
+ .toolbarWrapper > .bottom {
19
+ margin-top: auto;
20
+ }
21
+
22
+ .toolbarContainer > .icon {
23
+ color:grey;
24
+ }
25
+
26
+ .toolbarContainer > .icon:hover {
27
+ color:white;
28
+ cursor:pointer;
29
+ }