@nocobase/flow-engine 2.1.0-alpha.4 → 2.1.0-alpha.45
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/LICENSE +201 -661
- package/README.md +79 -10
- package/lib/FlowContextProvider.d.ts +5 -1
- package/lib/FlowContextProvider.js +9 -2
- package/lib/JSRunner.d.ts +10 -1
- package/lib/JSRunner.js +50 -5
- package/lib/ViewScopedFlowEngine.js +5 -1
- 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 +613 -21
- 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 +76 -11
- 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/StepSettingsDialog.js +16 -2
- 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 +27 -1
- package/lib/components/subModel/LazyDropdown.js +293 -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 +1 -1
- package/lib/components/subModel/utils.js +9 -3
- 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 +259 -5
- package/lib/executor/FlowExecutor.js +32 -9
- 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 +3 -0
- package/lib/flowContext.js +46 -1
- package/lib/flowEngine.d.ts +151 -1
- package/lib/flowEngine.js +392 -18
- 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 +81 -21
- package/lib/provider.js +38 -23
- package/lib/reactive/observer.js +46 -16
- package/lib/runjs-context/registry.d.ts +1 -1
- package/lib/runjs-context/setup.js +20 -12
- 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/scheduler/ModelOperationScheduler.d.ts +5 -1
- package/lib/scheduler/ModelOperationScheduler.js +3 -2
- 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/parsePathnameToViewParams.d.ts +5 -1
- package/lib/utils/parsePathnameToViewParams.js +29 -5
- 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 +22 -3
- package/lib/views/useDrawer.d.ts +2 -1
- package/lib/views/useDrawer.js +22 -3
- package/lib/views/usePage.d.ts +5 -11
- package/lib/views/usePage.js +304 -144
- package/package.json +6 -5
- package/src/FlowContextProvider.tsx +9 -1
- package/src/JSRunner.ts +68 -4
- package/src/ViewScopedFlowEngine.ts +4 -0
- package/src/__tests__/JSRunner.test.ts +27 -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 +82 -1
- package/src/__tests__/flowEngine.modelLoaders.test.ts +245 -0
- 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 +16 -0
- package/src/__tests__/runjsContextRuntime.test.ts +2 -0
- package/src/__tests__/runjsPreprocessDefault.test.ts +23 -0
- package/src/__tests__/runjsSnippets.test.ts +21 -0
- package/src/__tests__/viewScopedFlowEngine.test.ts +3 -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 +558 -3
- package/src/components/dnd/__tests__/DndProvider.test.tsx +98 -0
- package/src/components/dnd/gridDragPlanner.ts +758 -19
- 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 +99 -11
- package/src/components/settings/wrappers/contextual/FlowsFloatContextMenu.tsx +487 -440
- package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +18 -2
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +194 -5
- 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 +32 -2
- package/src/components/subModel/LazyDropdown.tsx +332 -56
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +522 -37
- package/src/components/subModel/__tests__/utils.test.ts +24 -0
- package/src/components/subModel/index.ts +1 -0
- package/src/components/subModel/utils.ts +7 -1
- 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 +68 -1
- package/src/data-source/index.ts +322 -6
- package/src/executor/FlowExecutor.ts +35 -10
- package/src/executor/__tests__/flowExecutor.test.ts +85 -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 +50 -3
- package/src/flowEngine.ts +449 -14
- 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__/dispatchEvent.when.test.ts +214 -0
- package/src/models/__tests__/flowEngine.resolveUse.test.ts +0 -15
- package/src/models/__tests__/flowModel.test.ts +80 -37
- package/src/models/flowModel.tsx +122 -36
- 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/registry.ts +1 -1
- package/src/runjs-context/setup.ts +22 -12
- 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/scheduler/ModelOperationScheduler.ts +14 -3
- package/src/types.ts +62 -0
- package/src/utils/__tests__/createCollectionContextMeta.test.ts +48 -0
- package/src/utils/__tests__/parsePathnameToViewParams.test.ts +28 -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/parsePathnameToViewParams.ts +47 -7
- 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 +13 -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 +27 -3
- package/src/views/useDrawer.tsx +27 -3
- package/src/views/usePage.tsx +367 -179
|
@@ -370,15 +370,17 @@ describe('FlowModel', () => {
|
|
|
370
370
|
};
|
|
371
371
|
|
|
372
372
|
TestFlowModel.registerFlow(exitFlow);
|
|
373
|
-
const
|
|
373
|
+
const loggerSpy = vi.spyOn(model.flowEngine.logger, 'debug').mockImplementation(() => {});
|
|
374
374
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
expect(result).toBeInstanceOf(FlowExitAllException);
|
|
378
|
-
expect(exitFlow.steps.step2.handler).not.toHaveBeenCalled();
|
|
379
|
-
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('[FlowModel]'));
|
|
375
|
+
try {
|
|
376
|
+
const result = await model.applyFlow('exitFlow');
|
|
380
377
|
|
|
381
|
-
|
|
378
|
+
expect(result).toBeInstanceOf(FlowExitAllException);
|
|
379
|
+
expect(exitFlow.steps.step2.handler).not.toHaveBeenCalled();
|
|
380
|
+
expect(loggerSpy).toHaveBeenCalledWith(expect.stringContaining('[FlowModel]'));
|
|
381
|
+
} finally {
|
|
382
|
+
loggerSpy.mockRestore();
|
|
383
|
+
}
|
|
382
384
|
});
|
|
383
385
|
|
|
384
386
|
test('should handle ctx.exit() as FlowExitAllException in beforeRender dispatch', async () => {
|
|
@@ -474,15 +476,17 @@ describe('FlowModel', () => {
|
|
|
474
476
|
};
|
|
475
477
|
|
|
476
478
|
TestFlowModel.registerFlow(exitFlow);
|
|
477
|
-
const
|
|
479
|
+
const loggerSpy = vi.spyOn(model.flowEngine.logger, 'debug').mockImplementation(() => {});
|
|
478
480
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
expect(result).toBeInstanceOf(FlowExitAllException);
|
|
482
|
-
expect(exitFlow.steps.step2.handler).not.toHaveBeenCalled();
|
|
483
|
-
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('[FlowModel]'));
|
|
481
|
+
try {
|
|
482
|
+
const result = await model.applyFlow('exitFlow');
|
|
484
483
|
|
|
485
|
-
|
|
484
|
+
expect(result).toBeInstanceOf(FlowExitAllException);
|
|
485
|
+
expect(exitFlow.steps.step2.handler).not.toHaveBeenCalled();
|
|
486
|
+
expect(loggerSpy).toHaveBeenCalledWith(expect.stringContaining('[FlowModel]'));
|
|
487
|
+
} finally {
|
|
488
|
+
loggerSpy.mockRestore();
|
|
489
|
+
}
|
|
486
490
|
});
|
|
487
491
|
|
|
488
492
|
test('should propagate step execution errors', async () => {
|
|
@@ -546,6 +550,34 @@ describe('FlowModel', () => {
|
|
|
546
550
|
|
|
547
551
|
loggerSpy.mockRestore();
|
|
548
552
|
});
|
|
553
|
+
|
|
554
|
+
test('should warn and skip step when use and handler are both missing', async () => {
|
|
555
|
+
const warnSpy = vi.spyOn(model.context.logger, 'warn').mockImplementation(() => {});
|
|
556
|
+
const errorSpy = vi.spyOn(model.context.logger, 'error').mockImplementation(() => {});
|
|
557
|
+
|
|
558
|
+
TestFlowModel.registerFlow({
|
|
559
|
+
key: 'settingsOnlyFlow',
|
|
560
|
+
steps: {
|
|
561
|
+
edit: {
|
|
562
|
+
title: 'Edit',
|
|
563
|
+
uiSchema: {},
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
const result = await model.applyFlow('settingsOnlyFlow');
|
|
569
|
+
|
|
570
|
+
expect(result).toEqual({});
|
|
571
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
572
|
+
expect.stringContaining("Step 'edit' in flow 'settingsOnlyFlow' has neither 'use' nor 'handler'"),
|
|
573
|
+
);
|
|
574
|
+
expect(errorSpy).not.toHaveBeenCalledWith(
|
|
575
|
+
expect.stringContaining("Step 'edit' in flow 'settingsOnlyFlow' has neither 'use' nor 'handler'"),
|
|
576
|
+
);
|
|
577
|
+
|
|
578
|
+
warnSpy.mockRestore();
|
|
579
|
+
errorSpy.mockRestore();
|
|
580
|
+
});
|
|
549
581
|
});
|
|
550
582
|
|
|
551
583
|
describe('beforeRender flows', () => {
|
|
@@ -768,7 +800,7 @@ describe('FlowModel', () => {
|
|
|
768
800
|
const eventFlow = createEventFlowDefinition('testEvent');
|
|
769
801
|
TestFlowModel.registerFlow(eventFlow);
|
|
770
802
|
|
|
771
|
-
const
|
|
803
|
+
const loggerSpy = vi.spyOn(model.flowEngine.logger, 'debug').mockImplementation(() => {});
|
|
772
804
|
|
|
773
805
|
try {
|
|
774
806
|
model.dispatchEvent('testEvent', { data: 'payload' });
|
|
@@ -776,7 +808,7 @@ describe('FlowModel', () => {
|
|
|
776
808
|
// Use a more reliable approach than arbitrary timeout
|
|
777
809
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
778
810
|
|
|
779
|
-
expect(
|
|
811
|
+
expect(loggerSpy).toHaveBeenCalledWith(
|
|
780
812
|
expect.stringContaining('[FlowModel] dispatchEvent: uid=test-model-uid, event=testEvent'),
|
|
781
813
|
);
|
|
782
814
|
expect(eventFlow.steps.eventStep.handler).toHaveBeenCalledWith(
|
|
@@ -786,7 +818,7 @@ describe('FlowModel', () => {
|
|
|
786
818
|
expect.any(Object),
|
|
787
819
|
);
|
|
788
820
|
} finally {
|
|
789
|
-
|
|
821
|
+
loggerSpy.mockRestore();
|
|
790
822
|
}
|
|
791
823
|
});
|
|
792
824
|
|
|
@@ -1597,7 +1629,7 @@ describe('FlowModel', () => {
|
|
|
1597
1629
|
fork1.dispose = vi.fn();
|
|
1598
1630
|
fork2.dispose = vi.fn();
|
|
1599
1631
|
|
|
1600
|
-
const
|
|
1632
|
+
const loggerSpy = vi.spyOn(model.flowEngine.logger, 'debug').mockImplementation(() => {});
|
|
1601
1633
|
|
|
1602
1634
|
try {
|
|
1603
1635
|
model.clearForks();
|
|
@@ -1606,19 +1638,19 @@ describe('FlowModel', () => {
|
|
|
1606
1638
|
expect(fork2.dispose).toHaveBeenCalled();
|
|
1607
1639
|
expect(model.forks.size).toBe(0);
|
|
1608
1640
|
} finally {
|
|
1609
|
-
|
|
1641
|
+
loggerSpy.mockRestore();
|
|
1610
1642
|
}
|
|
1611
1643
|
});
|
|
1612
1644
|
|
|
1613
1645
|
test('should handle empty forks collection when clearing', () => {
|
|
1614
|
-
const
|
|
1646
|
+
const loggerSpy = vi.spyOn(model.flowEngine.logger, 'debug').mockImplementation(() => {});
|
|
1615
1647
|
|
|
1616
1648
|
try {
|
|
1617
1649
|
model.clearForks();
|
|
1618
1650
|
|
|
1619
1651
|
expect(model.forks.size).toBe(0);
|
|
1620
1652
|
} finally {
|
|
1621
|
-
|
|
1653
|
+
loggerSpy.mockRestore();
|
|
1622
1654
|
}
|
|
1623
1655
|
});
|
|
1624
1656
|
});
|
|
@@ -1746,7 +1778,7 @@ describe('FlowModel', () => {
|
|
|
1746
1778
|
test('should clean up resources on remove', () => {
|
|
1747
1779
|
model.createFork();
|
|
1748
1780
|
model.createFork();
|
|
1749
|
-
const
|
|
1781
|
+
const loggerSpy = vi.spyOn(model.flowEngine.logger, 'debug').mockImplementation(() => {});
|
|
1750
1782
|
|
|
1751
1783
|
// Mock removeModel to simulate proper fork cleanup
|
|
1752
1784
|
flowEngine.removeModel = vi.fn().mockImplementation(() => {
|
|
@@ -1763,7 +1795,7 @@ describe('FlowModel', () => {
|
|
|
1763
1795
|
expect(model.forks.size).toBe(0);
|
|
1764
1796
|
expect(flowEngine.removeModel).toHaveBeenCalledWith(model.uid);
|
|
1765
1797
|
} finally {
|
|
1766
|
-
|
|
1798
|
+
loggerSpy.mockRestore();
|
|
1767
1799
|
}
|
|
1768
1800
|
});
|
|
1769
1801
|
});
|
|
@@ -1840,22 +1872,17 @@ describe('FlowModel', () => {
|
|
|
1840
1872
|
});
|
|
1841
1873
|
|
|
1842
1874
|
test('should rerender triggers beforeRender without cache', async () => {
|
|
1843
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
1844
1875
|
model.dispatchEvent = vi.fn().mockResolvedValue(undefined) as any;
|
|
1845
1876
|
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
});
|
|
1851
|
-
} finally {
|
|
1852
|
-
consoleSpy.mockRestore();
|
|
1853
|
-
}
|
|
1877
|
+
await expect(model.rerender()).resolves.not.toThrow();
|
|
1878
|
+
expect(model.dispatchEvent).toHaveBeenCalledWith('beforeRender', undefined, {
|
|
1879
|
+
useCache: false,
|
|
1880
|
+
});
|
|
1854
1881
|
});
|
|
1855
1882
|
});
|
|
1856
1883
|
|
|
1857
1884
|
describe('serialization', () => {
|
|
1858
|
-
test('should serialize basic model data
|
|
1885
|
+
test('should serialize basic model data with the latest props, excluding flowEngine', () => {
|
|
1859
1886
|
model.sortIndex = 5;
|
|
1860
1887
|
model.setProps({ name: 'Test Model', value: 42 });
|
|
1861
1888
|
model.setStepParams({
|
|
@@ -1867,13 +1894,12 @@ describe('FlowModel', () => {
|
|
|
1867
1894
|
expect(serialized).toEqual(
|
|
1868
1895
|
expect.objectContaining({
|
|
1869
1896
|
uid: model.uid,
|
|
1897
|
+
props: expect.objectContaining({ name: 'Test Model', value: 42 }),
|
|
1870
1898
|
stepParams: expect.objectContaining({ flow1: { step1: { param1: 'value1' } } }),
|
|
1871
1899
|
sortIndex: 5,
|
|
1872
1900
|
subModels: expect.any(Object),
|
|
1873
1901
|
}),
|
|
1874
1902
|
);
|
|
1875
|
-
// props should be excluded from serialization
|
|
1876
|
-
expect(serialized.props).toBeUndefined();
|
|
1877
1903
|
expect(serialized.flowEngine).toBeUndefined();
|
|
1878
1904
|
});
|
|
1879
1905
|
|
|
@@ -1892,6 +1918,7 @@ describe('FlowModel', () => {
|
|
|
1892
1918
|
expect(serialized).toEqual(
|
|
1893
1919
|
expect.objectContaining({
|
|
1894
1920
|
uid: 'empty-model',
|
|
1921
|
+
props: expect.objectContaining({ foo: 'bar' }),
|
|
1895
1922
|
stepParams: expect.any(Object),
|
|
1896
1923
|
sortIndex: expect.any(Number),
|
|
1897
1924
|
subModels: expect.any(Object),
|
|
@@ -1899,6 +1926,22 @@ describe('FlowModel', () => {
|
|
|
1899
1926
|
);
|
|
1900
1927
|
expect(serialized.flowEngine).toBeUndefined();
|
|
1901
1928
|
});
|
|
1929
|
+
|
|
1930
|
+
test('should serialize the latest props after multiple updates', () => {
|
|
1931
|
+
model.setProps({ fieldNames: { title: 'name' }, searchable: true });
|
|
1932
|
+
model.setProps({ fieldNames: { title: 'age' } });
|
|
1933
|
+
model.setProps('defaultExpandAll', false);
|
|
1934
|
+
|
|
1935
|
+
const serialized = model.serialize();
|
|
1936
|
+
|
|
1937
|
+
expect(serialized.props).toEqual(
|
|
1938
|
+
expect.objectContaining({
|
|
1939
|
+
fieldNames: { title: 'age' },
|
|
1940
|
+
searchable: true,
|
|
1941
|
+
defaultExpandAll: false,
|
|
1942
|
+
}),
|
|
1943
|
+
);
|
|
1944
|
+
});
|
|
1902
1945
|
});
|
|
1903
1946
|
});
|
|
1904
1947
|
|
|
@@ -2874,7 +2917,7 @@ describe('FlowModel', () => {
|
|
|
2874
2917
|
describe('Edge Cases & Error Handling', () => {
|
|
2875
2918
|
test('should handle model destruction gracefully', () => {
|
|
2876
2919
|
const model = new FlowModel(modelOptions);
|
|
2877
|
-
const
|
|
2920
|
+
const loggerSpy = vi.spyOn(model.flowEngine.logger, 'debug').mockImplementation(() => {});
|
|
2878
2921
|
|
|
2879
2922
|
model.createFork();
|
|
2880
2923
|
model.setProps({ testProp: 'value' });
|
|
@@ -2882,7 +2925,7 @@ describe('FlowModel', () => {
|
|
|
2882
2925
|
try {
|
|
2883
2926
|
expect(() => model.remove()).not.toThrow();
|
|
2884
2927
|
} finally {
|
|
2885
|
-
|
|
2928
|
+
loggerSpy.mockRestore();
|
|
2886
2929
|
}
|
|
2887
2930
|
});
|
|
2888
2931
|
|
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>();
|
|
@@ -62,16 +60,22 @@ const modelMetas = new WeakMap<typeof FlowModel, FlowModelMeta>();
|
|
|
62
60
|
const modelGlobalRegistries = new WeakMap<typeof FlowModel, GlobalFlowRegistry>();
|
|
63
61
|
|
|
64
62
|
type BaseMenuItem = NonNullable<MenuProps['items']>[number];
|
|
65
|
-
type
|
|
63
|
+
type MenuBaseItem = Omit<Exclude<BaseMenuItem, null>, 'key' | 'children'>;
|
|
66
64
|
|
|
67
|
-
export type FlowModelExtraMenuItem =
|
|
65
|
+
export type FlowModelExtraMenuItem = MenuBaseItem & {
|
|
68
66
|
key: React.Key;
|
|
69
67
|
group?: string;
|
|
70
68
|
sort?: number;
|
|
69
|
+
label?: React.ReactNode;
|
|
70
|
+
disabled?: boolean;
|
|
71
71
|
onClick?: () => void;
|
|
72
|
+
children?: FlowModelExtraMenuItem[];
|
|
72
73
|
};
|
|
73
74
|
|
|
74
|
-
type FlowModelExtraMenuItemInput = Omit<FlowModelExtraMenuItem, 'key'> & {
|
|
75
|
+
type FlowModelExtraMenuItemInput = Omit<FlowModelExtraMenuItem, 'key' | 'children'> & {
|
|
76
|
+
key?: React.Key;
|
|
77
|
+
children?: FlowModelExtraMenuItemInput[];
|
|
78
|
+
};
|
|
75
79
|
|
|
76
80
|
type ExtraMenuItemEntry = {
|
|
77
81
|
group?: string;
|
|
@@ -88,6 +92,66 @@ type ExtraMenuItemEntry = {
|
|
|
88
92
|
|
|
89
93
|
const classMenuExtensions = new WeakMap<typeof FlowModel, Set<ExtraMenuItemEntry>>();
|
|
90
94
|
|
|
95
|
+
const sortExtraMenuItems = (items: FlowModelExtraMenuItem[]) => {
|
|
96
|
+
return [...items].sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const isFlowModelExtraMenuItem = (item: FlowModelExtraMenuItem | null): item is FlowModelExtraMenuItem => {
|
|
100
|
+
return item !== null;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const normalizeExtraMenuItem = (
|
|
104
|
+
item: FlowModelExtraMenuItemInput,
|
|
105
|
+
{
|
|
106
|
+
group,
|
|
107
|
+
sort,
|
|
108
|
+
prefix,
|
|
109
|
+
path,
|
|
110
|
+
}: {
|
|
111
|
+
group: string;
|
|
112
|
+
sort: number;
|
|
113
|
+
prefix: string;
|
|
114
|
+
path: string;
|
|
115
|
+
},
|
|
116
|
+
): FlowModelExtraMenuItem | null => {
|
|
117
|
+
if (!item) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const normalizedGroup = item.group || group;
|
|
122
|
+
const normalizedSort = typeof item.sort === 'number' ? item.sort : sort;
|
|
123
|
+
const normalizedChildren = sortExtraMenuItems(
|
|
124
|
+
(item.children || [])
|
|
125
|
+
.map((child, index) =>
|
|
126
|
+
normalizeExtraMenuItem(child, {
|
|
127
|
+
group: normalizedGroup,
|
|
128
|
+
sort: normalizedSort,
|
|
129
|
+
prefix,
|
|
130
|
+
path: `${path}-${index}`,
|
|
131
|
+
}),
|
|
132
|
+
)
|
|
133
|
+
.filter(isFlowModelExtraMenuItem),
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
...item,
|
|
138
|
+
key: item.key ?? `${prefix}-${normalizedGroup}-${path}`,
|
|
139
|
+
group: normalizedGroup,
|
|
140
|
+
sort: normalizedSort,
|
|
141
|
+
children: normalizedChildren.length ? normalizedChildren : undefined,
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
async function loadOpenStepSettingsDialog() {
|
|
146
|
+
const mod = await import('../components/settings/wrappers/contextual/StepSettingsDialog');
|
|
147
|
+
return mod.openStepSettingsDialog;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function loadOpenRequiredParamsStepFormDialog() {
|
|
151
|
+
const mod = await import('../components/settings/wrappers/contextual/StepRequiredSettingsDialog');
|
|
152
|
+
return mod.openRequiredParamsStepFormDialog;
|
|
153
|
+
}
|
|
154
|
+
|
|
91
155
|
export enum ModelRenderMode {
|
|
92
156
|
ReactElement = 'reactElement',
|
|
93
157
|
RenderFunction = 'renderFunction',
|
|
@@ -208,11 +272,14 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
208
272
|
if (changed.type === 'set' && _.isEqual(changed.value, changed.oldValue)) {
|
|
209
273
|
return;
|
|
210
274
|
}
|
|
275
|
+
const hasLastAutoRun = !!this._lastAutoRunParams;
|
|
211
276
|
|
|
212
277
|
if (this.flowEngine) {
|
|
213
278
|
this.invalidateFlowCache('beforeRender');
|
|
214
279
|
}
|
|
215
|
-
|
|
280
|
+
if (hasLastAutoRun) {
|
|
281
|
+
this._rerunLastAutoRun();
|
|
282
|
+
}
|
|
216
283
|
this.forks.forEach((fork) => {
|
|
217
284
|
fork.rerender();
|
|
218
285
|
});
|
|
@@ -695,6 +762,8 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
695
762
|
} else {
|
|
696
763
|
this.props = { ...this.props, ...props };
|
|
697
764
|
}
|
|
765
|
+
|
|
766
|
+
this._options.props = { ...this.props };
|
|
698
767
|
}
|
|
699
768
|
|
|
700
769
|
getProps(): ReadonlyModelProps {
|
|
@@ -754,7 +823,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
754
823
|
}
|
|
755
824
|
const isFork = (this as any).isFork === true;
|
|
756
825
|
const target = this;
|
|
757
|
-
|
|
826
|
+
currentFlowEngine.logger.debug(
|
|
758
827
|
`[FlowModel] applyFlow: uid=${this.uid}, flowKey=${flowKey}, isFork=${isFork}, cleanRun=${
|
|
759
828
|
this.cleanRun
|
|
760
829
|
}, targetIsFork=${(target as any)?.isFork === true}`,
|
|
@@ -774,7 +843,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
774
843
|
}
|
|
775
844
|
const isFork = (this as any).isFork === true;
|
|
776
845
|
const target = this;
|
|
777
|
-
|
|
846
|
+
currentFlowEngine.logger.debug(
|
|
778
847
|
`[FlowModel] dispatchEvent: uid=${this.uid}, event=${eventName}, isFork=${isFork}, cleanRun=${
|
|
779
848
|
this.cleanRun
|
|
780
849
|
}, targetIsFork=${(target as any)?.isFork === true}`,
|
|
@@ -858,6 +927,11 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
858
927
|
}
|
|
859
928
|
}, 100);
|
|
860
929
|
|
|
930
|
+
private resetAutoRunState(): void {
|
|
931
|
+
this._rerunLastAutoRun?.cancel?.();
|
|
932
|
+
this._lastAutoRunParams = null;
|
|
933
|
+
}
|
|
934
|
+
|
|
861
935
|
/**
|
|
862
936
|
* 通用事件分发钩子:开始
|
|
863
937
|
* 子类可覆盖;beforeRender 事件可通过抛出 FlowExitException 提前终止。
|
|
@@ -951,7 +1025,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
951
1025
|
}
|
|
952
1026
|
|
|
953
1027
|
// 创建缓存的响应式包装器组件工厂(只创建一次)
|
|
954
|
-
const createReactiveWrapper = (modelInstance:
|
|
1028
|
+
const createReactiveWrapper = (modelInstance: FlowModel) => {
|
|
955
1029
|
const ReactiveWrapper = observer(() => {
|
|
956
1030
|
// 触发响应式更新的关键属性访问(读取 run/渲染目标的 props)
|
|
957
1031
|
const renderTarget = modelInstance;
|
|
@@ -977,6 +1051,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
977
1051
|
model: renderTarget,
|
|
978
1052
|
});
|
|
979
1053
|
return () => {
|
|
1054
|
+
renderTarget.resetAutoRunState();
|
|
980
1055
|
if (typeof renderTarget.onUnmount === 'function') {
|
|
981
1056
|
renderTarget.onUnmount();
|
|
982
1057
|
}
|
|
@@ -1177,17 +1252,17 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1177
1252
|
return model;
|
|
1178
1253
|
}
|
|
1179
1254
|
|
|
1180
|
-
filterSubModels<K extends keyof Structure['subModels']
|
|
1255
|
+
filterSubModels<K extends keyof NonNullable<Structure['subModels']>, R>(
|
|
1181
1256
|
subKey: K,
|
|
1182
|
-
callback: (model: ArrayElementType<Structure['subModels'][K]>, index: number) => boolean,
|
|
1183
|
-
): ArrayElementType<Structure['subModels'][K]>[] {
|
|
1257
|
+
callback: (model: ArrayElementType<NonNullable<Structure['subModels']>[K]>, index: number) => boolean,
|
|
1258
|
+
): ArrayElementType<NonNullable<Structure['subModels']>[K]>[] {
|
|
1184
1259
|
const model = (this.subModels as any)[subKey as string];
|
|
1185
1260
|
|
|
1186
1261
|
if (!model) {
|
|
1187
1262
|
return [];
|
|
1188
1263
|
}
|
|
1189
1264
|
|
|
1190
|
-
const results: ArrayElementType<Structure['subModels'][K]>[] = [];
|
|
1265
|
+
const results: ArrayElementType<NonNullable<Structure['subModels']>[K]>[] = [];
|
|
1191
1266
|
|
|
1192
1267
|
_.castArray(model)
|
|
1193
1268
|
.sort((a, b) => (a.sortIndex || 0) - (b.sortIndex || 0))
|
|
@@ -1201,9 +1276,9 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1201
1276
|
return results;
|
|
1202
1277
|
}
|
|
1203
1278
|
|
|
1204
|
-
mapSubModels<K extends keyof Structure['subModels']
|
|
1279
|
+
mapSubModels<K extends keyof NonNullable<Structure['subModels']>, R>(
|
|
1205
1280
|
subKey: K,
|
|
1206
|
-
callback: (model: ArrayElementType<Structure['subModels'][K]>, index: number) => R,
|
|
1281
|
+
callback: (model: ArrayElementType<NonNullable<Structure['subModels']>[K]>, index: number) => R,
|
|
1207
1282
|
): R[] {
|
|
1208
1283
|
const model = (this.subModels as any)[subKey as string];
|
|
1209
1284
|
|
|
@@ -1223,7 +1298,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1223
1298
|
return results;
|
|
1224
1299
|
}
|
|
1225
1300
|
|
|
1226
|
-
hasSubModel<K extends keyof Structure['subModels']
|
|
1301
|
+
hasSubModel<K extends keyof NonNullable<Structure['subModels']>>(subKey: K) {
|
|
1227
1302
|
const subModel = (this.subModels as any)[subKey as string];
|
|
1228
1303
|
if (!subModel) {
|
|
1229
1304
|
return false;
|
|
@@ -1231,10 +1306,10 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1231
1306
|
return _.castArray(subModel).length > 0;
|
|
1232
1307
|
}
|
|
1233
1308
|
|
|
1234
|
-
findSubModel<K extends keyof Structure['subModels']
|
|
1309
|
+
findSubModel<K extends keyof NonNullable<Structure['subModels']>, R>(
|
|
1235
1310
|
subKey: K,
|
|
1236
|
-
callback: (model: ArrayElementType<Structure['subModels'][K]>) => R,
|
|
1237
|
-
): ArrayElementType<Structure['subModels'][K]> | null {
|
|
1311
|
+
callback: (model: ArrayElementType<NonNullable<Structure['subModels']>[K]>) => R,
|
|
1312
|
+
): ArrayElementType<NonNullable<Structure['subModels']>[K]> | null {
|
|
1238
1313
|
const model = (this.subModels as any)[subKey as string];
|
|
1239
1314
|
|
|
1240
1315
|
if (!model) {
|
|
@@ -1244,7 +1319,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1244
1319
|
return (
|
|
1245
1320
|
(_.castArray(model).find((item) => {
|
|
1246
1321
|
return (callback as (model: any) => R)(item);
|
|
1247
|
-
}) as ArrayElementType<Structure['subModels'][K]> | undefined) || null
|
|
1322
|
+
}) as ArrayElementType<NonNullable<Structure['subModels']>[K]> | undefined) || null
|
|
1248
1323
|
);
|
|
1249
1324
|
}
|
|
1250
1325
|
|
|
@@ -1304,7 +1379,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1304
1379
|
}
|
|
1305
1380
|
|
|
1306
1381
|
clearForks() {
|
|
1307
|
-
|
|
1382
|
+
this.flowEngine.logger.debug(`FlowModel ${this.uid} clearing all forks.`);
|
|
1308
1383
|
// 主动使所有 fork 失效
|
|
1309
1384
|
if (this.forks?.size) {
|
|
1310
1385
|
this.forks.forEach((fork) => fork.dispose());
|
|
@@ -1369,7 +1444,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1369
1444
|
* @param {string} stepKey 步骤的唯一标识符
|
|
1370
1445
|
* @returns {void}
|
|
1371
1446
|
*/
|
|
1372
|
-
openStepSettingsDialog(flowKey: string, stepKey: string) {
|
|
1447
|
+
async openStepSettingsDialog(flowKey: string, stepKey: string) {
|
|
1373
1448
|
// 创建流程运行时上下文
|
|
1374
1449
|
const flow = this.getFlow(flowKey);
|
|
1375
1450
|
const step = flow?.steps?.[stepKey];
|
|
@@ -1383,7 +1458,9 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1383
1458
|
setupRuntimeContextSteps(ctx, flow.steps, this, flowKey);
|
|
1384
1459
|
ctx.defineProperty('currentStep', { value: step });
|
|
1385
1460
|
|
|
1386
|
-
|
|
1461
|
+
const openStepSettingsDialog = await loadOpenStepSettingsDialog();
|
|
1462
|
+
|
|
1463
|
+
return openStepSettingsDialog({
|
|
1387
1464
|
model: this,
|
|
1388
1465
|
flowKey,
|
|
1389
1466
|
stepKey,
|
|
@@ -1399,7 +1476,9 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1399
1476
|
* @returns {Promise<any>} 返回表单提交的值
|
|
1400
1477
|
*/
|
|
1401
1478
|
async configureRequiredSteps(dialogWidth?: number | string, dialogTitle?: string) {
|
|
1402
|
-
|
|
1479
|
+
const openRequiredParamsStepFormDialog = await loadOpenRequiredParamsStepFormDialog();
|
|
1480
|
+
|
|
1481
|
+
return openRequiredParamsStepFormDialog({
|
|
1403
1482
|
model: this,
|
|
1404
1483
|
dialogWidth,
|
|
1405
1484
|
dialogTitle,
|
|
@@ -1432,6 +1511,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1432
1511
|
const data = {
|
|
1433
1512
|
uid: this.uid,
|
|
1434
1513
|
..._.omit(this._options, ['flowEngine']),
|
|
1514
|
+
props: { ...this.props },
|
|
1435
1515
|
stepParams: this.stepParams,
|
|
1436
1516
|
sortIndex: this.sortIndex,
|
|
1437
1517
|
flowRegistry: {},
|
|
@@ -1586,6 +1666,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1586
1666
|
seen.add(Cls);
|
|
1587
1667
|
const reg = classMenuExtensions.get(Cls);
|
|
1588
1668
|
if (reg) {
|
|
1669
|
+
let entryIndex = 0;
|
|
1589
1670
|
for (const entry of reg) {
|
|
1590
1671
|
if (entry.matcher && !entry.matcher(model)) continue;
|
|
1591
1672
|
const items =
|
|
@@ -1593,16 +1674,21 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
|
|
|
1593
1674
|
const group = entry.group || 'common-actions';
|
|
1594
1675
|
const sort = entry.sort ?? 0;
|
|
1595
1676
|
const prefix = entry.keyPrefix || Cls.name || 'extra';
|
|
1596
|
-
(
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1677
|
+
sortExtraMenuItems(
|
|
1678
|
+
(items || [])
|
|
1679
|
+
.map((it, idx: number) =>
|
|
1680
|
+
normalizeExtraMenuItem(it, {
|
|
1681
|
+
group,
|
|
1682
|
+
sort,
|
|
1683
|
+
prefix,
|
|
1684
|
+
path: `${entryIndex}-${idx}`,
|
|
1685
|
+
}),
|
|
1686
|
+
)
|
|
1687
|
+
.filter(isFlowModelExtraMenuItem),
|
|
1688
|
+
).forEach((it) => {
|
|
1689
|
+
collected.push(it);
|
|
1605
1690
|
});
|
|
1691
|
+
entryIndex += 1;
|
|
1606
1692
|
}
|
|
1607
1693
|
}
|
|
1608
1694
|
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}>
|