@flowerforce/flower-react 4.0.10-beta.0 → 4.0.11-beta.1
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 +48 -0
- package/dist/index.cjs.js +206 -85
- package/dist/index.esm.js +204 -85
- package/dist/src/components/Flower.d.ts +3 -15
- package/dist/src/components/hooks/eventsHandlers.d.ts +5 -0
- package/dist/src/components/hooks/index.d.ts +5 -0
- package/dist/src/components/hooks/types/index.d.ts +29 -0
- package/dist/src/components/hooks/useBrowserNavigationSync.d.ts +12 -0
- package/dist/src/components/hooks/useDestroyFlow.d.ts +2 -0
- package/dist/src/components/hooks/useInitDevtools.d.ts +2 -0
- package/dist/src/components/hooks/useInitNodes.d.ts +2 -0
- package/dist/src/components/hooks/useSelectorsClient.d.ts +6 -0
- package/dist/src/components/useFlower/utils.d.ts +2 -0
- package/dist/src/features/selectors/selectors.d.ts +175 -0
- package/dist/src/index.d.ts +2 -1
- package/dist/src/types/Flower.d.ts +15 -0
- package/dist/src/types/utilsTypes.d.ts +4 -0
- package/dist/src/utils.d.ts +2 -10
- package/package.json +12 -7
package/dist/index.esm.js
CHANGED
@@ -1,20 +1,46 @@
|
|
1
|
-
import React, { memo, useRef, useState, useMemo, Children,
|
1
|
+
import React, { useEffect, memo, useRef, useState, useMemo, Children, useContext, useCallback } from 'react';
|
2
2
|
import _keyBy from 'lodash/keyBy';
|
3
|
-
import {
|
3
|
+
import { FlowUtils, FlowerCoreBaseReducers, REDUCER_NAME, FlowerCoreStateSelectors, FlowerStateUtils, Emitter, devtoolState } from '@flowerforce/flower-core';
|
4
4
|
import { FlowerReactProvider, FlowerReactContext } from '@flowerforce/flower-react-context';
|
5
5
|
import _get from 'lodash/get';
|
6
|
-
import { createSlice, configureStore, combineReducers, createAction } from '@reduxjs/toolkit';
|
7
6
|
import { reducerData, ReduxFlowerProvider, flowerDataActions, middlewares } from '@flowerforce/flower-react-store';
|
8
7
|
export { createApi } from '@flowerforce/flower-react-store';
|
8
|
+
import { createSlice, configureStore, combineReducers, createAction } from '@reduxjs/toolkit';
|
9
9
|
import { createSelector } from 'reselect';
|
10
|
+
import { useHistoryContext } from '@flowerforce/flower-react-history-context';
|
11
|
+
export { HistoryContextProvider } from '@flowerforce/flower-react-history-context';
|
10
12
|
import { FlowerRule } from '@flowerforce/flower-react-shared';
|
11
13
|
import set from 'lodash/set';
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
const res = CoreUtils.generateNodesForFlowerJson(nodes);
|
16
|
-
return res;
|
15
|
+
const getRulesExists = (rules) => {
|
16
|
+
return Object.keys(rules).length ? FlowUtils.mapEdge(rules) : undefined;
|
17
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 = 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
|
+
});
|
18
44
|
|
19
45
|
const flowerReducer = createSlice({
|
20
46
|
name: REDUCER_NAME.FLOWER_FLOW,
|
@@ -41,60 +67,37 @@ const selectFlowerHistory = (name) => createSelector(selectFlower(name), FlowerC
|
|
41
67
|
const makeSelectNodesIds = (name) => createSelector(selectFlower(name), FlowerCoreStateSelectors.makeSelectNodesIds);
|
42
68
|
const makeSelectStartNodeId = (name) => createSelector(selectFlower(name), FlowerCoreStateSelectors.makeSelectStartNodeId);
|
43
69
|
const makeSelectCurrentNodeId = (name) => createSelector(selectFlower(name), makeSelectStartNodeId(name), FlowerCoreStateSelectors.makeSelectCurrentNodeId);
|
70
|
+
const makeSelectIsCurrentNode = (name) => createSelector(selectFlower(name), makeSelectCurrentNodeId(name), (flower, current) => flower.nodes[current]);
|
44
71
|
const makeSelectPrevNodeRetain = (name) => createSelector(makeSelectNodesIds(name), selectFlowerHistory(name), makeSelectCurrentNodeId(name), FlowerCoreStateSelectors.makeSelectPrevNodeRetain);
|
45
72
|
const makeSelectCurrentNodeDisabled = (name) => createSelector(makeSelectNodesIds(name), makeSelectCurrentNodeId(name), FlowerCoreStateSelectors.makeSelectCurrentNodeDisabled);
|
46
73
|
// dati nel flow selezionato
|
47
74
|
const makeSelectData = (name) => createSelector(selectFlowerDataNode(name), (data) => data?.data ?? {});
|
48
75
|
createSelector(selectGlobalData, FlowerStateUtils.getAllData);
|
49
76
|
|
50
|
-
|
51
|
-
|
52
|
-
/*
|
53
|
-
* FlowerClient
|
54
|
-
*/
|
55
|
-
const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null, initialState = {}, initialData }) => {
|
56
|
-
const flowName = name;
|
57
|
-
const { dispatch, store, useSelector } = ReduxFlowerProvider.getReduxHooks();
|
58
|
-
const one = useRef(false);
|
59
|
-
const [wsDevtools, setWsDevtools] = useState(devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS_INITIALIZED__', false));
|
60
|
-
// TODO could make that transformation in CoreUtils.generateNodesForFlowerJson
|
61
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps, max-len
|
62
|
-
const nodes = useMemo(
|
63
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
64
|
-
() => convertElements(Children.toArray(children)), [children]);
|
65
|
-
const nodeById = useMemo(() => _keyBy(Children.toArray(children), 'props.id'), [children]);
|
66
|
-
const isInitialized = useSelector(makeSelectStartNodeId(name));
|
67
|
-
const history = useSelector(selectFlowerHistory(name));
|
68
|
-
const current = useSelector(makeSelectCurrentNodeId(flowName));
|
69
|
-
const isDisabled = useSelector(makeSelectCurrentNodeDisabled(flowName));
|
70
|
-
const prevFlowerNodeId = useSelector(makeSelectPrevNodeRetain(flowName));
|
77
|
+
const useInitNodes = ({ one, nodes, name, startId, persist = false, initialData, initialState = {} }) => {
|
78
|
+
const { dispatch } = ReduxFlowerProvider.getReduxHooks();
|
71
79
|
useEffect(() => {
|
72
80
|
if (nodes.length > 0 && one.current === false) {
|
73
81
|
one.current = true;
|
74
82
|
dispatch(flowerActions.initNodes({
|
75
|
-
name
|
76
|
-
// @ts-expect-error FIX ME
|
83
|
+
name,
|
77
84
|
nodes,
|
78
85
|
startId: startId ?? '',
|
79
|
-
persist
|
86
|
+
persist,
|
80
87
|
initialState
|
81
88
|
}));
|
82
|
-
if (initialData) {
|
83
|
-
dispatch(flowerDataActions.initData({
|
84
|
-
rootName: flowName,
|
85
|
-
initialData: initialData ?? {}
|
86
|
-
}));
|
87
|
-
}
|
88
89
|
}
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
90
|
+
if (initialData) {
|
91
|
+
dispatch(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 } = ReduxFlowerProvider.getReduxHooks();
|
98
101
|
useEffect(() => {
|
99
102
|
/* istanbul ignore next */
|
100
103
|
const eventCb = (msg) => {
|
@@ -108,12 +111,10 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
|
|
108
111
|
dispatch(flowerActions.setCurrentNode({ name: msg.name, node: msg.id }));
|
109
112
|
}
|
110
113
|
// if (msg.action === 'REPLACE_DATA' && msg.name === flowName) {
|
111
|
-
// dispatch(
|
112
|
-
// formActions.replaceData({ flowName: msg.name, value: msg.data })
|
113
|
-
// )
|
114
|
+
// dispatch(actions.replaceData({ flowName: msg.name, value: msg.data }))
|
114
115
|
// }
|
115
116
|
// if (msg.action === 'ADD_DATA' && msg.name === flowName) {
|
116
|
-
// dispatch(
|
117
|
+
// dispatch(actions.addData({ flowName: msg.name, value: msg.data }))
|
117
118
|
// }
|
118
119
|
};
|
119
120
|
/* istanbul ignore next */
|
@@ -127,13 +128,22 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
|
|
127
128
|
}
|
128
129
|
};
|
129
130
|
}, [dispatch, flowName]);
|
131
|
+
};
|
132
|
+
|
133
|
+
const useDestroyFlow = ({ flowName, one, persist }) => {
|
134
|
+
const { dispatch } = ReduxFlowerProvider.getReduxHooks();
|
130
135
|
useEffect(() => () => {
|
131
|
-
|
132
|
-
|
136
|
+
if (persist)
|
137
|
+
return;
|
138
|
+
if (one.current === true) {
|
133
139
|
one.current = false;
|
134
140
|
dispatch(flowerActions.destroy({ name: flowName }));
|
135
141
|
}
|
136
|
-
}, [dispatch, flowName,
|
142
|
+
}, [dispatch, flowName, persist]);
|
143
|
+
};
|
144
|
+
|
145
|
+
const useClientInitEvent = ({ flowName, isInitialized, wsDevtools }) => {
|
146
|
+
const { store } = ReduxFlowerProvider.getReduxHooks();
|
137
147
|
useEffect(() => {
|
138
148
|
/* istanbul ignore next */
|
139
149
|
if (isInitialized &&
|
@@ -149,7 +159,11 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
|
|
149
159
|
getState: store.getState
|
150
160
|
});
|
151
161
|
}
|
152
|
-
}, [
|
162
|
+
}, [flowName, wsDevtools, isInitialized, store]);
|
163
|
+
};
|
164
|
+
const useSetHistoryEvent = ({ flowName, isInitialized, wsDevtools }) => {
|
165
|
+
const { useSelector } = ReduxFlowerProvider.getReduxHooks();
|
166
|
+
const history = useSelector(selectFlowerHistory(flowName));
|
153
167
|
useEffect(() => {
|
154
168
|
/* istanbul ignore next */
|
155
169
|
if (isInitialized &&
|
@@ -163,7 +177,9 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
|
|
163
177
|
history
|
164
178
|
});
|
165
179
|
}
|
166
|
-
}, [
|
180
|
+
}, [flowName, history, wsDevtools, isInitialized]);
|
181
|
+
};
|
182
|
+
const useSetCurrentEvent = ({ current, flowName, isInitialized, wsDevtools }) => {
|
167
183
|
useEffect(() => {
|
168
184
|
if (!current)
|
169
185
|
return;
|
@@ -183,6 +199,9 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
|
|
183
199
|
});
|
184
200
|
}
|
185
201
|
}, [flowName, current, wsDevtools, isInitialized]);
|
202
|
+
};
|
203
|
+
const useFlowerNavigateEvent = ({ current, flowName, isInitialized, wsDevtools, isDisabled }) => {
|
204
|
+
const { dispatch } = ReduxFlowerProvider.getReduxHooks();
|
186
205
|
useEffect(() => {
|
187
206
|
if (!current)
|
188
207
|
return;
|
@@ -190,7 +209,7 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
|
|
190
209
|
if (!isInitialized)
|
191
210
|
return;
|
192
211
|
if (isDisabled) {
|
193
|
-
dispatch(
|
212
|
+
dispatch({ type: `${REDUCER_NAME.FLOWER_FLOW}/next`, payload: { flowName, disabled: true } });
|
194
213
|
// eslint-disable-next-line no-underscore-dangle, no-undef
|
195
214
|
/* istanbul ignore next */
|
196
215
|
if (wsDevtools &&
|
@@ -213,7 +232,7 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
|
|
213
232
|
devtoolState &&
|
214
233
|
_get(devtoolState, '__FLOWER_DEVTOOLS__')) {
|
215
234
|
if (isInitialized === current)
|
216
|
-
return; // salto il primo
|
235
|
+
return; // salto il primo evento
|
217
236
|
Emitter.emit('flower-devtool-from-client', {
|
218
237
|
source: 'flower-client',
|
219
238
|
action: 'SET_SELECTED',
|
@@ -222,15 +241,55 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
|
|
222
241
|
time: new Date()
|
223
242
|
});
|
224
243
|
}
|
225
|
-
}, [
|
226
|
-
|
227
|
-
|
244
|
+
}, [dispatch, flowName, current, isDisabled, wsDevtools, isInitialized]);
|
245
|
+
};
|
246
|
+
|
247
|
+
const useSelectorsClient = (flowName) => {
|
248
|
+
const { useSelector } = 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,
|
228
255
|
current,
|
229
256
|
isDisabled,
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
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 = useRef(false);
|
268
|
+
const [wsDevtools, setWsDevtools] = useState(devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS_INITIALIZED__', false));
|
269
|
+
const nodes = useMemo(() => generateNodesForFlowerJson(Children.toArray(children)), [children]);
|
270
|
+
const nodeById = useMemo(() => _keyBy(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, 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
|
+
});
|
234
293
|
const currentNodeId = prevFlowerNodeId || current;
|
235
294
|
const contextValues = useMemo(() => ({
|
236
295
|
name: flowName,
|
@@ -244,10 +303,10 @@ const FlowerClient = ({ children, name, destroyOnUnmount = true, startId = null,
|
|
244
303
|
React.createElement(FlowerReactProvider, { value: prevContextValues }, nodeById[currentNodeId]),
|
245
304
|
React.createElement(FlowerReactProvider, { value: contextValues }, !isDisabled && current !== currentNodeId && nodeById[current]))) : null;
|
246
305
|
};
|
247
|
-
const component$
|
248
|
-
component$
|
306
|
+
const component$8 = memo(FlowerClient);
|
307
|
+
component$8.displayName = 'Flower';
|
249
308
|
// workaround for let typescript read JSX component as a valid JSX element using react 19(?)
|
250
|
-
const Flower = component$
|
309
|
+
const Flower = component$8;
|
251
310
|
|
252
311
|
const FlowAction = ({ children, onEnter, onExit }) => {
|
253
312
|
useEffect(() => {
|
@@ -258,13 +317,12 @@ const FlowAction = ({ children, onEnter, onExit }) => {
|
|
258
317
|
}, [onEnter, onExit]);
|
259
318
|
return React.createElement(React.Fragment, null, children);
|
260
319
|
};
|
261
|
-
const component$
|
262
|
-
component$
|
263
|
-
const FlowerAction = component$
|
320
|
+
const component$7 = React.memo(FlowAction);
|
321
|
+
component$7.displayName = 'FlowerAction';
|
322
|
+
const FlowerAction = component$7;
|
264
323
|
|
265
324
|
const _FlowerComponent = ({ children }) => children;
|
266
|
-
|
267
|
-
const FlowerComponent = component$7;
|
325
|
+
memo(_FlowerComponent);
|
268
326
|
|
269
327
|
const _FlowerFlow = ({ children, onEnter, onExit }) => {
|
270
328
|
useEffect(() => {
|
@@ -310,6 +368,43 @@ const makeActionPayloadOnReset = makeActionPayload(ACTION_TYPES.reset, PAYLOAD_K
|
|
310
368
|
const makeActionPayloadOnNode = makeActionPayload(ACTION_TYPES.jump, PAYLOAD_KEYS_NEEDED.jump);
|
311
369
|
const makeActionPayloadOnNext = makeActionPayload(ACTION_TYPES.next, PAYLOAD_KEYS_NEEDED.next);
|
312
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 } = ReduxFlowerProvider.getReduxHooks();
|
387
|
+
const { index, isActive, setIndex, withUrl } = useHistoryContext();
|
388
|
+
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
|
+
};
|
313
408
|
|
314
409
|
/** This hook allows you to read flow informations, such as the flowName and ID of the current node.
|
315
410
|
*
|
@@ -331,10 +426,18 @@ const makeActionPayloadOnRestart = makeActionPayload(ACTION_TYPES.restart, PAYLO
|
|
331
426
|
*/
|
332
427
|
const useFlower = ({ flowName: customFlowName, name } = {}) => {
|
333
428
|
const { name: flowNameDefault, initialData } = useContext(FlowerReactContext);
|
429
|
+
const { index, isActive, setIndex, withUrl } = useHistoryContext();
|
334
430
|
const { store, dispatch, useSelector } = ReduxFlowerProvider.getReduxHooks();
|
335
431
|
const flowName = (customFlowName || name || flowNameDefault);
|
336
432
|
const nodeId = useSelector(makeSelectCurrentNodeId(flowName ?? ''));
|
433
|
+
const currentNode = useSelector(makeSelectIsCurrentNode(flowName ?? ''));
|
337
434
|
const startId = useSelector(makeSelectStartNodeId(flowName ?? ''));
|
435
|
+
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 = useMemo(() => window.history.state?.stack?.map((path) => path.split('/')[1]), [window.history.state?.stack]);
|
338
441
|
const emitNavigateEvent = useCallback(
|
339
442
|
//TODO check this function is needed
|
340
443
|
(params) => {
|
@@ -356,18 +459,37 @@ const useFlower = ({ flowName: customFlowName, name } = {}) => {
|
|
356
459
|
const { type, payload } = makeActionPayloadOnNext(flowName, params);
|
357
460
|
dispatch({
|
358
461
|
type: `${REDUCER_NAME.FLOWER_FLOW}/${type}`,
|
359
|
-
payload: {
|
360
|
-
...payload,
|
361
|
-
data: store.getState()
|
362
|
-
}
|
462
|
+
payload: { ...payload, data: store.getState() }
|
363
463
|
});
|
464
|
+
if (isActive) {
|
465
|
+
setIndex(handleHistoryStackChange(index, currentNode, flowName, withUrl));
|
466
|
+
}
|
364
467
|
emitNavigateEvent({ type, payload });
|
365
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
|
+
};
|
366
483
|
const back = useCallback((param) => {
|
367
|
-
const { type, payload } = makeActionPayloadOnBack(flowName, param);
|
368
|
-
dispatch({
|
484
|
+
const { type, payload } = makeActionPayloadOnBack(isActive ? (stack?.[stack?.length - 1] ?? flowName) : flowName, param);
|
485
|
+
dispatch({
|
486
|
+
type: `${REDUCER_NAME.FLOWER_FLOW}/${type}`,
|
487
|
+
payload
|
488
|
+
});
|
489
|
+
setIndex(index - 1);
|
369
490
|
emitNavigateEvent({ type, payload });
|
370
|
-
}, [dispatch, emitNavigateEvent, flowName]);
|
491
|
+
}, [dispatch, emitNavigateEvent, flowName, stack]);
|
492
|
+
useHistorySync({ backAction: back, nextAction: next });
|
371
493
|
const restart = useCallback((param) => {
|
372
494
|
const { type, payload } = makeActionPayloadOnRestart(flowName, param);
|
373
495
|
dispatch({ type: `${REDUCER_NAME.FLOWER_FLOW}/${type}`, payload });
|
@@ -376,10 +498,7 @@ const useFlower = ({ flowName: customFlowName, name } = {}) => {
|
|
376
498
|
const reset = useCallback((param) => {
|
377
499
|
const { type, payload } = makeActionPayloadOnReset(flowName, typeof param === 'string'
|
378
500
|
? { node: param, initialData }
|
379
|
-
: {
|
380
|
-
...param,
|
381
|
-
initialData
|
382
|
-
});
|
501
|
+
: { ...param, initialData });
|
383
502
|
dispatch({ type: `${REDUCER_NAME.FLOWER_FLOW}/${type}`, payload });
|
384
503
|
emitNavigateEvent({ type, payload });
|
385
504
|
}, [dispatch, emitNavigateEvent, flowName, initialData]);
|
@@ -402,7 +521,7 @@ const useFlower = ({ flowName: customFlowName, name } = {}) => {
|
|
402
521
|
startId,
|
403
522
|
next,
|
404
523
|
jump,
|
405
|
-
back,
|
524
|
+
back: isActive ? interceptBack : back,
|
406
525
|
reset,
|
407
526
|
restart
|
408
527
|
};
|
@@ -642,4 +761,4 @@ const createSliceWithFlower = (createSliceOptions) => {
|
|
642
761
|
return slice;
|
643
762
|
};
|
644
763
|
|
645
|
-
export { Flower, FlowerAction,
|
764
|
+
export { Flower, FlowerAction, FlowerFlow, FlowerNavigate, FlowerNode, FlowerProvider, FlowerRoute, FlowerServer, FlowerStart, createSliceWithFlower, createStoreWithFlower, flowerReducers, makeSelectData, reducerFlower, useFlower, useFlowerNavigate };
|
@@ -1,15 +1,3 @@
|
|
1
|
-
import React
|
2
|
-
|
3
|
-
|
4
|
-
current?: string;
|
5
|
-
history?: string[];
|
6
|
-
};
|
7
|
-
type FlowerClientProps = PropsWithChildren<{
|
8
|
-
name: string;
|
9
|
-
destroyOnUnmount?: boolean;
|
10
|
-
startId?: string | null;
|
11
|
-
initialState?: FlowerInitialState;
|
12
|
-
initialData?: Record<string, unknown>;
|
13
|
-
}>;
|
14
|
-
export declare const Flower: ({ children, name, destroyOnUnmount, startId, initialState, initialData }: FlowerClientProps) => React.JSX.Element | null;
|
15
|
-
export {};
|
1
|
+
import React from 'react';
|
2
|
+
import { FlowerClientProps } from '../types/Flower';
|
3
|
+
export declare const Flower: ({ children, name, destroyOnUnmount, persist, startId, initialState, initialData }: FlowerClientProps) => React.JSX.Element | null;
|
@@ -0,0 +1,5 @@
|
|
1
|
+
import { UseClientInitEventProps, UseFlowerNavigateEventProps, UseSetCurrentEventProps, UseSetHistoryEventProps } from './types';
|
2
|
+
export declare const useClientInitEvent: ({ flowName, isInitialized, wsDevtools }: UseClientInitEventProps) => void;
|
3
|
+
export declare const useSetHistoryEvent: ({ flowName, isInitialized, wsDevtools }: UseSetHistoryEventProps) => void;
|
4
|
+
export declare const useSetCurrentEvent: ({ current, flowName, isInitialized, wsDevtools }: UseSetCurrentEventProps) => void;
|
5
|
+
export declare const useFlowerNavigateEvent: ({ current, flowName, isInitialized, wsDevtools, isDisabled }: UseFlowerNavigateEventProps) => void;
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { NodeConfig } from '@flowerforce/flower-core';
|
2
|
+
import { FlowerClientProps } from '../../../types/Flower';
|
3
|
+
import { MutableRefObject } from 'react';
|
4
|
+
export type UseClientInitEventProps = {
|
5
|
+
isInitialized: string;
|
6
|
+
wsDevtools: boolean;
|
7
|
+
flowName: string;
|
8
|
+
};
|
9
|
+
export type UseSetHistoryEventProps = UseClientInitEventProps;
|
10
|
+
export type UseSetCurrentEventProps = UseClientInitEventProps & {
|
11
|
+
current: string;
|
12
|
+
};
|
13
|
+
export type UseFlowerNavigateEventProps = UseSetCurrentEventProps & {
|
14
|
+
isDisabled: boolean;
|
15
|
+
};
|
16
|
+
export type UseDestroyFlowProps = {
|
17
|
+
one: MutableRefObject<boolean>;
|
18
|
+
persist: boolean;
|
19
|
+
flowName: string;
|
20
|
+
};
|
21
|
+
export type UseInitDevtoolsProps = {
|
22
|
+
devtoolState: Object;
|
23
|
+
flowName: string;
|
24
|
+
setWsDevtools: (value: boolean) => void;
|
25
|
+
};
|
26
|
+
export type UseInitNodesProps = {
|
27
|
+
one: MutableRefObject<boolean>;
|
28
|
+
nodes: NodeConfig[];
|
29
|
+
} & Omit<FlowerClientProps, 'destroyOnUnmount'>;
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { useFlowerActions } from '../../types';
|
2
|
+
/**
|
3
|
+
* Hook centralizzato per sincronizzare la navigazione browser con Redux
|
4
|
+
* @param {function} backAction - Azione Redux da chiamare quando si fa "indietro"
|
5
|
+
* @param {function} nextAction - Azione Redux da chiamare quando si fa "avanti"
|
6
|
+
*/
|
7
|
+
type UseHistorySyncProps = {
|
8
|
+
backAction: () => ReturnType<useFlowerActions['back']>;
|
9
|
+
nextAction: () => ReturnType<useFlowerActions['next']>;
|
10
|
+
};
|
11
|
+
export declare const useHistorySync: ({ backAction, nextAction }: UseHistorySyncProps) => void;
|
12
|
+
export {};
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { INode } from '@flowerforce/flower-core';
|
1
2
|
export declare const makeActionPayloadOnBack: (flowName: string | undefined, params: any) => {
|
2
3
|
type: string;
|
3
4
|
payload: Record<string, any>;
|
@@ -18,3 +19,4 @@ export declare const makeActionPayloadOnRestart: (flowName: string | undefined,
|
|
18
19
|
type: string;
|
19
20
|
payload: Record<string, any>;
|
20
21
|
};
|
22
|
+
export declare const handleHistoryStackChange: (currentIndex: number, currentNode: INode, flowName: string, withUrl?: boolean) => number;
|