@flowerforce/flower-react 3.7.2 → 4.0.1-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.
Files changed (61) hide show
  1. package/README.md +290 -199
  2. package/dist/index.cjs.js +427 -670
  3. package/dist/index.esm.js +409 -654
  4. package/dist/src/components/Flower.d.ts +3 -15
  5. package/dist/src/components/FlowerAction.d.ts +2 -3
  6. package/dist/src/components/FlowerComponent.d.ts +2 -4
  7. package/dist/src/components/FlowerFlow.d.ts +2 -3
  8. package/dist/src/components/FlowerNavigate/FlowerNavigate.d.ts +3 -0
  9. package/dist/src/components/FlowerNavigate/WrapperComponent.d.ts +3 -3
  10. package/dist/src/components/FlowerNavigate/index.d.ts +2 -4
  11. package/dist/src/components/FlowerNavigate/useFlowerNavigate.d.ts +1 -1
  12. package/dist/src/components/FlowerNode.d.ts +2 -3
  13. package/dist/src/components/FlowerRoute.d.ts +2 -3
  14. package/dist/src/components/FlowerServer.d.ts +2 -3
  15. package/dist/src/components/FlowerStart.d.ts +3 -4
  16. package/dist/src/components/hooks/eventsHandlers.d.ts +5 -0
  17. package/dist/src/components/hooks/index.d.ts +5 -0
  18. package/dist/src/components/hooks/types/index.d.ts +29 -0
  19. package/dist/src/components/hooks/useBrowserNavigationSync.d.ts +12 -0
  20. package/dist/src/components/hooks/useDestroyFlow.d.ts +2 -0
  21. package/dist/src/components/hooks/useInitDevtools.d.ts +2 -0
  22. package/dist/src/components/hooks/useInitNodes.d.ts +2 -0
  23. package/dist/src/components/hooks/useSelectorsClient.d.ts +6 -0
  24. package/dist/src/components/index.d.ts +10 -0
  25. package/dist/src/components/{useFlower.d.ts → useFlower/index.d.ts} +2 -3
  26. package/dist/src/components/useFlower/utils.d.ts +22 -0
  27. package/dist/src/features/index.d.ts +2 -0
  28. package/dist/src/features/reducer/flowerReducer.d.ts +7 -0
  29. package/dist/src/{selectors.d.ts → features/selectors/selectors.d.ts} +669 -383
  30. package/dist/src/index.d.ts +6 -33
  31. package/dist/src/provider/createSliceWithFlower.d.ts +2 -0
  32. package/dist/src/provider/createStoreWithFlower.d.ts +8 -0
  33. package/dist/src/provider/index.d.ts +3 -0
  34. package/dist/src/provider/provider.d.ts +7 -0
  35. package/dist/src/types/FlowContext.d.ts +6 -0
  36. package/dist/src/types/Flower.d.ts +15 -0
  37. package/dist/src/types/FlowerHooks.d.ts +27 -0
  38. package/dist/src/{components/types → types}/FlowerProvider.d.ts +3 -3
  39. package/dist/src/types/index.d.ts +10 -0
  40. package/dist/src/types/middlewares.d.ts +16 -0
  41. package/dist/src/types/utilsTypes.d.ts +4 -0
  42. package/dist/src/utils.d.ts +2 -10
  43. package/package.json +15 -7
  44. package/dist/src/components/FlowerField.d.ts +0 -4
  45. package/dist/src/components/FlowerRule.d.ts +0 -4
  46. package/dist/src/components/FlowerValue.d.ts +0 -4
  47. package/dist/src/components/types/FlowerField.d.ts +0 -124
  48. package/dist/src/components/types/FlowerHooks.d.ts +0 -72
  49. package/dist/src/components/types/FlowerRule.d.ts +0 -35
  50. package/dist/src/components/types/FlowerValue.d.ts +0 -33
  51. package/dist/src/components/useFlowerForm.d.ts +0 -26
  52. package/dist/src/context.d.ts +0 -10
  53. package/dist/src/provider.d.ts +0 -25
  54. package/dist/src/reducer.d.ts +0 -7
  55. /package/dist/src/{components/types → types}/DefaultNode.d.ts +0 -0
  56. /package/dist/src/{components/types → types}/FlowerComponent.d.ts +0 -0
  57. /package/dist/src/{components/types → types}/FlowerFlow.d.ts +0 -0
  58. /package/dist/src/{components/types → types}/FlowerNavigate.d.ts +0 -0
  59. /package/dist/src/{components/types → types}/FlowerNode.d.ts +0 -0
  60. /package/dist/src/{components/types → types}/FlowerRoute.d.ts +0 -0
  61. /package/dist/src/{components/types → types}/FlowerServer.d.ts +0 -0
package/dist/index.cjs.js CHANGED
@@ -3,122 +3,101 @@
3
3
  var React = require('react');
4
4
  var _keyBy = require('lodash/keyBy');
5
5
  var flowerCore = require('@flowerforce/flower-core');
6
+ var flowerReactContext = require('@flowerforce/flower-react-context');
6
7
  var _get = require('lodash/get');
8
+ var flowerReactStore = require('@flowerforce/flower-react-store');
7
9
  var toolkit = require('@reduxjs/toolkit');
8
10
  var reselect = require('reselect');
9
- var reactRedux = require('react-redux');
10
- var debounce = require('lodash/debounce');
11
- var isEqual = require('lodash/isEqual');
11
+ var flowerReactHistoryContext = require('@flowerforce/flower-react-history-context');
12
+ var flowerReactShared = require('@flowerforce/flower-react-shared');
13
+ var set = require('lodash/set');
12
14
 
13
- const _context = React.createContext({});
14
- const context = _context;
15
- const Provider = _context.Provider;
16
- const Consumer = _context.Consumer;
17
-
18
- // eslint-disable-next-line import/prefer-default-export
19
- const convertElements = (nodes) => {
20
- const res = flowerCore.CoreUtils.generateNodesForFlowerJson(nodes);
21
- return res;
15
+ const getRulesExists = (rules) => {
16
+ return Object.keys(rules).length ? flowerCore.FlowUtils.mapEdge(rules) : undefined;
22
17
  };
18
+ function hasDisplayName(type) {
19
+ return (!!type &&
20
+ typeof type === 'object' &&
21
+ 'displayName' in type &&
22
+ typeof type.displayName === 'string');
23
+ }
24
+ const generateNodesForFlowerJson = (nodes) => nodes
25
+ .filter((child) => typeof child === 'object' && child !== null && 'props' in child)
26
+ .filter((e) => !!_get(e, 'props.id'))
27
+ .map((e) => {
28
+ const rules = flowerCore.FlowUtils.makeRules(e.props.to ?? {});
29
+ const nextRules = getRulesExists(rules);
30
+ const children = e.props.data?.children;
31
+ const nodeType = hasDisplayName(e.type)
32
+ ? e.type.displayName
33
+ : e.props.as || 'FlowerNode';
34
+ return {
35
+ nodeId: e.props.id,
36
+ nodeType,
37
+ nodeTitle: _get(e.props, 'data.title'),
38
+ children,
39
+ nextRules,
40
+ retain: e.props.retain,
41
+ disabled: e.props.disabled
42
+ };
43
+ });
23
44
 
24
45
  const flowerReducer = toolkit.createSlice({
25
- name: 'flower',
46
+ name: flowerCore.REDUCER_NAME.FLOWER_FLOW,
26
47
  initialState: {},
27
- reducers: flowerCore.FlowerCoreReducers
48
+ reducers: flowerCore.FlowerCoreBaseReducers
28
49
  });
29
- const { actions } = flowerReducer;
50
+ const flowerActions = flowerReducer.actions;
51
+ const flowerFlowReducer = flowerReducer.reducer;
30
52
  const reducerFlower = {
31
- flower: flowerReducer.reducer
53
+ [flowerCore.REDUCER_NAME.FLOWER_FLOW]: flowerFlowReducer
54
+ };
55
+ const flowerReducers = {
56
+ ...reducerFlower,
57
+ ...flowerReactStore.reducerData
32
58
  };
33
59
 
34
- const { getAllData: mapData } = flowerCore.FlowerStateUtils;
35
- const { selectGlobal } = flowerCore.Selectors;
36
- const selectFlower = (name) => reselect.createSelector(selectGlobal, flowerCore.Selectors.selectFlower(name));
37
- const selectFlowerFormNode = (name, id) => reselect.createSelector(selectFlower(name), flowerCore.Selectors.selectFlowerFormNode(id));
38
- const selectFlowerHistory = (name) => reselect.createSelector(selectFlower(name), flowerCore.Selectors.selectFlowerHistory);
39
- const makeSelectNodesIds = (name) => reselect.createSelector(selectFlower(name), flowerCore.Selectors.makeSelectNodesIds);
40
- const makeSelectStartNodeId = (name) => reselect.createSelector(selectFlower(name), flowerCore.Selectors.makeSelectStartNodeId);
41
- const makeSelectCurrentNodeId = (name) => reselect.createSelector(selectFlower(name), makeSelectStartNodeId(name), flowerCore.Selectors.makeSelectCurrentNodeId);
42
- const makeSelectPrevNodeRetain = (name) => reselect.createSelector(makeSelectNodesIds(name), selectFlowerHistory(name), makeSelectCurrentNodeId(name), flowerCore.Selectors.makeSelectPrevNodeRetain);
43
- const makeSelectCurrentNodeDisabled = (name) => reselect.createSelector(makeSelectNodesIds(name), makeSelectCurrentNodeId(name), flowerCore.Selectors.makeSelectCurrentNodeDisabled);
44
- // dati nel flow selezionato
45
- const getDataByFlow = (name) => reselect.createSelector(selectFlower(name), flowerCore.Selectors.getDataByFlow);
46
- // selettore per recuperare i dati di un flow specifico e id specifico
47
- const getDataFromState = (name, id) => reselect.createSelector(getDataByFlow(name), flowerCore.Selectors.getDataFromState(id));
48
- const makeSelectNodeErrors = (name, currentNodeId) => reselect.createSelector(selectFlowerFormNode(name, currentNodeId), flowerCore.Selectors.makeSelectNodeErrors);
49
- const makeSelectNodeFieldTouched = (name, currentNodeId, fieldId) => reselect.createSelector(selectFlowerFormNode(name, currentNodeId), flowerCore.Selectors.makeSelectNodeFormFieldTouched(fieldId));
50
- const makeSelectNodeFieldFocused = (name, currentNodeId, fieldId) => reselect.createSelector(selectFlowerFormNode(name, currentNodeId), flowerCore.Selectors.makeSelectNodeFormFieldFocused(fieldId));
51
- const makeSelectNodeFieldDirty = (name, currentNodeId, fieldId) => reselect.createSelector(selectFlowerFormNode(name, currentNodeId), flowerCore.Selectors.makeSelectNodeFormFieldDirty(fieldId));
52
- const makeSelectNodeFormSubmitted = (name, currentNodeId) => reselect.createSelector(selectFlowerFormNode(name, currentNodeId), flowerCore.Selectors.makeSelectNodeFormSubmitted);
53
- const getAllData = reselect.createSelector(selectGlobal, mapData);
54
- const selectFlowerFormCurrentNode = (name) => reselect.createSelector(selectFlower(name), makeSelectCurrentNodeId(name), (data, current) => {
55
- return _get(data, ['form', current]);
56
- });
57
- const makeSelectFieldError = (name, id, validate) => reselect.createSelector(getAllData, selectFlowerFormCurrentNode(name), flowerCore.Selectors.makeSelectFieldError(name, id, validate));
58
- const selectorRulesDisabled = (id, rules, keys, flowName, value, currentNode) => reselect.createSelector(getAllData, makeSelectNodeErrors(flowName, currentNode), flowerCore.Selectors.selectorRulesDisabled(id, rules, keys, flowName, value));
59
-
60
- //TODO check reduxContext type due to remove all any types
61
- const reduxContext = React.createContext(null);
62
- const useDispatch = reactRedux.createDispatchHook(reduxContext);
63
- const useSelector = reactRedux.createSelectorHook(reduxContext);
64
- const useStore = reactRedux.createStoreHook(reduxContext);
65
- const store = ({ enableDevtool }) => toolkit.configureStore({
66
- reducer: reducerFlower,
67
- devTools: enableDevtool ? { name: 'flower' } : false
60
+ const { selectGlobal, selectGlobalData } = flowerCore.FlowerCoreStateSelectors;
61
+ const selectFlower = (name) => reselect.createSelector(selectGlobal, flowerCore.FlowerCoreStateSelectors.selectFlower(name));
62
+ const selectFlowerData = selectGlobalData;
63
+ const selectFlowerDataNode = (name) => reselect.createSelector(selectFlowerData, (data) => {
64
+ return data[name];
68
65
  });
69
- class FlowerProvider extends React.PureComponent {
70
- constructor(props) {
71
- super(props);
72
- this.store = store({ enableDevtool: props.enableReduxDevtool });
73
- }
74
- render() {
75
- const { children } = this.props;
76
- return (React.createElement(reactRedux.Provider, { context: reduxContext, store: this.store }, children));
77
- }
78
- }
66
+ const selectFlowerHistory = (name) => reselect.createSelector(selectFlower(name), flowerCore.FlowerCoreStateSelectors.selectFlowerHistory);
67
+ const makeSelectNodesIds = (name) => reselect.createSelector(selectFlower(name), flowerCore.FlowerCoreStateSelectors.makeSelectNodesIds);
68
+ const makeSelectStartNodeId = (name) => reselect.createSelector(selectFlower(name), flowerCore.FlowerCoreStateSelectors.makeSelectStartNodeId);
69
+ const makeSelectCurrentNodeId = (name) => reselect.createSelector(selectFlower(name), makeSelectStartNodeId(name), flowerCore.FlowerCoreStateSelectors.makeSelectCurrentNodeId);
70
+ const makeSelectIsCurrentNode = (name) => reselect.createSelector(selectFlower(name), makeSelectCurrentNodeId(name), (flower, current) => flower.nodes[current]);
71
+ const makeSelectPrevNodeRetain = (name) => reselect.createSelector(makeSelectNodesIds(name), selectFlowerHistory(name), makeSelectCurrentNodeId(name), flowerCore.FlowerCoreStateSelectors.makeSelectPrevNodeRetain);
72
+ const makeSelectCurrentNodeDisabled = (name) => reselect.createSelector(makeSelectNodesIds(name), makeSelectCurrentNodeId(name), flowerCore.FlowerCoreStateSelectors.makeSelectCurrentNodeDisabled);
73
+ // dati nel flow selezionato
74
+ const makeSelectData = (name) => reselect.createSelector(selectFlowerDataNode(name), (data) => data?.data ?? {});
75
+ reselect.createSelector(selectGlobalData, flowerCore.FlowerStateUtils.getAllData);
79
76
 
80
- /* eslint-disable no-undef */
81
- /* eslint-disable no-underscore-dangle */
82
- /**
83
- * FlowerClient
84
- */
85
- const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null, initialData = {}, initialState = {} }) => {
86
- const flowName = name;
87
- const dispatch = useDispatch();
88
- const one = React.useRef(false);
89
- const [wsDevtools, setWsDevtools] = React.useState(flowerCore.devtoolState && _get(flowerCore.devtoolState, '__FLOWER_DEVTOOLS_INITIALIZED__', false));
90
- // TODO rivedere il giro, potremmo fare le trasformazioni in CoreUtils.generateNodesForFlowerJson
91
- // eslint-disable-next-line react-hooks/exhaustive-deps, max-len
92
- const nodes = React.useMemo(() => convertElements(React.Children.toArray(children)), [children]);
93
- const nodeById = React.useMemo(() => _keyBy(React.Children.toArray(children), 'props.id'), [children]);
94
- const isInitialized = useSelector(makeSelectStartNodeId(name));
95
- const history = useSelector(selectFlowerHistory(name));
96
- const current = useSelector(makeSelectCurrentNodeId(flowName));
97
- const isDisabled = useSelector(makeSelectCurrentNodeDisabled(flowName));
98
- const prevFlowerNodeId = useSelector(makeSelectPrevNodeRetain(flowName));
99
- const store = useStore();
77
+ const useInitNodes = ({ one, nodes, name, startId, persist = false, initialData, initialState = {} }) => {
78
+ const { dispatch } = flowerReactStore.ReduxFlowerProvider.getReduxHooks();
100
79
  React.useEffect(() => {
101
80
  if (nodes.length > 0 && one.current === false) {
102
81
  one.current = true;
103
- dispatch(actions.initNodes({
104
- name: flowName,
105
- // @ts-expect-error FIX ME
82
+ dispatch(flowerActions.initNodes({
83
+ name,
106
84
  nodes,
107
85
  startId: startId ?? '',
108
- persist: destroyOnUnmount === false,
109
- initialData,
86
+ persist,
110
87
  initialState
111
88
  }));
112
89
  }
113
- }, [
114
- dispatch,
115
- flowName,
116
- nodes,
117
- startId,
118
- initialData,
119
- destroyOnUnmount,
120
- initialState
121
- ]);
90
+ if (initialData) {
91
+ dispatch(flowerReactStore.flowerDataActions.initData({
92
+ rootName: name,
93
+ initialData: initialData ?? {}
94
+ }));
95
+ }
96
+ }, [dispatch, name, nodes, startId, initialData, initialState, persist]);
97
+ };
98
+
99
+ const useInitDevtools = ({ devtoolState, setWsDevtools, flowName, }) => {
100
+ const { dispatch } = flowerReactStore.ReduxFlowerProvider.getReduxHooks();
122
101
  React.useEffect(() => {
123
102
  /* istanbul ignore next */
124
103
  const eventCb = (msg) => {
@@ -129,33 +108,42 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
129
108
  setWsDevtools(true);
130
109
  }
131
110
  if (msg.action === 'SELECTED_NODE' && msg.name === flowName) {
132
- dispatch(actions.setCurrentNode({ name: msg.name, node: msg.id }));
133
- }
134
- if (msg.action === 'REPLACE_DATA' && msg.name === flowName) {
135
- dispatch(actions.replaceData({ flowName: msg.name, value: msg.data }));
136
- }
137
- if (msg.action === 'ADD_DATA' && msg.name === flowName) {
138
- dispatch(actions.addData({ flowName: msg.name, value: msg.data }));
111
+ dispatch(flowerActions.setCurrentNode({ name: msg.name, node: msg.id }));
139
112
  }
113
+ // if (msg.action === 'REPLACE_DATA' && msg.name === flowName) {
114
+ // dispatch(actions.replaceData({ flowName: msg.name, value: msg.data }))
115
+ // }
116
+ // if (msg.action === 'ADD_DATA' && msg.name === flowName) {
117
+ // dispatch(actions.addData({ flowName: msg.name, value: msg.data }))
118
+ // }
140
119
  };
141
120
  /* istanbul ignore next */
142
- if (flowerCore.devtoolState && _get(flowerCore.devtoolState, '__FLOWER_DEVTOOLS__')) {
121
+ if (devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS__')) {
143
122
  flowerCore.Emitter.on('flower-devtool-to-client', eventCb);
144
123
  }
145
124
  return () => {
146
125
  /* istanbul ignore next */
147
- if (flowerCore.devtoolState && _get(flowerCore.devtoolState, '__FLOWER_DEVTOOLS__')) {
126
+ if (devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS__')) {
148
127
  flowerCore.Emitter.off('flower-devtool-to-client', eventCb);
149
128
  }
150
129
  };
151
130
  }, [dispatch, flowName]);
131
+ };
132
+
133
+ const useDestroyFlow = ({ flowName, one, persist }) => {
134
+ const { dispatch } = flowerReactStore.ReduxFlowerProvider.getReduxHooks();
152
135
  React.useEffect(() => () => {
153
- // unmount function
154
- if (destroyOnUnmount && one.current === true) {
136
+ if (persist)
137
+ return;
138
+ if (one.current === true) {
155
139
  one.current = false;
156
- dispatch(actions.destroy({ name: flowName }));
140
+ dispatch(flowerActions.destroy({ name: flowName }));
157
141
  }
158
- }, [dispatch, flowName, destroyOnUnmount]);
142
+ }, [dispatch, flowName, persist]);
143
+ };
144
+
145
+ const useClientInitEvent = ({ flowName, isInitialized, wsDevtools }) => {
146
+ const { store } = flowerReactStore.ReduxFlowerProvider.getReduxHooks();
159
147
  React.useEffect(() => {
160
148
  /* istanbul ignore next */
161
149
  if (isInitialized &&
@@ -171,7 +159,11 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
171
159
  getState: store.getState
172
160
  });
173
161
  }
174
- }, [dispatch, flowName, wsDevtools, isInitialized, store]);
162
+ }, [flowName, wsDevtools, isInitialized, store]);
163
+ };
164
+ const useSetHistoryEvent = ({ flowName, isInitialized, wsDevtools }) => {
165
+ const { useSelector } = flowerReactStore.ReduxFlowerProvider.getReduxHooks();
166
+ const history = useSelector(selectFlowerHistory(flowName));
175
167
  React.useEffect(() => {
176
168
  /* istanbul ignore next */
177
169
  if (isInitialized &&
@@ -185,7 +177,9 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
185
177
  history
186
178
  });
187
179
  }
188
- }, [dispatch, flowName, history, wsDevtools, isInitialized]);
180
+ }, [flowName, history, wsDevtools, isInitialized]);
181
+ };
182
+ const useSetCurrentEvent = ({ current, flowName, isInitialized, wsDevtools }) => {
189
183
  React.useEffect(() => {
190
184
  if (!current)
191
185
  return;
@@ -205,6 +199,9 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
205
199
  });
206
200
  }
207
201
  }, [flowName, current, wsDevtools, isInitialized]);
202
+ };
203
+ const useFlowerNavigateEvent = ({ current, flowName, isInitialized, wsDevtools, isDisabled }) => {
204
+ const { dispatch } = flowerReactStore.ReduxFlowerProvider.getReduxHooks();
208
205
  React.useEffect(() => {
209
206
  if (!current)
210
207
  return;
@@ -212,7 +209,7 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
212
209
  if (!isInitialized)
213
210
  return;
214
211
  if (isDisabled) {
215
- dispatch({ type: 'flower/next', payload: { flowName, disabled: true } });
212
+ dispatch({ type: `${flowerCore.REDUCER_NAME.FLOWER_FLOW}/next`, payload: { flowName, disabled: true } });
216
213
  // eslint-disable-next-line no-underscore-dangle, no-undef
217
214
  /* istanbul ignore next */
218
215
  if (wsDevtools &&
@@ -244,43 +241,72 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
244
241
  time: new Date()
245
242
  });
246
243
  }
247
- }, [
248
- dispatch,
249
- flowName,
244
+ }, [dispatch, flowName, current, isDisabled, wsDevtools, isInitialized]);
245
+ };
246
+
247
+ const useSelectorsClient = (flowName) => {
248
+ const { useSelector } = flowerReactStore.ReduxFlowerProvider.getReduxHooks();
249
+ const isInitialized = useSelector(makeSelectStartNodeId(flowName));
250
+ const current = useSelector(makeSelectCurrentNodeId(flowName));
251
+ const isDisabled = useSelector(makeSelectCurrentNodeDisabled(flowName));
252
+ const prevFlowerNodeId = useSelector(makeSelectPrevNodeRetain(flowName));
253
+ return {
254
+ isInitialized,
250
255
  current,
251
256
  isDisabled,
252
- store,
253
- wsDevtools,
254
- isInitialized
255
- ]);
257
+ prevFlowerNodeId
258
+ };
259
+ };
260
+
261
+ /*
262
+ * FlowerClient
263
+ */
264
+ const FlowerClient = ({ children, name, destroyOnUnmount = true, persist = false, startId = null, initialState = {}, initialData }) => {
265
+ const flowName = name;
266
+ const _persist = persist || !destroyOnUnmount;
267
+ const one = React.useRef(false);
268
+ const [wsDevtools, setWsDevtools] = React.useState(flowerCore.devtoolState && _get(flowerCore.devtoolState, '__FLOWER_DEVTOOLS_INITIALIZED__', false));
269
+ const nodes = React.useMemo(() => generateNodesForFlowerJson(React.Children.toArray(children)), [children]);
270
+ const nodeById = React.useMemo(() => _keyBy(React.Children.toArray(children), 'props.id'), [children]);
271
+ const { current, isDisabled, isInitialized, prevFlowerNodeId } = useSelectorsClient(flowName);
272
+ useInitNodes({
273
+ name,
274
+ nodes,
275
+ one,
276
+ initialData,
277
+ initialState,
278
+ persist: _persist,
279
+ startId
280
+ });
281
+ useDestroyFlow({ flowName, one, persist: _persist });
282
+ useInitDevtools({ devtoolState: flowerCore.devtoolState, setWsDevtools, flowName });
283
+ useClientInitEvent({ flowName, isInitialized, wsDevtools });
284
+ useSetHistoryEvent({ flowName, isInitialized, wsDevtools });
285
+ useSetCurrentEvent({ current, flowName, isInitialized, wsDevtools });
286
+ useFlowerNavigateEvent({
287
+ current,
288
+ flowName,
289
+ isDisabled,
290
+ isInitialized,
291
+ wsDevtools
292
+ });
256
293
  const currentNodeId = prevFlowerNodeId || current;
257
294
  const contextValues = React.useMemo(() => ({
258
- flowName,
259
- initialData,
295
+ name: flowName,
260
296
  currentNode: current
261
- }), [flowName, initialData, current]);
297
+ }), [flowName, current]);
262
298
  const prevContextValues = React.useMemo(() => ({
263
- flowName,
264
- initialData,
299
+ name: flowName,
265
300
  currentNode: currentNodeId
266
- }), [flowName, initialData, currentNodeId]);
301
+ }), [flowName, currentNodeId]);
267
302
  return isInitialized ? (React.createElement(React.Fragment, null,
268
- React.createElement(Provider, { value: prevContextValues }, nodeById[currentNodeId]),
269
- React.createElement(Provider, { value: contextValues }, !isDisabled && current !== currentNodeId && nodeById[current]))) : null;
270
- };
271
- const component$c = React.memo(FlowerClient);
272
-
273
- const FlowerNode = ({ children, onEnter, onExit }) => {
274
- React.useEffect(() => {
275
- onEnter?.();
276
- return () => {
277
- onExit?.();
278
- };
279
- }, [onEnter, onExit]);
280
- return children;
303
+ React.createElement(flowerReactContext.FlowerReactProvider, { value: prevContextValues }, nodeById[currentNodeId]),
304
+ React.createElement(flowerReactContext.FlowerReactProvider, { value: contextValues }, !isDisabled && current !== currentNodeId && nodeById[current]))) : null;
281
305
  };
282
- const component$b = React.memo(FlowerNode);
283
- component$b.displayName = 'FlowerNode';
306
+ const component$8 = React.memo(FlowerClient);
307
+ component$8.displayName = 'Flower';
308
+ // workaround for let typescript read JSX component as a valid JSX element using react 19(?)
309
+ const Flower = component$8;
284
310
 
285
311
  const FlowAction = ({ children, onEnter, onExit }) => {
286
312
  React.useEffect(() => {
@@ -289,400 +315,39 @@ const FlowAction = ({ children, onEnter, onExit }) => {
289
315
  onExit?.();
290
316
  };
291
317
  }, [onEnter, onExit]);
292
- return children;
318
+ return React.createElement(React.Fragment, null, children);
293
319
  };
294
- const component$a = React.memo(FlowAction);
295
- component$a.displayName = 'FlowerAction';
320
+ const component$7 = React.memo(FlowAction);
321
+ component$7.displayName = 'FlowerAction';
322
+ const FlowerAction = component$7;
296
323
 
297
- const FlowerServer = ({ children }) => {
298
- return children;
299
- };
300
- const component$9 = React.memo(FlowerServer);
301
- component$9.displayName = 'FlowerServer';
302
-
303
- const FlowerFlow = ({ children, onEnter, onExit }) => {
304
- React.useEffect(() => {
305
- onEnter?.();
306
- return () => {
307
- onExit?.();
308
- };
309
- }, [onEnter, onExit]);
310
- return children;
311
- };
312
- const component$8 = React.memo(FlowerFlow);
313
- component$8.displayName = 'FlowerFlow';
314
-
315
- function FlowerStart() {
316
- const dispatch = useDispatch();
317
- const one = React.useRef(false);
318
- const { flowName, autostart = true, currentNode } = React.useContext(context);
319
- const startNodeId = useSelector(makeSelectStartNodeId(flowName ?? ''));
320
- React.useEffect(() => {
321
- if (startNodeId === currentNode && autostart && one.current === false) {
322
- one.current = true;
323
- dispatch({ type: 'flower/next', payload: { flowName, isStart: true } });
324
- // if (global.window
325
- // // eslint-disable-next-line no-underscore-dangle, no-undef
326
- // && global.window.__FLOWER_DEVTOOLS__ && global.window.__FLOWER_DEVTOOLS__AUTO) {
327
- // Emitter.emit('flower-devtool-from-client', {
328
- // source: 'flower-client',
329
- // action: 'START_FLOWER',
330
- // name: flowName,
331
- // });
332
- // }
333
- }
334
- }, [dispatch, autostart, startNodeId, currentNode, flowName]);
335
- return null;
336
- }
337
- const component$7 = React.memo(FlowerStart);
338
- component$7.displayName = 'FlowerStart';
324
+ const _FlowerComponent = ({ children }) => children;
325
+ React.memo(_FlowerComponent);
339
326
 
340
- const FlowerRoute = ({ autostart = true, children, onEnter, onExit }) => {
341
- const dispatch = useDispatch();
342
- const one = React.useRef(false);
343
- const { flowName } = React.useContext(context);
327
+ const _FlowerFlow = ({ children, onEnter, onExit }) => {
344
328
  React.useEffect(() => {
345
329
  onEnter?.();
346
330
  return () => {
347
331
  onExit?.();
348
332
  };
349
333
  }, [onEnter, onExit]);
350
- React.useEffect(() => {
351
- if (autostart && one.current === false) {
352
- one.current = true;
353
- dispatch({ type: 'flower/next', payload: { flowName } });
354
- }
355
- }, [dispatch, flowName, autostart]);
356
- return children;
334
+ return React.createElement(React.Fragment, null, children);
357
335
  };
358
- const component$6 = React.memo(FlowerRoute);
359
- component$6.displayName = 'FlowerRoute';
360
-
361
- const FlowerRule = ({ children, rules, value, alwaysDisplay, flowName, id, onUpdate }) => {
362
- const { flowName: flowNameContext, currentNode } = React.useContext(context);
363
- const name = flowName || flowNameContext;
364
- const keys = flowerCore.MatchRules.utils.getKeys(rules, { prefix: name });
365
- const hidden = useSelector(selectorRulesDisabled(id ?? '', rules, keys ?? [], name ?? '', value, currentNode ?? ''));
366
- React.useEffect(() => {
367
- if (onUpdate) {
368
- onUpdate(hidden);
369
- }
370
- }, [hidden, onUpdate]);
371
- if (typeof children === 'function') {
372
- if (alwaysDisplay && hidden) {
373
- return children({ hidden });
374
- }
375
- if (hidden) {
376
- return undefined;
377
- }
378
- return children({});
379
- }
380
- if (alwaysDisplay && hidden) {
381
- return React.Children.map(children, (child, i) => {
382
- if (React.isValidElement(child)) {
383
- const { props, type } = child;
384
- const Component = type;
385
- // eslint-disable-next-line react/jsx-props-no-spreading
386
- return Component && React.createElement(Component, { key: i, hidden: true, ...props });
387
- }
388
- return child;
389
- });
390
- }
391
- return hidden
392
- ? undefined
393
- : React.Children.map(children, (child, i) => {
394
- if (React.isValidElement(child)) {
395
- const { props, type } = child;
396
- const Component = type;
397
- // eslint-disable-next-line react/jsx-props-no-spreading
398
- return Component && React.createElement(Component, { key: i, ...props });
399
- }
400
- return child;
401
- });
402
- };
403
- const component$5 = React.memo(FlowerRule);
404
- component$5.displayName = 'FlowerRule';
405
-
406
- /* eslint-disable */
407
- function isIntrinsicElement$1(x) {
408
- return typeof x === 'string';
409
- }
410
- //TODO make types for wrapper function props
411
- function Wrapper$1({ Component, id, flowName, currentNode, validate, asyncDebounce = 0, asyncValidate, asyncInitialError, asyncWaitingError, destroyValue, destroyOnHide, onBlur, onFocus, hidden, onUpdate, defaultValue, ...props }) {
412
- const dispatch = useDispatch();
413
- const [customAsyncErrors, setCustomAsyncErrors] = React.useState(asyncValidate && asyncInitialError && [asyncInitialError]);
414
- const [isValidating, setIsValidating] = React.useState(undefined);
415
- const { flowNameFromPath = flowName, path } = React.useMemo(() => flowerCore.CoreUtils.getPath(id), [id]);
416
- const value = useSelector(getDataFromState(flowNameFromPath, path));
417
- const errors = useSelector(makeSelectFieldError(flowName, id, validate), flowerCore.CoreUtils.allEqual);
418
- const dirty = useSelector(makeSelectNodeFieldDirty(flowName, currentNode, id));
419
- const touched = useSelector(makeSelectNodeFieldTouched(flowName, currentNode, id));
420
- const focused = useSelector(makeSelectNodeFieldFocused(flowName, currentNode, id));
421
- const refValue = React.useRef();
422
- const isSubmitted = useSelector(makeSelectNodeFormSubmitted(flowName, currentNode));
423
- const allErrors = React.useMemo(() => hidden ? [] : [...errors, ...(customAsyncErrors || []).filter(Boolean)], [errors, hidden, customAsyncErrors]);
424
- const setTouched = React.useCallback((touched) => {
425
- dispatch({
426
- type: 'flower/formFieldTouch',
427
- payload: {
428
- name: flowName,
429
- id,
430
- currentNode,
431
- touched
432
- }
433
- });
434
- }, [dispatch, flowName, currentNode, id]);
435
- const setFocus = React.useCallback((focused) => {
436
- dispatch({
437
- type: 'flower/formFieldFocus',
438
- payload: {
439
- name: flowName,
440
- id,
441
- currentNode,
442
- focused
443
- }
444
- });
445
- }, [dispatch, flowName, currentNode, id]);
446
- const validateFn = React.useCallback(async (value) => {
447
- if (asyncWaitingError) {
448
- setCustomAsyncErrors([asyncWaitingError]);
449
- }
450
- setIsValidating(true);
451
- const state = flowerCore.FlowerStateUtils.getAllData(store);
452
- const res = await asyncValidate(value, state, errors);
453
- setIsValidating(false);
454
- setCustomAsyncErrors(res);
455
- }, [asyncWaitingError, errors]);
456
- const debouncedValidation = React.useCallback(debounce(validateFn, asyncDebounce), [
457
- validateFn
458
- ]);
459
- const onChange = React.useCallback((val) => {
460
- if (asyncValidate && asyncWaitingError) {
461
- setCustomAsyncErrors([asyncWaitingError]);
462
- }
463
- dispatch({
464
- type: `flower/addDataByPath`,
465
- payload: {
466
- flowName: flowNameFromPath,
467
- id,
468
- value: val,
469
- dirty: defaultValue ? !isEqual(val, defaultValue) : true
470
- }
471
- });
472
- }, [flowNameFromPath, id, dispatch, setCustomAsyncErrors, asyncValidate, asyncWaitingError]);
473
- const onBlurInternal = React.useCallback((e) => {
474
- setTouched(true);
475
- setFocus(false);
476
- onBlur && onBlur(e);
477
- }, [onBlur, setTouched, setFocus]);
478
- const onFocusInternal = React.useCallback((e) => {
479
- setFocus(true);
480
- onFocus && onFocus(e);
481
- }, [onFocus, setFocus]);
482
- React.useEffect(() => {
483
- if (hidden)
484
- return;
485
- if (asyncValidate) {
486
- if (refValue.current === value)
487
- return;
488
- refValue.current = value;
489
- const hasValue = !flowerCore.MatchRules.utils.isEmpty(value);
490
- if (!hasValue) {
491
- setCustomAsyncErrors(asyncInitialError && [asyncInitialError]);
492
- setIsValidating(false);
493
- return;
494
- }
495
- setTouched(true);
496
- debouncedValidation(value);
497
- }
498
- }, [asyncValidate, asyncInitialError, value, debouncedValidation, setTouched, hidden]);
499
- React.useEffect(() => {
500
- if (onUpdate) {
501
- onUpdate(value);
502
- }
503
- }, [value, onUpdate]);
504
- React.useEffect(() => {
505
- dispatch({
506
- type: 'flower/formAddErrors',
507
- payload: {
508
- name: flowName,
509
- id,
510
- currentNode,
511
- errors: allErrors
512
- }
513
- });
514
- }, [id, flowName, allErrors, currentNode, touched]);
515
- React.useEffect(() => {
516
- dispatch({
517
- type: 'flower/setFormIsValidating',
518
- payload: {
519
- name: flowName,
520
- currentNode,
521
- isValidating
522
- }
523
- });
524
- }, [flowName, currentNode, isValidating]);
525
- const resetField = React.useCallback(() => {
526
- dispatch({
527
- type: 'flower/formFieldTouch',
528
- payload: {
529
- name: flowName,
530
- id,
531
- currentNode,
532
- touched: false
533
- }
534
- });
535
- dispatch({
536
- type: 'flower/formFieldDirty',
537
- payload: {
538
- name: flowName,
539
- id,
540
- currentNode,
541
- dirty: false
542
- }
543
- });
544
- dispatch({
545
- type: 'flower/formRemoveErrors',
546
- payload: {
547
- name: flowName,
548
- id,
549
- currentNode
550
- }
551
- });
552
- }, [currentNode, id, flowName]);
553
- React.useEffect(() => {
554
- // destroy
555
- return () => {
556
- if (destroyValue) {
557
- dispatch({
558
- type: `flower/unsetData`,
559
- payload: { flowName: flowNameFromPath, id: path }
560
- });
561
- }
562
- resetField();
563
- };
564
- }, [destroyValue, id, flowNameFromPath, path, resetField]);
565
- React.useEffect(() => {
566
- if (hidden) {
567
- if (destroyOnHide) {
568
- dispatch({
569
- type: `flower/unsetData`,
570
- payload: { flowName: flowNameFromPath, id: path }
571
- });
572
- resetField();
573
- }
574
- }
575
- }, [destroyOnHide, hidden, flowNameFromPath, path, resetField]);
576
- React.useEffect(() => {
577
- if (defaultValue && !dirty && !isEqual(value, defaultValue)) {
578
- onChange(defaultValue);
579
- }
580
- }, [defaultValue, value, dirty, onChange]);
581
- const newProps = React.useMemo(() => ({
582
- ...props,
583
- id,
584
- value,
585
- errors: allErrors,
586
- hasError: !!allErrors.length,
587
- onChange,
588
- onBlur: onBlurInternal,
589
- onFocus: onFocusInternal,
590
- focused: !!focused,
591
- touched,
592
- dirty,
593
- hidden,
594
- isValidating,
595
- isSubmitted,
596
- }), [
597
- props,
598
- id,
599
- value,
600
- allErrors,
601
- touched,
602
- dirty,
603
- onChange,
604
- onBlurInternal,
605
- onFocusInternal,
606
- hidden,
607
- isValidating,
608
- isSubmitted,
609
- focused
610
- ]);
611
- if (typeof Component === 'function') {
612
- return Component(newProps);
613
- }
614
- // TODO si arriva in questa condizione quando si passa un componente primitivo es. div
615
- // in questo caso non posso props custom di flower
616
- if (isIntrinsicElement$1(Component)) {
617
- return React.createElement(Component, { id: id, ...props });
618
- }
619
- return Component && React.createElement(Component, { ...newProps });
620
- }
621
- const FlowerField = ({ id, validate, asyncValidate, asyncDebounce, asyncInitialError, asyncWaitingError, rules, alwaysDisplay, value, children, defaultValue, destroyOnHide, destroyValue, flowName, onUpdate }) => {
622
- const { flowName: flowNameContext, currentNode } = React.useContext(context);
623
- const name = flowName || flowNameContext;
624
- if (typeof children === 'function') {
625
- return (React.createElement(component$5, { alwaysDisplay: alwaysDisplay, rules: rules, value: value, flowName: name, id: id }, ({ hidden }) => (React.createElement(Wrapper$1, { hidden: hidden, id: id, Component: children, flowName: name, currentNode: currentNode, validate: validate, asyncValidate: asyncValidate, asyncDebounce: asyncDebounce, asyncInitialError: asyncInitialError, asyncWaitingError: asyncWaitingError, destroyValue: destroyValue, onUpdate: onUpdate, defaultValue: defaultValue, destroyOnHide: destroyOnHide }))));
626
- }
627
- return React.Children.map(children, (child, i) => {
628
- if (!React.isValidElement(child)) {
629
- return child;
630
- }
631
- const { type, props } = child;
632
- const Component = type;
633
- return (React.createElement(component$5, { key: i, alwaysDisplay: alwaysDisplay, rules: rules, value: value, flowName: name }, ({ hidden }) => (React.createElement(Wrapper$1, { ...props, hidden: hidden, id: id, Component: Component, flowName: name, currentNode: currentNode, validate: validate, asyncValidate: asyncValidate, asyncDebounce: asyncDebounce, asyncInitialError: asyncInitialError, asyncWaitingError: asyncWaitingError, destroyValue: destroyValue, onUpdate: onUpdate, defaultValue: defaultValue }))));
634
- });
635
- };
636
- const component$4 = React.memo(FlowerField);
637
- component$4.displayName = 'FlowerField';
638
-
639
- /* eslint-disable */
640
- //TODO make types for wrapper function
641
- function Wrapper({ Component, id, flowName, spreadValue, hidden, onUpdate, ...props }) {
642
- const { flowNameFromPath = flowName, path } = React.useMemo(() => flowerCore.CoreUtils.getPath(id), [id]);
643
- const value = useSelector(getDataFromState(flowNameFromPath, path));
644
- const values = spreadValue && typeof value === 'object' && !Array.isArray(value)
645
- ? value
646
- : { value };
647
- React.useEffect(() => {
648
- if (onUpdate) {
649
- onUpdate(value);
650
- }
651
- }, [onUpdate, value]);
652
- return (React.createElement(Component, { id: id, ...props, flowName: flowName, hidden: hidden, ...values }));
653
- }
654
- const RenderRules$1 = ({ id, alwaysDisplay, rules, value, Component, spreadValue, flowName, onUpdate, ...props }) => {
655
- return (React.createElement(component$5, { alwaysDisplay: alwaysDisplay, rules: rules, value: value, flowName: flowName, id: id }, ({ hidden }) => (React.createElement(Wrapper, { ...props, hidden: hidden, id: id, Component: Component, spreadValue: spreadValue, flowName: flowName, onUpdate: onUpdate }))));
656
- };
657
- const FlowerValue = ({ id = '*', rules, alwaysDisplay, value, children, spreadValue, flowName, onUpdate, }) => {
658
- const { flowName: flowNameContext } = React.useContext(context);
659
- const name = flowName || flowNameContext;
660
- if (typeof children === 'function') {
661
- return (React.createElement(RenderRules$1, { id: id, alwaysDisplay: alwaysDisplay, rules: rules, value: value, spreadValue: spreadValue, Component: children, flowName: name, onUpdate: onUpdate }));
662
- }
663
- return React.Children.map(children, (child, i) => {
664
- if (!React.isValidElement(child))
665
- return child;
666
- const { type, props } = child;
667
- const Component = type;
668
- // eslint-disable-next-line react/jsx-props-no-spreading
669
- return (React.createElement(RenderRules$1, { key: i, id: id, alwaysDisplay: alwaysDisplay, rules: rules, value: value, spreadValue: spreadValue, flowName: name, Component: Component, ...props, onUpdate: onUpdate }));
670
- });
671
- };
672
- const component$3 = React.memo(FlowerValue);
673
- component$3.displayName = 'FlowerValue';
336
+ const component$6 = React.memo(_FlowerFlow);
337
+ component$6.displayName = 'FlowerFlow';
338
+ const FlowerFlow = component$6;
674
339
 
675
340
  const ACTION_TYPES = {
676
- back: ['prev', 'prevToNode'],
341
+ back: ['back', 'prevToNode'],
677
342
  jump: ['node', 'node'],
678
343
  next: ['next', 'next'],
679
344
  restart: ['restart', 'restart'],
680
345
  reset: ['reset', 'initializeFromNode']
681
346
  };
682
- const PAYLAOAD_KEYS_NEEDED = {
347
+ const PAYLOAD_KEYS_NEEDED = {
683
348
  back: ['node'],
684
349
  jump: ['node', 'history'],
685
- next: ['node', 'route', 'data'],
350
+ next: ['node', 'route', 'data', 'dataIn'],
686
351
  restart: ['node'],
687
352
  reset: ['node', 'initialData']
688
353
  };
@@ -698,11 +363,49 @@ const makeActionPayload = (actions, keys) => (flowName, params) => {
698
363
  payload
699
364
  };
700
365
  };
701
- const makeActionPayloadOnPrev = makeActionPayload(ACTION_TYPES.back, PAYLAOAD_KEYS_NEEDED.back);
702
- const makeActionPayloadOnReset = makeActionPayload(ACTION_TYPES.reset, PAYLAOAD_KEYS_NEEDED.reset);
703
- const makeActionPayloadOnNode = makeActionPayload(ACTION_TYPES.jump, PAYLAOAD_KEYS_NEEDED.jump);
704
- const makeActionPayloadOnNext = makeActionPayload(ACTION_TYPES.next, PAYLAOAD_KEYS_NEEDED.next);
705
- const makeActionPayloadOnRestart = makeActionPayload(ACTION_TYPES.restart, PAYLAOAD_KEYS_NEEDED.restart);
366
+ const makeActionPayloadOnBack = makeActionPayload(ACTION_TYPES.back, PAYLOAD_KEYS_NEEDED.back);
367
+ const makeActionPayloadOnReset = makeActionPayload(ACTION_TYPES.reset, PAYLOAD_KEYS_NEEDED.reset);
368
+ const makeActionPayloadOnNode = makeActionPayload(ACTION_TYPES.jump, PAYLOAD_KEYS_NEEDED.jump);
369
+ const makeActionPayloadOnNext = makeActionPayload(ACTION_TYPES.next, PAYLOAD_KEYS_NEEDED.next);
370
+ const makeActionPayloadOnRestart = makeActionPayload(ACTION_TYPES.restart, PAYLOAD_KEYS_NEEDED.restart);
371
+ const handleHistoryStackChange = (currentIndex, currentNode, flowName, withUrl) => {
372
+ const historyNode = `/${flowName}/${currentNode.nodeId}`;
373
+ if (currentNode.nodeType === 'FlowerAction')
374
+ return currentIndex;
375
+ const nextIndex = currentIndex + 1;
376
+ if (history.state?.index !== nextIndex) {
377
+ window.history.pushState({
378
+ index: nextIndex,
379
+ stack: [...(window.history.state.stack ?? []), historyNode]
380
+ }, '', withUrl ? historyNode : '');
381
+ }
382
+ return nextIndex;
383
+ };
384
+
385
+ const useHistorySync = ({ backAction, nextAction }) => {
386
+ const { dispatch } = flowerReactStore.ReduxFlowerProvider.getReduxHooks();
387
+ const { index, isActive, setIndex, withUrl } = flowerReactHistoryContext.useHistoryContext();
388
+ React.useEffect(() => {
389
+ if (!isActive)
390
+ return;
391
+ const initialIndex = window.history.state?.index ?? 0;
392
+ setIndex(initialIndex);
393
+ window.history.replaceState({ index: initialIndex, stack: [...(window.history.state?.stack ?? [])] }, withUrl ? '/' : '', '');
394
+ const onPopState = (event) => {
395
+ const newIndex = window.history.state?.index ?? 0;
396
+ if (newIndex > index) {
397
+ nextAction();
398
+ }
399
+ if (newIndex < index) {
400
+ backAction();
401
+ }
402
+ setIndex(newIndex);
403
+ };
404
+ window.addEventListener('popstate', onPopState);
405
+ return () => window.removeEventListener('popstate', onPopState);
406
+ }, [dispatch, backAction, nextAction]);
407
+ };
408
+
706
409
  /** This hook allows you to read flow informations, such as the flowName and ID of the current node.
707
410
  *
708
411
  * It also exposes all the functions to navigate within the flow:
@@ -722,12 +425,19 @@ const makeActionPayloadOnRestart = makeActionPayload(ACTION_TYPES.restart, PAYLA
722
425
  * @param {string} name - optional parameter, if flowName exist, name is not used
723
426
  */
724
427
  const useFlower = ({ flowName: customFlowName, name } = {}) => {
725
- const dispatch = useDispatch();
726
- const { flowName: flowNameDefault, initialData } = React.useContext(context);
727
- const store = useStore();
428
+ const { name: flowNameDefault, initialData } = React.useContext(flowerReactContext.FlowerReactContext);
429
+ const { index, isActive, setIndex, withUrl } = flowerReactHistoryContext.useHistoryContext();
430
+ const { store, dispatch, useSelector } = flowerReactStore.ReduxFlowerProvider.getReduxHooks();
728
431
  const flowName = (customFlowName || name || flowNameDefault);
729
432
  const nodeId = useSelector(makeSelectCurrentNodeId(flowName ?? ''));
433
+ const currentNode = useSelector(makeSelectIsCurrentNode(flowName ?? ''));
730
434
  const startId = useSelector(makeSelectStartNodeId(flowName ?? ''));
435
+ React.useEffect(() => {
436
+ if (currentNode.nodeType === 'FlowerAction')
437
+ return;
438
+ window.history.replaceState({ ...window.history.state }, '', withUrl ? `/${flowName}/${nodeId}` : '');
439
+ }, [nodeId, flowName, withUrl, currentNode]);
440
+ const stack = React.useMemo(() => window.history.state?.stack?.map((path) => path.split('/')[1]), [window.history.state?.stack]);
731
441
  const emitNavigateEvent = React.useCallback(
732
442
  //TODO check this function is needed
733
443
  (params) => {
@@ -745,42 +455,61 @@ const useFlower = ({ flowName: customFlowName, name } = {}) => {
745
455
  }
746
456
  }, [flowName, nodeId]);
747
457
  const next = React.useCallback((param) => {
748
- const params = typeof param === 'string' ? { route: param } : { data: param };
458
+ const params = typeof param === 'string' ? { route: param } : { dataIn: param };
749
459
  const { type, payload } = makeActionPayloadOnNext(flowName, params);
750
460
  dispatch({
751
- type: `flower/${type}`,
752
- payload
461
+ type: `${flowerCore.REDUCER_NAME.FLOWER_FLOW}/${type}`,
462
+ payload: { ...payload, data: store.getState() }
753
463
  });
464
+ if (isActive) {
465
+ setIndex(handleHistoryStackChange(index, currentNode, flowName, withUrl));
466
+ }
754
467
  emitNavigateEvent({ type, payload });
755
- }, [dispatch, emitNavigateEvent, flowName]);
468
+ }, [dispatch, emitNavigateEvent, flowName, store]);
469
+ /**
470
+ * By doing this, we have a full control over flower navigation from both our buttons and browser back and forward navigation
471
+ * In order to trigger back correctly, trigger a real history back
472
+ * If you use replaceState({ index: 2 }) while at index 3,
473
+ * you visually move to step 2, but the browser still sees you at step 3.
474
+ * As a result:
475
+ * - The browser's Forward button stays disabled
476
+ * - No popstate event is triggered
477
+ * - The history flow is broken
478
+ * Use history.back() instead to preserve proper browser navigation.
479
+ */
480
+ const interceptBack = () => {
481
+ window.history.back();
482
+ };
756
483
  const back = React.useCallback((param) => {
757
- const { type, payload } = makeActionPayloadOnPrev(flowName, param);
758
- dispatch({ type: `flower/${type}`, payload });
484
+ const { type, payload } = makeActionPayloadOnBack(isActive ? (stack?.[stack?.length - 1] ?? flowName) : flowName, param);
485
+ dispatch({
486
+ type: `${flowerCore.REDUCER_NAME.FLOWER_FLOW}/${type}`,
487
+ payload
488
+ });
489
+ setIndex(index - 1);
759
490
  emitNavigateEvent({ type, payload });
760
- }, [dispatch, emitNavigateEvent, flowName]);
491
+ }, [dispatch, emitNavigateEvent, flowName, stack]);
492
+ useHistorySync({ backAction: back, nextAction: next });
761
493
  const restart = React.useCallback((param) => {
762
494
  const { type, payload } = makeActionPayloadOnRestart(flowName, param);
763
- dispatch({ type: `flower/${type}`, payload });
495
+ dispatch({ type: `${flowerCore.REDUCER_NAME.FLOWER_FLOW}/${type}`, payload });
764
496
  emitNavigateEvent({ type, payload });
765
497
  }, [dispatch, emitNavigateEvent, flowName]);
766
498
  const reset = React.useCallback((param) => {
767
499
  const { type, payload } = makeActionPayloadOnReset(flowName, typeof param === 'string'
768
500
  ? { node: param, initialData }
769
- : {
770
- ...param,
771
- initialData
772
- });
773
- dispatch({ type: `flower/${type}`, payload });
501
+ : { ...param, initialData });
502
+ dispatch({ type: `${flowerCore.REDUCER_NAME.FLOWER_FLOW}/${type}`, payload });
774
503
  emitNavigateEvent({ type, payload });
775
504
  }, [dispatch, emitNavigateEvent, flowName, initialData]);
776
505
  const jump = React.useCallback((param) => {
777
506
  const { type, payload } = makeActionPayloadOnNode(flowName, param);
778
- dispatch({ type: `flower/${type}`, payload });
507
+ dispatch({ type: `${flowerCore.REDUCER_NAME.FLOWER_FLOW}/${type}`, payload });
779
508
  emitNavigateEvent({ type, payload });
780
509
  }, [dispatch, emitNavigateEvent, flowName]);
781
510
  const getCurrentNodeId = React.useCallback((customFlowName) => {
782
511
  return _get(store.getState(), [
783
- 'flower',
512
+ flowerCore.REDUCER_NAME.FLOWER_FLOW,
784
513
  customFlowName || flowName,
785
514
  'current'
786
515
  ]);
@@ -792,7 +521,7 @@ const useFlower = ({ flowName: customFlowName, name } = {}) => {
792
521
  startId,
793
522
  next,
794
523
  jump,
795
- back,
524
+ back: isActive ? interceptBack : back,
796
525
  reset,
797
526
  restart
798
527
  };
@@ -821,7 +550,7 @@ const useFlower = ({ flowName: customFlowName, name } = {}) => {
821
550
  // }
822
551
  // );
823
552
  const useFlowerNavigate = ({ flowName, action, route, node }) => {
824
- const { flowName: flowNameContext } = React.useContext(context);
553
+ const { name: flowNameContext } = React.useContext(flowerReactContext.FlowerReactContext);
825
554
  const name = flowName || flowNameContext;
826
555
  const { next, jump, back, reset, restart } = useFlower({ flowName: name });
827
556
  const onNavigate = React.useCallback(() => {
@@ -857,7 +586,7 @@ function isIntrinsicElement(x) {
857
586
  return typeof x === 'string';
858
587
  }
859
588
  //TODO type FlowerNavigateWrapper props
860
- function FlowerNavigateWrapper({ hidden, Component, onNavigate, ...props }) {
589
+ function _FlowerNavigateWrapper({ hidden, Component, onNavigate, ...props }) {
861
590
  const newProps = React.useMemo(() => ({
862
591
  ...props,
863
592
  hidden,
@@ -876,155 +605,183 @@ function FlowerNavigateWrapper({ hidden, Component, onNavigate, ...props }) {
876
605
  /* istanbul ignore next */
877
606
  return Component && React.createElement(Component, { ...newProps });
878
607
  }
879
- const component$2 = React.memo(FlowerNavigateWrapper);
608
+ const component$5 = React.memo(_FlowerNavigateWrapper);
609
+ const FlowerNavigateWrapper = component$5;
880
610
 
881
611
  /* eslint-disable */
882
612
  //TODO type RenderRules props
613
+ // ! Probably in this scenario we must replace `FlowerRule` or refactor `FlowerNavigate`
883
614
  const RenderRules = ({ alwaysDisplay, rules, Component, flowName, onNavigate, ...props }) => {
884
- return (React.createElement(component$5, { alwaysDisplay: alwaysDisplay, rules: rules, flowName: flowName }, ({ hidden }) => React.createElement(component$2, { ...props, Component: Component, hidden: hidden, onNavigate: onNavigate })));
615
+ return (React.createElement(flowerReactShared.FlowerRule, { alwaysDisplay: alwaysDisplay, rules: rules, rootName: flowName }, ({ hidden }) => (React.createElement(FlowerNavigateWrapper, { ...props, Component: Component, hidden: hidden, onNavigate: onNavigate }))));
885
616
  };
886
- const FlowerNavigate = ({ children, flowName: forceFlowName, action, route, node, rules, alwaysDisplay, }) => {
887
- const { onNavigate, flowName } = useFlowerNavigate({ flowName: forceFlowName, action, route, node });
617
+ const _FlowerNavigate = ({ children, flowName: forceFlowName, action, route, node, rules, alwaysDisplay }) => {
618
+ const { onNavigate, flowName } = useFlowerNavigate({
619
+ flowName: forceFlowName,
620
+ action,
621
+ route,
622
+ node
623
+ });
888
624
  if (typeof children === 'function') {
889
- return React.createElement(RenderRules, { alwaysDisplay: alwaysDisplay, rules: rules, Component: children, flowName: flowName, onNavigate: onNavigate });
625
+ return (React.createElement(RenderRules, { alwaysDisplay: alwaysDisplay, rules: rules, Component: children, flowName: flowName, onNavigate: onNavigate }));
890
626
  }
891
- return React.Children.map(children, (child, i) => {
627
+ return (React.createElement(React.Fragment, null, React.Children.map(children, (child, i) => {
892
628
  if (!React.isValidElement(child))
893
629
  return child;
894
630
  const { type, props } = child;
895
631
  const Component = type;
896
- // eslint-disable-next-line react/jsx-props-no-spreading
897
- return React.createElement(RenderRules, { key: i, alwaysDisplay: alwaysDisplay, rules: rules, Component: Component, flowName: flowName, onNavigate: onNavigate, ...props });
898
- });
632
+ return (React.createElement(RenderRules, { key: i, alwaysDisplay: alwaysDisplay, rules: rules, Component: Component, flowName: flowName, onNavigate: onNavigate, ...props }));
633
+ })));
899
634
  };
900
- const component$1 = React.memo(FlowerNavigate);
901
- component$1.displayName = 'FlowerNavigate';
635
+ const component$4 = React.memo(_FlowerNavigate);
636
+ component$4.displayName = 'FlowerNavigate';
637
+ const FlowerNavigate = component$4;
902
638
 
903
- const FlowerComponent = ({ children }) => children;
904
- const component = React.memo(FlowerComponent);
639
+ const _FlowerNode = ({ children, onEnter, onExit }) => {
640
+ React.useEffect(() => {
641
+ onEnter?.();
642
+ return () => {
643
+ onExit?.();
644
+ };
645
+ }, [onEnter, onExit]);
646
+ return React.createElement(React.Fragment, null, children);
647
+ };
648
+ const component$3 = React.memo(_FlowerNode);
649
+ component$3.displayName = 'FlowerNode';
650
+ const FlowerNode = component$3;
905
651
 
906
- /** This hook allows you to manage and retrieve information about Forms.
907
- *
908
- * It exposes details regarding the form's state and a set of methods for reading and writing within it:
909
- *
910
- * - isSubmitted,
911
- * - isDirty,
912
- * - errors,
913
- * - customErrors,
914
- * - isValid,
915
- * - isValidating,
916
- * - getData,
917
- * - setData,
918
- * - unsetData,
919
- * - replaceData,
920
- * - reset,
921
- * - setCustomErrors
922
- * - getFormStatus
923
- *
924
- * @param {string} flowName - first optional parameter
925
- *
926
- * @param {string} name - alias optional parameter, if flowName exist, name is not used
652
+ const _FlowerRoute = ({ autostart = true, children, onEnter, onExit }) => {
653
+ const { dispatch } = flowerReactStore.ReduxFlowerProvider.getReduxHooks();
654
+ const one = React.useRef(false);
655
+ const { name } = React.useContext(flowerReactContext.FlowerReactContext);
656
+ React.useEffect(() => {
657
+ onEnter?.();
658
+ return () => {
659
+ onExit?.();
660
+ };
661
+ }, [onEnter, onExit]);
662
+ React.useEffect(() => {
663
+ if (autostart && one.current === false) {
664
+ one.current = true;
665
+ dispatch(flowerActions.next({ flowName: name }));
666
+ }
667
+ }, [dispatch, name, autostart]);
668
+ return React.createElement(React.Fragment, null, children);
669
+ };
670
+ const component$2 = React.memo(_FlowerRoute);
671
+ component$2.displayName = 'FlowerRoute';
672
+ const FlowerRoute = component$2;
673
+
674
+ const _FlowerServer = ({ children }) => {
675
+ return React.createElement(React.Fragment, null, children);
676
+ };
677
+ const component$1 = React.memo(_FlowerServer);
678
+ component$1.displayName = 'FlowerServer';
679
+ const FlowerServer = component$1;
680
+
681
+ function _FlowerStart() {
682
+ const { dispatch, useSelector } = flowerReactStore.ReduxFlowerProvider.getReduxHooks();
683
+ const one = React.useRef(false);
684
+ const { name, autostart = true, currentNode } = React.useContext(flowerReactContext.FlowerReactContext);
685
+ const startNodeId = useSelector(makeSelectStartNodeId(name ?? ''));
686
+ React.useEffect(() => {
687
+ if (startNodeId === currentNode && autostart && one.current === false) {
688
+ one.current = true;
689
+ dispatch(flowerActions.next({ flowName: name, isStart: true }));
690
+ // if (global.window
691
+ // // eslint-disable-next-line no-underscore-dangle, no-undef
692
+ // && global.window.__FLOWER_DEVTOOLS__ && global.window.__FLOWER_DEVTOOLS__AUTO) {
693
+ // Emitter.emit('flower-devtool-from-client', {
694
+ // source: 'flower-client',
695
+ // action: 'START_FLOWER',
696
+ // name: flowName,
697
+ // });
698
+ // }
699
+ }
700
+ }, [dispatch, autostart, startNodeId, currentNode, name]);
701
+ return null;
702
+ }
703
+ const component = React.memo(_FlowerStart);
704
+ component.displayName = 'FlowerStart';
705
+ const FlowerStart = component;
706
+
707
+ const FlowerProvider = ({ children, enableReduxDevtool, configureStore }) => {
708
+ const { reducer, ...rest } = configureStore ?? {
709
+ devTools: enableReduxDevtool
710
+ };
711
+ return (React.createElement(flowerReactStore.ReduxFlowerProvider, { configureStore: {
712
+ reducer: { ...reducerFlower, ...(reducer ?? {}) },
713
+ ...rest,
714
+ devTools: enableReduxDevtool ?? rest?.devTools
715
+ } }, children));
716
+ };
717
+
718
+ /**
927
719
  *
720
+ * @param {ConfigureStoreOptions} configureStore object to configure store - same structure as redux configureStore options
721
+ * @param {MiddlewareList} middlewaresBlacklist list of flower middlewares to blacklist
722
+ * @returns {Store} redux store instance
928
723
  */
929
- const useFlowerForm = ({ flowName: customFlowName, name } = {}) => {
930
- const { flowName: flowNameDefault } = React.useContext(context);
931
- const dispatch = useDispatch();
932
- const store = useStore();
933
- const flowName = customFlowName || name || flowNameDefault || '';
934
- const currentNode = useSelector(makeSelectCurrentNodeId(flowName));
935
- const { errors, customErrors, isValid, isSubmitted, isDirty, hasFocus, isValidating } = useSelector(makeSelectNodeErrors(flowName, currentNode));
936
- const getData = React.useCallback((path) => {
937
- const { flowNameFromPath = flowName, path: newpath } = flowerCore.CoreUtils.getPath(path);
938
- return _get(store.getState(), [
939
- 'flower',
940
- flowNameFromPath,
941
- 'data',
942
- ...newpath
943
- ]);
944
- }, [store, flowName]);
945
- const getFormStatus = React.useCallback((path) => {
946
- const { flowNameFromPath = flowName, path: newpath } = flowerCore.CoreUtils.getPath(path);
947
- return _get(store.getState(), [
948
- 'flower',
949
- flowNameFromPath,
950
- 'form',
951
- ...newpath
952
- ]);
953
- }, [store, flowName]);
954
- const setDataField = React.useCallback((id, val, dirty = true) => {
955
- const { flowNameFromPath = flowName } = flowerCore.CoreUtils.getPath(id);
956
- dispatch(actions.addDataByPath({
957
- flowName: flowNameFromPath,
958
- id,
959
- value: val,
960
- dirty
961
- }));
962
- return;
963
- }, [flowName, dispatch]);
964
- const setData = React.useCallback((val, id) => {
965
- if (id) {
966
- setDataField(id, val);
967
- return;
968
- }
969
- dispatch(actions.addData({ flowName, value: val }));
970
- }, [flowName, setDataField, dispatch]);
971
- const unsetData = React.useCallback((path) => {
972
- const { flowNameFromPath = flowName, path: newpath } = flowerCore.CoreUtils.getPath(path);
973
- dispatch(actions.unsetData({ flowName: flowNameFromPath, id: newpath }));
974
- }, [flowName, dispatch]);
975
- const replaceData = React.useCallback((val) => {
976
- dispatch(actions.replaceData({ flowName, value: val }));
977
- }, [flowName, dispatch]);
978
- const reset = React.useCallback((nodeId) => {
979
- dispatch(actions.resetForm({ flowName, id: nodeId || currentNode }));
980
- }, [flowName, currentNode, dispatch]);
981
- const setCustomErrors = React.useCallback((field, errors, nodeId) => {
982
- dispatch({
983
- type: 'flower/formAddCustomErrors',
984
- payload: {
985
- name: flowName,
986
- id: field,
987
- currentNode: nodeId || currentNode,
988
- errors
989
- }
724
+ const createStoreWithFlower = (configureStore, middlewaresBlacklist) => {
725
+ if (!configureStore) {
726
+ return toolkit.configureStore({
727
+ reducer: toolkit.combineReducers({ ...flowerReactStore.reducerData, ...reducerFlower }),
728
+ devTools: true
990
729
  });
991
- }, [flowName, currentNode, dispatch]);
992
- return {
993
- isSubmitted,
994
- isDirty,
995
- hasFocus,
996
- errors,
997
- customErrors,
998
- isValid,
999
- isValidating,
1000
- getData,
1001
- setData,
1002
- setDataField,
1003
- unsetData,
1004
- replaceData,
1005
- reset,
1006
- setCustomErrors,
1007
- getFormStatus
1008
- };
730
+ }
731
+ const { reducer, ...configOptions } = configureStore ?? {};
732
+ const combinedReducers = toolkit.combineReducers({
733
+ ...flowerReactStore.reducerData,
734
+ ...reducerFlower,
735
+ ...(reducer || {})
736
+ });
737
+ const flowerMiddlewares = middlewaresBlacklist && !!middlewaresBlacklist.length
738
+ ? flowerReactStore.middlewares.filter((middleware) => !middlewaresBlacklist.includes(middleware.name))
739
+ : flowerReactStore.middlewares;
740
+ return toolkit.configureStore({
741
+ reducer: combinedReducers,
742
+ ...configOptions,
743
+ middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(...flowerMiddlewares, ...(configOptions.middleware ?? []))
744
+ });
1009
745
  };
1010
746
 
1011
- exports.Flower = component$c;
1012
- exports.FlowerAction = component$a;
1013
- exports.FlowerComponent = component;
1014
- exports.FlowerContext = context;
1015
- exports.FlowerContextConsumer = Consumer;
1016
- exports.FlowerContextProvider = Provider;
1017
- exports.FlowerField = component$4;
1018
- exports.FlowerFlow = component$8;
1019
- exports.FlowerNavigate = component$1;
1020
- exports.FlowerNode = component$b;
747
+ const updateAction = (reducerName) => toolkit.createAction(`${reducerName}/flowerUpdateData`);
748
+ const createSliceWithFlower = (createSliceOptions) => {
749
+ const { name, reducers } = createSliceOptions;
750
+ updateAction(name);
751
+ const slice = toolkit.createSlice({
752
+ ...createSliceOptions,
753
+ reducers: {
754
+ ...reducers,
755
+ flowerUpdateData: (state, { payload }) => {
756
+ const { path, value } = payload;
757
+ set(state, path, value);
758
+ }
759
+ }
760
+ });
761
+ return slice;
762
+ };
763
+
764
+ Object.defineProperty(exports, "createApi", {
765
+ enumerable: true,
766
+ get: function () { return flowerReactStore.createApi; }
767
+ });
768
+ Object.defineProperty(exports, "HistoryContextProvider", {
769
+ enumerable: true,
770
+ get: function () { return flowerReactHistoryContext.HistoryContextProvider; }
771
+ });
772
+ exports.Flower = Flower;
773
+ exports.FlowerAction = FlowerAction;
774
+ exports.FlowerFlow = FlowerFlow;
775
+ exports.FlowerNavigate = FlowerNavigate;
776
+ exports.FlowerNode = FlowerNode;
1021
777
  exports.FlowerProvider = FlowerProvider;
1022
- exports.FlowerRoute = component$6;
1023
- exports.FlowerRule = component$5;
1024
- exports.FlowerServer = component$9;
1025
- exports.FlowerStart = component$7;
1026
- exports.FlowerValue = component$3;
1027
- exports.getDataByFlow = getDataByFlow;
778
+ exports.FlowerRoute = FlowerRoute;
779
+ exports.FlowerServer = FlowerServer;
780
+ exports.FlowerStart = FlowerStart;
781
+ exports.createSliceWithFlower = createSliceWithFlower;
782
+ exports.createStoreWithFlower = createStoreWithFlower;
783
+ exports.flowerReducers = flowerReducers;
784
+ exports.makeSelectData = makeSelectData;
785
+ exports.reducerFlower = reducerFlower;
1028
786
  exports.useFlower = useFlower;
1029
- exports.useFlowerForm = useFlowerForm;
1030
- exports.useSelector = useSelector;
787
+ exports.useFlowerNavigate = useFlowerNavigate;