@nocobase/flow-engine 2.1.0-alpha.3 → 2.1.0-alpha.30
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/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/MobilePopup.js +6 -5
- package/lib/components/dnd/gridDragPlanner.d.ts +59 -2
- package/lib/components/dnd/gridDragPlanner.js +601 -21
- package/lib/components/dnd/index.d.ts +19 -1
- package/lib/components/dnd/index.js +243 -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 +68 -10
- 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/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 +2 -2
- package/lib/data-source/index.d.ts +73 -0
- package/lib/data-source/index.js +211 -1
- package/lib/executor/FlowExecutor.js +31 -8
- package/lib/flowContext.d.ts +2 -0
- package/lib/flowContext.js +31 -1
- package/lib/flowEngine.d.ts +151 -1
- package/lib/flowEngine.js +389 -15
- package/lib/flowI18n.js +2 -1
- package/lib/flowSettings.d.ts +14 -6
- package/lib/flowSettings.js +34 -6
- 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 +78 -18
- 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 +47 -1
- package/lib/utils/createCollectionContextMeta.js +6 -2
- package/lib/utils/index.d.ts +2 -2
- package/lib/utils/index.js +4 -0
- package/lib/utils/parsePathnameToViewParams.js +1 -1
- 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/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 +20 -3
- package/lib/views/useDrawer.d.ts +2 -1
- package/lib/views/useDrawer.js +20 -3
- package/lib/views/usePage.d.ts +2 -1
- package/lib/views/usePage.js +10 -3
- package/package.json +6 -5
- package/src/JSRunner.ts +68 -4
- package/src/ViewScopedFlowEngine.ts +4 -0
- package/src/__tests__/JSRunner.test.ts +27 -1
- package/src/__tests__/flow-engine.test.ts +166 -0
- package/src/__tests__/flowContext.test.ts +65 -1
- package/src/__tests__/flowEngine.modelLoaders.test.ts +245 -0
- 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/MobilePopup.tsx +4 -2
- package/src/components/__tests__/FlowModelRenderer.test.tsx +65 -2
- 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 +512 -3
- package/src/components/dnd/__tests__/DndProvider.test.tsx +98 -0
- package/src/components/dnd/gridDragPlanner.ts +743 -19
- package/src/components/dnd/index.tsx +291 -27
- package/src/components/settings/wrappers/component/SelectWithTitle.tsx +21 -9
- package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +88 -10
- 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 +189 -3
- 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/__tests__/AddSubModelButton.test.tsx +142 -32
- package/src/components/subModel/index.ts +1 -0
- package/src/components/subModel/utils.ts +1 -1
- package/src/data-source/__tests__/index.test.ts +34 -1
- package/src/data-source/index.ts +258 -2
- package/src/executor/FlowExecutor.ts +34 -9
- package/src/executor/__tests__/flowExecutor.test.ts +57 -0
- package/src/flowContext.ts +37 -3
- package/src/flowEngine.ts +445 -11
- package/src/flowI18n.ts +2 -1
- package/src/flowSettings.ts +40 -6
- 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__/flowModel.test.ts +19 -3
- package/src/models/flowModel.tsx +119 -33
- 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 +60 -0
- package/src/utils/__tests__/createCollectionContextMeta.test.ts +48 -0
- package/src/utils/__tests__/parsePathnameToViewParams.test.ts +7 -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 +2 -1
- package/src/utils/parsePathnameToViewParams.ts +2 -2
- 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 +11 -1
- package/src/views/__tests__/runViewBeforeClose.test.ts +30 -0
- package/src/views/__tests__/useDialog.closeDestroy.test.tsx +13 -12
- package/src/views/runViewBeforeClose.ts +19 -0
- package/src/views/useDialog.tsx +25 -3
- package/src/views/useDrawer.tsx +25 -3
- package/src/views/usePage.tsx +12 -3
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.
|
|
@@ -119,6 +141,12 @@ const _FlowEngine = class _FlowEngine {
|
|
|
119
141
|
*/
|
|
120
142
|
__publicField(this, "_previousEngine");
|
|
121
143
|
__publicField(this, "_nextEngine");
|
|
144
|
+
/**
|
|
145
|
+
* 视图销毁回调。由 useDrawer / useDialog 在创建弹窗视图时注册,
|
|
146
|
+
* 供外部(如 afterSuccess)通过引擎栈遍历来关闭多层弹窗。
|
|
147
|
+
* embed 视图(usePage)不注册此回调,因此 destroyView() 会自然跳过。
|
|
148
|
+
*/
|
|
149
|
+
__publicField(this, "_destroyView");
|
|
122
150
|
__publicField(this, "_resources", /* @__PURE__ */ new Map());
|
|
123
151
|
/**
|
|
124
152
|
* Data change registry used to coordinate "refresh on active" across view-scoped engines.
|
|
@@ -258,6 +286,26 @@ const _FlowEngine = class _FlowEngine {
|
|
|
258
286
|
prev._nextEngine = void 0;
|
|
259
287
|
}
|
|
260
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* 注册视图销毁回调(由 useDrawer / useDialog 调用)。
|
|
291
|
+
*/
|
|
292
|
+
setDestroyView(fn) {
|
|
293
|
+
this._destroyView = fn;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* 关闭当前引擎关联的弹窗视图。
|
|
297
|
+
* 路由触发的弹窗会先 navigation.back() 清理 URL,再 destroy() 移除元素;
|
|
298
|
+
* 非路由弹窗直接 destroy()。
|
|
299
|
+
* embed 视图不注册回调,调用时返回 false 自动跳过。
|
|
300
|
+
* @returns 是否成功执行
|
|
301
|
+
*/
|
|
302
|
+
destroyView() {
|
|
303
|
+
if (this._destroyView) {
|
|
304
|
+
this._destroyView();
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
261
309
|
// (已移除)getModelGlobal/forEachModelGlobal/getAllModelsGlobal:不再维护冗余全局遍历 API
|
|
262
310
|
/**
|
|
263
311
|
* Get the flow engine context object.
|
|
@@ -346,6 +394,16 @@ const _FlowEngine = class _FlowEngine {
|
|
|
346
394
|
getEvents() {
|
|
347
395
|
return this._eventRegistry.getEvents();
|
|
348
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
|
+
}
|
|
349
407
|
/**
|
|
350
408
|
* Register multiple model classes.
|
|
351
409
|
* @param {Record<string, ModelConstructor>} models Model class map, key is model name, value is model constructor
|
|
@@ -358,6 +416,274 @@ const _FlowEngine = class _FlowEngine {
|
|
|
358
416
|
__privateMethod(this, _FlowEngine_instances, registerModel_fn).call(this, name, modelClass);
|
|
359
417
|
}
|
|
360
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
|
+
}
|
|
361
687
|
registerResources(resources) {
|
|
362
688
|
for (const [name, resourceClass] of Object.entries(resources)) {
|
|
363
689
|
this._resources.set(name, resourceClass);
|
|
@@ -421,6 +747,55 @@ const _FlowEngine = class _FlowEngine {
|
|
|
421
747
|
}
|
|
422
748
|
return result;
|
|
423
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
|
+
}
|
|
424
799
|
/**
|
|
425
800
|
* Create and register a model instance.
|
|
426
801
|
* If an instance with the same UID exists, returns the existing instance.
|
|
@@ -706,7 +1081,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
706
1081
|
* Hydrate a model into current engine from an already-existing model instance in previous engines.
|
|
707
1082
|
* - Avoids repository requests when the model tree is already present in memory.
|
|
708
1083
|
*/
|
|
709
|
-
hydrateModelFromPreviousEngines(options, extra) {
|
|
1084
|
+
async hydrateModelFromPreviousEngines(options, extra) {
|
|
710
1085
|
var _a;
|
|
711
1086
|
const uid = options == null ? void 0 : options.uid;
|
|
712
1087
|
const parentId = options == null ? void 0 : options.parentId;
|
|
@@ -718,7 +1093,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
718
1093
|
}
|
|
719
1094
|
if (existing) {
|
|
720
1095
|
const data = existing.serialize();
|
|
721
|
-
return this.
|
|
1096
|
+
return this.createModelAsync(data, extra);
|
|
722
1097
|
}
|
|
723
1098
|
}
|
|
724
1099
|
if (parentId && subKey) {
|
|
@@ -729,10 +1104,10 @@ const _FlowEngine = class _FlowEngine {
|
|
|
729
1104
|
if (!localParent) {
|
|
730
1105
|
const parentData = parentFromPrev.serialize();
|
|
731
1106
|
delete parentData.subModels;
|
|
732
|
-
localParent = this.
|
|
1107
|
+
localParent = await this.createModelAsync(parentData, extra);
|
|
733
1108
|
}
|
|
734
1109
|
const modelData = modelFromPrev.serialize();
|
|
735
|
-
const localModel = this.
|
|
1110
|
+
const localModel = await this.createModelAsync(modelData, extra);
|
|
736
1111
|
const mounted = (_a = localParent.subModels) == null ? void 0 : _a[subKey];
|
|
737
1112
|
if (Array.isArray(mounted)) {
|
|
738
1113
|
const exists = mounted.some((m) => (m == null ? void 0 : m.uid) === (localModel == null ? void 0 : localModel.uid));
|
|
@@ -768,20 +1143,21 @@ const _FlowEngine = class _FlowEngine {
|
|
|
768
1143
|
if (model) {
|
|
769
1144
|
return model;
|
|
770
1145
|
}
|
|
771
|
-
const hydrated = this.hydrateModelFromPreviousEngines(options);
|
|
1146
|
+
const hydrated = await this.hydrateModelFromPreviousEngines(options);
|
|
772
1147
|
if (hydrated) {
|
|
773
1148
|
return hydrated;
|
|
774
1149
|
}
|
|
775
1150
|
}
|
|
776
1151
|
const data = await this._modelRepository.findOne(options);
|
|
777
1152
|
if (!(data == null ? void 0 : data.uid)) return null;
|
|
1153
|
+
await this.resolveModelTree(data);
|
|
778
1154
|
if (refresh) {
|
|
779
1155
|
const existing = this.getModel(data.uid);
|
|
780
1156
|
if (existing) {
|
|
781
1157
|
this.removeModelWithSubModels(existing.uid);
|
|
782
1158
|
}
|
|
783
1159
|
}
|
|
784
|
-
return this.
|
|
1160
|
+
return this.createModelAsync(data);
|
|
785
1161
|
}
|
|
786
1162
|
/**
|
|
787
1163
|
* Find a sub-model by parent model ID and subKey.
|
|
@@ -819,17 +1195,19 @@ const _FlowEngine = class _FlowEngine {
|
|
|
819
1195
|
if (m) {
|
|
820
1196
|
return m;
|
|
821
1197
|
}
|
|
822
|
-
const hydrated = this.hydrateModelFromPreviousEngines(options, extra);
|
|
1198
|
+
const hydrated = await this.hydrateModelFromPreviousEngines(options, extra);
|
|
823
1199
|
if (hydrated) {
|
|
824
1200
|
return hydrated;
|
|
825
1201
|
}
|
|
826
1202
|
const data = await this._modelRepository.findOne(options);
|
|
827
1203
|
let model = null;
|
|
828
1204
|
if (data == null ? void 0 : data.uid) {
|
|
829
|
-
model = this.
|
|
1205
|
+
model = await this.createModelAsync(data, extra);
|
|
830
1206
|
} else {
|
|
831
|
-
model = this.
|
|
832
|
-
|
|
1207
|
+
model = await this.createModelAsync(options, extra);
|
|
1208
|
+
if (!(extra == null ? void 0 : extra.skipSave)) {
|
|
1209
|
+
await model.save();
|
|
1210
|
+
}
|
|
833
1211
|
}
|
|
834
1212
|
if (model.parent) {
|
|
835
1213
|
const subModel = model.parent.findSubModel(model.subKey, (m2) => {
|
|
@@ -1070,11 +1448,7 @@ _FlowEngine_instances = new WeakSet();
|
|
|
1070
1448
|
* @private
|
|
1071
1449
|
*/
|
|
1072
1450
|
registerModel_fn = /* @__PURE__ */ __name(function(name, modelClass) {
|
|
1073
|
-
|
|
1074
|
-
console.warn(`FlowEngine: Model class with name '${name}' is already registered and will be overwritten.`);
|
|
1075
|
-
}
|
|
1076
|
-
Object.defineProperty(modelClass, "name", { value: name });
|
|
1077
|
-
this._modelClasses.set(name, modelClass);
|
|
1451
|
+
return this._registerModel(name, modelClass);
|
|
1078
1452
|
}, "#registerModel");
|
|
1079
1453
|
__name(_FlowEngine, "FlowEngine");
|
|
1080
1454
|
let FlowEngine = _FlowEngine;
|
package/lib/flowI18n.js
CHANGED
|
@@ -71,7 +71,8 @@ const _FlowI18n = class _FlowI18n {
|
|
|
71
71
|
translateKey(key, options) {
|
|
72
72
|
var _a, _b;
|
|
73
73
|
if ((_b = (_a = this.context) == null ? void 0 : _a.i18n) == null ? void 0 : _b.t) {
|
|
74
|
-
|
|
74
|
+
const translated = this.context.i18n.t(key, options);
|
|
75
|
+
return translated == null || translated === "" ? key : translated;
|
|
75
76
|
}
|
|
76
77
|
return key;
|
|
77
78
|
}
|
package/lib/flowSettings.d.ts
CHANGED
|
@@ -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 项目配置
|
package/lib/flowSettings.js
CHANGED
|
@@ -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 {};
|