@nocobase/flow-engine 2.1.0-beta.15 → 2.1.0-beta.17

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 (31) hide show
  1. package/lib/components/MobilePopup.js +6 -5
  2. package/lib/components/subModel/AddSubModelButton.js +1 -1
  3. package/lib/components/subModel/utils.js +2 -2
  4. package/lib/flowEngine.d.ts +132 -1
  5. package/lib/flowEngine.js +360 -14
  6. package/lib/flowSettings.d.ts +14 -6
  7. package/lib/flowSettings.js +34 -6
  8. package/lib/lazy-helper.d.ts +14 -0
  9. package/lib/lazy-helper.js +71 -0
  10. package/lib/models/flowModel.js +17 -7
  11. package/lib/types.d.ts +46 -0
  12. package/lib/utils/runjsTemplateCompat.js +1 -1
  13. package/package.json +4 -4
  14. package/src/__tests__/flow-engine.test.ts +166 -0
  15. package/src/__tests__/flowEngine.modelLoaders.test.ts +245 -0
  16. package/src/__tests__/flowSettings.test.ts +94 -15
  17. package/src/__tests__/renderHiddenInConfig.test.tsx +6 -6
  18. package/src/__tests__/viewScopedFlowEngine.test.ts +3 -3
  19. package/src/components/MobilePopup.tsx +4 -2
  20. package/src/components/__tests__/FlowModelRenderer.test.tsx +22 -0
  21. package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +3 -3
  22. package/src/components/settings/wrappers/contextual/__tests__/FlowsFloatContextMenu.test.tsx +6 -6
  23. package/src/components/subModel/AddSubModelButton.tsx +1 -1
  24. package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +93 -33
  25. package/src/components/subModel/utils.ts +1 -1
  26. package/src/flowEngine.ts +412 -10
  27. package/src/flowSettings.ts +40 -6
  28. package/src/lazy-helper.tsx +57 -0
  29. package/src/models/flowModel.tsx +18 -6
  30. package/src/types.ts +59 -0
  31. package/src/utils/runjsTemplateCompat.ts +1 -1
package/lib/flowEngine.js CHANGED
@@ -83,6 +83,28 @@ const _FlowEngine = class _FlowEngine {
83
83
  * @private
84
84
  */
85
85
  __publicField(this, "_modelClasses", import_reactive.observable.shallow(/* @__PURE__ */ new Map()));
86
+ /**
87
+ * Registered model entries.
88
+ * Key is the model class name, value is the model loader entry.
89
+ * @private
90
+ */
91
+ __publicField(this, "_modelLoaders", /* @__PURE__ */ new Map());
92
+ /**
93
+ * In-flight model loading promises.
94
+ * Key is the model class name, value is the loading promise.
95
+ * @private
96
+ */
97
+ __publicField(this, "_loadingModelPromises", /* @__PURE__ */ new Map());
98
+ /**
99
+ * Whether model-loader preload has completed in this session.
100
+ * @private
101
+ */
102
+ __publicField(this, "_modelLoadersPreloaded", false);
103
+ /**
104
+ * In-flight model-loader preload promise.
105
+ * @private
106
+ */
107
+ __publicField(this, "_modelLoadersPreloadPromise");
86
108
  /**
87
109
  * Created model instances.
88
110
  * Key is the model instance UID, value is the model instance object.
@@ -372,6 +394,16 @@ const _FlowEngine = class _FlowEngine {
372
394
  getEvents() {
373
395
  return this._eventRegistry.getEvents();
374
396
  }
397
+ /**
398
+ * for proxy instance, the #registerModel can't be called.
399
+ */
400
+ _registerModel(name, modelClass) {
401
+ if (this._modelClasses.has(name)) {
402
+ console.warn(`FlowEngine: Model class with name '${name}' is already registered and will be overwritten.`);
403
+ }
404
+ Object.defineProperty(modelClass, "name", { value: name });
405
+ this._modelClasses.set(name, modelClass);
406
+ }
375
407
  /**
376
408
  * Register multiple model classes.
377
409
  * @param {Record<string, ModelConstructor>} models Model class map, key is model name, value is model constructor
@@ -384,6 +416,274 @@ const _FlowEngine = class _FlowEngine {
384
416
  __privateMethod(this, _FlowEngine_instances, registerModel_fn).call(this, name, modelClass);
385
417
  }
386
418
  }
419
+ /**
420
+ * Register multiple model loader entries.
421
+ * The `extends` field declares parent class(es) for async subclass discovery via `getSubclassesOfAsync`.
422
+ * It accepts `string | ModelConstructor | (string | ModelConstructor)[]` and is normalized to `string[]` internally.
423
+ * @param {FlowModelLoaderInputMap} loaders Model loader input map
424
+ * @returns {void}
425
+ * @example
426
+ * flowEngine.registerModelLoaders({
427
+ * DemoModel: {
428
+ * extends: 'BaseModel',
429
+ * loader: () => import('./models/DemoModel'),
430
+ * },
431
+ * });
432
+ */
433
+ registerModelLoaders(loaders) {
434
+ let changed = false;
435
+ for (const [name, input] of Object.entries(loaders)) {
436
+ if (this._modelLoaders.has(name)) {
437
+ console.warn(`FlowEngine: Model loader with name '${name}' is already registered and will be overwritten.`);
438
+ }
439
+ const entry = {
440
+ loader: input.loader
441
+ };
442
+ if (input.extends != null) {
443
+ const raw = Array.isArray(input.extends) ? input.extends : [input.extends];
444
+ entry.extends = raw.map((item) => typeof item === "string" ? item : item.name);
445
+ }
446
+ this._modelLoaders.set(name, entry);
447
+ changed = true;
448
+ }
449
+ if (changed) {
450
+ this._modelLoadersPreloaded = false;
451
+ this._modelLoadersPreloadPromise = void 0;
452
+ }
453
+ }
454
+ /**
455
+ * Get a registered model class (constructor) asynchronously.
456
+ * This will first ensure the model loader entry is resolved.
457
+ * @param {string} name Model class name
458
+ * @returns {Promise<ModelConstructor | undefined>} Model constructor, or undefined if not found
459
+ */
460
+ async getModelClassAsync(name) {
461
+ await this.ensureModel(name);
462
+ return this.getModelClass(name);
463
+ }
464
+ /**
465
+ * Get all registered model classes asynchronously.
466
+ * This will first ensure all registered model loader entries are resolved.
467
+ * @returns {Promise<Map<string, ModelConstructor>>} Model class map
468
+ */
469
+ async getModelClassesAsync() {
470
+ await this.ensureModels(Array.from(this._modelLoaders.keys()));
471
+ return this.getModelClasses();
472
+ }
473
+ /**
474
+ * Create and register a model instance asynchronously.
475
+ * This will first ensure all string-based model references in the model tree are resolved.
476
+ * @template T FlowModel subclass type, defaults to FlowModel.
477
+ * @param {CreateModelOptions} options Model creation options
478
+ * @returns {Promise<T>} Created model instance
479
+ */
480
+ async createModelAsync(options, extra) {
481
+ await this.resolveModelTree(options);
482
+ return this.createModel(options, extra);
483
+ }
484
+ /**
485
+ * Normalize a loader result into a model constructor.
486
+ * @param {string} name Model class name
487
+ * @param {FlowModelLoaderResult} loaded Loader result
488
+ * @returns {ModelConstructor | null} Normalized model constructor
489
+ * @private
490
+ */
491
+ normalizeModelLoaderResult(name, loaded) {
492
+ if (typeof loaded === "function") {
493
+ return loaded;
494
+ }
495
+ if (loaded && typeof loaded === "object") {
496
+ const defaultExport = loaded.default;
497
+ if (typeof defaultExport === "function") {
498
+ return defaultExport;
499
+ }
500
+ const namedExport = loaded[name];
501
+ if (typeof namedExport === "function") {
502
+ return namedExport;
503
+ }
504
+ }
505
+ console.warn(`FlowEngine: model loader for '${name}' did not resolve to a valid model constructor.`);
506
+ return null;
507
+ }
508
+ /**
509
+ * Collect string-based model names from a model tree.
510
+ * @param {unknown} data Model tree data
511
+ * @param {Set<string>} names Model name set
512
+ * @private
513
+ */
514
+ collectModelNamesFromTree(data, names) {
515
+ if (!data || typeof data !== "object") {
516
+ return;
517
+ }
518
+ if (Array.isArray(data)) {
519
+ data.forEach((item) => this.collectModelNamesFromTree(item, names));
520
+ return;
521
+ }
522
+ const tree = data;
523
+ if (typeof tree.use === "string") {
524
+ names.add(tree.use);
525
+ }
526
+ const subModels = tree.subModels;
527
+ if (!subModels || typeof subModels !== "object") {
528
+ return;
529
+ }
530
+ Object.values(subModels).forEach((value) => {
531
+ this.collectModelNamesFromTree(value, names);
532
+ });
533
+ }
534
+ /**
535
+ * Collect additional model names from object-form meta.createModelOptions defaults.
536
+ * @param {ModelConstructor} modelClass Model class constructor
537
+ * @param {Set<string>} names Model name set
538
+ * @private
539
+ */
540
+ collectModelNamesFromMetaDefaults(modelClass, names) {
541
+ var _a;
542
+ const metaCreate = (_a = modelClass.meta) == null ? void 0 : _a.createModelOptions;
543
+ if (metaCreate && typeof metaCreate === "object") {
544
+ this.collectModelNamesFromTree(metaCreate, names);
545
+ }
546
+ }
547
+ /**
548
+ * Ensure a single model class is available.
549
+ * @param {string} name Model class name
550
+ * @returns {Promise<ModelConstructor | null>} Model constructor or null when resolution fails
551
+ * @private
552
+ */
553
+ async ensureModel(name) {
554
+ const existing = this._modelClasses.get(name);
555
+ if (existing) {
556
+ return existing;
557
+ }
558
+ const inflight = this._loadingModelPromises.get(name);
559
+ if (inflight) {
560
+ return inflight;
561
+ }
562
+ const entry = this._modelLoaders.get(name);
563
+ if (!entry) {
564
+ console.warn(`FlowEngine: Model entry '${name}' not found. Falling back to ErrorFlowModel when needed.`);
565
+ return null;
566
+ }
567
+ const promise = (async () => {
568
+ try {
569
+ const loaded = await entry.loader();
570
+ const modelClass = this.normalizeModelLoaderResult(name, loaded);
571
+ if (!modelClass) {
572
+ return null;
573
+ }
574
+ this._registerModel(name, modelClass);
575
+ return modelClass;
576
+ } catch (error) {
577
+ console.warn(`FlowEngine: Failed to load model '${name}'. Falling back to ErrorFlowModel when needed.`, error);
578
+ return null;
579
+ } finally {
580
+ this._loadingModelPromises.delete(name);
581
+ }
582
+ })();
583
+ this._loadingModelPromises.set(name, promise);
584
+ return promise;
585
+ }
586
+ /**
587
+ * Ensure multiple model classes are available.
588
+ * @param {string[]} names Model class names
589
+ * @returns {Promise<EnsureBatchResult>} Batch ensure result
590
+ * @private
591
+ */
592
+ async ensureModels(names) {
593
+ const requested = Array.from(new Set(names.filter((name) => !!name)));
594
+ const loaded = [];
595
+ const failed = [];
596
+ const results = await Promise.all(
597
+ requested.map(async (name) => {
598
+ const modelClass = await this.ensureModel(name);
599
+ return { name, modelClass };
600
+ })
601
+ );
602
+ results.forEach(({ name, modelClass }) => {
603
+ if (modelClass) {
604
+ loaded.push(name);
605
+ } else {
606
+ failed.push({ name });
607
+ }
608
+ });
609
+ return { requested, loaded, failed };
610
+ }
611
+ /**
612
+ * Resolve all unresolved string-based model references in a model tree before synchronous creation begins.
613
+ *
614
+ * Use this when you already have a model tree object, such as repository-returned data or resolved
615
+ * `createModelOptions`, and you need to ensure every string `use` in that tree has been loaded and
616
+ * registered into `_modelClasses` before calling `createModel()`.
617
+ *
618
+ * @param {unknown} data Model tree data
619
+ * @returns {Promise<EnsureBatchResult>} Batch ensure result
620
+ */
621
+ async resolveModelTree(data) {
622
+ const requested = /* @__PURE__ */ new Set();
623
+ const loaded = /* @__PURE__ */ new Set();
624
+ const failed = /* @__PURE__ */ new Map();
625
+ const processed = /* @__PURE__ */ new Set();
626
+ const pending = /* @__PURE__ */ new Set();
627
+ this.collectModelNamesFromTree(data, pending);
628
+ while (pending.size > 0) {
629
+ const batch = Array.from(pending).filter((name) => !processed.has(name));
630
+ pending.clear();
631
+ if (batch.length === 0) {
632
+ break;
633
+ }
634
+ batch.forEach((name) => requested.add(name));
635
+ const result = await this.ensureModels(batch);
636
+ result.loaded.forEach((name) => {
637
+ processed.add(name);
638
+ loaded.add(name);
639
+ const modelClass = this.getModelClass(name);
640
+ if (modelClass) {
641
+ const discovered = /* @__PURE__ */ new Set();
642
+ this.collectModelNamesFromMetaDefaults(modelClass, discovered);
643
+ discovered.forEach((discoveredName) => {
644
+ if (!processed.has(discoveredName)) {
645
+ pending.add(discoveredName);
646
+ }
647
+ });
648
+ }
649
+ });
650
+ result.failed.forEach((item) => {
651
+ processed.add(item.name);
652
+ failed.set(item.name, item);
653
+ });
654
+ }
655
+ return {
656
+ requested: Array.from(requested),
657
+ loaded: Array.from(loaded),
658
+ failed: Array.from(failed.values())
659
+ };
660
+ }
661
+ /**
662
+ * Preload all currently registered unresolved model loaders.
663
+ *
664
+ * This method is intended for flow-settings/discovery style entry points that need registered model
665
+ * classes to exist before UI is rendered, without requiring callers to know which specific models
666
+ * will be touched next.
667
+ *
668
+ * @returns {Promise<EnsureBatchResult>} Batch ensure result
669
+ */
670
+ async preloadModelLoaders() {
671
+ const unresolved = Array.from(this._modelLoaders.keys()).filter((name) => !this._modelClasses.has(name));
672
+ if (unresolved.length === 0) {
673
+ this._modelLoadersPreloaded = true;
674
+ return { requested: [], loaded: [], failed: [] };
675
+ }
676
+ if (this._modelLoadersPreloadPromise) {
677
+ return this._modelLoadersPreloadPromise;
678
+ }
679
+ this._modelLoadersPreloadPromise = (async () => {
680
+ const result = await this.ensureModels(unresolved);
681
+ this._modelLoadersPreloaded = result.failed.length === 0;
682
+ this._modelLoadersPreloadPromise = void 0;
683
+ return result;
684
+ })();
685
+ return this._modelLoadersPreloadPromise;
686
+ }
387
687
  registerResources(resources) {
388
688
  for (const [name, resourceClass] of Object.entries(resources)) {
389
689
  this._resources.set(name, resourceClass);
@@ -447,6 +747,55 @@ const _FlowEngine = class _FlowEngine {
447
747
  }
448
748
  return result;
449
749
  }
750
+ /**
751
+ * Asynchronously get all subclasses of a base class, including those registered via model loaders.
752
+ * Merges results from already-loaded classes (_modelClasses) and async loader entries with matching `extends` declarations.
753
+ * Loader-resolved classes are validated with `isInheritedFrom`; mismatches are warned and excluded.
754
+ * @param {string | ModelConstructor} baseClass Base class name or constructor
755
+ * @param {(ModelClass: ModelConstructor, className: string) => boolean} [filter] Optional filter function
756
+ * @returns {Promise<Map<string, ModelConstructor>>} Model classes that are subclasses of the base class
757
+ */
758
+ async getSubclassesOfAsync(baseClass, filter) {
759
+ var _a;
760
+ const baseClassName = typeof baseClass === "string" ? baseClass : baseClass.name;
761
+ let parentModelClass;
762
+ if (typeof baseClass === "string") {
763
+ if (!this.getModelClass(baseClass)) {
764
+ await this.ensureModel(baseClass);
765
+ }
766
+ parentModelClass = this.getModelClass(baseClass);
767
+ } else {
768
+ parentModelClass = baseClass;
769
+ }
770
+ if (!parentModelClass) {
771
+ return /* @__PURE__ */ new Map();
772
+ }
773
+ const result = this.getSubclassesOf(parentModelClass, filter);
774
+ const loaderCandidates = [];
775
+ for (const [name, entry] of this._modelLoaders) {
776
+ if (result.has(name) || this._modelClasses.has(name)) continue;
777
+ if ((_a = entry.extends) == null ? void 0 : _a.includes(baseClassName)) {
778
+ loaderCandidates.push(name);
779
+ }
780
+ }
781
+ if (loaderCandidates.length > 0) {
782
+ await this.ensureModels(loaderCandidates);
783
+ }
784
+ for (const name of loaderCandidates) {
785
+ const ModelClass = this._modelClasses.get(name);
786
+ if (!ModelClass) continue;
787
+ if (!(0, import_utils.isInheritedFrom)(ModelClass, parentModelClass)) {
788
+ console.warn(
789
+ `FlowEngine: Model '${name}' declares extends '${baseClassName}' but does not actually inherit from it. Skipping.`
790
+ );
791
+ continue;
792
+ }
793
+ if (!filter || filter(ModelClass, name)) {
794
+ result.set(name, ModelClass);
795
+ }
796
+ }
797
+ return result;
798
+ }
450
799
  /**
451
800
  * Create and register a model instance.
452
801
  * If an instance with the same UID exists, returns the existing instance.
@@ -732,7 +1081,7 @@ const _FlowEngine = class _FlowEngine {
732
1081
  * Hydrate a model into current engine from an already-existing model instance in previous engines.
733
1082
  * - Avoids repository requests when the model tree is already present in memory.
734
1083
  */
735
- hydrateModelFromPreviousEngines(options, extra) {
1084
+ async hydrateModelFromPreviousEngines(options, extra) {
736
1085
  var _a;
737
1086
  const uid = options == null ? void 0 : options.uid;
738
1087
  const parentId = options == null ? void 0 : options.parentId;
@@ -744,7 +1093,7 @@ const _FlowEngine = class _FlowEngine {
744
1093
  }
745
1094
  if (existing) {
746
1095
  const data = existing.serialize();
747
- return this.createModel(data, extra);
1096
+ return this.createModelAsync(data, extra);
748
1097
  }
749
1098
  }
750
1099
  if (parentId && subKey) {
@@ -755,10 +1104,10 @@ const _FlowEngine = class _FlowEngine {
755
1104
  if (!localParent) {
756
1105
  const parentData = parentFromPrev.serialize();
757
1106
  delete parentData.subModels;
758
- localParent = this.createModel(parentData, extra);
1107
+ localParent = await this.createModelAsync(parentData, extra);
759
1108
  }
760
1109
  const modelData = modelFromPrev.serialize();
761
- const localModel = this.createModel(modelData, extra);
1110
+ const localModel = await this.createModelAsync(modelData, extra);
762
1111
  const mounted = (_a = localParent.subModels) == null ? void 0 : _a[subKey];
763
1112
  if (Array.isArray(mounted)) {
764
1113
  const exists = mounted.some((m) => (m == null ? void 0 : m.uid) === (localModel == null ? void 0 : localModel.uid));
@@ -794,20 +1143,21 @@ const _FlowEngine = class _FlowEngine {
794
1143
  if (model) {
795
1144
  return model;
796
1145
  }
797
- const hydrated = this.hydrateModelFromPreviousEngines(options);
1146
+ const hydrated = await this.hydrateModelFromPreviousEngines(options);
798
1147
  if (hydrated) {
799
1148
  return hydrated;
800
1149
  }
801
1150
  }
802
1151
  const data = await this._modelRepository.findOne(options);
803
1152
  if (!(data == null ? void 0 : data.uid)) return null;
1153
+ await this.resolveModelTree(data);
804
1154
  if (refresh) {
805
1155
  const existing = this.getModel(data.uid);
806
1156
  if (existing) {
807
1157
  this.removeModelWithSubModels(existing.uid);
808
1158
  }
809
1159
  }
810
- return this.createModel(data);
1160
+ return this.createModelAsync(data);
811
1161
  }
812
1162
  /**
813
1163
  * Find a sub-model by parent model ID and subKey.
@@ -845,16 +1195,16 @@ const _FlowEngine = class _FlowEngine {
845
1195
  if (m) {
846
1196
  return m;
847
1197
  }
848
- const hydrated = this.hydrateModelFromPreviousEngines(options, extra);
1198
+ const hydrated = await this.hydrateModelFromPreviousEngines(options, extra);
849
1199
  if (hydrated) {
850
1200
  return hydrated;
851
1201
  }
852
1202
  const data = await this._modelRepository.findOne(options);
853
1203
  let model = null;
854
1204
  if (data == null ? void 0 : data.uid) {
855
- model = this.createModel(data, extra);
1205
+ model = await this.createModelAsync(data, extra);
856
1206
  } else {
857
- model = this.createModel(options, extra);
1207
+ model = await this.createModelAsync(options, extra);
858
1208
  if (!(extra == null ? void 0 : extra.skipSave)) {
859
1209
  await model.save();
860
1210
  }
@@ -1098,11 +1448,7 @@ _FlowEngine_instances = new WeakSet();
1098
1448
  * @private
1099
1449
  */
1100
1450
  registerModel_fn = /* @__PURE__ */ __name(function(name, modelClass) {
1101
- if (this._modelClasses.has(name)) {
1102
- console.warn(`FlowEngine: Model class with name '${name}' is already registered and will be overwritten.`);
1103
- }
1104
- Object.defineProperty(modelClass, "name", { value: name });
1105
- this._modelClasses.set(name, modelClass);
1451
+ return this._registerModel(name, modelClass);
1106
1452
  }, "#registerModel");
1107
1453
  __name(_FlowEngine, "FlowEngine");
1108
1454
  let FlowEngine = _FlowEngine;
@@ -79,12 +79,19 @@ export interface FlowSettingsOpenOptions {
79
79
  /** 配置保存成功后触发的回调 */
80
80
  onSaved?: () => void | Promise<void>;
81
81
  }
82
+ export type FlowSettingsComponent = React.ComponentType<any>;
83
+ export type FlowSettingsComponentModule = {
84
+ default?: FlowSettingsComponent;
85
+ } | Record<string, FlowSettingsComponent>;
86
+ export type FlowSettingsComponentLoader = () => Promise<FlowSettingsComponentModule | FlowSettingsComponent>;
87
+ export type FlowSettingsComponentLoaderMap = Record<string, FlowSettingsComponentLoader>;
82
88
  export declare class FlowSettings {
83
89
  #private;
84
90
  components: Record<string, any>;
85
91
  scopes: Record<string, any>;
86
92
  private antdComponentsLoaded;
87
93
  enabled: boolean;
94
+ private engine;
88
95
  toolbarItems: ToolbarItemConfig[];
89
96
  constructor(engine: FlowEngine);
90
97
  on(event: 'beforeOpen', callback: (...args: any[]) => void): void;
@@ -110,6 +117,7 @@ export declare class FlowSettings {
110
117
  * flowSettings.registerComponents({ MyComponent, AnotherComponent });
111
118
  */
112
119
  registerComponents(components: Record<string, any>): void;
120
+ registerComponentLoaders(loaders: FlowSettingsComponentLoaderMap): void;
113
121
  /**
114
122
  * 添加作用域到 FlowSettings 的作用域注册表中。
115
123
  * 这些作用域可以在 flow step 的 uiSchema 中使用。
@@ -122,17 +130,17 @@ export declare class FlowSettings {
122
130
  /**
123
131
  * 启用流程设置组件的显示
124
132
  * @example
125
- * flowSettings.enable();
133
+ * await flowSettings.enable();
126
134
  */
127
- enable(): void;
128
- forceEnable(): void;
135
+ enable(): Promise<void>;
136
+ forceEnable(): Promise<void>;
129
137
  /**
130
138
  * 禁用流程设置组件的显示
131
139
  * @example
132
- * flowSettings.disable();
140
+ * await flowSettings.disable();
133
141
  */
134
- disable(): void;
135
- forceDisable(): void;
142
+ disable(): Promise<void>;
143
+ forceDisable(): Promise<void>;
136
144
  /**
137
145
  * 添加扩展工具栏项目
138
146
  * @param {ToolbarItemConfig} config 项目配置
@@ -65,6 +65,7 @@ var import_utils = require("./utils");
65
65
  var import_exceptions = require("./utils/exceptions");
66
66
  var import_useFlowStep = require("./hooks/useFlowStep");
67
67
  var import_views = require("./views");
68
+ var import_lazy_helper = require("./lazy-helper");
68
69
  var _forceEnabled, _emitter;
69
70
  const Panel = import_antd.Collapse.Panel;
70
71
  const _FlowSettings = class _FlowSettings {
@@ -73,10 +74,12 @@ const _FlowSettings = class _FlowSettings {
73
74
  __publicField(this, "scopes", {});
74
75
  __publicField(this, "antdComponentsLoaded", false);
75
76
  __publicField(this, "enabled");
77
+ __publicField(this, "engine");
76
78
  __privateAdd(this, _forceEnabled, false);
77
79
  // 强制启用状态,主要用于设计模式下的强制启用
78
80
  __publicField(this, "toolbarItems", []);
79
81
  __privateAdd(this, _emitter, new import_emitter.Emitter());
82
+ this.engine = engine;
80
83
  this.enabled = false;
81
84
  engine.context.defineProperty("flowSettingsEnabled", {
82
85
  get: /* @__PURE__ */ __name(() => this.enabled, "get"),
@@ -215,6 +218,29 @@ const _FlowSettings = class _FlowSettings {
215
218
  this.components[name] = components[name];
216
219
  });
217
220
  }
221
+ registerComponentLoaders(loaders) {
222
+ Object.entries(loaders).forEach(([name, loader]) => {
223
+ if (this.components[name]) {
224
+ console.warn(`FlowSettings: Component with name '${name}' is already registered and will be overwritten.`);
225
+ }
226
+ this.components[name] = (0, import_lazy_helper.lazy)(async () => {
227
+ const loaded = await loader();
228
+ if (typeof loaded === "function") {
229
+ return { default: loaded };
230
+ }
231
+ if ((loaded == null ? void 0 : loaded.default) && typeof loaded.default === "function") {
232
+ return { default: loaded.default };
233
+ }
234
+ const namedComponent = loaded == null ? void 0 : loaded[name];
235
+ if (typeof namedComponent === "function") {
236
+ return { default: namedComponent };
237
+ }
238
+ throw new Error(
239
+ `FlowSettings: component loader for '${name}' must resolve to a React component or a module exporting it.`
240
+ );
241
+ });
242
+ });
243
+ }
218
244
  /**
219
245
  * 添加作用域到 FlowSettings 的作用域注册表中。
220
246
  * 这些作用域可以在 flow step 的 uiSchema 中使用。
@@ -234,27 +260,29 @@ const _FlowSettings = class _FlowSettings {
234
260
  /**
235
261
  * 启用流程设置组件的显示
236
262
  * @example
237
- * flowSettings.enable();
263
+ * await flowSettings.enable();
238
264
  */
239
- enable() {
265
+ async enable() {
266
+ await this.engine.preloadModelLoaders();
240
267
  this.enabled = true;
241
268
  }
242
- forceEnable() {
269
+ async forceEnable() {
270
+ await this.engine.preloadModelLoaders();
243
271
  __privateSet(this, _forceEnabled, true);
244
272
  this.enabled = true;
245
273
  }
246
274
  /**
247
275
  * 禁用流程设置组件的显示
248
276
  * @example
249
- * flowSettings.disable();
277
+ * await flowSettings.disable();
250
278
  */
251
- disable() {
279
+ async disable() {
252
280
  if (__privateGet(this, _forceEnabled)) {
253
281
  return;
254
282
  }
255
283
  this.enabled = false;
256
284
  }
257
- forceDisable() {
285
+ async forceDisable() {
258
286
  __privateSet(this, _forceEnabled, false);
259
287
  this.enabled = false;
260
288
  }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ type LazyComponentType<M extends Record<string, any>, K extends keyof M> = {
10
+ [P in K]: M[P];
11
+ };
12
+ export declare function lazy<M extends Record<'default', any>>(factory: () => Promise<M>): M['default'];
13
+ export declare function lazy<M extends Record<string, any>, K extends keyof M = keyof M>(factory: () => Promise<M>, ...componentNames: K[]): LazyComponentType<M, K>;
14
+ export {};
@@ -0,0 +1,71 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __create = Object.create;
11
+ var __defProp = Object.defineProperty;
12
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
+ var __getOwnPropNames = Object.getOwnPropertyNames;
14
+ var __getProtoOf = Object.getPrototypeOf;
15
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
16
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
17
+ var __export = (target, all) => {
18
+ for (var name in all)
19
+ __defProp(target, name, { get: all[name], enumerable: true });
20
+ };
21
+ var __copyProps = (to, from, except, desc) => {
22
+ if (from && typeof from === "object" || typeof from === "function") {
23
+ for (let key of __getOwnPropNames(from))
24
+ if (!__hasOwnProp.call(to, key) && key !== except)
25
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
26
+ }
27
+ return to;
28
+ };
29
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
30
+ // If the importer is in node compatibility mode or this is not an ESM
31
+ // file that has been converted to a CommonJS file using a Babel-
32
+ // compatible transform (i.e. "__esModule" has not been set), then set
33
+ // "default" to the CommonJS "module.exports" for node compatibility.
34
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
35
+ mod
36
+ ));
37
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
38
+ var lazy_helper_exports = {};
39
+ __export(lazy_helper_exports, {
40
+ lazy: () => lazy
41
+ });
42
+ module.exports = __toCommonJS(lazy_helper_exports);
43
+ var import_react = __toESM(require("react"));
44
+ function lazy(factory, ...componentNames) {
45
+ if (componentNames.length === 0) {
46
+ const LazyComponent = (0, import_react.lazy)(
47
+ () => factory().then((module2) => ({
48
+ default: module2.default
49
+ }))
50
+ );
51
+ const Component = /* @__PURE__ */ __name((props) => /* @__PURE__ */ import_react.default.createElement(import_react.default.Suspense, { fallback: null }, /* @__PURE__ */ import_react.default.createElement(LazyComponent, { ...props })), "Component");
52
+ return Component;
53
+ }
54
+ return componentNames.reduce(
55
+ (acc, name) => {
56
+ const LazyComponent = (0, import_react.lazy)(
57
+ () => factory().then((module2) => ({
58
+ default: module2[name]
59
+ }))
60
+ );
61
+ acc[name] = (props) => /* @__PURE__ */ import_react.default.createElement(import_react.default.Suspense, { fallback: null }, /* @__PURE__ */ import_react.default.createElement(LazyComponent, { ...props }));
62
+ return acc;
63
+ },
64
+ {}
65
+ );
66
+ }
67
+ __name(lazy, "lazy");
68
+ // Annotate the CommonJS export names for ESM import in node:
69
+ 0 && (module.exports = {
70
+ lazy
71
+ });