@nocobase/flow-engine 2.1.0-beta.2 → 2.1.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/LICENSE +201 -661
  2. package/README.md +79 -10
  3. package/lib/JSRunner.d.ts +10 -1
  4. package/lib/JSRunner.js +50 -5
  5. package/lib/ViewScopedFlowEngine.js +5 -1
  6. package/lib/components/FlowModelRenderer.d.ts +1 -1
  7. package/lib/components/FlowModelRenderer.js +10 -6
  8. package/lib/components/MobilePopup.js +6 -5
  9. package/lib/components/dnd/gridDragPlanner.js +6 -2
  10. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.d.ts +3 -0
  11. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +48 -9
  12. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.d.ts +19 -43
  13. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.js +339 -295
  14. package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +16 -2
  15. package/lib/components/settings/wrappers/contextual/useFloatToolbarPortal.d.ts +36 -0
  16. package/lib/components/settings/wrappers/contextual/useFloatToolbarPortal.js +272 -0
  17. package/lib/components/settings/wrappers/contextual/useFloatToolbarVisibility.d.ts +30 -0
  18. package/lib/components/settings/wrappers/contextual/useFloatToolbarVisibility.js +247 -0
  19. package/lib/components/subModel/AddSubModelButton.js +27 -1
  20. package/lib/components/subModel/utils.js +2 -2
  21. package/lib/data-source/index.js +6 -0
  22. package/lib/executor/FlowExecutor.js +31 -8
  23. package/lib/flowContext.js +31 -1
  24. package/lib/flowEngine.d.ts +151 -1
  25. package/lib/flowEngine.js +389 -15
  26. package/lib/flowSettings.d.ts +14 -6
  27. package/lib/flowSettings.js +34 -6
  28. package/lib/lazy-helper.d.ts +14 -0
  29. package/lib/lazy-helper.js +71 -0
  30. package/lib/locale/en-US.json +1 -0
  31. package/lib/locale/index.d.ts +2 -0
  32. package/lib/locale/zh-CN.json +1 -0
  33. package/lib/models/flowModel.d.ts +2 -1
  34. package/lib/models/flowModel.js +28 -9
  35. package/lib/reactive/observer.js +46 -16
  36. package/lib/runjs-context/registry.d.ts +1 -1
  37. package/lib/runjs-context/setup.js +20 -12
  38. package/lib/runjs-context/snippets/index.js +13 -2
  39. package/lib/runjs-context/snippets/scene/detail/set-field-style.snippet.d.ts +11 -0
  40. package/lib/runjs-context/snippets/scene/detail/set-field-style.snippet.js +50 -0
  41. package/lib/runjs-context/snippets/scene/table/set-cell-style.snippet.d.ts +11 -0
  42. package/lib/runjs-context/snippets/scene/table/set-cell-style.snippet.js +54 -0
  43. package/lib/scheduler/ModelOperationScheduler.d.ts +5 -1
  44. package/lib/scheduler/ModelOperationScheduler.js +3 -2
  45. package/lib/types.d.ts +47 -1
  46. package/lib/utils/index.d.ts +2 -2
  47. package/lib/utils/index.js +4 -0
  48. package/lib/utils/parsePathnameToViewParams.js +1 -1
  49. package/lib/utils/runjsTemplateCompat.js +1 -1
  50. package/lib/utils/runjsValue.js +41 -11
  51. package/lib/utils/schema-utils.d.ts +7 -1
  52. package/lib/utils/schema-utils.js +19 -0
  53. package/lib/views/FlowView.d.ts +7 -1
  54. package/lib/views/runViewBeforeClose.d.ts +10 -0
  55. package/lib/views/runViewBeforeClose.js +45 -0
  56. package/lib/views/useDialog.d.ts +2 -1
  57. package/lib/views/useDialog.js +20 -3
  58. package/lib/views/useDrawer.d.ts +2 -1
  59. package/lib/views/useDrawer.js +20 -3
  60. package/lib/views/usePage.d.ts +2 -1
  61. package/lib/views/usePage.js +10 -3
  62. package/package.json +6 -5
  63. package/src/JSRunner.ts +68 -4
  64. package/src/ViewScopedFlowEngine.ts +4 -0
  65. package/src/__tests__/JSRunner.test.ts +27 -1
  66. package/src/__tests__/flow-engine.test.ts +166 -0
  67. package/src/__tests__/flowContext.test.ts +65 -1
  68. package/src/__tests__/flowEngine.modelLoaders.test.ts +245 -0
  69. package/src/__tests__/flowSettings.test.ts +94 -15
  70. package/src/__tests__/renderHiddenInConfig.test.tsx +6 -6
  71. package/src/__tests__/runjsContext.test.ts +16 -0
  72. package/src/__tests__/runjsContextRuntime.test.ts +2 -0
  73. package/src/__tests__/runjsPreprocessDefault.test.ts +23 -0
  74. package/src/__tests__/runjsSnippets.test.ts +21 -0
  75. package/src/__tests__/viewScopedFlowEngine.test.ts +3 -3
  76. package/src/components/FlowModelRenderer.tsx +12 -6
  77. package/src/components/MobilePopup.tsx +4 -2
  78. package/src/components/__tests__/FlowModelRenderer.test.tsx +65 -2
  79. package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +20 -10
  80. package/src/components/__tests__/gridDragPlanner.test.ts +88 -0
  81. package/src/components/dnd/gridDragPlanner.ts +8 -2
  82. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +63 -9
  83. package/src/components/settings/wrappers/contextual/FlowsFloatContextMenu.tsx +468 -440
  84. package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +18 -2
  85. package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +95 -0
  86. package/src/components/settings/wrappers/contextual/__tests__/FlowsFloatContextMenu.test.tsx +609 -0
  87. package/src/components/settings/wrappers/contextual/useFloatToolbarPortal.ts +358 -0
  88. package/src/components/settings/wrappers/contextual/useFloatToolbarVisibility.ts +281 -0
  89. package/src/components/subModel/AddSubModelButton.tsx +32 -2
  90. package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +142 -32
  91. package/src/components/subModel/utils.ts +1 -1
  92. package/src/data-source/index.ts +6 -0
  93. package/src/executor/FlowExecutor.ts +34 -9
  94. package/src/executor/__tests__/flowExecutor.test.ts +57 -0
  95. package/src/flowContext.ts +35 -3
  96. package/src/flowEngine.ts +445 -11
  97. package/src/flowSettings.ts +40 -6
  98. package/src/lazy-helper.tsx +57 -0
  99. package/src/locale/en-US.json +1 -0
  100. package/src/locale/zh-CN.json +1 -0
  101. package/src/models/__tests__/dispatchEvent.when.test.ts +214 -0
  102. package/src/models/flowModel.tsx +31 -10
  103. package/src/reactive/__tests__/observer.test.tsx +82 -0
  104. package/src/reactive/observer.tsx +87 -25
  105. package/src/runjs-context/registry.ts +1 -1
  106. package/src/runjs-context/setup.ts +22 -12
  107. package/src/runjs-context/snippets/index.ts +12 -1
  108. package/src/runjs-context/snippets/scene/detail/set-field-style.snippet.ts +30 -0
  109. package/src/runjs-context/snippets/scene/table/set-cell-style.snippet.ts +34 -0
  110. package/src/scheduler/ModelOperationScheduler.ts +14 -3
  111. package/src/types.ts +60 -0
  112. package/src/utils/__tests__/parsePathnameToViewParams.test.ts +7 -0
  113. package/src/utils/__tests__/runjsValue.test.ts +11 -0
  114. package/src/utils/__tests__/utils.test.ts +62 -0
  115. package/src/utils/index.ts +2 -1
  116. package/src/utils/parsePathnameToViewParams.ts +2 -2
  117. package/src/utils/runjsTemplateCompat.ts +1 -1
  118. package/src/utils/runjsValue.ts +50 -11
  119. package/src/utils/schema-utils.ts +30 -1
  120. package/src/views/FlowView.tsx +11 -1
  121. package/src/views/__tests__/runViewBeforeClose.test.ts +30 -0
  122. package/src/views/__tests__/useDialog.closeDestroy.test.tsx +13 -12
  123. package/src/views/runViewBeforeClose.ts +19 -0
  124. package/src/views/useDialog.tsx +25 -3
  125. package/src/views/useDrawer.tsx +25 -3
  126. package/src/views/usePage.tsx +12 -3
@@ -134,6 +134,17 @@ const openStepSettingsDialog = async ({
134
134
  };
135
135
 
136
136
  const openView = model.context.viewer[mode].bind(model.context.viewer);
137
+ const resolvedUiModeProps = toJS(uiModeProps) || {};
138
+ const { zIndex: uiModeZIndex, ...restUiModeProps } = resolvedUiModeProps;
139
+ const resolveDialogZIndex = (rawZIndex?: number) => {
140
+ const nextZIndex =
141
+ typeof model.context.viewer?.getNextZIndex === 'function'
142
+ ? model.context.viewer.getNextZIndex()
143
+ : (model.context.themeToken?.zIndexPopupBase || 1000) + 1;
144
+ const inputZIndex = Number(rawZIndex) || 0;
145
+ return Math.max(nextZIndex, inputZIndex);
146
+ };
147
+ const mergedZIndex = resolveDialogZIndex(uiModeZIndex);
137
148
 
138
149
  const form = createForm({
139
150
  initialValues: compileUiSchema(scopes, initialValues),
@@ -152,7 +163,8 @@ const openStepSettingsDialog = async ({
152
163
  title: dialogTitle || t(title),
153
164
  width: dialogWidth,
154
165
  destroyOnClose: true,
155
- ...toJS(uiModeProps),
166
+ ...restUiModeProps,
167
+ zIndex: mergedZIndex,
156
168
  // 透传 navigation,便于变量元信息根据真实视图栈推断父级弹窗
157
169
  inputArgs,
158
170
  onClose: () => {
@@ -165,7 +177,11 @@ const openStepSettingsDialog = async ({
165
177
  useEffect(() => {
166
178
  return autorun(() => {
167
179
  const dynamicProps = toJS(uiModeProps);
168
- currentDialog.update(dynamicProps);
180
+ const { zIndex, ...restDynamicProps } = dynamicProps || {};
181
+ currentDialog.update({
182
+ ...restDynamicProps,
183
+ zIndex: resolveDialogZIndex(zIndex),
184
+ });
169
185
  });
170
186
  }, []);
171
187
 
@@ -37,6 +37,7 @@ vi.mock('antd', async (importOriginal) => {
37
37
  (globalThis as any).__lastDropdownMenu = props.menu;
38
38
  (globalThis as any).__lastDropdownOnOpenChange = props.onOpenChange;
39
39
  (globalThis as any).__lastDropdownOpen = props.open;
40
+ (globalThis as any).__lastDropdownGetPopupContainer = props.getPopupContainer;
40
41
  dropdownMenus.push(props.menu);
41
42
  return React.createElement('span', { 'data-testid': 'dropdown' }, props.children);
42
43
  };
@@ -132,6 +133,7 @@ describe('DefaultSettingsIcon - only static flows are shown', () => {
132
133
  (globalThis as any).__lastDropdownMenu = undefined;
133
134
  (globalThis as any).__lastDropdownOnOpenChange = undefined;
134
135
  (globalThis as any).__lastDropdownOpen = undefined;
136
+ (globalThis as any).__lastDropdownGetPopupContainer = undefined;
135
137
  });
136
138
 
137
139
  afterEach(() => {
@@ -414,6 +416,99 @@ describe('DefaultSettingsIcon - only static flows are shown', () => {
414
416
  });
415
417
  });
416
418
 
419
+ it('prefers the local toolbar container as popup host inside contextual toolbars', async () => {
420
+ class TestFlowModel extends FlowModel {}
421
+ const engine = new FlowEngine();
422
+ const model = new TestFlowModel({ uid: 'm-toolbar-popup-host', flowEngine: engine });
423
+ const externalPopupRoot = document.createElement('div');
424
+ externalPopupRoot.id = 'external-popup-root';
425
+ document.body.appendChild(externalPopupRoot);
426
+
427
+ TestFlowModel.registerFlow({
428
+ key: 'flowPopupHost',
429
+ title: 'Flow Popup Host',
430
+ steps: {
431
+ general: { title: 'General', uiSchema: { f: { type: 'string', 'x-component': 'Input' } } },
432
+ },
433
+ });
434
+
435
+ const { getByTestId, unmount } = render(
436
+ React.createElement(
437
+ ConfigProvider as any,
438
+ null,
439
+ React.createElement(
440
+ App as any,
441
+ null,
442
+ React.createElement(
443
+ 'div',
444
+ { className: 'nb-toolbar-container' },
445
+ React.createElement(
446
+ 'div',
447
+ { className: 'nb-toolbar-container-icons' },
448
+ React.createElement(DefaultSettingsIcon as any, {
449
+ model,
450
+ getPopupContainer: () => externalPopupRoot,
451
+ }),
452
+ ),
453
+ ),
454
+ ),
455
+ ),
456
+ );
457
+
458
+ await waitFor(() => {
459
+ expect((globalThis as any).__lastDropdownGetPopupContainer).toBeTruthy();
460
+ });
461
+
462
+ const popupContainer = (globalThis as any).__lastDropdownGetPopupContainer?.(getByTestId('dropdown'));
463
+ expect(popupContainer).toBeTruthy();
464
+ expect(popupContainer?.className).toContain('nb-toolbar-container-icons');
465
+
466
+ unmount();
467
+ externalPopupRoot.remove();
468
+ });
469
+
470
+ it('falls back to the provided popup host outside contextual toolbars', async () => {
471
+ class TestFlowModel extends FlowModel {}
472
+ const engine = new FlowEngine();
473
+ const model = new TestFlowModel({ uid: 'm-external-popup-host', flowEngine: engine });
474
+ const externalPopupRoot = document.createElement('div');
475
+ externalPopupRoot.id = 'external-popup-root';
476
+ document.body.appendChild(externalPopupRoot);
477
+
478
+ TestFlowModel.registerFlow({
479
+ key: 'flowExternalPopupHost',
480
+ title: 'Flow External Popup Host',
481
+ steps: {
482
+ general: { title: 'General', uiSchema: { f: { type: 'string', 'x-component': 'Input' } } },
483
+ },
484
+ });
485
+
486
+ const { getByTestId, unmount } = render(
487
+ React.createElement(
488
+ ConfigProvider as any,
489
+ null,
490
+ React.createElement(
491
+ App as any,
492
+ null,
493
+ React.createElement(DefaultSettingsIcon as any, {
494
+ model,
495
+ getPopupContainer: () => externalPopupRoot,
496
+ }),
497
+ ),
498
+ ),
499
+ );
500
+
501
+ await waitFor(() => {
502
+ expect((globalThis as any).__lastDropdownGetPopupContainer).toBeTruthy();
503
+ });
504
+
505
+ const popupContainer = (globalThis as any).__lastDropdownGetPopupContainer?.(getByTestId('dropdown'));
506
+ expect(popupContainer).toBe(externalPopupRoot);
507
+
508
+ unmount();
509
+ externalPopupRoot.remove();
510
+ });
511
+
417
512
  it('copy UID action writes model uid to clipboard', async () => {
418
513
  class TestFlowModel extends FlowModel {}
419
514
  const engine = new FlowEngine();