sample-ui-component-library 0.0.39-dev → 0.0.40-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/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/BehavioralGraphBuilder/BehavioralGraphBuilder.jsx +23 -150
- package/src/components/BehavioralGraphBuilder/DagreLayout.js +33 -0
- package/src/components/BehavioralGraphBuilder/Flow.jsx +164 -0
- package/src/components/BehavioralGraphBuilder/helper.js +35 -0
- package/src/stories/BehavioralGraphBuilder.stories.js +26 -2
- package/src/stories/data/Designs/simple_design_temp.json +1 -0
package/package.json
CHANGED
|
@@ -1,159 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import "@xyflow/react/dist/style.css";
|
|
1
|
+
import { Flow } from "./Flow";
|
|
2
|
+
import { ReactFlowProvider } from "@xyflow/react";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { designToReactFlowElements } from "./helper";
|
|
5
|
+
|
|
6
|
+
export const BehavioralGraphBuilder = ({
|
|
7
|
+
activeTool,
|
|
8
|
+
onBehaviorSelect,
|
|
9
|
+
onAddBehavior,
|
|
10
|
+
onAddEdge,
|
|
11
|
+
design,
|
|
12
|
+
}) => {
|
|
13
|
+
const [initialElements, setInitialElements] = useState(null);
|
|
15
14
|
|
|
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
15
|
useEffect(() => {
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
);
|
|
16
|
+
if (design) {
|
|
17
|
+
setInitialElements(designToReactFlowElements(design));
|
|
110
18
|
}
|
|
111
|
-
}, [
|
|
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
|
-
};
|
|
19
|
+
}, [design]);
|
|
152
20
|
|
|
153
|
-
export const BehavioralGraphBuilder = ({ activeTool }) => {
|
|
154
21
|
return (
|
|
155
22
|
<ReactFlowProvider>
|
|
156
|
-
<Flow
|
|
23
|
+
<Flow
|
|
24
|
+
initialElements={initialElements}
|
|
25
|
+
activeTool={activeTool}
|
|
26
|
+
onBehaviorSelect={onBehaviorSelect}
|
|
27
|
+
onAddBehavior={onAddBehavior}
|
|
28
|
+
onAddEdge={onAddEdge}
|
|
29
|
+
/>
|
|
157
30
|
</ReactFlowProvider>
|
|
158
31
|
);
|
|
159
32
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
import Dagre from '@dagrejs/dagre';
|
|
3
|
+
|
|
4
|
+
export const getLayoutedElements = (nodes, edges, options) => {
|
|
5
|
+
// Reference: https://reactflow.dev/learn/layouting/layouting
|
|
6
|
+
|
|
7
|
+
const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
|
|
8
|
+
g.setGraph({ rankdir: options.direction, ranksep: 100, nodesep: 170});
|
|
9
|
+
|
|
10
|
+
edges.forEach((edge) => g.setEdge(edge.source, edge.target));
|
|
11
|
+
nodes.forEach((node) =>
|
|
12
|
+
g.setNode(node.id, {
|
|
13
|
+
...node,
|
|
14
|
+
width: node.measured?.width ?? 0,
|
|
15
|
+
height: node.measured?.height ?? 0,
|
|
16
|
+
}),
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
Dagre.layout(g);
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
nodes: nodes.map((node) => {
|
|
23
|
+
const position = g.node(node.id);
|
|
24
|
+
// We are shifting the dagre node position (anchor=center center) to the top left
|
|
25
|
+
// so it matches the React Flow node anchor point (top left).
|
|
26
|
+
const x = position.x - (node.measured?.width ?? 0) / 2;
|
|
27
|
+
const y = position.y - (node.measured?.height ?? 0) / 2;
|
|
28
|
+
|
|
29
|
+
return { ...node, position: { x, y } };
|
|
30
|
+
}),
|
|
31
|
+
edges,
|
|
32
|
+
};
|
|
33
|
+
};
|
|
@@ -0,0 +1,164 @@
|
|
|
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
|
+
BezierEdge ,
|
|
12
|
+
applyNodeChanges,
|
|
13
|
+
applyEdgeChanges
|
|
14
|
+
} from "@xyflow/react";
|
|
15
|
+
import "@xyflow/react/dist/style.css";
|
|
16
|
+
|
|
17
|
+
import { getLayoutedElements } from "./DagreLayout.js";
|
|
18
|
+
|
|
19
|
+
const edgeTypes = {
|
|
20
|
+
bezier: BezierEdge,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const NODE_WIDTH = 150;
|
|
24
|
+
const NODE_HEIGHT = 40;
|
|
25
|
+
export const Flow = ({ activeTool, onBehaviorSelect, onAddBehavior, onAddEdge, initialElements }) => {
|
|
26
|
+
const { screenToFlowPosition } = useReactFlow();
|
|
27
|
+
const [nodes, setNodes] = useState([]);
|
|
28
|
+
const [edges, setEdges] = useState([]);
|
|
29
|
+
const [ghostNode, setGhostNode] = useState(null);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!initialElements) return;
|
|
33
|
+
const layouted = getLayoutedElements(
|
|
34
|
+
initialElements.nodes,
|
|
35
|
+
initialElements.edges,
|
|
36
|
+
{direction:"TB"}
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
setNodes([...layouted.nodes]);
|
|
40
|
+
setEdges([...layouted.edges]);
|
|
41
|
+
}, [initialElements]);
|
|
42
|
+
|
|
43
|
+
// Callbacks to apply node changes
|
|
44
|
+
const onNodesChange = useCallback((changes) => {
|
|
45
|
+
setNodes((nds) => applyNodeChanges(changes, nds));
|
|
46
|
+
}, []);
|
|
47
|
+
const onEdgesChange = useCallback((changes) => {
|
|
48
|
+
setEdges((eds) => applyEdgeChanges(changes, eds));
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
// Console messages for debugging
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
console.log("Active Tool:", activeTool);
|
|
54
|
+
}, [activeTool]);
|
|
55
|
+
|
|
56
|
+
// Callback for mouse pane move
|
|
57
|
+
const onPaneMouseMove = useCallback( (event) => {
|
|
58
|
+
const pos = screenToFlowPosition({
|
|
59
|
+
x: event.clientX,
|
|
60
|
+
y: event.clientY,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (activeTool === "drop") {
|
|
64
|
+
setGhostNode({
|
|
65
|
+
id: "ghost",
|
|
66
|
+
position: {
|
|
67
|
+
x: pos.x - NODE_WIDTH / 2,
|
|
68
|
+
y: pos.y - NODE_HEIGHT / 2,
|
|
69
|
+
},
|
|
70
|
+
data: { label: "Behavior" },
|
|
71
|
+
draggable: false,
|
|
72
|
+
selectable: false,
|
|
73
|
+
style: {
|
|
74
|
+
opacity: 0.5,
|
|
75
|
+
pointerEvents: "none",
|
|
76
|
+
width: NODE_WIDTH,
|
|
77
|
+
height: NODE_HEIGHT,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}, [activeTool]);
|
|
82
|
+
|
|
83
|
+
// Callback for when mouse leaves pane
|
|
84
|
+
const onPaneMouseLeave = useCallback(() => {
|
|
85
|
+
setGhostNode(null);
|
|
86
|
+
}, []);
|
|
87
|
+
|
|
88
|
+
// Callback for pane click
|
|
89
|
+
const onPaneClick = useCallback((event, node) => {
|
|
90
|
+
if (activeTool === "drop") {
|
|
91
|
+
if (!ghostNode) return;
|
|
92
|
+
setNodes((nds) => [
|
|
93
|
+
...nds,
|
|
94
|
+
{
|
|
95
|
+
...ghostNode,
|
|
96
|
+
draggable: true,
|
|
97
|
+
selectable: true,
|
|
98
|
+
id: crypto.randomUUID(),
|
|
99
|
+
style: {},
|
|
100
|
+
},
|
|
101
|
+
]);
|
|
102
|
+
onAddBehavior(ghostNode);
|
|
103
|
+
setGhostNode(null);
|
|
104
|
+
}
|
|
105
|
+
}, [ghostNode, activeTool, onAddBehavior]);
|
|
106
|
+
|
|
107
|
+
// Callback for node click to delete it (if tool is active)
|
|
108
|
+
const onNodeClick = useCallback((event, node) => {
|
|
109
|
+
if (activeTool === "delete") {
|
|
110
|
+
setNodes((nds) => nds.filter((n) => n.id !== node.id));
|
|
111
|
+
setEdges((eds) =>
|
|
112
|
+
eds.filter((e) => e.source !== node.id && e.target !== node.id)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}, [activeTool, onBehaviorSelect]);
|
|
116
|
+
|
|
117
|
+
// Callback for edge click to delete it (if tool is active)
|
|
118
|
+
const onEdgeClick = useCallback((event, edge) => {
|
|
119
|
+
if (activeTool === "delete") {
|
|
120
|
+
setEdges((eds) => eds.filter((e) => e.id !== edge.id));
|
|
121
|
+
}
|
|
122
|
+
}, [activeTool]);
|
|
123
|
+
|
|
124
|
+
const onSelectionChange = useCallback((selection) => {
|
|
125
|
+
if (selection.nodes.length > 0) {
|
|
126
|
+
onBehaviorSelect(selection.nodes[0]);
|
|
127
|
+
}
|
|
128
|
+
}, [ onBehaviorSelect]);
|
|
129
|
+
|
|
130
|
+
// Callback for when edge is connected, nodesConnectable={activeTool === "connect"}
|
|
131
|
+
// is used to determine connectability
|
|
132
|
+
const onConnect = useCallback((connection) => {
|
|
133
|
+
const edge = {
|
|
134
|
+
...connection,
|
|
135
|
+
id: crypto.randomUUID(),
|
|
136
|
+
type: "smoothstep",
|
|
137
|
+
animated: true,
|
|
138
|
+
};
|
|
139
|
+
setEdges((eds) => [...eds, edge]);
|
|
140
|
+
onAddEdge(edge);
|
|
141
|
+
}, [activeTool, onAddEdge]);
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<ReactFlow
|
|
145
|
+
nodes={ghostNode ? [...nodes, ghostNode] : nodes}
|
|
146
|
+
edges={edges}
|
|
147
|
+
onPaneMouseMove={onPaneMouseMove}
|
|
148
|
+
onPaneMouseLeave={onPaneMouseLeave}
|
|
149
|
+
onPaneClick={onPaneClick}
|
|
150
|
+
onConnect={onConnect}
|
|
151
|
+
onNodesChange={onNodesChange}
|
|
152
|
+
onEdgesChange={onEdgesChange}
|
|
153
|
+
onNodeClick={onNodeClick}
|
|
154
|
+
onEdgeClick={onEdgeClick}
|
|
155
|
+
colorMode={"dark"}
|
|
156
|
+
edgeTypes={edgeTypes}
|
|
157
|
+
onSelectionChange={onSelectionChange}
|
|
158
|
+
fitView
|
|
159
|
+
>
|
|
160
|
+
<Background />
|
|
161
|
+
<Controls />
|
|
162
|
+
</ReactFlow>
|
|
163
|
+
);
|
|
164
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* Converts a DAL design specification object into React Flow elements (nodes and edges).
|
|
4
|
+
* @param {Object} design
|
|
5
|
+
* @returns {Object} An object containing nodes and edges for React Flow
|
|
6
|
+
*/
|
|
7
|
+
export const designToReactFlowElements = (design) => {
|
|
8
|
+
|
|
9
|
+
let edges = [];
|
|
10
|
+
let nodes = [];
|
|
11
|
+
design.nodes.forEach((node) => {
|
|
12
|
+
nodes.push({
|
|
13
|
+
id: node.behavior.uid,
|
|
14
|
+
type: 'default',
|
|
15
|
+
data: { label: node.behavior.name },
|
|
16
|
+
position: { x: 10, y: 10 },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
node.goToBehaviors.forEach((goTo) => {
|
|
20
|
+
edges.push({
|
|
21
|
+
id: `${node.behavior.uid}->${goTo.uid}`,
|
|
22
|
+
type: "bezier",
|
|
23
|
+
source: node.behavior.uid,
|
|
24
|
+
target: goTo.uid,
|
|
25
|
+
animated: true
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
nodes: nodes,
|
|
32
|
+
edges: edges
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { useCallback, useState, useEffect } from "react";
|
|
2
2
|
import { BehavioralGraphBuilder } from "../components/BehavioralGraphBuilder";
|
|
3
|
-
import "./BehavioralGraphBuilderStories.scss"
|
|
4
3
|
import { ToolBar } from "./components/ToolBar/ToolBar";
|
|
5
4
|
import { useArgs } from "@storybook/preview-api";
|
|
5
|
+
import { action } from "@storybook/addon-actions";
|
|
6
|
+
|
|
7
|
+
import design from "./data/Designs/simple_design_temp.json";
|
|
8
|
+
import "./BehavioralGraphBuilderStories.scss"
|
|
6
9
|
|
|
7
10
|
export default {
|
|
8
11
|
title: 'BehavioralGraphBuilder',
|
|
@@ -19,9 +22,29 @@ const Template = (args) => {
|
|
|
19
22
|
const selectTool = useCallback((tool) => {
|
|
20
23
|
updateArgs({activeTool : tool});
|
|
21
24
|
}, [activeTool, setActiveTool]);
|
|
25
|
+
|
|
26
|
+
const onBehaviorSelect = useCallback((behavior) => {
|
|
27
|
+
action('Selected Behavior:')(behavior);
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
const onAddBehavior = useCallback((behavior) => {
|
|
31
|
+
action('Added Behavior:')(behavior);
|
|
32
|
+
}, []);
|
|
33
|
+
|
|
34
|
+
const onAddEdge = useCallback((edge) => {
|
|
35
|
+
action('Added Edge:')(edge);
|
|
36
|
+
}, []);
|
|
22
37
|
|
|
23
38
|
useEffect(() => {
|
|
24
|
-
updateArgs(
|
|
39
|
+
updateArgs(
|
|
40
|
+
{
|
|
41
|
+
activeTool: activeTool,
|
|
42
|
+
design: design,
|
|
43
|
+
onBehaviorSelect: onBehaviorSelect,
|
|
44
|
+
onAddBehavior: onAddBehavior,
|
|
45
|
+
onAddEdge: onAddEdge
|
|
46
|
+
}
|
|
47
|
+
);
|
|
25
48
|
}, []);
|
|
26
49
|
|
|
27
50
|
return (
|
|
@@ -39,4 +62,5 @@ const Template = (args) => {
|
|
|
39
62
|
export const Default = Template.bind({})
|
|
40
63
|
|
|
41
64
|
Default.args = {
|
|
65
|
+
design: design
|
|
42
66
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"uid":"60e79b4f-fc4b-4237-989f-e1b1559c2ee9","type":5,"nodes":[{"uid":"08a4e18c-f293-4ba1-be0a-c8451b50e819","type":6,"goToBehaviors":[{"uid":"35a2759b-e0df-47db-93b7-1f18c216922d","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AcceptBookFromUser"}],"behavior":{"uid":"a873c4ba-3a63-4097-80df-46ed361f2e26","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AcceptChoiceToAddBookToBasket"}},{"uid":"db74773a-2f47-4873-b66f-08da9aaf196f","type":6,"goToBehaviors":[{"uid":"ac522716-13d2-48e5-b241-f2366b37efdb","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AddBookToBasket"}],"behavior":{"uid":"35a2759b-e0df-47db-93b7-1f18c216922d","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AcceptBookFromUser"}},{"uid":"31bba9d3-9af2-41bc-bd70-d757f62b40c6","type":6,"goToBehaviors":[{"uid":"d4ec5fe5-acd2-4a8b-8e09-9ea3d5212a1f","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"GenerateAuditReport"}],"behavior":{"uid":"53cebd65-5c19-4dde-8e50-4acc581dc166","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AcceptChoiceToAuditLibrary"}},{"uid":"836fdec5-db3e-4770-95fe-70c2d2c1261c","type":6,"goToBehaviors":[{"uid":"3e0b400c-f92a-48b5-b67b-e65d69f18a21","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"HandAuditToUser"}],"behavior":{"uid":"d4ec5fe5-acd2-4a8b-8e09-9ea3d5212a1f","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"GenerateAuditReport"}},{"uid":"f79c407e-66fc-4d64-bcce-4a1c4841d098","type":6,"goToBehaviors":[{"uid":"1c75d75c-8970-4dd1-b91e-c79590354bd9","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"GetBookFromBasket"}],"behavior":{"uid":"b39dfb15-d121-4ded-aa29-d317149308e9","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AcceptChoiceToPlaceBooksOnShelf"}},{"uid":"0e266610-d498-4857-9249-7e6c98a1077f","type":6,"goToBehaviors":[{"uid":"7e80caf3-7fa9-4684-af89-fe2556addb87","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"GetFirstLetterOfBookName"}],"behavior":{"uid":"1c75d75c-8970-4dd1-b91e-c79590354bd9","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"GetBookFromBasket"}},{"uid":"b009ee09-e037-4864-99a2-15f80187623f","type":6,"goToBehaviors":[{"uid":"457332a3-3c79-4c04-83c7-2b5b0539c0da","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"CreateSlotOnBookShelf"}],"behavior":{"uid":"7e80caf3-7fa9-4684-af89-fe2556addb87","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"GetFirstLetterOfBookName"}},{"uid":"b2acd6d3-d6b8-41de-b8a1-f22b4db4fcbe","type":6,"goToBehaviors":[{"uid":"a666f210-c00c-48bc-a13f-aea72f068e37","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AddBookToShelf"}],"behavior":{"uid":"7e80caf3-7fa9-4684-af89-fe2556addb87","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"GetFirstLetterOfBookName"}},{"uid":"3baca40a-9584-4ba7-be1b-235a660ca472","type":6,"goToBehaviors":[{"uid":"a666f210-c00c-48bc-a13f-aea72f068e37","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AddBookToShelf"}],"behavior":{"uid":"457332a3-3c79-4c04-83c7-2b5b0539c0da","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"CreateSlotOnBookShelf"}},{"uid":"14acf45d-ca77-48f4-9ff0-5692133a6258","type":6,"goToBehaviors":[{"uid":"1c75d75c-8970-4dd1-b91e-c79590354bd9","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"GetBookFromBasket"}],"behavior":{"uid":"a666f210-c00c-48bc-a13f-aea72f068e37","type":1,"participants":[],"abstractionIds":[],"invalidWorldState":false,"name":"AddBookToShelf"}}]}
|