@flowerforce/flower-react 3.0.0
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/CHANGELOG.md +3 -0
- package/LICENSE +15 -0
- package/README.md +771 -0
- package/dist/index.cjs.d.ts +1 -0
- package/dist/index.cjs.default.js +1 -0
- package/dist/index.cjs.js +1134 -0
- package/dist/index.cjs.mjs +2 -0
- package/dist/index.esm.d.ts +1 -0
- package/dist/index.esm.js +1113 -0
- package/dist/src/components/Flower.d.ts +9 -0
- package/dist/src/components/FlowerAction.d.ts +4 -0
- package/dist/src/components/FlowerComponent.d.ts +4 -0
- package/dist/src/components/FlowerField.d.ts +4 -0
- package/dist/src/components/FlowerFlow.d.ts +4 -0
- package/dist/src/components/FlowerNavigate/WrapperComponent.d.ts +4 -0
- package/dist/src/components/FlowerNavigate/index.d.ts +4 -0
- package/dist/src/components/FlowerNavigate/useFlowerNavigate.d.ts +7 -0
- package/dist/src/components/FlowerNode.d.ts +4 -0
- package/dist/src/components/FlowerRoute.d.ts +4 -0
- package/dist/src/components/FlowerRule.d.ts +4 -0
- package/dist/src/components/FlowerServer.d.ts +4 -0
- package/dist/src/components/FlowerStart.d.ts +4 -0
- package/dist/src/components/FlowerValue.d.ts +4 -0
- package/dist/src/components/types/DefaultNode.d.ts +8 -0
- package/dist/src/components/types/FlowerComponent.d.ts +4 -0
- package/dist/src/components/types/FlowerField.d.ts +30 -0
- package/dist/src/components/types/FlowerFlow.d.ts +4 -0
- package/dist/src/components/types/FlowerHooks.d.ts +26 -0
- package/dist/src/components/types/FlowerNavigate.d.ts +37 -0
- package/dist/src/components/types/FlowerNode.d.ts +4 -0
- package/dist/src/components/types/FlowerProvider.d.ts +13 -0
- package/dist/src/components/types/FlowerRoute.d.ts +8 -0
- package/dist/src/components/types/FlowerRule.d.ts +12 -0
- package/dist/src/components/types/FlowerServer.d.ts +7 -0
- package/dist/src/components/types/FlowerValue.d.ts +11 -0
- package/dist/src/components/useFlower.d.ts +3 -0
- package/dist/src/components/useFlowerForm.d.ts +3 -0
- package/dist/src/context.d.ts +9 -0
- package/dist/src/index.d.ts +20 -0
- package/dist/src/provider.d.ts +19 -0
- package/dist/src/reducer.d.ts +7 -0
- package/dist/src/selectors.d.ts +1377 -0
- package/dist/src/utils.d.ts +10 -0
- package/package.json +49 -0
package/README.md
ADDED
@@ -0,0 +1,771 @@
|
|
1
|
+
# Flower React
|
2
|
+
|
3
|
+
<a alt="Flower logo" href="https://flower.stackhouse.dev/" target="_blank" rel="noreferrer"><img src="https://flower.stackhouse.dev/_next/static/media/flower-logo.bb32f863.svg" width="50"></a>
|
4
|
+
|
5
|
+
Flower React is a front-end development library built on top of Flower Core, specifically designed for React applications. It seamlessly integrates Flower's powerful capabilities into React projects, providing a user-friendly interface for creating, modifying, and monitoring workflows.
|
6
|
+
|
7
|
+
For more info [flower.stackhouse.dev/](https://flower.stackhouse.dev/)
|
8
|
+
|
9
|
+
<!--  -->
|
10
|
+
|
11
|
+
## Features
|
12
|
+
|
13
|
+
- **Workflow Management**: Comprehensive API for creating, updating, and managing workflows programmatically.
|
14
|
+
- **Node and Connection Handling**: Functions to manage nodes and connections, including adding, removing, and editing.
|
15
|
+
- **State Management**: Built-in state management to keep track of workflow changes and updates.
|
16
|
+
- **Event System**: Customizable event handling to respond to user interactions and changes within the workflow.
|
17
|
+
- **Serialization**: Convert workflows to and from different formats (e.g., JSON) for easy storage and retrieval.
|
18
|
+
- **Validation**: Ensure workflows follow predefined rules and constraints to maintain integrity.
|
19
|
+
- **Form Validation**: Built-in functionalities to validate form inputs within nodes, ensuring data integrity and correctness.
|
20
|
+
- **History Management**: Internal management of flow history, tracking node traversal and changes for debugging and visualization purposes.
|
21
|
+
|
22
|
+
|
23
|
+
### Installation
|
24
|
+
|
25
|
+
Flower React can be installed via npm or yarn for use in any JavaScript project.
|
26
|
+
|
27
|
+
### Using npm
|
28
|
+
|
29
|
+
1. Ensure you have Node.js and npm installed on your system.
|
30
|
+
2. Run the following command to install the library:
|
31
|
+
|
32
|
+
```bash
|
33
|
+
#NPM
|
34
|
+
npm install @flowerforce/flower-core
|
35
|
+
```
|
36
|
+
|
37
|
+
### Using yarn
|
38
|
+
|
39
|
+
1. Ensure you have yarn installed on your system.
|
40
|
+
2. Run the following command to install the library:
|
41
|
+
|
42
|
+
```bash
|
43
|
+
#YARN
|
44
|
+
yarn add @flowerforce/flower-core
|
45
|
+
```
|
46
|
+
|
47
|
+
## Configuration
|
48
|
+
|
49
|
+
The **FlowerProvider** component wraps the entire application, providing a global context for managing the application flow.
|
50
|
+
|
51
|
+
```jsx
|
52
|
+
import React from 'react';
|
53
|
+
import Flower, { FlowerProvider } from '@flowerforce/flower-react';
|
54
|
+
|
55
|
+
function Root() {
|
56
|
+
return (
|
57
|
+
<FlowerProvider>
|
58
|
+
<App />
|
59
|
+
</FlowerProvider>
|
60
|
+
);
|
61
|
+
}
|
62
|
+
```
|
63
|
+
|
64
|
+
|
65
|
+
## How to use
|
66
|
+
### Simple Example
|
67
|
+
|
68
|
+
The **Flower** component defines an application flow with a specific name, which serves as a unique identifier for the flow. It is the main component for defining the application flow, accepting a required "name" property and an initialData field for prepopulating values.
|
69
|
+
|
70
|
+
The **FlowerNode** component represents a UI state or a step within the application flow. Transitions between nodes can be specified using the **to** object.
|
71
|
+
|
72
|
+
|
73
|
+
```jsx
|
74
|
+
import React from 'react'
|
75
|
+
import Flower, { FlowerNavigate, FlowerNode } from '@flowerforce/flower-react'
|
76
|
+
|
77
|
+
export const Page = () => {
|
78
|
+
return (
|
79
|
+
<Flower name="demo">
|
80
|
+
<FlowerNode id="step1"
|
81
|
+
to={{ step2: null }}>
|
82
|
+
...
|
83
|
+
|
84
|
+
<FlowerNavigate action="next">
|
85
|
+
<button>click me to go next</button>
|
86
|
+
</FlowerNavigate>
|
87
|
+
</FlowerNode>
|
88
|
+
|
89
|
+
<FlowerNode id="step2" to={{ step3: null }}>
|
90
|
+
...
|
91
|
+
|
92
|
+
<FlowerNavigate action="back">
|
93
|
+
<button>click me to go back</button>
|
94
|
+
</FlowerNavigate>
|
95
|
+
<FlowerNavigate action="next">
|
96
|
+
<button>click me to go next</button>
|
97
|
+
</FlowerNavigate>
|
98
|
+
</FlowerNode>
|
99
|
+
|
100
|
+
<FlowerNode id="step3">
|
101
|
+
...
|
102
|
+
<FlowerNavigate action="reset">
|
103
|
+
<button>Reset</button>
|
104
|
+
</FlowerNavigate>
|
105
|
+
</FlowerNode>
|
106
|
+
</Flower>
|
107
|
+
)
|
108
|
+
}
|
109
|
+
|
110
|
+
```
|
111
|
+
|
112
|
+
Edit on [codesandbox/](https://codesandbox.io/p/sandbox/flower-react-example-1-9wsjv7)
|
113
|
+
|
114
|
+
### Navigate with routes
|
115
|
+
|
116
|
+
Additionally, it's possible to navigate between nodes by defining specific routes.
|
117
|
+
|
118
|
+
```jsx
|
119
|
+
import React from 'react'
|
120
|
+
import Flower, { FlowerRoute, FlowerNavigate, FlowerNode } from '@flowerforce/flower-react'
|
121
|
+
|
122
|
+
export const Page = () => {
|
123
|
+
return (
|
124
|
+
<Flower name="demo">
|
125
|
+
<FlowerRoute id="start" to={{ step1: null }} /> {/* autonext */}
|
126
|
+
|
127
|
+
<FlowerNode id="step1"
|
128
|
+
to={{
|
129
|
+
stepOK: "onSuccess",
|
130
|
+
stepKO: "onError",
|
131
|
+
default: null
|
132
|
+
}}>
|
133
|
+
...
|
134
|
+
|
135
|
+
<FlowerNavigate action="next" route="onSuccess">
|
136
|
+
<button>click me to go on "stepOK"</button>
|
137
|
+
</FlowerNavigate>
|
138
|
+
|
139
|
+
<FlowerNavigate action="next" route="onError">
|
140
|
+
<button>click me to go on "stepKO"</button>
|
141
|
+
</FlowerNavigate>
|
142
|
+
|
143
|
+
<FlowerNavigate action="next">
|
144
|
+
<button>click me to go on "default" </button>
|
145
|
+
</FlowerNavigate>
|
146
|
+
</FlowerNode>
|
147
|
+
|
148
|
+
<FlowerNode id="stepOK">... </FlowerNode>
|
149
|
+
<FlowerNode id="stepKO">... </FlowerNode>
|
150
|
+
<FlowerNode id="default">... </FlowerNode>
|
151
|
+
|
152
|
+
</Flower>
|
153
|
+
)
|
154
|
+
}
|
155
|
+
|
156
|
+
```
|
157
|
+
Edit on [codesandbox/](https://codesandbox.io/p/sandbox/flower-react-example-1-forked-9k4kfk)
|
158
|
+
|
159
|
+
### Navigation State Rules
|
160
|
+
|
161
|
+
Flower has an internal state to control flow paths more advancedly, adding rules that determine one path over another. Below is an example of how a rule works.
|
162
|
+
|
163
|
+
In this example, we're passing the initialData object to the Flower component through the initialData prop. You can initialize this object with desired data, such as the value of skipStep2 that we set to true. When the Flower is initiated, it will use this initial data to establish the initial state of the flow.
|
164
|
+
|
165
|
+
The FlowerNode step1 connects to both step2 and step3. However, the rule states that if skipStep2 is true, it should go directly to step3.
|
166
|
+
|
167
|
+
```jsx
|
168
|
+
import React from 'react'
|
169
|
+
import Flower, { FlowerRoute, FlowerNavigate, FlowerNode } from '@flowerforce/flower-react'
|
170
|
+
|
171
|
+
export const Page = () => {
|
172
|
+
return (
|
173
|
+
<Flower name="demo" initialData={{ skipStep2: true }}>
|
174
|
+
<FlowerRoute id="start" to={{ step1: null }} />
|
175
|
+
|
176
|
+
<FlowerNode
|
177
|
+
id="step1"
|
178
|
+
to={{
|
179
|
+
step3: {
|
180
|
+
rules: { $and: [{ skipStep2: { $eq: true } }] },
|
181
|
+
},
|
182
|
+
step2: null
|
183
|
+
}}
|
184
|
+
>
|
185
|
+
...
|
186
|
+
|
187
|
+
<FlowerNavigate action="next">
|
188
|
+
<button>click me to go next</button>
|
189
|
+
</FlowerNavigate>
|
190
|
+
</FlowerNode>
|
191
|
+
|
192
|
+
<FlowerNode id="step2">...</FlowerNode>
|
193
|
+
|
194
|
+
<FlowerNode id="step3">...</FlowerNode>
|
195
|
+
|
196
|
+
</Flower>
|
197
|
+
)
|
198
|
+
}
|
199
|
+
|
200
|
+
```
|
201
|
+
Edit on [codesandbox/](https://codesandbox.io/p/sandbox/flower-react-example-1-forked-5c4rs4)
|
202
|
+
|
203
|
+
|
204
|
+
### Basic WRITE | READ State
|
205
|
+
|
206
|
+
To modify the internal state of Flower, besides passing initialData as a prop, we can always modify and read the state through the components **FlowerField** and **FlowerValue**.
|
207
|
+
|
208
|
+
*FlowerField* pass two props, onChange and value, to properly modify and read the value from the state of Flower.
|
209
|
+
*FlowerValue* pass value, to properly read the value from the state of Flower.
|
210
|
+
|
211
|
+
Here's an example of how it works:
|
212
|
+
|
213
|
+
```jsx
|
214
|
+
import React from 'react'
|
215
|
+
import Flower, { FlowerRoute, FlowerNavigate, FlowerNode, FlowerField, FlowerValue } from '@flowerforce/flower-react'
|
216
|
+
|
217
|
+
export const Page = () => {
|
218
|
+
return (
|
219
|
+
<Flower name="demo">
|
220
|
+
<FlowerNode
|
221
|
+
id="step1"
|
222
|
+
to={{
|
223
|
+
step3: {
|
224
|
+
rules={{ $and: [{ skipStep2: { $eq: true } }] }}
|
225
|
+
},
|
226
|
+
step2: null
|
227
|
+
}}
|
228
|
+
>
|
229
|
+
...
|
230
|
+
|
231
|
+
<FlowerField id="skipStep2">
|
232
|
+
{({ onChange, value }) => <input type="checkbox" checked={value} onChange={e => onChange(e.target.checked)} />}
|
233
|
+
</FlowerField>
|
234
|
+
|
235
|
+
<FlowerNavigate action="next">
|
236
|
+
<button>click me to go next</button>
|
237
|
+
</FlowerNavigate>
|
238
|
+
</FlowerNode>
|
239
|
+
|
240
|
+
<FlowerNode id="step2">...</FlowerNode>
|
241
|
+
|
242
|
+
<FlowerNode id="step3">
|
243
|
+
<FlowerValue id="enableFinal">
|
244
|
+
{({ value }) => <span>skipStep2: {String(!!value)}</span>}
|
245
|
+
</FlowerValue>
|
246
|
+
</FlowerNode>
|
247
|
+
|
248
|
+
</Flower>
|
249
|
+
)
|
250
|
+
}
|
251
|
+
|
252
|
+
```
|
253
|
+
|
254
|
+
Edit on [codesandbox/](https://codesandbox.io/p/sandbox/flower-react-example-3-forked-r3hgnj)
|
255
|
+
|
256
|
+
|
257
|
+
|
258
|
+
### Action Node
|
259
|
+
|
260
|
+
|
261
|
+
The **FlowerAction** component serves as an action entity within the application flow, enabling the definition of specific actions to execute during the progression of the flow.
|
262
|
+
|
263
|
+
The distinction between **FlowerNode** and **FlowerAction** lies in how they behave within the flow.
|
264
|
+
In the context of a **FlowerNode**, if a "back" action is taken but the preceding step is a **FlowerAction**, that particular step is skipped.
|
265
|
+
|
266
|
+
|
267
|
+
```jsx
|
268
|
+
import Flower, {
|
269
|
+
FlowerAction,
|
270
|
+
FlowerNavigate,
|
271
|
+
FlowerNode,
|
272
|
+
useFlower,
|
273
|
+
} from "@flowerforce/flower-react";
|
274
|
+
import { memo, useEffect } from "react";
|
275
|
+
|
276
|
+
const ComponentAction = memo(
|
277
|
+
() => {
|
278
|
+
const { next } = useFlower();
|
279
|
+
|
280
|
+
useEffect(() => {
|
281
|
+
// * do your staff here - api call etc **
|
282
|
+
|
283
|
+
next();
|
284
|
+
}, [next]);
|
285
|
+
|
286
|
+
return <span className="loader"></span>;
|
287
|
+
}
|
288
|
+
);
|
289
|
+
|
290
|
+
export default function App() {
|
291
|
+
return (
|
292
|
+
<Flower name="demo">
|
293
|
+
{/* step 1 */}
|
294
|
+
<FlowerNode id="step1" to={{ step2: null }}>
|
295
|
+
...
|
296
|
+
<FlowerNavigate action="next">
|
297
|
+
<button>click me to go next</button>
|
298
|
+
</FlowerNavigate>
|
299
|
+
</FlowerNode>
|
300
|
+
|
301
|
+
{/* step 2 */}
|
302
|
+
<FlowerAction id="step2" to={{ step3: null }}>
|
303
|
+
...
|
304
|
+
<ComponentAction />
|
305
|
+
</FlowerAction>
|
306
|
+
|
307
|
+
{/* step 3 */}
|
308
|
+
<FlowerNode id="success">
|
309
|
+
...
|
310
|
+
<FlowerNavigate action="back">
|
311
|
+
<button>click me to go back</button>
|
312
|
+
</FlowerNavigate>
|
313
|
+
</FlowerNode>
|
314
|
+
</Flower>
|
315
|
+
);
|
316
|
+
}
|
317
|
+
|
318
|
+
```
|
319
|
+
|
320
|
+
Edit on [codesandbox/](https://codesandbox.io/p/sandbox/flower-react-example-actionnode-766vhj)
|
321
|
+
|
322
|
+
Another difference between **FlowerNode** and **FlowerAction** is that upon mounting a FlowerAction, if the preceding node of type **FlowerNode** has the **retain** property, this node will not be unmounted.
|
323
|
+
|
324
|
+
|
325
|
+
```jsx
|
326
|
+
import Flower, {
|
327
|
+
FlowerAction,
|
328
|
+
FlowerNavigate,
|
329
|
+
FlowerNode,
|
330
|
+
useFlower,
|
331
|
+
} from "@flowerforce/flower-react";
|
332
|
+
import { memo, useEffect } from "react";
|
333
|
+
|
334
|
+
const ComponentAction = memo(
|
335
|
+
() => {
|
336
|
+
const { next } = useFlower();
|
337
|
+
|
338
|
+
useEffect(() => {
|
339
|
+
// * do your staff here - api call etc **
|
340
|
+
|
341
|
+
next();
|
342
|
+
}, [next]);
|
343
|
+
|
344
|
+
return <span className="loader"></span>;
|
345
|
+
}
|
346
|
+
);
|
347
|
+
|
348
|
+
export default function App() {
|
349
|
+
return (
|
350
|
+
<Flower name="demo">
|
351
|
+
{/* step 1 */}
|
352
|
+
<FlowerNode id="step1" to={{ step2: null }} retain>
|
353
|
+
...
|
354
|
+
<FlowerNavigate action="next">
|
355
|
+
<button>click me to go next</button>
|
356
|
+
</FlowerNavigate>
|
357
|
+
</FlowerNode>
|
358
|
+
|
359
|
+
{/* step 2 */}
|
360
|
+
<FlowerAction id="step2" to={{ step3: null }}>
|
361
|
+
...
|
362
|
+
<ComponentAction />
|
363
|
+
</FlowerAction>
|
364
|
+
|
365
|
+
{/* step 3 */}
|
366
|
+
<FlowerNode id="success">
|
367
|
+
...
|
368
|
+
<FlowerNavigate action="back">
|
369
|
+
<button>click me to go back</button>
|
370
|
+
</FlowerNavigate>
|
371
|
+
</FlowerNode>
|
372
|
+
</Flower>
|
373
|
+
);
|
374
|
+
}
|
375
|
+
|
376
|
+
```
|
377
|
+
Edit on [codesandbox/](https://codesandbox.io/p/sandbox/flower-react-example-actionnode-forked-7cd68s)
|
378
|
+
|
379
|
+
|
380
|
+
### Hook - useFlower
|
381
|
+
|
382
|
+
Here, we are using the useFlower hook to obtain some essential functions for navigation and handling of the application flow.
|
383
|
+
|
384
|
+
#### useFlower as child <Flower>...</Flower>
|
385
|
+
|
386
|
+
```jsx
|
387
|
+
import React from 'react'
|
388
|
+
import Flower, { FlowerRoute, FlowerNavigate, FlowerNode, useFlower } from '@flowerforce/flower-react'
|
389
|
+
|
390
|
+
const ButtonNext = () => {
|
391
|
+
// useFlower get the context of the parent Flower
|
392
|
+
const { next, back, jump } = useFlower();
|
393
|
+
return (
|
394
|
+
<button onClick={() => next()}>click me to go next</button>
|
395
|
+
)
|
396
|
+
}
|
397
|
+
|
398
|
+
export const Page = () => {
|
399
|
+
return (
|
400
|
+
<Flower name="demo">
|
401
|
+
<FlowerRoute id="start" to={{ step1: null }} />
|
402
|
+
|
403
|
+
<FlowerNode id="step1"
|
404
|
+
to={{ step2: null }}>
|
405
|
+
...
|
406
|
+
|
407
|
+
<ButtonNext />
|
408
|
+
</FlowerNode>
|
409
|
+
|
410
|
+
<FlowerNode id="step2">
|
411
|
+
...
|
412
|
+
</FlowerNode>
|
413
|
+
</Flower>
|
414
|
+
)
|
415
|
+
}
|
416
|
+
|
417
|
+
```
|
418
|
+
Edit on [codesandbox/](https://codesandbox.io/p/sandbox/flower-react-example-1-forked-6wj3l9)
|
419
|
+
|
420
|
+
|
421
|
+
#### External use
|
422
|
+
```jsx
|
423
|
+
import React from 'react'
|
424
|
+
import Flower, { FlowerRoute, FlowerNavigate, FlowerNode, useFlower } from '@flowerforce/flower-react'
|
425
|
+
|
426
|
+
export const Page = () => {
|
427
|
+
// useFlower in external usage need to know context passing flowName
|
428
|
+
const { next, back, jump } = useFlower({ flowName: "demo" });
|
429
|
+
|
430
|
+
return (
|
431
|
+
<>
|
432
|
+
<button onClick={() => next()}>click me and go next</button>
|
433
|
+
|
434
|
+
<Flower name="demo">
|
435
|
+
...
|
436
|
+
</Flower>
|
437
|
+
</>
|
438
|
+
)
|
439
|
+
}
|
440
|
+
|
441
|
+
```
|
442
|
+
Edit on [codesandbox/](https://codesandbox.io/p/sandbox/flower-react-example-1-forked-jk86mh)
|
443
|
+
|
444
|
+
### Utils Callback onEnter - onExit
|
445
|
+
|
446
|
+
onEnter (function): A callback function that is executed when entering the node state. It's useful for performing specific operations when the user transitions to this state.
|
447
|
+
|
448
|
+
onExit (function): A callback function that is executed when exiting the node state. It's useful for performing specific operations when the user leaves this state.
|
449
|
+
|
450
|
+
```jsx
|
451
|
+
import React from 'react'
|
452
|
+
import Flower, { FlowerRoute, FlowerNavigate, FlowerNode } from '@flowerforce/flower-react'
|
453
|
+
|
454
|
+
export const Page = () => {
|
455
|
+
return (
|
456
|
+
<Flower name="demo">
|
457
|
+
<FlowerRoute id="start" to={{ step1: null }} />
|
458
|
+
|
459
|
+
<FlowerNode id="step1"
|
460
|
+
to={{ step2: null }}
|
461
|
+
|
462
|
+
// On mount component
|
463
|
+
onEnter={() => console.log("enter on step1")}
|
464
|
+
|
465
|
+
// On unmount component
|
466
|
+
onExit={() => console.log("exit from step1")}
|
467
|
+
>
|
468
|
+
...
|
469
|
+
|
470
|
+
<FlowerNavigate action="next">
|
471
|
+
<button>click me to go next</button>
|
472
|
+
</FlowerNavigate>
|
473
|
+
</FlowerNode>
|
474
|
+
|
475
|
+
<FlowerNode id="step2">
|
476
|
+
...
|
477
|
+
</FlowerNode>
|
478
|
+
</Flower>
|
479
|
+
)
|
480
|
+
}
|
481
|
+
|
482
|
+
```
|
483
|
+
|
484
|
+
## Form
|
485
|
+
Flower enables the quick creation of forms.
|
486
|
+
|
487
|
+
It keeps track of the form's validity status. This status not only facilitates displaying error messages to the user but can also be leveraged for implementing flow rules.
|
488
|
+
|
489
|
+
### Basic Usage
|
490
|
+
|
491
|
+
```jsx
|
492
|
+
import Flower, {
|
493
|
+
FlowerNavigate,
|
494
|
+
FlowerNode,
|
495
|
+
FlowerField,
|
496
|
+
FlowerAction,
|
497
|
+
useFlower,
|
498
|
+
useFlowerForm,
|
499
|
+
} from "@flowerforce/flower-react";
|
500
|
+
import { useEffect } from "react";
|
501
|
+
import "./styles.css";
|
502
|
+
|
503
|
+
const ComponentAction = () => {
|
504
|
+
const { next } = useFlower();
|
505
|
+
const { getData } = useFlowerForm();
|
506
|
+
|
507
|
+
useEffect(() => {
|
508
|
+
// get form data
|
509
|
+
const formData = getData();
|
510
|
+
|
511
|
+
try {
|
512
|
+
// * do your staff here - api call etc **
|
513
|
+
// example setTimout to simulate delay api call
|
514
|
+
setTimeout(() => {
|
515
|
+
// navigate to success step
|
516
|
+
next("onSuccess");
|
517
|
+
}, 500);
|
518
|
+
} catch (error) {
|
519
|
+
// navigate to error step
|
520
|
+
next("onError");
|
521
|
+
}
|
522
|
+
}, [next, getData]);
|
523
|
+
|
524
|
+
return <span className="loader"></span>;
|
525
|
+
};
|
526
|
+
|
527
|
+
export default function App() {
|
528
|
+
return (
|
529
|
+
<Flower name="demo">
|
530
|
+
{/* step 1 */}
|
531
|
+
<FlowerNode id="step1" to={{ step2: null }}>
|
532
|
+
<div className="page step1">
|
533
|
+
<span>1</span>
|
534
|
+
|
535
|
+
<div className="field">
|
536
|
+
<label htmlFor="username">Username *</label>
|
537
|
+
<FlowerField
|
538
|
+
id="username"
|
539
|
+
validate={[
|
540
|
+
{
|
541
|
+
rules: { $and: [{ username: { $exists: true } }] },
|
542
|
+
message: "Field is required",
|
543
|
+
},
|
544
|
+
{
|
545
|
+
rules: { $and: [{ username: { $strGte: "6" } }] },
|
546
|
+
message: "Field length must be greater than or equal to 6.",
|
547
|
+
},
|
548
|
+
]}
|
549
|
+
>
|
550
|
+
{({ onChange, value, errors }) => (
|
551
|
+
<div className="input-container">
|
552
|
+
<input
|
553
|
+
id="username"
|
554
|
+
type="text"
|
555
|
+
value={value}
|
556
|
+
placeholder="Username"
|
557
|
+
onChange={(e) => onChange(e.target.value)}
|
558
|
+
/>
|
559
|
+
{errors && <div className="error">{errors.join(", ")}</div>}
|
560
|
+
</div>
|
561
|
+
)}
|
562
|
+
</FlowerField>
|
563
|
+
</div>
|
564
|
+
|
565
|
+
<div className="field">
|
566
|
+
<label htmlFor="password">Password *</label>
|
567
|
+
<FlowerField
|
568
|
+
id="password"
|
569
|
+
validate={[
|
570
|
+
{
|
571
|
+
rules: { $and: [{ password: { $exists: true } }] },
|
572
|
+
message: "Field is required",
|
573
|
+
},
|
574
|
+
]}
|
575
|
+
>
|
576
|
+
{({ onChange, value, errors }) => (
|
577
|
+
<>
|
578
|
+
<input
|
579
|
+
id="password"
|
580
|
+
type="password"
|
581
|
+
value={value}
|
582
|
+
placeholder="Password"
|
583
|
+
onChange={(e) => onChange(e.target.value)}
|
584
|
+
/>
|
585
|
+
{errors && <div className="error">{errors.join(", ")}</div>}
|
586
|
+
</>
|
587
|
+
)}
|
588
|
+
</FlowerField>
|
589
|
+
</div>
|
590
|
+
|
591
|
+
<FlowerNavigate
|
592
|
+
action="next"
|
593
|
+
rules={{ $and: [{ "$form.isValid": { $eq: true } }] }}
|
594
|
+
alwaysDisplay
|
595
|
+
>
|
596
|
+
{({ onClick, hidden }) => (
|
597
|
+
<button disabled={hidden} onClick={onClick}>
|
598
|
+
Submit →
|
599
|
+
</button>
|
600
|
+
)}
|
601
|
+
</FlowerNavigate>
|
602
|
+
</div>
|
603
|
+
</FlowerNode>
|
604
|
+
|
605
|
+
{/* step 2 */}
|
606
|
+
<FlowerAction id="step2" to={{ success: "onSuccess", error: "onError" }}>
|
607
|
+
<div className="page step2">
|
608
|
+
<ComponentAction />
|
609
|
+
</div>
|
610
|
+
</FlowerAction>
|
611
|
+
|
612
|
+
{/* step 3 */}
|
613
|
+
<FlowerNode id="success">
|
614
|
+
<div className="page step3">
|
615
|
+
<span>Success</span>
|
616
|
+
|
617
|
+
<FlowerNavigate action="reset">
|
618
|
+
<button>Reset</button>
|
619
|
+
</FlowerNavigate>
|
620
|
+
</div>
|
621
|
+
</FlowerNode>
|
622
|
+
|
623
|
+
{/* step 4 */}
|
624
|
+
<FlowerNode id="error">
|
625
|
+
<div className="page step4">
|
626
|
+
<span>Error</span>
|
627
|
+
<FlowerNavigate action="reset">
|
628
|
+
<button>Reset</button>
|
629
|
+
</FlowerNavigate>
|
630
|
+
</div>
|
631
|
+
</FlowerNode>
|
632
|
+
</Flower>
|
633
|
+
);
|
634
|
+
}
|
635
|
+
|
636
|
+
```
|
637
|
+
|
638
|
+
Edit on [codesandbox/](https://codesandbox.io/p/sandbox/flower-react-example-1-forked-2f43gh)
|
639
|
+
|
640
|
+
## Operators Rules
|
641
|
+
|
642
|
+
The "rules" in Flower are used to define conditions and conditional behaviors within the workflow. These rules allow for dynamically changing the display or behavior of certain fields or components based on specific conditions.
|
643
|
+
|
644
|
+
The rules schema follows the MongoDB style, below is the list of available operators:
|
645
|
+
|
646
|
+
- $exists: Checks if a value exists or not.
|
647
|
+
- $eq: Checks if two values are equal.
|
648
|
+
- $ne: Checks if two values are not equal.
|
649
|
+
- $gt: Checks if the first value is greater than the second.
|
650
|
+
- $gte: Checks if the first value is greater than or equal to the second.
|
651
|
+
- $lt: Checks if the first value is less than the second.
|
652
|
+
- $lte: Checks if the first value is less than or equal to the second.
|
653
|
+
- $strGt: Checks if the length of a string is greater than the specified value.
|
654
|
+
- $strGte: Checks if the length of a string is greater than or equal to the specified value.
|
655
|
+
- $strLt: Checks if the length of a string is less than the specified value.
|
656
|
+
- $strLte: Checks if the length of a string is less than or equal to the specified value.
|
657
|
+
- $in: Checks if a value is present in a given array.
|
658
|
+
- $nin: Checks if a value is not present in a given array.
|
659
|
+
- $all: Checks if all values are present in a given array.
|
660
|
+
- $regex: Checks if a string matches a regular expression.
|
661
|
+
|
662
|
+
### Examples
|
663
|
+
|
664
|
+
Rules in $and | $or
|
665
|
+
|
666
|
+
```jsx
|
667
|
+
<FlowerNode id="node"
|
668
|
+
to={{
|
669
|
+
node2: {
|
670
|
+
rules: { $and: [
|
671
|
+
{ myValue: { $exists: true } },
|
672
|
+
{ myValue: { $strGt: 6 } }
|
673
|
+
|
674
|
+
]}
|
675
|
+
}
|
676
|
+
}}>
|
677
|
+
...
|
678
|
+
</Flower>
|
679
|
+
|
680
|
+
<FlowerNode id="node"
|
681
|
+
to={{
|
682
|
+
node2: {
|
683
|
+
rules: { $or: [
|
684
|
+
{ myValue: { $exists: false } },
|
685
|
+
{ myValue: { $strGt: 6 } }
|
686
|
+
|
687
|
+
]}
|
688
|
+
}
|
689
|
+
}}>
|
690
|
+
...
|
691
|
+
</Flower>
|
692
|
+
|
693
|
+
```
|
694
|
+
|
695
|
+
Compare state value, use '$ref:'
|
696
|
+
|
697
|
+
```jsx
|
698
|
+
<Flower name="demo" initialData={{ myValue1: 'test', myValue2: 'test2' }}>
|
699
|
+
<FlowerNode id="node"
|
700
|
+
to={{
|
701
|
+
node2: {
|
702
|
+
rules: [
|
703
|
+
{ myValue1: { $eq: '$ref:myValue2' } }
|
704
|
+
]}
|
705
|
+
}}>
|
706
|
+
...
|
707
|
+
</Flower>
|
708
|
+
```
|
709
|
+
|
710
|
+
|
711
|
+
## Display Rules
|
712
|
+
|
713
|
+
Showing or Hiding Fields: You can use rules to show or hide specific fields based on user choices. For example, hiding a "Buttons" unless the user selects a certain option.
|
714
|
+
|
715
|
+
We can use the FlowerRule component to hide a part of the UI according to certain rules.
|
716
|
+
|
717
|
+
If the "alwaysDisplay" property is passed, however, the component will not be automatically hidden, but a "hidden" property will be provided when the rules are not met.
|
718
|
+
|
719
|
+
### Example
|
720
|
+
|
721
|
+
```jsx
|
722
|
+
import React from 'react'
|
723
|
+
import Flower, { FlowerRoute, FlowerNode, FlowerRule, FlowerNavigate } from '@flowerforce/flower-react'
|
724
|
+
|
725
|
+
|
726
|
+
export const Page = () => {
|
727
|
+
return (
|
728
|
+
<Flower name="demo" initialData={{ enableNav: true }}>
|
729
|
+
<FlowerNode id="step1"
|
730
|
+
to={{ step2: null }}>
|
731
|
+
...
|
732
|
+
|
733
|
+
{/* show / hidden based on rule */}
|
734
|
+
<FlowerRule rules={{ enableNav: { $eq: true } }}>
|
735
|
+
<p>Buttons nav are enabled</p>
|
736
|
+
</FlowerRule>
|
737
|
+
|
738
|
+
{/* always visible component, hidden prop is true when rule is not matched */}
|
739
|
+
<FlowerNavigate
|
740
|
+
action="next"
|
741
|
+
rules={{ enableNav: { $eq: true } }}
|
742
|
+
alwaysDisplay
|
743
|
+
>
|
744
|
+
{({ onClick, hidden }) => (
|
745
|
+
<button disabled={hidden} onClick={onClick}>
|
746
|
+
Next →
|
747
|
+
</button>
|
748
|
+
)}
|
749
|
+
</FlowerNavigate>
|
750
|
+
|
751
|
+
{/* visible only when rule is matched */}
|
752
|
+
<FlowerNavigate
|
753
|
+
action="reset"
|
754
|
+
rules={{ enableNav: { $eq: true } }}
|
755
|
+
>
|
756
|
+
<button>Reset</button>
|
757
|
+
</FlowerNavigate>
|
758
|
+
</FlowerNode>
|
759
|
+
|
760
|
+
...
|
761
|
+
</Flower>
|
762
|
+
)
|
763
|
+
}
|
764
|
+
|
765
|
+
```
|
766
|
+
Edit on [codesandbox/](https://codesandbox.io/p/sandbox/flower-react-example-1-forked-sfn6ml)
|
767
|
+
|
768
|
+
|
769
|
+
# Documentation
|
770
|
+
|
771
|
+
The Flower React docs are published at [flower.stackhouse.dev/](https://flower.stackhouse.dev)
|