@flowerforce/flower-react 3.1.1-beta.1 → 3.1.2-beta.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 +7 -0
- package/README.md +85 -10
- package/dist/index.cjs.js +126 -13
- package/dist/index.esm.js +128 -15
- package/dist/src/components/types/DefaultNode.d.ts +17 -0
- package/dist/src/components/types/FlowerComponent.d.ts +2 -0
- package/dist/src/components/types/FlowerField.d.ts +78 -0
- package/dist/src/components/types/FlowerFlow.d.ts +10 -0
- package/dist/src/components/types/FlowerHooks.d.ts +25 -2
- package/dist/src/components/types/FlowerNavigate.d.ts +14 -0
- package/dist/src/components/types/FlowerNode.d.ts +10 -0
- package/dist/src/components/types/FlowerRoute.d.ts +17 -0
- package/dist/src/components/types/FlowerRule.d.ts +23 -0
- package/dist/src/components/types/FlowerServer.d.ts +2 -0
- package/dist/src/components/types/FlowerValue.d.ts +22 -0
- package/dist/src/components/useFlower.d.ts +18 -0
- package/dist/src/components/useFlowerForm.d.ts +17 -0
- package/dist/src/provider.d.ts +7 -5
- package/package.json +2 -2
package/dist/index.esm.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
import React, { createContext,
|
1
|
+
import React, { createContext, PureComponent, memo, useRef, useState, useMemo, Children, useEffect, useContext, useCallback, useLayoutEffect } from 'react';
|
2
2
|
import _keyBy from 'lodash/keyBy';
|
3
|
-
import { CoreUtils, FlowerCoreReducers, FlowerStateUtils, Selectors, Emitter, MatchRules } from '@flowerforce/flower-core';
|
3
|
+
import { CoreUtils, FlowerCoreReducers, FlowerStateUtils, Selectors, devtoolState, Emitter, MatchRules } from '@flowerforce/flower-core';
|
4
4
|
import _get from 'lodash/get';
|
5
5
|
import { createSlice, configureStore } from '@reduxjs/toolkit';
|
6
6
|
import { createSelector } from 'reselect';
|
@@ -12,6 +12,7 @@ const context = _context;
|
|
12
12
|
const Provider = _context.Provider;
|
13
13
|
const Consumer = _context.Consumer;
|
14
14
|
|
15
|
+
// eslint-disable-next-line import/prefer-default-export
|
15
16
|
const convertElements = nodes => {
|
16
17
|
const res = CoreUtils.generateNodesForFlowerJson(nodes);
|
17
18
|
return res;
|
@@ -43,7 +44,9 @@ const makeSelectStartNodeId = name => createSelector(selectFlower(name), Selecto
|
|
43
44
|
const makeSelectCurrentNodeId = name => createSelector(selectFlower(name), makeSelectStartNodeId(name), Selectors.makeSelectCurrentNodeId);
|
44
45
|
const makeSelectPrevNodeRetain = name => createSelector(makeSelectNodesIds(name), selectFlowerHistory(name), makeSelectCurrentNodeId(name), Selectors.makeSelectPrevNodeRetain);
|
45
46
|
const makeSelectCurrentNodeDisabled = name => createSelector(makeSelectNodesIds(name), makeSelectCurrentNodeId(name), Selectors.makeSelectCurrentNodeDisabled);
|
47
|
+
// dati nel flow selezionato
|
46
48
|
const getDataByFlow = name => createSelector(selectFlower(name), Selectors.getDataByFlow);
|
49
|
+
// selettore per recuperare i dati di un flow specifico e id specifico
|
47
50
|
const getDataFromState = (name, id) => createSelector(getDataByFlow(name), Selectors.getDataFromState(id));
|
48
51
|
const makeSelectNodeErrors = (name, currentNodeId) => createSelector(selectFlowerFormNode(name, currentNodeId), Selectors.makeSelectNodeErrors);
|
49
52
|
const makeSelectNodeFormTouched = (name, currentNodeId) => createSelector(selectFlowerFormNode(name, currentNodeId), Selectors.makeSelectNodeFormTouched);
|
@@ -51,6 +54,7 @@ const getAllData = createSelector(selectGlobal, mapData);
|
|
51
54
|
const makeSelectFieldError = (name, id, validate) => createSelector(getAllData, Selectors.makeSelectFieldError(name, id, validate));
|
52
55
|
const selectorRulesDisabled = (id, rules, keys, flowName, value, currentNode) => createSelector(getAllData, makeSelectNodeErrors(flowName, currentNode), Selectors.selectorRulesDisabled(id, rules, keys, flowName, value));
|
53
56
|
|
57
|
+
//TODO check reduxContext type due to remove all any types
|
54
58
|
const reduxContext = /*#__PURE__*/createContext(null);
|
55
59
|
const useDispatch = createDispatchHook(reduxContext);
|
56
60
|
const useSelector = createSelectorHook(reduxContext);
|
@@ -63,11 +67,11 @@ const store = ({
|
|
63
67
|
name: 'flower'
|
64
68
|
} : false
|
65
69
|
});
|
66
|
-
class FlowerProvider extends
|
70
|
+
class FlowerProvider extends PureComponent {
|
67
71
|
constructor(props) {
|
68
72
|
super(props);
|
69
73
|
this.store = store({
|
70
|
-
enableDevtool: props.
|
74
|
+
enableDevtool: props.enableReduxDevtool
|
71
75
|
});
|
72
76
|
}
|
73
77
|
render() {
|
@@ -81,6 +85,11 @@ class FlowerProvider extends Component {
|
|
81
85
|
}
|
82
86
|
}
|
83
87
|
|
88
|
+
/* eslint-disable no-undef */
|
89
|
+
/* eslint-disable no-underscore-dangle */
|
90
|
+
/**
|
91
|
+
* FlowerClient
|
92
|
+
*/
|
84
93
|
const FlowerClient = ({
|
85
94
|
children,
|
86
95
|
name,
|
@@ -91,7 +100,9 @@ const FlowerClient = ({
|
|
91
100
|
const flowName = name;
|
92
101
|
const dispatch = useDispatch();
|
93
102
|
const one = useRef(false);
|
94
|
-
const [wsDevtools, setWsDevtools] = useState(
|
103
|
+
const [wsDevtools, setWsDevtools] = useState(devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS_INITIALIZED__', false));
|
104
|
+
// TODO rivedere il giro, potremmo fare le trasformazioni in CoreUtils.generateNodesForFlowerJson
|
105
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps, max-len
|
95
106
|
const nodes = useMemo(() => convertElements(Children.toArray(children)), [children]);
|
96
107
|
const nodeById = useMemo(() => _keyBy(Children.toArray(children), 'props.id'), [children]);
|
97
108
|
const isInitialized = useSelector(makeSelectStartNodeId(name));
|
@@ -105,6 +116,7 @@ const FlowerClient = ({
|
|
105
116
|
one.current = true;
|
106
117
|
dispatch(actions.initNodes({
|
107
118
|
name: flowName,
|
119
|
+
// @ts-expect-error FIX ME
|
108
120
|
nodes,
|
109
121
|
startId: _startId != null ? _startId : '',
|
110
122
|
persist: _destroyOnUnmount === false,
|
@@ -113,6 +125,7 @@ const FlowerClient = ({
|
|
113
125
|
}
|
114
126
|
}, [dispatch, flowName, nodes, _startId, _initialData, _destroyOnUnmount]);
|
115
127
|
useEffect(() => {
|
128
|
+
/* istanbul ignore next */
|
116
129
|
const eventCb = msg => {
|
117
130
|
if (msg.source !== 'flower-devtool') return;
|
118
131
|
if (msg.action === 'FLOWER_EXTENSION_INIT' || msg.action === 'FLOWER_DEVTOOL_WEB_INIT') {
|
@@ -137,16 +150,19 @@ const FlowerClient = ({
|
|
137
150
|
}));
|
138
151
|
}
|
139
152
|
};
|
140
|
-
|
153
|
+
/* istanbul ignore next */
|
154
|
+
if (devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS__')) {
|
141
155
|
Emitter.on('flower-devtool-to-client', eventCb);
|
142
156
|
}
|
143
157
|
return () => {
|
144
|
-
|
158
|
+
/* istanbul ignore next */
|
159
|
+
if (devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS__')) {
|
145
160
|
Emitter.off('flower-devtool-to-client', eventCb);
|
146
161
|
}
|
147
162
|
};
|
148
163
|
}, [dispatch, flowName]);
|
149
164
|
useEffect(() => () => {
|
165
|
+
// unmount function
|
150
166
|
if (_destroyOnUnmount && one.current === true) {
|
151
167
|
one.current = false;
|
152
168
|
dispatch(actions.destroy({
|
@@ -155,7 +171,8 @@ const FlowerClient = ({
|
|
155
171
|
}
|
156
172
|
}, [dispatch, flowName, _destroyOnUnmount]);
|
157
173
|
useEffect(() => {
|
158
|
-
|
174
|
+
/* istanbul ignore next */
|
175
|
+
if (isInitialized && wsDevtools && devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS__')) {
|
159
176
|
Emitter.emit('flower-devtool-from-client', {
|
160
177
|
source: 'flower-client',
|
161
178
|
action: 'FLOWER_CLIENT_INIT',
|
@@ -167,7 +184,8 @@ const FlowerClient = ({
|
|
167
184
|
}
|
168
185
|
}, [dispatch, flowName, wsDevtools, isInitialized, store]);
|
169
186
|
useEffect(() => {
|
170
|
-
|
187
|
+
/* istanbul ignore next */
|
188
|
+
if (isInitialized && wsDevtools && devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS__')) {
|
171
189
|
Emitter.emit('flower-devtool-from-client', {
|
172
190
|
source: 'flower-client',
|
173
191
|
action: 'SET_HISTORY',
|
@@ -178,8 +196,10 @@ const FlowerClient = ({
|
|
178
196
|
}, [dispatch, flowName, history, wsDevtools, isInitialized]);
|
179
197
|
useEffect(() => {
|
180
198
|
if (!current) return;
|
199
|
+
/* istanbul ignore next */
|
181
200
|
if (!isInitialized) return;
|
182
|
-
|
201
|
+
/* istanbul ignore next */
|
202
|
+
if (isInitialized && wsDevtools && devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS__')) {
|
183
203
|
Emitter.emit('flower-devtool-from-client', {
|
184
204
|
source: 'flower-client',
|
185
205
|
action: 'SET_CURRENT',
|
@@ -190,6 +210,7 @@ const FlowerClient = ({
|
|
190
210
|
}, [flowName, current, wsDevtools, isInitialized]);
|
191
211
|
useEffect(() => {
|
192
212
|
if (!current) return;
|
213
|
+
/* istanbul ignore next */
|
193
214
|
if (!isInitialized) return;
|
194
215
|
if (isDisabled) {
|
195
216
|
dispatch({
|
@@ -199,7 +220,9 @@ const FlowerClient = ({
|
|
199
220
|
disabled: true
|
200
221
|
}
|
201
222
|
});
|
202
|
-
|
223
|
+
// eslint-disable-next-line no-underscore-dangle, no-undef
|
224
|
+
/* istanbul ignore next */
|
225
|
+
if (wsDevtools && devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS__')) {
|
203
226
|
Emitter.emit('flower-devtool-from-client', {
|
204
227
|
source: 'flower-client',
|
205
228
|
action: 'FLOWER_NAVIGATE',
|
@@ -217,8 +240,10 @@ const FlowerClient = ({
|
|
217
240
|
}
|
218
241
|
return;
|
219
242
|
}
|
220
|
-
|
221
|
-
|
243
|
+
// eslint-disable-next-line no-underscore-dangle, no-undef
|
244
|
+
/* istanbul ignore next */
|
245
|
+
if (wsDevtools && devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS__')) {
|
246
|
+
if (isInitialized === current) return; // salto il primo evento
|
222
247
|
Emitter.emit('flower-devtool-from-client', {
|
223
248
|
source: 'flower-client',
|
224
249
|
action: 'SET_SELECTED',
|
@@ -314,6 +339,15 @@ function FlowerStart() {
|
|
314
339
|
isStart: true
|
315
340
|
}
|
316
341
|
});
|
342
|
+
// if (global.window
|
343
|
+
// // eslint-disable-next-line no-underscore-dangle, no-undef
|
344
|
+
// && global.window.__FLOWER_DEVTOOLS__ && global.window.__FLOWER_DEVTOOLS__AUTO) {
|
345
|
+
// Emitter.emit('flower-devtool-from-client', {
|
346
|
+
// source: 'flower-client',
|
347
|
+
// action: 'START_FLOWER',
|
348
|
+
// name: flowName,
|
349
|
+
// });
|
350
|
+
// }
|
317
351
|
}
|
318
352
|
}, [dispatch, autostart, startNodeId, currentNode, flowName]);
|
319
353
|
return null;
|
@@ -415,6 +449,7 @@ const FlowerRule = ({
|
|
415
449
|
type
|
416
450
|
} = child;
|
417
451
|
const Component = type;
|
452
|
+
// eslint-disable-next-line react/jsx-props-no-spreading
|
418
453
|
return Component && /*#__PURE__*/React.createElement(Component, _extends({
|
419
454
|
key: i,
|
420
455
|
hidden: true
|
@@ -430,6 +465,7 @@ const FlowerRule = ({
|
|
430
465
|
type
|
431
466
|
} = child;
|
432
467
|
const Component = type;
|
468
|
+
// eslint-disable-next-line react/jsx-props-no-spreading
|
433
469
|
return Component && /*#__PURE__*/React.createElement(Component, _extends({
|
434
470
|
key: i
|
435
471
|
}, props));
|
@@ -444,6 +480,7 @@ const _excluded$3 = ["Component", "id", "flowName", "currentNode", "validate", "
|
|
444
480
|
function isIntrinsicElement$1(x) {
|
445
481
|
return typeof x === 'string';
|
446
482
|
}
|
483
|
+
//TODO make types for wrapper function props
|
447
484
|
function Wrapper$1(_ref) {
|
448
485
|
let {
|
449
486
|
Component,
|
@@ -542,6 +579,7 @@ function Wrapper$1(_ref) {
|
|
542
579
|
});
|
543
580
|
}, [flowName, currentNode, isValidating]);
|
544
581
|
useLayoutEffect(() => {
|
582
|
+
// destroy
|
545
583
|
return () => {
|
546
584
|
if (destroyValue) {
|
547
585
|
dispatch({
|
@@ -583,6 +621,8 @@ function Wrapper$1(_ref) {
|
|
583
621
|
if (typeof Component === 'function') {
|
584
622
|
return Component(newProps);
|
585
623
|
}
|
624
|
+
// TODO si arriva in questa condizione quando si passa un componente primitivo es. div
|
625
|
+
// in questo caso non posso props custom di flower
|
586
626
|
if (isIntrinsicElement$1(Component)) {
|
587
627
|
return /*#__PURE__*/React.createElement(Component, _extends({
|
588
628
|
id: id
|
@@ -675,6 +715,7 @@ component$4.displayName = 'FlowerField';
|
|
675
715
|
|
676
716
|
const _excluded$2 = ["Component", "id", "flowName", "spreadValue", "hidden", "onUpdate"],
|
677
717
|
_excluded2 = ["id", "alwaysDisplay", "rules", "value", "Component", "spreadValue", "flowName", "onUpdate"];
|
718
|
+
//TODO make types for wrapper function
|
678
719
|
function Wrapper(_ref) {
|
679
720
|
let {
|
680
721
|
Component,
|
@@ -767,6 +808,7 @@ const FlowerValue = ({
|
|
767
808
|
props
|
768
809
|
} = child;
|
769
810
|
const Component = type;
|
811
|
+
// eslint-disable-next-line react/jsx-props-no-spreading
|
770
812
|
return /*#__PURE__*/React.createElement(RenderRules$1, _extends({
|
771
813
|
key: i,
|
772
814
|
id: _id,
|
@@ -816,6 +858,24 @@ const makeActionPayloadOnReset = makeActionPayload(ACTION_TYPES.reset, PAYLAOAD_
|
|
816
858
|
const makeActionPayloadOnNode = makeActionPayload(ACTION_TYPES.jump, PAYLAOAD_KEYS_NEEDED.jump);
|
817
859
|
const makeActionPayloadOnNext = makeActionPayload(ACTION_TYPES.next, PAYLAOAD_KEYS_NEEDED.next);
|
818
860
|
const makeActionPayloadOnRestart = makeActionPayload(ACTION_TYPES.restart, PAYLAOAD_KEYS_NEEDED.restart);
|
861
|
+
/** This hook allows you to read flow informations, such as the flowName and ID of the current node.
|
862
|
+
*
|
863
|
+
* It also exposes all the functions to navigate within the flow:
|
864
|
+
*
|
865
|
+
* - next
|
866
|
+
*
|
867
|
+
* - back
|
868
|
+
*
|
869
|
+
* - jump
|
870
|
+
*
|
871
|
+
* - reset
|
872
|
+
*
|
873
|
+
* - restart
|
874
|
+
*
|
875
|
+
* @param {string} flowName - first optional parameter
|
876
|
+
*
|
877
|
+
* @param {string} name - optional parameter, if flowName exist, name is not used
|
878
|
+
*/
|
819
879
|
const useFlower = ({
|
820
880
|
flowName: customFlowName,
|
821
881
|
name
|
@@ -827,8 +887,13 @@ const useFlower = ({
|
|
827
887
|
} = useContext(context);
|
828
888
|
const flowName = customFlowName || name || flowNameDefault;
|
829
889
|
const nodeId = useSelector(makeSelectCurrentNodeId(flowName != null ? flowName : ''));
|
830
|
-
const
|
831
|
-
|
890
|
+
const startId = useSelector(makeSelectStartNodeId(flowName != null ? flowName : ''));
|
891
|
+
const emitNavigateEvent = useCallback(
|
892
|
+
//TODO check this function is needed
|
893
|
+
params => {
|
894
|
+
/* istanbul ignore next */
|
895
|
+
// eslint-disable-next-line no-underscore-dangle, no-undef
|
896
|
+
if (_get(devtoolState, '__FLOWER_DEVTOOLS__')) {
|
832
897
|
Emitter.emit('flower-devtool-from-client', {
|
833
898
|
source: 'flower-client',
|
834
899
|
action: 'FLOWER_NAVIGATE',
|
@@ -922,6 +987,7 @@ const useFlower = ({
|
|
922
987
|
return {
|
923
988
|
flowName,
|
924
989
|
nodeId,
|
990
|
+
startId,
|
925
991
|
next,
|
926
992
|
jump,
|
927
993
|
back,
|
@@ -930,6 +996,28 @@ const useFlower = ({
|
|
930
996
|
};
|
931
997
|
};
|
932
998
|
|
999
|
+
/* eslint-disable */
|
1000
|
+
// {
|
1001
|
+
// flowName?: string | undefined;
|
1002
|
+
// action?: 'next' | 'back' | 'reset' | 'jump';
|
1003
|
+
// } & (
|
1004
|
+
// | {
|
1005
|
+
// node?: undefined;
|
1006
|
+
// route?: Route;
|
1007
|
+
// }
|
1008
|
+
// | {
|
1009
|
+
// node?: RoutePrev;
|
1010
|
+
// route?: undefined;
|
1011
|
+
// }
|
1012
|
+
// | {
|
1013
|
+
// node?: RouteReset;
|
1014
|
+
// route?: undefined;
|
1015
|
+
// }
|
1016
|
+
// | {
|
1017
|
+
// node?: RouteNode;
|
1018
|
+
// route?: undefined;
|
1019
|
+
// }
|
1020
|
+
// );
|
933
1021
|
const useFlowerNavigate = ({
|
934
1022
|
flowName,
|
935
1023
|
action,
|
@@ -981,6 +1069,7 @@ const _excluded$1 = ["hidden", "Component", "onNavigate"];
|
|
981
1069
|
function isIntrinsicElement(x) {
|
982
1070
|
return typeof x === 'string';
|
983
1071
|
}
|
1072
|
+
//TODO type FlowerNavigateWrapper props
|
984
1073
|
function FlowerNavigateWrapper(_ref) {
|
985
1074
|
let {
|
986
1075
|
hidden,
|
@@ -995,16 +1084,22 @@ function FlowerNavigateWrapper(_ref) {
|
|
995
1084
|
if (typeof Component === 'function') {
|
996
1085
|
return Component(newProps);
|
997
1086
|
}
|
1087
|
+
// TODO si arriva in questa condizione quando si passa un componente primitivo es. div
|
1088
|
+
// in questo caso non posso props custom di flower
|
998
1089
|
if (isIntrinsicElement(Component)) {
|
999
1090
|
return /*#__PURE__*/React.createElement(Component, _extends({}, props, {
|
1000
1091
|
onClick: onNavigate
|
1001
1092
|
}));
|
1002
1093
|
}
|
1094
|
+
// TODO in questa condizione si arriva se nel progetto si utilizza Vite, in questo caso i componenti non sono Function ma Object,
|
1095
|
+
// oppure nel caso di un testo semplice come children di questo componente
|
1096
|
+
/* istanbul ignore next */
|
1003
1097
|
return Component && /*#__PURE__*/React.createElement(Component, _extends({}, newProps));
|
1004
1098
|
}
|
1005
1099
|
const component$2 = /*#__PURE__*/React.memo(FlowerNavigateWrapper);
|
1006
1100
|
|
1007
1101
|
const _excluded = ["alwaysDisplay", "rules", "Component", "flowName", "onNavigate"];
|
1102
|
+
//TODO type RenderRules props
|
1008
1103
|
const RenderRules = _ref => {
|
1009
1104
|
let {
|
1010
1105
|
alwaysDisplay,
|
@@ -1060,6 +1155,7 @@ const FlowerNavigate = ({
|
|
1060
1155
|
props
|
1061
1156
|
} = child;
|
1062
1157
|
const Component = type;
|
1158
|
+
// eslint-disable-next-line react/jsx-props-no-spreading
|
1063
1159
|
return /*#__PURE__*/React.createElement(RenderRules, _extends({
|
1064
1160
|
key: i,
|
1065
1161
|
alwaysDisplay: alwaysDisplay,
|
@@ -1078,6 +1174,23 @@ const FlowerComponent = ({
|
|
1078
1174
|
}) => children;
|
1079
1175
|
const component = /*#__PURE__*/memo(FlowerComponent);
|
1080
1176
|
|
1177
|
+
/** This hook allows you to manage and retrieve information about Forms.
|
1178
|
+
*
|
1179
|
+
* It exposes details regarding the form's state and a set of methods for reading and writing within it:
|
1180
|
+
*
|
1181
|
+
* - getData
|
1182
|
+
*
|
1183
|
+
* - setData
|
1184
|
+
*
|
1185
|
+
* - unSetData
|
1186
|
+
*
|
1187
|
+
* - replaceData
|
1188
|
+
*
|
1189
|
+
* @param {string} flowName - first optional parameter
|
1190
|
+
*
|
1191
|
+
* @param {string} name - optional parameter, if flowName exist, name is not used
|
1192
|
+
*
|
1193
|
+
*/
|
1081
1194
|
const useFlowerForm = ({
|
1082
1195
|
flowName: customFlowName,
|
1083
1196
|
name
|
@@ -1,8 +1,25 @@
|
|
1
1
|
export type FlowerNodeDefaultProps = {
|
2
|
+
/** Unique identifier for the node inside the Flow */
|
2
3
|
id: string;
|
4
|
+
/** An object containing the informations about the node's links.
|
5
|
+
*
|
6
|
+
* Example: to={{ step2: { rules: { $and: [{ name: { $eq: 'John' } }] } } }}
|
7
|
+
*
|
8
|
+
* In that case, the FlowerNode is linked to the node with the ID 'step2'
|
9
|
+
*
|
10
|
+
* You can move from the current node to the step2 node only if the rules are satisfied
|
11
|
+
*
|
12
|
+
* Set it to null when you don't have rules
|
13
|
+
*
|
14
|
+
* Example: to={{ step2: null}
|
15
|
+
*/
|
3
16
|
to?: Record<string, any>;
|
17
|
+
/** The children of the FlowerNode */
|
4
18
|
children?: React.ReactNode;
|
19
|
+
/** The function executed when you enter into this FlowerNode */
|
5
20
|
onEnter?: () => void;
|
21
|
+
/** The function executed when you leave this FlowerNode */
|
6
22
|
onExit?: () => void;
|
23
|
+
/** When set to true, the FlowerNode is ignored and skipped */
|
7
24
|
disabled?: boolean;
|
8
25
|
};
|
@@ -1,30 +1,108 @@
|
|
1
1
|
import { FunctionRule, RulesObject } from '@flowerforce/flower-core';
|
2
2
|
export type FlowerFieldProps<T extends Record<string, any> = Record<string, any>> = {
|
3
|
+
/** The path to the value you want to read from the flow's data
|
4
|
+
*
|
5
|
+
* Example: id="loginForm.name"
|
6
|
+
*
|
7
|
+
* The FlowerField component reads the value of the key "name" of the loginForm object in the flow's data
|
8
|
+
*/
|
3
9
|
id?: string;
|
10
|
+
/** The FlowerField's children */
|
4
11
|
children: React.ReactNode | ((props: {
|
12
|
+
/** The string passed to the "id" FlowerField's prop */
|
5
13
|
id: string;
|
14
|
+
/** The value found at the "id" key in the flow data
|
15
|
+
*
|
16
|
+
* Example: id="loginForm.name"
|
17
|
+
*
|
18
|
+
* This parameter will hold the value found at the key 'name' of the loginForm object in the flow's data.
|
19
|
+
*/
|
6
20
|
value: any;
|
21
|
+
/** An array of strings containing error messages associated with validation rules that are not satisfied. */
|
7
22
|
errors: undefined | string[];
|
23
|
+
/** This parameter will notify you when there are validation errors. */
|
8
24
|
hasError: undefined | boolean;
|
25
|
+
/** Use this function to write a new value at the "id" key
|
26
|
+
*
|
27
|
+
* Example: id="loginForm.name"
|
28
|
+
*
|
29
|
+
* onChange("John") will write "John" at the key 'name' of the loginForm object in the flow's data.
|
30
|
+
*/
|
9
31
|
onChange: (props: any) => void;
|
32
|
+
/** The function executed to test all the validation rules*/
|
10
33
|
onBlur: () => void;
|
34
|
+
/** This parameter will notify you whether the form field has been touched */
|
11
35
|
isTouched: boolean;
|
36
|
+
/** true when some of the display rules are not satisfied, and you have passed true to the "alwaysDisplay" FlowerField's prop*/
|
12
37
|
hidden: boolean;
|
38
|
+
/** true when you have an async validation in progress */
|
13
39
|
isValidating: boolean | undefined;
|
14
40
|
}) => React.ReactNode | React.ReactElement | undefined);
|
41
|
+
/**The validation rules for that field
|
42
|
+
*
|
43
|
+
* Example: validate={[
|
44
|
+
*
|
45
|
+
* {
|
46
|
+
* rules: {
|
47
|
+
* $self: {
|
48
|
+
* $regex:
|
49
|
+
* '^([A-Z]{6}[0-9LMNPQRSTUV]{2}[ABCDEHLMPRST]{1}[0-9LMNPQRSTUV]{2}[A-Z]{1}[0-9LMNPQRSTUV]{3}[A-Z]{1})$|([0-9]{11})$',
|
50
|
+
* },
|
51
|
+
* },
|
52
|
+
* message: 'Value not valid',
|
53
|
+
* },
|
54
|
+
* {
|
55
|
+
* // Don't use promises
|
56
|
+
* rules: (data)=> {
|
57
|
+
* return data.name === 'Andrea'
|
58
|
+
* },
|
59
|
+
* message: 'Value not valid',
|
60
|
+
* }
|
61
|
+
* ]}
|
62
|
+
*
|
63
|
+
* For every rule you can pass an error message, that Flower returns when that condition is note satisfied
|
64
|
+
*/
|
15
65
|
validate?: Record<string, any>[] | string[];
|
66
|
+
/** A function to perform an async validation */
|
16
67
|
asyncValidate?: (value: any, data?: Record<string, any>, errors?: undefined | string[]) => string[] | undefined | Promise<string[]> | {
|
17
68
|
message: string;
|
18
69
|
}[] | boolean | Promise<boolean>;
|
70
|
+
/** Use this to set a debounce for the async validation
|
71
|
+
*
|
72
|
+
* The default value is 0
|
73
|
+
*/
|
19
74
|
asyncDebounce?: number;
|
75
|
+
/** The initial error message when you have an async validation
|
76
|
+
*
|
77
|
+
* The default value is undefined
|
78
|
+
*/
|
20
79
|
asyncInitialError?: string;
|
80
|
+
/** The message that the FlowerField returns while validating*/
|
21
81
|
asyncWaitingError?: string;
|
82
|
+
/** An object containing the display rules of that component. When the conditions are not satisfied, the children is hidden.
|
83
|
+
*
|
84
|
+
* Example: rules={{ $and: [{ name: { $exist: true } }] }}
|
85
|
+
*/
|
22
86
|
rules?: RulesObject<T> | FunctionRule;
|
87
|
+
/** The name of the flow from which read the data
|
88
|
+
*
|
89
|
+
* - note: the default value is the name of the flow where the component is used
|
90
|
+
*/
|
23
91
|
flowName?: string;
|
92
|
+
/** Initial value field */
|
24
93
|
defaultValue?: unknown;
|
25
94
|
destroyValue?: boolean;
|
26
95
|
value?: any;
|
96
|
+
/** When set to true, the children is shown even if the rules are not satisfied
|
97
|
+
*
|
98
|
+
* The FlowerField returns the boolean variable "hidden" to notify you if the conditions are satisfied or not
|
99
|
+
*/
|
27
100
|
alwaysDisplay?: boolean;
|
101
|
+
/** The function executed when the value found at the path passed to the "id" prop changes */
|
28
102
|
onUpdate?: (value: any) => void;
|
103
|
+
/** The function executed at the "onBlur" event, for example for Input components
|
104
|
+
*
|
105
|
+
* The onBlur function will test all the validation rules
|
106
|
+
*/
|
29
107
|
onBlur?: (e: any) => void;
|
30
108
|
};
|
@@ -1,4 +1,14 @@
|
|
1
1
|
import { FlowerNodeDefaultProps } from './DefaultNode';
|
2
2
|
export type FlowerFlowProps = FlowerNodeDefaultProps & {
|
3
|
+
/** A Flower node is visible only when is the current node in the navigation history of the flow.
|
4
|
+
*
|
5
|
+
* By setting this prop to true, you have the ability to view this node even when it is not the current node.
|
6
|
+
*
|
7
|
+
* This can only be set to true for FlowerNode and FlowerFlow components.
|
8
|
+
*
|
9
|
+
* An example use case is when transitioning from a FlowerNode containing a particular screen to an action node.
|
10
|
+
*
|
11
|
+
* The node with retain set to true will continue to be displayed until a new FlowerNode or FlowerFlow is added to the history
|
12
|
+
* */
|
3
13
|
retain?: boolean;
|
4
14
|
};
|
@@ -3,25 +3,48 @@ export type UseFlowerProps = {
|
|
3
3
|
[x in 'name' | 'flowName']?: string;
|
4
4
|
};
|
5
5
|
export type UseFlowerForm = (options?: UseFlowerProps) => {
|
6
|
+
/** This value is set to true when the form has been touched at least once. */
|
6
7
|
touched: boolean;
|
8
|
+
/** An object containing all the form errors */
|
7
9
|
errors: Record<string, any>;
|
10
|
+
/** This value is set to true when all the validation rules are satisfied and the form is valid*/
|
8
11
|
isValid: boolean;
|
12
|
+
/** This value is set to true during asynchronous validation.*/
|
9
13
|
isValidating: boolean | undefined;
|
14
|
+
/** Use this function to read values from the flow's data. */
|
10
15
|
getData: (path?: string) => any;
|
11
|
-
|
12
|
-
|
16
|
+
/** Use this function to set values in the flow's data. */
|
17
|
+
setData: (
|
18
|
+
/** The value that you want to set */
|
19
|
+
value: any,
|
20
|
+
/** Specify the target path to set the value*/
|
21
|
+
path?: string) => void;
|
22
|
+
/** Use this function to unset values in the flow's data. */
|
23
|
+
unsetData: (
|
24
|
+
/** Specify the target path to the value tha you want to unset*/
|
25
|
+
path: string) => void;
|
26
|
+
/** Use this function to replace a value in the flow's data. */
|
13
27
|
replaceData: (value: any) => void;
|
14
28
|
};
|
15
29
|
type useFlowerActions = {
|
30
|
+
/** Use this function to move to the next node inside the flow*/
|
16
31
|
next: (payload?: Route) => void;
|
32
|
+
/**Use this function to move to the previous node inside the flow*/
|
17
33
|
back: (payload?: RoutePrev) => void;
|
34
|
+
/**Use this function to return to the first node and restore history */
|
18
35
|
reset: (payload?: RouteReset) => void;
|
36
|
+
/**Use this function to move to a specific node*/
|
19
37
|
jump: (payload: RouteNode) => void;
|
38
|
+
/**Use this function to reset the flow data and history */
|
20
39
|
restart: (payload?: RouteRestart) => void;
|
21
40
|
};
|
22
41
|
export type UseFlower = (options?: UseFlowerProps) => useFlowerActions & {
|
42
|
+
/**The flow in which the hook is used.*/
|
23
43
|
flowName?: string;
|
44
|
+
/**Current node id*/
|
24
45
|
nodeId: string;
|
46
|
+
/**Initial start node id*/
|
47
|
+
startId: string;
|
25
48
|
};
|
26
49
|
export type NavigateFunctionParams = string | Record<string, any>;
|
27
50
|
export {};
|
@@ -3,6 +3,10 @@ export type Route = string | Record<string, any>;
|
|
3
3
|
export type RouteNode = string | {
|
4
4
|
node: string;
|
5
5
|
flowName?: string;
|
6
|
+
/**
|
7
|
+
* The flow's history on server component
|
8
|
+
* @experimental
|
9
|
+
*/
|
6
10
|
history?: string[];
|
7
11
|
};
|
8
12
|
export type RouteRestart = string | {
|
@@ -19,9 +23,19 @@ export type RoutePrev = string | {
|
|
19
23
|
flowName?: string;
|
20
24
|
};
|
21
25
|
export type FlowerNavigateProps = {
|
26
|
+
/** The name of the flow from which read the data
|
27
|
+
*
|
28
|
+
* - note: the default value is the name of the flow where the component is used
|
29
|
+
*/
|
22
30
|
flowName?: string | undefined;
|
31
|
+
/** The FlowerNavigate's children */
|
23
32
|
children: React.ReactNode | ((props: Record<string, any>) => React.ReactNode | React.ReactElement | undefined);
|
33
|
+
/** An object containing the display rules of that component. When the conditions are not satisfied, the children is hidden. */
|
24
34
|
rules?: RulesObject<Record<string, any>> | Record<string, RulesObject<any>>;
|
35
|
+
/** When set to true, the children is shown even if the rules are not satisfied
|
36
|
+
*
|
37
|
+
* The FlowerValue returns the boolean variable "hidden" to notify you if the conditions are satisfied or not
|
38
|
+
*/
|
25
39
|
alwaysDisplay?: boolean;
|
26
40
|
} & ({
|
27
41
|
action?: 'next';
|
@@ -1,4 +1,14 @@
|
|
1
1
|
import { FlowerNodeDefaultProps } from './DefaultNode';
|
2
2
|
export type FlowerNodeProps = FlowerNodeDefaultProps & {
|
3
|
+
/** A Flower node is visible only when is the current node in the navigation history of the flow.
|
4
|
+
*
|
5
|
+
* By setting this prop to true, you have the ability to view this node even when it is not the current node.
|
6
|
+
*
|
7
|
+
* This can only be set to true for FlowerNode and FlowerFlow components.
|
8
|
+
*
|
9
|
+
* An example use case is when transitioning from a FlowerNode containing a particular screen to an action node.
|
10
|
+
*
|
11
|
+
* The node with retain set to true will continue to be displayed until a new FlowerNode or FlowerFlow is added to the history
|
12
|
+
* */
|
3
13
|
retain?: boolean;
|
4
14
|
};
|
@@ -1,8 +1,25 @@
|
|
1
1
|
export type FlowerRouteProps = {
|
2
|
+
/** Unique identifier for the node inside the Flow */
|
2
3
|
id: string;
|
4
|
+
/** TODO document what this props does */
|
3
5
|
autostart?: boolean;
|
6
|
+
/** An object containing the informations about the node's links.
|
7
|
+
*
|
8
|
+
* Example: to={{ step2: { rules: { $and: [{ name: { $eq: 'John' } }] } } }}
|
9
|
+
*
|
10
|
+
* In that case, the FlowerRoute is linked to the node with the ID 'step2'
|
11
|
+
*
|
12
|
+
* You can move from the current node to the step2 node only if the rules are satisfied
|
13
|
+
*
|
14
|
+
* Set it to null when you don't have rules
|
15
|
+
*
|
16
|
+
* Example: to={{ step2: null}
|
17
|
+
*/
|
4
18
|
to?: Record<string, any>;
|
19
|
+
/** The children of the FlowerRoute */
|
5
20
|
children?: React.ReactNode;
|
21
|
+
/** The function executed when you enter into this FlowerRoute */
|
6
22
|
onEnter?: () => void;
|
23
|
+
/** The function executed when you leave this FlowerRoute */
|
7
24
|
onExit?: () => void;
|
8
25
|
};
|