@nocobase/flow-engine 2.1.0-beta.9 → 2.2.0-alpha.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/lib/FlowContextProvider.d.ts +5 -1
- package/lib/FlowContextProvider.js +9 -2
- package/lib/components/FieldModelRenderer.js +2 -2
- package/lib/components/FlowModelRenderer.d.ts +3 -1
- package/lib/components/FlowModelRenderer.js +12 -6
- package/lib/components/FormItem.d.ts +6 -0
- package/lib/components/FormItem.js +11 -3
- package/lib/components/MobilePopup.js +6 -5
- package/lib/components/dnd/gridDragPlanner.d.ts +59 -2
- package/lib/components/dnd/gridDragPlanner.js +607 -19
- package/lib/components/dnd/index.d.ts +31 -2
- package/lib/components/dnd/index.js +244 -23
- package/lib/components/settings/wrappers/component/SelectWithTitle.d.ts +2 -1
- package/lib/components/settings/wrappers/component/SelectWithTitle.js +14 -12
- package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.d.ts +3 -0
- package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +152 -42
- package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.d.ts +23 -43
- package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.js +352 -295
- package/lib/components/settings/wrappers/contextual/useFloatToolbarPortal.d.ts +36 -0
- package/lib/components/settings/wrappers/contextual/useFloatToolbarPortal.js +274 -0
- package/lib/components/settings/wrappers/contextual/useFloatToolbarVisibility.d.ts +30 -0
- package/lib/components/settings/wrappers/contextual/useFloatToolbarVisibility.js +315 -0
- package/lib/components/subModel/AddSubModelButton.js +12 -1
- package/lib/components/subModel/LazyDropdown.js +301 -52
- package/lib/components/subModel/index.d.ts +1 -0
- package/lib/components/subModel/index.js +19 -0
- package/lib/components/subModel/utils.d.ts +2 -1
- package/lib/components/subModel/utils.js +15 -5
- package/lib/components/variables/VariableHybridInput.d.ts +27 -0
- package/lib/components/variables/VariableHybridInput.js +499 -0
- package/lib/components/variables/index.d.ts +2 -0
- package/lib/components/variables/index.js +3 -0
- package/lib/data-source/index.d.ts +84 -0
- package/lib/data-source/index.js +269 -7
- package/lib/executor/FlowExecutor.js +6 -3
- package/lib/flow-registry/DetachedFlowRegistry.d.ts +21 -0
- package/lib/flow-registry/DetachedFlowRegistry.js +80 -0
- package/lib/flow-registry/index.d.ts +1 -0
- package/lib/flow-registry/index.js +3 -1
- package/lib/flowContext.d.ts +9 -1
- package/lib/flowContext.js +77 -6
- package/lib/flowEngine.d.ts +136 -4
- package/lib/flowEngine.js +429 -51
- package/lib/flowI18n.js +2 -1
- package/lib/flowSettings.d.ts +14 -6
- package/lib/flowSettings.js +34 -6
- package/lib/index.d.ts +2 -0
- package/lib/index.js +7 -0
- package/lib/lazy-helper.d.ts +14 -0
- package/lib/lazy-helper.js +71 -0
- package/lib/locale/en-US.json +1 -0
- package/lib/locale/index.d.ts +2 -0
- package/lib/locale/zh-CN.json +1 -0
- package/lib/models/DisplayItemModel.d.ts +1 -1
- package/lib/models/EditableItemModel.d.ts +1 -1
- package/lib/models/FilterableItemModel.d.ts +1 -1
- package/lib/models/flowModel.d.ts +13 -10
- package/lib/models/flowModel.js +126 -34
- package/lib/provider.js +38 -23
- package/lib/reactive/observer.js +46 -16
- package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +4 -3
- package/lib/runjs-context/contexts/JSBlockRunJSContext.js +4 -15
- package/lib/runjs-context/contexts/JSColumnRunJSContext.js +5 -2
- package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +5 -8
- package/lib/runjs-context/contexts/JSFieldRunJSContext.js +4 -3
- package/lib/runjs-context/contexts/JSItemRunJSContext.js +4 -3
- package/lib/runjs-context/contexts/base.js +464 -29
- package/lib/runjs-context/contexts/elementDoc.d.ts +11 -0
- package/lib/runjs-context/contexts/elementDoc.js +152 -0
- package/lib/runjs-context/setup.js +1 -0
- package/lib/runjs-context/snippets/index.js +13 -2
- package/lib/runjs-context/snippets/scene/detail/set-field-style.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/detail/set-field-style.snippet.js +50 -0
- package/lib/runjs-context/snippets/scene/table/set-cell-style.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/table/set-cell-style.snippet.js +54 -0
- package/lib/types.d.ts +50 -2
- package/lib/types.js +1 -0
- package/lib/utils/createCollectionContextMeta.js +6 -2
- package/lib/utils/index.d.ts +3 -2
- package/lib/utils/index.js +7 -0
- package/lib/utils/loadedPageCache.d.ts +24 -0
- package/lib/utils/loadedPageCache.js +139 -0
- package/lib/utils/parsePathnameToViewParams.d.ts +5 -1
- package/lib/utils/parsePathnameToViewParams.js +28 -4
- package/lib/utils/randomId.d.ts +39 -0
- package/lib/utils/randomId.js +45 -0
- package/lib/utils/runjsTemplateCompat.js +1 -1
- package/lib/utils/runjsValue.js +41 -11
- package/lib/utils/schema-utils.d.ts +7 -1
- package/lib/utils/schema-utils.js +19 -0
- package/lib/views/FlowView.d.ts +7 -1
- package/lib/views/FlowView.js +11 -1
- package/lib/views/PageComponent.js +8 -6
- package/lib/views/ViewNavigation.d.ts +12 -2
- package/lib/views/ViewNavigation.js +28 -9
- package/lib/views/createViewMeta.js +114 -50
- package/lib/views/inheritLayoutContext.d.ts +10 -0
- package/lib/views/inheritLayoutContext.js +50 -0
- package/lib/views/runViewBeforeClose.d.ts +10 -0
- package/lib/views/runViewBeforeClose.js +45 -0
- package/lib/views/useDialog.d.ts +2 -1
- package/lib/views/useDialog.js +12 -3
- package/lib/views/useDrawer.d.ts +2 -1
- package/lib/views/useDrawer.js +12 -3
- package/lib/views/usePage.d.ts +5 -11
- package/lib/views/usePage.js +304 -144
- package/package.json +5 -4
- package/src/FlowContextProvider.tsx +9 -1
- package/src/__tests__/createViewMeta.popup.test.ts +115 -1
- package/src/__tests__/flow-engine.test.ts +166 -0
- package/src/__tests__/flowContext.test.ts +105 -1
- package/src/__tests__/flowEngine.modelLoaders.test.ts +245 -0
- package/src/__tests__/flowEngine.moveModel.test.ts +81 -1
- package/src/__tests__/flowEngine.removeModel.test.ts +47 -3
- package/src/__tests__/flowSettings.test.ts +94 -15
- package/src/__tests__/objectVariable.test.ts +24 -0
- package/src/__tests__/provider.test.tsx +24 -2
- package/src/__tests__/renderHiddenInConfig.test.tsx +6 -6
- package/src/__tests__/runjsContext.test.ts +21 -0
- package/src/__tests__/runjsContextImplementations.test.ts +9 -2
- package/src/__tests__/runjsContextRuntime.test.ts +2 -0
- package/src/__tests__/runjsLocales.test.ts +6 -5
- package/src/__tests__/runjsSnippets.test.ts +21 -0
- package/src/__tests__/viewScopedFlowEngine.test.ts +136 -3
- package/src/components/FieldModelRenderer.tsx +2 -1
- package/src/components/FlowModelRenderer.tsx +18 -6
- package/src/components/FormItem.tsx +7 -1
- package/src/components/MobilePopup.tsx +4 -2
- package/src/components/__tests__/FlowModelRenderer.test.tsx +65 -2
- package/src/components/__tests__/FormItem.test.tsx +25 -0
- package/src/components/__tests__/dnd.test.ts +44 -0
- package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +20 -10
- package/src/components/__tests__/gridDragPlanner.test.ts +472 -5
- package/src/components/dnd/__tests__/DndProvider.test.tsx +98 -0
- package/src/components/dnd/gridDragPlanner.ts +750 -17
- package/src/components/dnd/index.tsx +305 -28
- package/src/components/settings/wrappers/component/SelectWithTitle.tsx +21 -9
- package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +178 -48
- package/src/components/settings/wrappers/contextual/FlowsFloatContextMenu.tsx +487 -440
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +344 -8
- package/src/components/settings/wrappers/contextual/__tests__/FlowsFloatContextMenu.test.tsx +778 -0
- package/src/components/settings/wrappers/contextual/useFloatToolbarPortal.ts +360 -0
- package/src/components/settings/wrappers/contextual/useFloatToolbarVisibility.ts +361 -0
- package/src/components/subModel/AddSubModelButton.tsx +16 -2
- package/src/components/subModel/LazyDropdown.tsx +341 -56
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +524 -38
- package/src/components/subModel/__tests__/utils.test.ts +24 -0
- package/src/components/subModel/index.ts +1 -0
- package/src/components/subModel/utils.ts +13 -2
- package/src/components/variables/VariableHybridInput.tsx +531 -0
- package/src/components/variables/index.ts +2 -0
- package/src/data-source/__tests__/collection.test.ts +41 -2
- package/src/data-source/__tests__/index.test.ts +69 -2
- package/src/data-source/index.ts +332 -8
- package/src/executor/FlowExecutor.ts +6 -3
- package/src/executor/__tests__/flowExecutor.test.ts +57 -0
- package/src/flow-registry/DetachedFlowRegistry.ts +46 -0
- package/src/flow-registry/__tests__/detachedFlowRegistry.test.ts +47 -0
- package/src/flow-registry/index.ts +1 -0
- package/src/flowContext.ts +85 -6
- package/src/flowEngine.ts +484 -45
- package/src/flowI18n.ts +2 -1
- package/src/flowSettings.ts +40 -6
- package/src/index.ts +2 -0
- package/src/lazy-helper.tsx +57 -0
- package/src/locale/en-US.json +1 -0
- package/src/locale/zh-CN.json +1 -0
- package/src/models/DisplayItemModel.tsx +1 -1
- package/src/models/EditableItemModel.tsx +1 -1
- package/src/models/FilterableItemModel.tsx +1 -1
- package/src/models/__tests__/flowEngine.resolveUse.test.ts +0 -15
- package/src/models/__tests__/flowModel.test.ts +65 -37
- package/src/models/flowModel.tsx +184 -65
- package/src/provider.tsx +41 -25
- package/src/reactive/__tests__/observer.test.tsx +82 -0
- package/src/reactive/observer.tsx +87 -25
- package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +4 -3
- package/src/runjs-context/contexts/JSBlockRunJSContext.ts +4 -15
- package/src/runjs-context/contexts/JSColumnRunJSContext.ts +4 -2
- package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +5 -9
- package/src/runjs-context/contexts/JSFieldRunJSContext.ts +4 -3
- package/src/runjs-context/contexts/JSItemRunJSContext.ts +4 -3
- package/src/runjs-context/contexts/base.ts +467 -31
- package/src/runjs-context/contexts/elementDoc.ts +130 -0
- package/src/runjs-context/setup.ts +1 -0
- package/src/runjs-context/snippets/index.ts +12 -1
- package/src/runjs-context/snippets/scene/detail/set-field-style.snippet.ts +30 -0
- package/src/runjs-context/snippets/scene/table/set-cell-style.snippet.ts +34 -0
- package/src/types.ts +62 -0
- package/src/utils/__tests__/createCollectionContextMeta.test.ts +48 -0
- package/src/utils/__tests__/parsePathnameToViewParams.test.ts +21 -0
- package/src/utils/__tests__/runjsValue.test.ts +11 -0
- package/src/utils/__tests__/utils.test.ts +62 -0
- package/src/utils/createCollectionContextMeta.ts +6 -2
- package/src/utils/index.ts +5 -1
- package/src/utils/loadedPageCache.ts +147 -0
- package/src/utils/parsePathnameToViewParams.ts +45 -5
- package/src/utils/randomId.ts +48 -0
- package/src/utils/runjsTemplateCompat.ts +1 -1
- package/src/utils/runjsValue.ts +50 -11
- package/src/utils/schema-utils.ts +30 -1
- package/src/views/FlowView.tsx +22 -2
- package/src/views/PageComponent.tsx +7 -4
- package/src/views/ViewNavigation.ts +46 -9
- package/src/views/__tests__/FlowView.usePage.test.tsx +243 -3
- package/src/views/__tests__/ViewNavigation.test.ts +52 -0
- package/src/views/__tests__/inheritLayoutContext.test.ts +53 -0
- package/src/views/__tests__/runViewBeforeClose.test.ts +30 -0
- package/src/views/__tests__/useDialog.closeDestroy.test.tsx +12 -12
- package/src/views/createViewMeta.ts +106 -34
- package/src/views/inheritLayoutContext.ts +26 -0
- package/src/views/runViewBeforeClose.ts +19 -0
- package/src/views/useDialog.tsx +13 -3
- package/src/views/useDrawer.tsx +13 -3
- package/src/views/usePage.tsx +367 -180
package/src/models/flowModel.tsx
CHANGED
|
@@ -11,8 +11,6 @@ import { batch, define, observable, observe } from '@formily/reactive';
|
|
|
11
11
|
import _ from 'lodash';
|
|
12
12
|
import React from 'react';
|
|
13
13
|
import { uid } from 'uid/secure';
|
|
14
|
-
import { openRequiredParamsStepFormDialog as openRequiredParamsStepFormDialogFn } from '../components/settings/wrappers/contextual/StepRequiredSettingsDialog';
|
|
15
|
-
import { openStepSettingsDialog as openStepSettingsDialogFn } from '../components/settings/wrappers/contextual/StepSettingsDialog';
|
|
16
14
|
import { Emitter } from '../emitter';
|
|
17
15
|
import { InstanceFlowRegistry } from '../flow-registry/InstanceFlowRegistry';
|
|
18
16
|
import { FlowContext, FlowModelContext, FlowRuntimeContext } from '../flowContext';
|
|
@@ -36,7 +34,9 @@ import type {
|
|
|
36
34
|
import { IModelComponentProps, ReadonlyModelProps } from '../types';
|
|
37
35
|
import { isInheritedFrom, setupRuntimeContextSteps } from '../utils';
|
|
38
36
|
// import { FlowExitAllException } from '../utils/exceptions';
|
|
39
|
-
import { Typography } from 'antd
|
|
37
|
+
import { Typography } from 'antd';
|
|
38
|
+
import type { MenuProps } from 'antd';
|
|
39
|
+
import { observer } from '..';
|
|
40
40
|
import { ModelActionRegistry } from '../action-registry/ModelActionRegistry';
|
|
41
41
|
import { buildSubModelItem } from '../components/subModel/utils';
|
|
42
42
|
import { ModelEventRegistry } from '../event-registry/ModelEventRegistry';
|
|
@@ -46,8 +46,6 @@ import { FlowSettingsOpenOptions } from '../flowSettings';
|
|
|
46
46
|
import type { ScheduleOptions } from '../scheduler/ModelOperationScheduler';
|
|
47
47
|
import type { DispatchEventOptions, EventDefinition } from '../types';
|
|
48
48
|
import { ForkFlowModel } from './forkFlowModel';
|
|
49
|
-
import type { MenuProps } from 'antd';
|
|
50
|
-
import { observer } from '..';
|
|
51
49
|
|
|
52
50
|
// 使用 WeakMap 为每个类缓存一个 ModelActionRegistry 实例
|
|
53
51
|
const classActionRegistries = new WeakMap<typeof FlowModel, ModelActionRegistry>();
|
|
@@ -58,20 +56,45 @@ const classEventRegistries = new WeakMap<typeof FlowModel, ModelEventRegistry>()
|
|
|
58
56
|
// 使用WeakMap存储每个类的meta
|
|
59
57
|
const modelMetas = new WeakMap<typeof FlowModel, FlowModelMeta>();
|
|
60
58
|
|
|
59
|
+
type SortableModelLike = {
|
|
60
|
+
sortIndex?: number | null;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
function getStableSortIndex(item: SortableModelLike, fallbackIndex: number) {
|
|
64
|
+
return typeof item?.sortIndex === 'number' && Number.isFinite(item.sortIndex) ? item.sortIndex : fallbackIndex + 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function sortByStableSortIndex<T extends SortableModelLike>(items: T[]) {
|
|
68
|
+
return items
|
|
69
|
+
.map((item, index) => ({
|
|
70
|
+
item,
|
|
71
|
+
index,
|
|
72
|
+
sortIndex: getStableSortIndex(item, index),
|
|
73
|
+
}))
|
|
74
|
+
.sort((a, b) => a.sortIndex - b.sortIndex || a.index - b.index)
|
|
75
|
+
.map(({ item }) => item);
|
|
76
|
+
}
|
|
77
|
+
|
|
61
78
|
// 使用WeakMap存储每个类的 GlobalFlowRegistry
|
|
62
79
|
const modelGlobalRegistries = new WeakMap<typeof FlowModel, GlobalFlowRegistry>();
|
|
63
80
|
|
|
64
81
|
type BaseMenuItem = NonNullable<MenuProps['items']>[number];
|
|
65
|
-
type
|
|
82
|
+
type MenuBaseItem = Omit<Exclude<BaseMenuItem, null>, 'key' | 'children'>;
|
|
66
83
|
|
|
67
|
-
export type FlowModelExtraMenuItem =
|
|
84
|
+
export type FlowModelExtraMenuItem = MenuBaseItem & {
|
|
68
85
|
key: React.Key;
|
|
69
86
|
group?: string;
|
|
70
87
|
sort?: number;
|
|
88
|
+
label?: React.ReactNode;
|
|
89
|
+
disabled?: boolean;
|
|
71
90
|
onClick?: () => void;
|
|
91
|
+
children?: FlowModelExtraMenuItem[];
|
|
72
92
|
};
|
|
73
93
|
|
|
74
|
-
type FlowModelExtraMenuItemInput = Omit<FlowModelExtraMenuItem, 'key'> & {
|
|
94
|
+
type FlowModelExtraMenuItemInput = Omit<FlowModelExtraMenuItem, 'key' | 'children'> & {
|
|
95
|
+
key?: React.Key;
|
|
96
|
+
children?: FlowModelExtraMenuItemInput[];
|
|
97
|
+
};
|
|
75
98
|
|
|
76
99
|
type ExtraMenuItemEntry = {
|
|
77
100
|
group?: string;
|
|
@@ -88,6 +111,66 @@ type ExtraMenuItemEntry = {
|
|
|
88
111
|
|
|
89
112
|
const classMenuExtensions = new WeakMap<typeof FlowModel, Set<ExtraMenuItemEntry>>();
|
|
90
113
|
|
|
114
|
+
const sortExtraMenuItems = (items: FlowModelExtraMenuItem[]) => {
|
|
115
|
+
return [...items].sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const isFlowModelExtraMenuItem = (item: FlowModelExtraMenuItem | null): item is FlowModelExtraMenuItem => {
|
|
119
|
+
return item !== null;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const normalizeExtraMenuItem = (
|
|
123
|
+
item: FlowModelExtraMenuItemInput,
|
|
124
|
+
{
|
|
125
|
+
group,
|
|
126
|
+
sort,
|
|
127
|
+
prefix,
|
|
128
|
+
path,
|
|
129
|
+
}: {
|
|
130
|
+
group: string;
|
|
131
|
+
sort: number;
|
|
132
|
+
prefix: string;
|
|
133
|
+
path: string;
|
|
134
|
+
},
|
|
135
|
+
): FlowModelExtraMenuItem | null => {
|
|
136
|
+
if (!item) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const normalizedGroup = item.group || group;
|
|
141
|
+
const normalizedSort = typeof item.sort === 'number' ? item.sort : sort;
|
|
142
|
+
const normalizedChildren = sortExtraMenuItems(
|
|
143
|
+
(item.children || [])
|
|
144
|
+
.map((child, index) =>
|
|
145
|
+
normalizeExtraMenuItem(child, {
|
|
146
|
+
group: normalizedGroup,
|
|
147
|
+
sort: normalizedSort,
|
|
148
|
+
prefix,
|
|
149
|
+
path: `${path}-${index}`,
|
|
150
|
+
}),
|
|
151
|
+
)
|
|
152
|
+
.filter(isFlowModelExtraMenuItem),
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
...item,
|
|
157
|
+
key: item.key ?? `${prefix}-${normalizedGroup}-${path}`,
|
|
158
|
+
group: normalizedGroup,
|
|
159
|
+
sort: normalizedSort,
|
|
160
|
+
children: normalizedChildren.length ? normalizedChildren : undefined,
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
async function loadOpenStepSettingsDialog() {
|
|
165
|
+
const mod = await import('../components/settings/wrappers/contextual/StepSettingsDialog');
|
|
166
|
+
return mod.openStepSettingsDialog;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function loadOpenRequiredParamsStepFormDialog() {
|
|
170
|
+
const mod = await import('../components/settings/wrappers/contextual/StepRequiredSettingsDialog');
|
|
171
|
+
return mod.openRequiredParamsStepFormDialog;
|
|
172
|
+
}
|
|
173
|
+
|
|
91
174
|
export enum ModelRenderMode {
|
|
92
175
|
ReactElement = 'reactElement',
|
|
93
176
|
RenderFunction = 'renderFunction',
|
|
@@ -178,7 +261,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
178
261
|
};
|
|
179
262
|
this.stepParams = options.stepParams || {};
|
|
180
263
|
this.subModels = {};
|
|
181
|
-
this.sortIndex = options.sortIndex
|
|
264
|
+
this.sortIndex = getStableSortIndex({ sortIndex: options.sortIndex }, -1);
|
|
182
265
|
this._options = options;
|
|
183
266
|
this._title = '';
|
|
184
267
|
this._extraTitle = '';
|
|
@@ -208,11 +291,14 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
208
291
|
if (changed.type === 'set' && _.isEqual(changed.value, changed.oldValue)) {
|
|
209
292
|
return;
|
|
210
293
|
}
|
|
294
|
+
const hasLastAutoRun = !!this._lastAutoRunParams;
|
|
211
295
|
|
|
212
296
|
if (this.flowEngine) {
|
|
213
297
|
this.invalidateFlowCache('beforeRender');
|
|
214
298
|
}
|
|
215
|
-
|
|
299
|
+
if (hasLastAutoRun) {
|
|
300
|
+
this._rerunLastAutoRun();
|
|
301
|
+
}
|
|
216
302
|
this.forks.forEach((fork) => {
|
|
217
303
|
fork.rerender();
|
|
218
304
|
});
|
|
@@ -444,11 +530,9 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
444
530
|
|
|
445
531
|
Object.entries(mergedSubModels || {}).forEach(([key, value]) => {
|
|
446
532
|
if (Array.isArray(value)) {
|
|
447
|
-
value
|
|
448
|
-
.
|
|
449
|
-
|
|
450
|
-
this.addSubModel(key, item);
|
|
451
|
-
});
|
|
533
|
+
sortByStableSortIndex(value).forEach((item) => {
|
|
534
|
+
this.addSubModel(key, item);
|
|
535
|
+
});
|
|
452
536
|
} else {
|
|
453
537
|
this.setSubModel(key, value);
|
|
454
538
|
}
|
|
@@ -695,6 +779,8 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
695
779
|
} else {
|
|
696
780
|
this.props = { ...this.props, ...props };
|
|
697
781
|
}
|
|
782
|
+
|
|
783
|
+
this._options.props = { ...this.props };
|
|
698
784
|
}
|
|
699
785
|
|
|
700
786
|
getProps(): ReadonlyModelProps {
|
|
@@ -709,26 +795,43 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
709
795
|
stepKeyOrStepsParams?: string | Record<string, ParamObject>,
|
|
710
796
|
params?: ParamObject,
|
|
711
797
|
): void {
|
|
798
|
+
let hasChanged = false;
|
|
799
|
+
|
|
712
800
|
if (typeof flowKeyOrAllParams === 'string') {
|
|
713
801
|
const flowKey = flowKeyOrAllParams;
|
|
714
802
|
if (typeof stepKeyOrStepsParams === 'string' && params !== undefined) {
|
|
715
|
-
|
|
716
|
-
|
|
803
|
+
const currentStepParams = this.stepParams[flowKey]?.[stepKeyOrStepsParams] || {};
|
|
804
|
+
const nextStepParams = { ...currentStepParams, ...params };
|
|
805
|
+
if (!_.isEqual(currentStepParams, nextStepParams)) {
|
|
806
|
+
if (!this.stepParams[flowKey]) {
|
|
807
|
+
this.stepParams[flowKey] = {};
|
|
808
|
+
}
|
|
809
|
+
this.stepParams[flowKey][stepKeyOrStepsParams] = nextStepParams;
|
|
810
|
+
hasChanged = true;
|
|
717
811
|
}
|
|
718
|
-
this.stepParams[flowKey][stepKeyOrStepsParams] = {
|
|
719
|
-
...this.stepParams[flowKey][stepKeyOrStepsParams],
|
|
720
|
-
...params,
|
|
721
|
-
};
|
|
722
812
|
} else if (typeof stepKeyOrStepsParams === 'object' && stepKeyOrStepsParams !== null) {
|
|
723
|
-
|
|
813
|
+
const currentFlowParams = this.stepParams[flowKey] || {};
|
|
814
|
+
const nextFlowParams = { ...currentFlowParams, ...stepKeyOrStepsParams };
|
|
815
|
+
if (!_.isEqual(currentFlowParams, nextFlowParams)) {
|
|
816
|
+
this.stepParams[flowKey] = nextFlowParams;
|
|
817
|
+
hasChanged = true;
|
|
818
|
+
}
|
|
724
819
|
}
|
|
725
820
|
} else if (typeof flowKeyOrAllParams === 'object' && flowKeyOrAllParams !== null) {
|
|
726
821
|
for (const fk in flowKeyOrAllParams) {
|
|
727
822
|
if (Object.prototype.hasOwnProperty.call(flowKeyOrAllParams, fk)) {
|
|
728
|
-
|
|
823
|
+
const currentFlowParams = this.stepParams[fk] || {};
|
|
824
|
+
const nextFlowParams = { ...currentFlowParams, ...flowKeyOrAllParams[fk] };
|
|
825
|
+
if (!_.isEqual(currentFlowParams, nextFlowParams)) {
|
|
826
|
+
this.stepParams[fk] = nextFlowParams;
|
|
827
|
+
hasChanged = true;
|
|
828
|
+
}
|
|
729
829
|
}
|
|
730
830
|
}
|
|
731
831
|
}
|
|
832
|
+
if (!hasChanged) {
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
732
835
|
// 发起配置修改事件
|
|
733
836
|
this.emitter.emit('onStepParamsChanged');
|
|
734
837
|
}
|
|
@@ -754,7 +857,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
754
857
|
}
|
|
755
858
|
const isFork = (this as any).isFork === true;
|
|
756
859
|
const target = this;
|
|
757
|
-
|
|
860
|
+
currentFlowEngine.logger.debug(
|
|
758
861
|
`[FlowModel] applyFlow: uid=${this.uid}, flowKey=${flowKey}, isFork=${isFork}, cleanRun=${
|
|
759
862
|
this.cleanRun
|
|
760
863
|
}, targetIsFork=${(target as any)?.isFork === true}`,
|
|
@@ -774,7 +877,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
774
877
|
}
|
|
775
878
|
const isFork = (this as any).isFork === true;
|
|
776
879
|
const target = this;
|
|
777
|
-
|
|
880
|
+
currentFlowEngine.logger.debug(
|
|
778
881
|
`[FlowModel] dispatchEvent: uid=${this.uid}, event=${eventName}, isFork=${isFork}, cleanRun=${
|
|
779
882
|
this.cleanRun
|
|
780
883
|
}, targetIsFork=${(target as any)?.isFork === true}`,
|
|
@@ -858,6 +961,11 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
858
961
|
}
|
|
859
962
|
}, 100);
|
|
860
963
|
|
|
964
|
+
private resetAutoRunState(): void {
|
|
965
|
+
this._rerunLastAutoRun?.cancel?.();
|
|
966
|
+
this._lastAutoRunParams = null;
|
|
967
|
+
}
|
|
968
|
+
|
|
861
969
|
/**
|
|
862
970
|
* 通用事件分发钩子:开始
|
|
863
971
|
* 子类可覆盖;beforeRender 事件可通过抛出 FlowExitException 提前终止。
|
|
@@ -951,7 +1059,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
951
1059
|
}
|
|
952
1060
|
|
|
953
1061
|
// 创建缓存的响应式包装器组件工厂(只创建一次)
|
|
954
|
-
const createReactiveWrapper = (modelInstance:
|
|
1062
|
+
const createReactiveWrapper = (modelInstance: FlowModel) => {
|
|
955
1063
|
const ReactiveWrapper = observer(() => {
|
|
956
1064
|
// 触发响应式更新的关键属性访问(读取 run/渲染目标的 props)
|
|
957
1065
|
const renderTarget = modelInstance;
|
|
@@ -977,6 +1085,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
977
1085
|
model: renderTarget,
|
|
978
1086
|
});
|
|
979
1087
|
return () => {
|
|
1088
|
+
renderTarget.resetAutoRunState();
|
|
980
1089
|
if (typeof renderTarget.onUnmount === 'function') {
|
|
981
1090
|
renderTarget.onUnmount();
|
|
982
1091
|
}
|
|
@@ -1131,7 +1240,10 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1131
1240
|
if (!Array.isArray(subModels[subKey])) {
|
|
1132
1241
|
subModels[subKey] = observable.shallow([]);
|
|
1133
1242
|
}
|
|
1134
|
-
const maxSortIndex = Math.max(
|
|
1243
|
+
const maxSortIndex = Math.max(
|
|
1244
|
+
...(subModels[subKey] as FlowModel[]).map((item, index) => getStableSortIndex(item, index)),
|
|
1245
|
+
0,
|
|
1246
|
+
);
|
|
1135
1247
|
model.sortIndex = maxSortIndex + 1;
|
|
1136
1248
|
subModels[subKey].push(model);
|
|
1137
1249
|
actualParent.emitter.emit('onSubModelAdded', model);
|
|
@@ -1177,33 +1289,31 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1177
1289
|
return model;
|
|
1178
1290
|
}
|
|
1179
1291
|
|
|
1180
|
-
filterSubModels<K extends keyof Structure['subModels']
|
|
1292
|
+
filterSubModels<K extends keyof NonNullable<Structure['subModels']>, R>(
|
|
1181
1293
|
subKey: K,
|
|
1182
|
-
callback: (model: ArrayElementType<Structure['subModels'][K]>, index: number) => boolean,
|
|
1183
|
-
): ArrayElementType<Structure['subModels'][K]>[] {
|
|
1294
|
+
callback: (model: ArrayElementType<NonNullable<Structure['subModels']>[K]>, index: number) => boolean,
|
|
1295
|
+
): ArrayElementType<NonNullable<Structure['subModels']>[K]>[] {
|
|
1184
1296
|
const model = (this.subModels as any)[subKey as string];
|
|
1185
1297
|
|
|
1186
1298
|
if (!model) {
|
|
1187
1299
|
return [];
|
|
1188
1300
|
}
|
|
1189
1301
|
|
|
1190
|
-
const results: ArrayElementType<Structure['subModels'][K]>[] = [];
|
|
1302
|
+
const results: ArrayElementType<NonNullable<Structure['subModels']>[K]>[] = [];
|
|
1191
1303
|
|
|
1192
|
-
_.castArray(model)
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
}
|
|
1199
|
-
});
|
|
1304
|
+
sortByStableSortIndex(_.castArray(model)).forEach((item, index) => {
|
|
1305
|
+
const result = (callback as (model: any, index: number) => boolean)(item, index);
|
|
1306
|
+
if (result) {
|
|
1307
|
+
results.push(item);
|
|
1308
|
+
}
|
|
1309
|
+
});
|
|
1200
1310
|
|
|
1201
1311
|
return results;
|
|
1202
1312
|
}
|
|
1203
1313
|
|
|
1204
|
-
mapSubModels<K extends keyof Structure['subModels']
|
|
1314
|
+
mapSubModels<K extends keyof NonNullable<Structure['subModels']>, R>(
|
|
1205
1315
|
subKey: K,
|
|
1206
|
-
callback: (model: ArrayElementType<Structure['subModels'][K]>, index: number) => R,
|
|
1316
|
+
callback: (model: ArrayElementType<NonNullable<Structure['subModels']>[K]>, index: number) => R,
|
|
1207
1317
|
): R[] {
|
|
1208
1318
|
const model = (this.subModels as any)[subKey as string];
|
|
1209
1319
|
|
|
@@ -1213,17 +1323,15 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1213
1323
|
|
|
1214
1324
|
const results: R[] = [];
|
|
1215
1325
|
|
|
1216
|
-
_.castArray(model)
|
|
1217
|
-
|
|
1218
|
-
.
|
|
1219
|
-
|
|
1220
|
-
results.push(result);
|
|
1221
|
-
});
|
|
1326
|
+
sortByStableSortIndex(_.castArray(model)).forEach((item, index) => {
|
|
1327
|
+
const result = (callback as (model: any, index: number) => R)(item, index);
|
|
1328
|
+
results.push(result);
|
|
1329
|
+
});
|
|
1222
1330
|
|
|
1223
1331
|
return results;
|
|
1224
1332
|
}
|
|
1225
1333
|
|
|
1226
|
-
hasSubModel<K extends keyof Structure['subModels']
|
|
1334
|
+
hasSubModel<K extends keyof NonNullable<Structure['subModels']>>(subKey: K) {
|
|
1227
1335
|
const subModel = (this.subModels as any)[subKey as string];
|
|
1228
1336
|
if (!subModel) {
|
|
1229
1337
|
return false;
|
|
@@ -1231,10 +1339,10 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1231
1339
|
return _.castArray(subModel).length > 0;
|
|
1232
1340
|
}
|
|
1233
1341
|
|
|
1234
|
-
findSubModel<K extends keyof Structure['subModels']
|
|
1342
|
+
findSubModel<K extends keyof NonNullable<Structure['subModels']>, R>(
|
|
1235
1343
|
subKey: K,
|
|
1236
|
-
callback: (model: ArrayElementType<Structure['subModels'][K]>) => R,
|
|
1237
|
-
): ArrayElementType<Structure['subModels'][K]> | null {
|
|
1344
|
+
callback: (model: ArrayElementType<NonNullable<Structure['subModels']>[K]>) => R,
|
|
1345
|
+
): ArrayElementType<NonNullable<Structure['subModels']>[K]> | null {
|
|
1238
1346
|
const model = (this.subModels as any)[subKey as string];
|
|
1239
1347
|
|
|
1240
1348
|
if (!model) {
|
|
@@ -1244,7 +1352,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1244
1352
|
return (
|
|
1245
1353
|
(_.castArray(model).find((item) => {
|
|
1246
1354
|
return (callback as (model: any) => R)(item);
|
|
1247
|
-
}) as ArrayElementType<Structure['subModels'][K]> | undefined) || null
|
|
1355
|
+
}) as ArrayElementType<NonNullable<Structure['subModels']>[K]> | undefined) || null
|
|
1248
1356
|
);
|
|
1249
1357
|
}
|
|
1250
1358
|
|
|
@@ -1304,7 +1412,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1304
1412
|
}
|
|
1305
1413
|
|
|
1306
1414
|
clearForks() {
|
|
1307
|
-
|
|
1415
|
+
this.flowEngine.logger.debug(`FlowModel ${this.uid} clearing all forks.`);
|
|
1308
1416
|
// 主动使所有 fork 失效
|
|
1309
1417
|
if (this.forks?.size) {
|
|
1310
1418
|
this.forks.forEach((fork) => fork.dispose());
|
|
@@ -1369,7 +1477,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1369
1477
|
* @param {string} stepKey 步骤的唯一标识符
|
|
1370
1478
|
* @returns {void}
|
|
1371
1479
|
*/
|
|
1372
|
-
openStepSettingsDialog(flowKey: string, stepKey: string) {
|
|
1480
|
+
async openStepSettingsDialog(flowKey: string, stepKey: string) {
|
|
1373
1481
|
// 创建流程运行时上下文
|
|
1374
1482
|
const flow = this.getFlow(flowKey);
|
|
1375
1483
|
const step = flow?.steps?.[stepKey];
|
|
@@ -1383,7 +1491,9 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1383
1491
|
setupRuntimeContextSteps(ctx, flow.steps, this, flowKey);
|
|
1384
1492
|
ctx.defineProperty('currentStep', { value: step });
|
|
1385
1493
|
|
|
1386
|
-
|
|
1494
|
+
const openStepSettingsDialog = await loadOpenStepSettingsDialog();
|
|
1495
|
+
|
|
1496
|
+
return openStepSettingsDialog({
|
|
1387
1497
|
model: this,
|
|
1388
1498
|
flowKey,
|
|
1389
1499
|
stepKey,
|
|
@@ -1399,7 +1509,9 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1399
1509
|
* @returns {Promise<any>} 返回表单提交的值
|
|
1400
1510
|
*/
|
|
1401
1511
|
async configureRequiredSteps(dialogWidth?: number | string, dialogTitle?: string) {
|
|
1402
|
-
|
|
1512
|
+
const openRequiredParamsStepFormDialog = await loadOpenRequiredParamsStepFormDialog();
|
|
1513
|
+
|
|
1514
|
+
return openRequiredParamsStepFormDialog({
|
|
1403
1515
|
model: this,
|
|
1404
1516
|
dialogWidth,
|
|
1405
1517
|
dialogTitle,
|
|
@@ -1432,6 +1544,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1432
1544
|
const data = {
|
|
1433
1545
|
uid: this.uid,
|
|
1434
1546
|
..._.omit(this._options, ['flowEngine']),
|
|
1547
|
+
props: { ...this.props },
|
|
1435
1548
|
stepParams: this.stepParams,
|
|
1436
1549
|
sortIndex: this.sortIndex,
|
|
1437
1550
|
flowRegistry: {},
|
|
@@ -1586,6 +1699,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1586
1699
|
seen.add(Cls);
|
|
1587
1700
|
const reg = classMenuExtensions.get(Cls);
|
|
1588
1701
|
if (reg) {
|
|
1702
|
+
let entryIndex = 0;
|
|
1589
1703
|
for (const entry of reg) {
|
|
1590
1704
|
if (entry.matcher && !entry.matcher(model)) continue;
|
|
1591
1705
|
const items =
|
|
@@ -1593,16 +1707,21 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1593
1707
|
const group = entry.group || 'common-actions';
|
|
1594
1708
|
const sort = entry.sort ?? 0;
|
|
1595
1709
|
const prefix = entry.keyPrefix || Cls.name || 'extra';
|
|
1596
|
-
(
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1710
|
+
sortExtraMenuItems(
|
|
1711
|
+
(items || [])
|
|
1712
|
+
.map((it, idx: number) =>
|
|
1713
|
+
normalizeExtraMenuItem(it, {
|
|
1714
|
+
group,
|
|
1715
|
+
sort,
|
|
1716
|
+
prefix,
|
|
1717
|
+
path: `${entryIndex}-${idx}`,
|
|
1718
|
+
}),
|
|
1719
|
+
)
|
|
1720
|
+
.filter(isFlowModelExtraMenuItem),
|
|
1721
|
+
).forEach((it) => {
|
|
1722
|
+
collected.push(it);
|
|
1605
1723
|
});
|
|
1724
|
+
entryIndex += 1;
|
|
1606
1725
|
}
|
|
1607
1726
|
}
|
|
1608
1727
|
const ParentClass = Object.getPrototypeOf(Cls) as typeof FlowModel;
|
package/src/provider.tsx
CHANGED
|
@@ -45,34 +45,50 @@ export const FlowEngineGlobalsContextProvider: React.FC<{ children: React.ReactN
|
|
|
45
45
|
const engine = useFlowEngine();
|
|
46
46
|
const config = useContext(ConfigProvider.ConfigContext);
|
|
47
47
|
const { token } = theme.useToken();
|
|
48
|
+
const isDarkTheme = React.useMemo(() => {
|
|
49
|
+
const algorithm = config?.theme?.algorithm;
|
|
50
|
+
if (Array.isArray(algorithm)) {
|
|
51
|
+
return algorithm.includes(theme.darkAlgorithm);
|
|
52
|
+
}
|
|
53
|
+
return algorithm === theme.darkAlgorithm;
|
|
54
|
+
}, [config]);
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (value) {
|
|
64
|
-
engine.context.defineProperty(key, { value });
|
|
65
|
-
}
|
|
56
|
+
// 这些全局能力需要在 children 首次渲染前就可读,不能等到 effect 后再挂到上下文。
|
|
57
|
+
engine.context.defineProperty('viewer', {
|
|
58
|
+
cache: false,
|
|
59
|
+
get: (ctx) => new FlowViewer(ctx, { drawer, embed, popover, dialog }),
|
|
60
|
+
});
|
|
61
|
+
for (const item of Object.entries({
|
|
62
|
+
antdConfig: config,
|
|
63
|
+
modal,
|
|
64
|
+
message,
|
|
65
|
+
notification,
|
|
66
|
+
})) {
|
|
67
|
+
const [key, value] = item;
|
|
68
|
+
if (value) {
|
|
69
|
+
engine.context.defineProperty(key, { value });
|
|
66
70
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
}
|
|
72
|
+
// 将 themeToken 定义为 observable, 使组件能够响应主题的变更。
|
|
73
|
+
engine.context.defineProperty('themeToken', {
|
|
74
|
+
get: () => token,
|
|
75
|
+
observable: true,
|
|
76
|
+
cache: true,
|
|
77
|
+
});
|
|
78
|
+
// 统一把暗色模式暴露到 Flow 上下文,避免 flow 侧继续依赖 global-theme。
|
|
79
|
+
engine.context.defineProperty('isDarkTheme', {
|
|
80
|
+
get: () => isDarkTheme,
|
|
81
|
+
observable: true,
|
|
82
|
+
cache: true,
|
|
83
|
+
info: {
|
|
84
|
+
description: 'Whether current theme algorithm is dark mode.',
|
|
85
|
+
detail: 'boolean',
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
74
90
|
engine.reactView.refresh();
|
|
75
|
-
}, [engine, drawer, modal, message, notification, config, popover, token, dialog, embed]);
|
|
91
|
+
}, [engine, drawer, modal, message, notification, config, popover, token, dialog, embed, isDarkTheme]);
|
|
76
92
|
|
|
77
93
|
return (
|
|
78
94
|
<ConfigProvider {...config} locale={engine.context.locales?.antd} popupMatchSelectWidth={false}>
|
|
@@ -208,4 +208,86 @@ describe('observer', () => {
|
|
|
208
208
|
expect(screen.getByText('Count: 0')).toBeInTheDocument();
|
|
209
209
|
expect(screen.queryByText('Count: 1')).not.toBeInTheDocument();
|
|
210
210
|
});
|
|
211
|
+
|
|
212
|
+
it('should flush pending update without TDZ error when context becomes active before timer callback runs', async () => {
|
|
213
|
+
vi.useFakeTimers();
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
const model = observable({ count: 0 });
|
|
217
|
+
const pageActive = observable.ref(false);
|
|
218
|
+
const tabActive = observable.ref(true);
|
|
219
|
+
|
|
220
|
+
const context = {
|
|
221
|
+
pageActive,
|
|
222
|
+
tabActive,
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
(useFlowContext as any).mockReturnValue(context);
|
|
226
|
+
|
|
227
|
+
const Component = observer(() => <div>Count: {model.count}</div>);
|
|
228
|
+
|
|
229
|
+
render(<Component />);
|
|
230
|
+
|
|
231
|
+
expect(screen.getByText('Count: 0')).toBeInTheDocument();
|
|
232
|
+
|
|
233
|
+
act(() => {
|
|
234
|
+
model.count++;
|
|
235
|
+
pageActive.value = true;
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
await act(async () => {
|
|
239
|
+
await vi.runAllTimersAsync();
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
expect(screen.getByText('Count: 1')).toBeInTheDocument();
|
|
243
|
+
} finally {
|
|
244
|
+
vi.useRealTimers();
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('should cleanup pending timer and listener on unmount', async () => {
|
|
249
|
+
vi.useFakeTimers();
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
const model = observable({ count: 0 });
|
|
253
|
+
const pageActive = observable.ref(false);
|
|
254
|
+
const tabActive = observable.ref(true);
|
|
255
|
+
const renderSpy = vi.fn();
|
|
256
|
+
|
|
257
|
+
const context = {
|
|
258
|
+
pageActive,
|
|
259
|
+
tabActive,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
(useFlowContext as any).mockReturnValue(context);
|
|
263
|
+
|
|
264
|
+
const Component = observer(() => {
|
|
265
|
+
renderSpy(model.count);
|
|
266
|
+
return <div>Count: {model.count}</div>;
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
const { unmount } = render(<Component />);
|
|
270
|
+
|
|
271
|
+
expect(renderSpy).toHaveBeenCalledTimes(1);
|
|
272
|
+
expect(screen.getByText('Count: 0')).toBeInTheDocument();
|
|
273
|
+
|
|
274
|
+
act(() => {
|
|
275
|
+
model.count++;
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
unmount();
|
|
279
|
+
|
|
280
|
+
act(() => {
|
|
281
|
+
pageActive.value = true;
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
await act(async () => {
|
|
285
|
+
await vi.runAllTimersAsync();
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
expect(renderSpy).toHaveBeenCalledTimes(1);
|
|
289
|
+
} finally {
|
|
290
|
+
vi.useRealTimers();
|
|
291
|
+
}
|
|
292
|
+
});
|
|
211
293
|
});
|