@@ -10,36 +10,185 @@ import { FlowModeler } from "../src/index";
1010# FlowModeler
1111## Editing Structure
1212
13+ <Props of = { FlowModeler } exclude = { Object .keys (FlowModeler .propTypes ).filter (propName => propName !== " onChange" && propName !== " editActions" )} />
14+
1315Enable editing by providing an ` onChange ` callback function that will receive the updated ` flow ` after each structural change.
1416This should then be stored in some external (e.g. Redux) store/state and provided as ` flow ` prop.
1517
16- export const externalState = {
17- flow: {
18- firstElementId: " 1" ,
19- elements: {
20- " 1" : { data: { label: " First" }, nextElementId: " 2" },
21- " 2" : { data: { label: " Second" }, nextElementId: " 3" },
22- " 3" : { data: { label: " Condition" }, nextElements: [
23- { id: " 4.1" , conditionData: { label: " Fulfilled" } },
24- { id: " 4.2" , conditionData: { label: " Not fulfilled" } }] },
25- " 4.1" : { data: { label: " Option 1" }, nextElementId: " 5" },
26- " 4.2" : { data: { label: " Option 2" }, nextElementId: " 5" },
27- " 5" : { data: { label: " Follow-Up" }}
28- }
18+ export const initialState = {
19+ firstElementId: " 1" ,
20+ elements: {
21+ " 1" : { data: { label: " First" }, nextElementId: " 2" },
22+ " 2" : { data: { label: " Second" }, nextElementId: " 3" },
23+ " 3" : { data: { label: " Condition" }, nextElements: [
24+ { id: " 4.1" , conditionData: { label: " Fulfilled" } },
25+ { id: " 4.2" , conditionData: { label: " Not fulfilled" } }] },
26+ " 4.1" : { data: { label: " Option 1" }, nextElementId: " 5" },
27+ " 4.2" : { data: { label: " Option 2" }, nextElementId: " 5" },
28+ " 5" : { data: { label: " Follow-Up" }}
2929 }
3030}
3131
32+ export const externalState = {
33+ flow1: initialState ,
34+ flow2: initialState ,
35+ flow3: initialState ,
36+ flow4: initialState ,
37+ flow5: initialState
38+ }
39+
3240<Preview >
3341 <Story name = " structure" >
3442 <FlowModeler
35- flow = { externalState .flow }
43+ flow = { externalState .flow1 }
44+ renderStep = { ({ data }) => <span >{ data && data .label } </span >}
45+ renderGatewayConditionType = { ({ data }) => <label >{ data && data .label } </label >}
46+ renderGatewayConditionValue = { ({ data }) => <label >{ data && data .label } </label >}
47+ onChange = { ({ changedFlow }) => {
48+ externalState .flow1 = changedFlow ;
49+ forceReRender ();
50+ }}
51+ />
52+ </Story >
53+ </Preview >
54+
55+ If the elements themselves are supposed to be editable, you'll have to use the appropriate components within the
56+ ` renderStep() ` /` renderGatewayConditionType() ` /` renderGatewayConditionValue() ` functions and take care of the update of the state yourself.
57+
58+ <Preview >
59+ <Story name = " elements" >
60+ <FlowModeler
61+ flow = { externalState .flow2 }
62+ renderStep = { ({ id , data }) => (
63+ <input
64+ type = " text"
65+ value = { data ? data .label : " " }
66+ onChange = { (event ) => {
67+ externalState .flow2 .elements [id ].data = ({ label: event .target .value });
68+ forceReRender ();
69+ }}
70+ />
71+ )}
72+ renderGatewayConditionType = { ({ id , data }) => (
73+ <select
74+ value = { data ? data .label : " " }
75+ onChange = { (event ) => {
76+ externalState .flow2 .elements [id ].data = ({ label: event .target .value });
77+ event .stopPropagation ();
78+ forceReRender ();
79+ }} >
80+ <option value = " " >n/a</option >
81+ <option value = " Condition" >Condition</option >
82+ <option value = " Check" >Check</option >
83+ </select >
84+ )}
85+ renderGatewayConditionValue = { ({ precedingElement , branchIndex , data }) => (
86+ <input
87+ type = " text"
88+ value = { data ? data .label : " " }
89+ onChange = { (event ) => {
90+ externalState .flow2 .elements [precedingElement .id ].nextElements [branchIndex ].conditionData = ({ label: event .target .value });
91+ forceReRender ();
92+ }}
93+ />
94+ )}
95+ onChange = { ({ changedFlow }) => {
96+ externalState .flow2 = changedFlow ;
97+ forceReRender ();
98+ }}
99+ />
100+ </Story >
101+ </Preview >
102+
103+ ----
104+
105+ You can additionally customise the ` editActions ` to align the ` <FlowModeler> ` with the rest of your application and/or offer meaningful defaults.
106+
107+ Via the ` editActions ` you can specify your own ` className ` for each action in the edit context menu – e.g. enabling you to use your own icons.
108+ This completely replaces the standard styles (via which the standard symbols are associated),
109+ i.e. to build on-top of the existing styles, you'll need to include the standard css classes again.
110+ Additionally, you may specify an alternative ` title ` as tool-tip for each offered action.
111+
112+ <Preview >
113+ <Story name = " menu styles and tool-tips" >
114+ <FlowModeler
115+ flow = { externalState .flow3 }
36116 renderStep = { ({ data }) => <span >{ data && data .label } </span >}
37117 renderGatewayConditionType = { ({ data }) => <label >{ data && data .label } </label >}
38118 renderGatewayConditionValue = { ({ data }) => <label >{ data && data .label } </label >}
39119 onChange = { ({ changedFlow }) => {
40- externalState .flow = changedFlow ;
120+ externalState .flow3 = changedFlow ;
41121 forceReRender ();
42122 }}
123+ editActions = { {
124+ addDivergingBranch: { className: " menu-item add-branch customised-color-1" , title: " Add Branch" },
125+ addFollowingStepElement: { className: " menu-item add-step customised-color-2" , title: " Add Step Element" },
126+ addFollowingDivergingGateway: { className: " menu-item add-gateway customised-color-3" , title: " Add Diverging Gateway" },
127+ changeNextElement: { className: " menu-item change-next customised-color-4" , title: " Change next Element to…" },
128+ removeElement: { className: " menu-item remove customised-color-5" , title: " Remove" }
129+ }}
130+ />
131+ </Story >
132+ </Preview >
133+
134+ Via the ` editActions ` you can also influence the default contents of newly added step, diverging gateway or branches of diverging gateways
135+ – through the corresponding callbacks.
136+
137+ <Preview >
138+ <Story name = " custom contents on creation" >
139+ <FlowModeler
140+ flow = { externalState .flow4 }
141+ renderStep = { ({ data }) => <span >{ data && data .label } </span >}
142+ renderGatewayConditionType = { ({ data }) => <label >{ data && data .label } </label >}
143+ renderGatewayConditionValue = { ({ data }) => <label >{ data && data .label } </label >}
144+ onChange = { ({ changedFlow }) => {
145+ externalState .flow4 = changedFlow ;
146+ forceReRender ();
147+ }}
148+ editActions = { {
149+ addDivergingBranch: { getBranchConditionData : (gateway ) => ({ label: ` custom branch ${1 + gateway .followingBranches .length } ` }) },
150+ addFollowingStepElement: { getStepData : () => ({ label: " custom element" }) },
151+ addFollowingDivergingGateway: {
152+ getGatewayData : () => ({ label: " custom gateway" }),
153+ getBranchConditionData : () => [{ label: " branch 1" }, { label: " branch 2" }, { label: " branch 3" }]
154+ }
155+ }}
156+ />
157+ </Story >
158+ </Preview >
159+
160+ One more setting in the ` editActions ` is to answer in a particular context the question: ` isActionAllowed ` ? This allows you to preserve certain parts
161+ of the model as-is while still allowing other parts to be changed. Or to completely disable certain actions, e.g. not even allow any gateways or
162+ additional branches to be added.
163+
164+ <Preview >
165+ <Story name = " disabled actions" >
166+ <FlowModeler
167+ flow = { externalState .flow5 }
168+ renderStep = { ({ data }) => <span >{ data && data .label } </span >}
169+ renderGatewayConditionType = { ({ data }) => <label >{ data && data .label } </label >}
170+ renderGatewayConditionValue = { ({ data }) => <label >{ data && data .label } </label >}
171+ onChange = { ({ changedFlow }) => {
172+ externalState .flow5 = changedFlow ;
173+ forceReRender ();
174+ }}
175+ editActions = { {
176+ addDivergingBranch: {
177+ isActionAllowed : (gateway ) => (gateway .followingBranches .length < 3 )
178+ },
179+ addFollowingStepElement: {
180+ isActionAllowed : (prior ) => prior .type !== " step" || prior .followingElement .type !== " step"
181+ },
182+ addFollowingDivergingGateway: {
183+ isActionAllowed : (prior ) => prior .type !== " conv-gw"
184+ },
185+ changeNextElement: {
186+ isActionAllowed : () => false
187+ },
188+ removeElement: {
189+ isActionAllowed : () => true
190+ }
191+ }}
43192 />
44193 </Story >
45194</Preview >
0 commit comments