@nocobase/flow-engine 2.1.0-beta.36 → 2.1.0-beta.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/FlowContextProvider.d.ts +5 -1
- package/lib/FlowContextProvider.js +9 -2
- package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +8 -1
- package/lib/components/subModel/LazyDropdown.js +200 -16
- package/lib/flowContext.js +3 -0
- package/lib/flowEngine.js +3 -3
- package/lib/models/flowModel.js +3 -3
- package/lib/utils/parsePathnameToViewParams.d.ts +5 -1
- package/lib/utils/parsePathnameToViewParams.js +28 -4
- package/lib/views/ViewNavigation.d.ts +12 -2
- package/lib/views/ViewNavigation.js +22 -7
- 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/useDialog.js +2 -0
- package/lib/views/useDrawer.js +2 -0
- package/lib/views/usePage.js +2 -0
- package/package.json +4 -4
- package/src/FlowContextProvider.tsx +9 -1
- package/src/__tests__/createViewMeta.popup.test.ts +115 -1
- package/src/__tests__/flowEngine.removeModel.test.ts +47 -3
- package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +11 -1
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +5 -2
- package/src/components/subModel/LazyDropdown.tsx +228 -16
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +203 -1
- package/src/executor/__tests__/flowExecutor.test.ts +28 -0
- package/src/flowContext.ts +3 -0
- package/src/flowEngine.ts +4 -3
- package/src/models/__tests__/flowEngine.resolveUse.test.ts +0 -15
- package/src/models/__tests__/flowModel.test.ts +33 -34
- package/src/models/flowModel.tsx +3 -3
- package/src/utils/__tests__/parsePathnameToViewParams.test.ts +21 -0
- package/src/utils/parsePathnameToViewParams.ts +45 -5
- package/src/views/ViewNavigation.ts +40 -7
- package/src/views/__tests__/ViewNavigation.test.ts +52 -0
- package/src/views/__tests__/inheritLayoutContext.test.ts +53 -0
- package/src/views/createViewMeta.ts +106 -34
- package/src/views/inheritLayoutContext.ts +26 -0
- package/src/views/useDialog.tsx +2 -0
- package/src/views/useDrawer.tsx +2 -0
- package/src/views/usePage.tsx +2 -0
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import { FlowContext, FlowEngineContext } from './flowContext';
|
|
11
|
-
import { FlowView } from './views/FlowView';
|
|
11
|
+
import { FlowView, FlowViewer } from './views/FlowView';
|
|
12
12
|
export declare const FlowReactContext: React.Context<FlowContext>;
|
|
13
13
|
export declare const FlowViewContext: React.Context<FlowContext>;
|
|
14
14
|
export declare function FlowContextProvider(props: {
|
|
@@ -22,3 +22,7 @@ export declare const FlowViewContextProvider: React.MemoExoticComponent<(props:
|
|
|
22
22
|
export declare function useFlowContext<T = FlowEngineContext>(): T;
|
|
23
23
|
export declare function useFlowViewContext<T = FlowEngineContext>(): T;
|
|
24
24
|
export declare function useFlowView(): FlowView;
|
|
25
|
+
/**
|
|
26
|
+
* Access the `FlowViewer` that opens new drawers / modals / pages (`viewer.drawer({...})`, `viewer.modal({...})`, etc.). This is the counterpart to `useFlowView()`: `useFlowView()` returns the *current* mounted view (use it to close yourself, render Header/Footer slots, etc.), while `useFlowViewer()` returns the surface that lets you open a *new* view from inside any flow-context subtree.
|
|
27
|
+
*/
|
|
28
|
+
export declare function useFlowViewer(): FlowViewer;
|
|
@@ -43,7 +43,8 @@ __export(FlowContextProvider_exports, {
|
|
|
43
43
|
FlowViewContextProvider: () => FlowViewContextProvider,
|
|
44
44
|
useFlowContext: () => useFlowContext,
|
|
45
45
|
useFlowView: () => useFlowView,
|
|
46
|
-
useFlowViewContext: () => useFlowViewContext
|
|
46
|
+
useFlowViewContext: () => useFlowViewContext,
|
|
47
|
+
useFlowViewer: () => useFlowViewer
|
|
47
48
|
});
|
|
48
49
|
module.exports = __toCommonJS(FlowContextProvider_exports);
|
|
49
50
|
var import_react = __toESM(require("react"));
|
|
@@ -70,6 +71,11 @@ function useFlowView() {
|
|
|
70
71
|
return ctx.view;
|
|
71
72
|
}
|
|
72
73
|
__name(useFlowView, "useFlowView");
|
|
74
|
+
function useFlowViewer() {
|
|
75
|
+
const ctx = useFlowContext();
|
|
76
|
+
return ctx.viewer;
|
|
77
|
+
}
|
|
78
|
+
__name(useFlowViewer, "useFlowViewer");
|
|
73
79
|
// Annotate the CommonJS export names for ESM import in node:
|
|
74
80
|
0 && (module.exports = {
|
|
75
81
|
FlowContextProvider,
|
|
@@ -78,5 +84,6 @@ __name(useFlowView, "useFlowView");
|
|
|
78
84
|
FlowViewContextProvider,
|
|
79
85
|
useFlowContext,
|
|
80
86
|
useFlowView,
|
|
81
|
-
useFlowViewContext
|
|
87
|
+
useFlowViewContext,
|
|
88
|
+
useFlowViewer
|
|
82
89
|
});
|
|
@@ -183,6 +183,13 @@ const getToolbarPopupContainer = /* @__PURE__ */ __name((triggerNode) => {
|
|
|
183
183
|
}
|
|
184
184
|
return triggerNode.closest(TOOLBAR_ICONS_SELECTOR) || triggerNode.closest(TOOLBAR_CONTAINER_SELECTOR);
|
|
185
185
|
}, "getToolbarPopupContainer");
|
|
186
|
+
const removeExtraMenuItemClickHandlers = /* @__PURE__ */ __name((item) => {
|
|
187
|
+
const { onClick: _onClick, children, ...rest } = item;
|
|
188
|
+
return {
|
|
189
|
+
...rest,
|
|
190
|
+
children: (children == null ? void 0 : children.length) ? children.map(removeExtraMenuItemClickHandlers) : void 0
|
|
191
|
+
};
|
|
192
|
+
}, "removeExtraMenuItemClickHandlers");
|
|
186
193
|
const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
187
194
|
model,
|
|
188
195
|
showDeleteButton = true,
|
|
@@ -698,7 +705,7 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
698
705
|
type: "divider"
|
|
699
706
|
});
|
|
700
707
|
if (commonExtras.length > 0) {
|
|
701
|
-
items.push(...commonExtras);
|
|
708
|
+
items.push(...commonExtras.map(removeExtraMenuItemClickHandlers));
|
|
702
709
|
}
|
|
703
710
|
if (showCopyUidButton && model.uid) {
|
|
704
711
|
items.push({
|
|
@@ -169,16 +169,66 @@ const useKeepDropdownOpen = /* @__PURE__ */ __name(() => {
|
|
|
169
169
|
}, "useKeepDropdownOpen");
|
|
170
170
|
const useMenuSearch = /* @__PURE__ */ __name(() => {
|
|
171
171
|
const [searchValues, setSearchValues] = (0, import_react.useState)({});
|
|
172
|
+
const [inputValues, setInputValues] = (0, import_react.useState)({});
|
|
172
173
|
const [isSearching, setIsSearching] = (0, import_react.useState)(false);
|
|
174
|
+
const [composingCount, setComposingCount] = (0, import_react.useState)(0);
|
|
175
|
+
const composingKeysRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
|
|
173
176
|
const searchTimeoutRef = (0, import_react.useRef)(null);
|
|
174
|
-
const updateSearchValue =
|
|
177
|
+
const updateSearchValue = (0, import_react.useCallback)((key, value) => {
|
|
175
178
|
setIsSearching(true);
|
|
179
|
+
setInputValues((prev) => ({ ...prev, [key]: value }));
|
|
176
180
|
setSearchValues((prev) => ({ ...prev, [key]: value }));
|
|
177
181
|
if (searchTimeoutRef.current) {
|
|
178
182
|
clearTimeout(searchTimeoutRef.current);
|
|
179
183
|
}
|
|
180
184
|
searchTimeoutRef.current = setTimeout(() => setIsSearching(false), 300);
|
|
181
|
-
},
|
|
185
|
+
}, []);
|
|
186
|
+
const startComposition = (0, import_react.useCallback)((key) => {
|
|
187
|
+
composingKeysRef.current.add(key);
|
|
188
|
+
setIsSearching(true);
|
|
189
|
+
setComposingCount(composingKeysRef.current.size);
|
|
190
|
+
if (searchTimeoutRef.current) {
|
|
191
|
+
clearTimeout(searchTimeoutRef.current);
|
|
192
|
+
searchTimeoutRef.current = null;
|
|
193
|
+
}
|
|
194
|
+
}, []);
|
|
195
|
+
const endComposition = (0, import_react.useCallback)(
|
|
196
|
+
(key, value) => {
|
|
197
|
+
composingKeysRef.current.delete(key);
|
|
198
|
+
setComposingCount(composingKeysRef.current.size);
|
|
199
|
+
updateSearchValue(key, value);
|
|
200
|
+
},
|
|
201
|
+
[updateSearchValue]
|
|
202
|
+
);
|
|
203
|
+
const updateInputValue = (0, import_react.useCallback)((key, value) => {
|
|
204
|
+
setInputValues((prev) => ({ ...prev, [key]: value }));
|
|
205
|
+
}, []);
|
|
206
|
+
const clearSearchValue = (0, import_react.useCallback)((key) => {
|
|
207
|
+
composingKeysRef.current.delete(key);
|
|
208
|
+
setComposingCount(composingKeysRef.current.size);
|
|
209
|
+
setInputValues((prev) => {
|
|
210
|
+
if (!(key in prev)) return prev;
|
|
211
|
+
const next = { ...prev };
|
|
212
|
+
delete next[key];
|
|
213
|
+
return next;
|
|
214
|
+
});
|
|
215
|
+
setSearchValues((prev) => {
|
|
216
|
+
if (!(key in prev)) return prev;
|
|
217
|
+
const next = { ...prev };
|
|
218
|
+
delete next[key];
|
|
219
|
+
return next;
|
|
220
|
+
});
|
|
221
|
+
}, []);
|
|
222
|
+
const clearAllSearchValues = (0, import_react.useCallback)(() => {
|
|
223
|
+
composingKeysRef.current.clear();
|
|
224
|
+
setComposingCount(0);
|
|
225
|
+
setInputValues({});
|
|
226
|
+
setSearchValues({});
|
|
227
|
+
setIsSearching(false);
|
|
228
|
+
}, []);
|
|
229
|
+
const isComposing = (0, import_react.useCallback)((key) => {
|
|
230
|
+
return key ? composingKeysRef.current.has(key) : composingKeysRef.current.size > 0;
|
|
231
|
+
}, []);
|
|
182
232
|
(0, import_react.useEffect)(() => {
|
|
183
233
|
return () => {
|
|
184
234
|
if (searchTimeoutRef.current) {
|
|
@@ -188,8 +238,15 @@ const useMenuSearch = /* @__PURE__ */ __name(() => {
|
|
|
188
238
|
}, []);
|
|
189
239
|
return {
|
|
190
240
|
searchValues,
|
|
191
|
-
|
|
192
|
-
|
|
241
|
+
inputValues,
|
|
242
|
+
isSearching: isSearching || composingCount > 0,
|
|
243
|
+
updateSearchValue,
|
|
244
|
+
updateInputValue,
|
|
245
|
+
startComposition,
|
|
246
|
+
endComposition,
|
|
247
|
+
clearSearchValue,
|
|
248
|
+
clearAllSearchValues,
|
|
249
|
+
isComposing
|
|
193
250
|
};
|
|
194
251
|
}, "useMenuSearch");
|
|
195
252
|
const useSubmenuStyles = /* @__PURE__ */ __name((menuVisible, dropdownMaxHeight) => {
|
|
@@ -275,10 +332,10 @@ const getLabelSearchText = /* @__PURE__ */ __name((label) => {
|
|
|
275
332
|
}
|
|
276
333
|
return "";
|
|
277
334
|
}, "getLabelSearchText");
|
|
278
|
-
const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchValue, menuVisible, t,
|
|
335
|
+
const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchValue, menuVisible, t, searchHandlers, activateSearchSubmenu, deactivateSearchSubmenu) => ({
|
|
279
336
|
key: `${searchKey}-search`,
|
|
280
337
|
type: "group",
|
|
281
|
-
label: /* @__PURE__ */ import_react.default.createElement("div",
|
|
338
|
+
label: /* @__PURE__ */ import_react.default.createElement("div", { onMouseDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ import_react.default.createElement(
|
|
282
339
|
SearchInputWithAutoFocus,
|
|
283
340
|
{
|
|
284
341
|
visible: menuVisible,
|
|
@@ -286,11 +343,44 @@ const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchV
|
|
|
286
343
|
allowClear: true,
|
|
287
344
|
placeholder: t(item.searchPlaceholder || "Search"),
|
|
288
345
|
value: currentSearchValue,
|
|
346
|
+
onFocus: (e) => {
|
|
347
|
+
e.stopPropagation();
|
|
348
|
+
},
|
|
289
349
|
onChange: (e) => {
|
|
350
|
+
var _a;
|
|
351
|
+
e.stopPropagation();
|
|
352
|
+
const value = e.target.value;
|
|
353
|
+
activateSearchSubmenu(searchKey);
|
|
354
|
+
if (((_a = e.nativeEvent) == null ? void 0 : _a.isComposing) || searchHandlers.isComposing(searchKey)) {
|
|
355
|
+
searchHandlers.updateInputValue(searchKey, value);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (!value) {
|
|
359
|
+
deactivateSearchSubmenu(searchKey);
|
|
360
|
+
}
|
|
361
|
+
searchHandlers.updateSearchValue(searchKey, value);
|
|
362
|
+
},
|
|
363
|
+
onCompositionStart: (e) => {
|
|
364
|
+
e.stopPropagation();
|
|
365
|
+
activateSearchSubmenu(searchKey);
|
|
366
|
+
searchHandlers.startComposition(searchKey);
|
|
367
|
+
},
|
|
368
|
+
onCompositionEnd: (e) => {
|
|
369
|
+
e.stopPropagation();
|
|
370
|
+
const value = e.currentTarget.value;
|
|
371
|
+
if (value) {
|
|
372
|
+
activateSearchSubmenu(searchKey);
|
|
373
|
+
} else {
|
|
374
|
+
deactivateSearchSubmenu(searchKey);
|
|
375
|
+
}
|
|
376
|
+
searchHandlers.endComposition(searchKey, value);
|
|
377
|
+
},
|
|
378
|
+
onClick: (e) => {
|
|
379
|
+
e.stopPropagation();
|
|
380
|
+
},
|
|
381
|
+
onKeyDown: (e) => {
|
|
290
382
|
e.stopPropagation();
|
|
291
|
-
updateSearchValue(searchKey, e.target.value);
|
|
292
383
|
},
|
|
293
|
-
onClick: (e) => e.stopPropagation(),
|
|
294
384
|
onMouseDown: (e) => {
|
|
295
385
|
e.stopPropagation();
|
|
296
386
|
},
|
|
@@ -313,13 +403,21 @@ const KEEP_OPEN_LABEL_STYLE = {
|
|
|
313
403
|
width: "100%"
|
|
314
404
|
};
|
|
315
405
|
const DROPDOWN_PERSIST_TTL_MS = 350;
|
|
406
|
+
const SUBMENU_CLOSE_DELAY = 0.05;
|
|
407
|
+
const SUBMENU_MOTION_DISABLED = {
|
|
408
|
+
motionEnter: false,
|
|
409
|
+
motionLeave: false
|
|
410
|
+
};
|
|
316
411
|
const dropdownPersistRegistry = /* @__PURE__ */ new Map();
|
|
317
412
|
const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
318
413
|
const engine = (0, import_provider.useFlowEngine)();
|
|
319
414
|
const [menuVisible, setMenuVisible] = (0, import_react.useState)(false);
|
|
320
415
|
const [openKeys, setOpenKeys] = (0, import_react.useState)(/* @__PURE__ */ new Set());
|
|
416
|
+
const [activeSearchKey, setActiveSearchKey] = (0, import_react.useState)(null);
|
|
321
417
|
const [rootItems, setRootItems] = (0, import_react.useState)([]);
|
|
322
418
|
const [rootLoading, setRootLoading] = (0, import_react.useState)(false);
|
|
419
|
+
const closeByOutsideClickRef = (0, import_react.useRef)(false);
|
|
420
|
+
const skipPreserveActiveSearchRef = (0, import_react.useRef)(false);
|
|
323
421
|
const dropdownMaxHeight = useNiceDropdownMaxHeight();
|
|
324
422
|
const t = engine.translate.bind(engine);
|
|
325
423
|
const { items: menuItems, keepDropdownOpen, persistKey, stateVersion, refreshKeys, ...dropdownMenuProps } = menu;
|
|
@@ -330,22 +428,89 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
330
428
|
openKeys,
|
|
331
429
|
refreshKeys
|
|
332
430
|
);
|
|
333
|
-
const
|
|
431
|
+
const searchHandlers = useMenuSearch();
|
|
432
|
+
const { searchValues, inputValues, clearSearchValue, clearAllSearchValues } = searchHandlers;
|
|
334
433
|
const { requestKeepOpen, shouldPreventClose } = useKeepDropdownOpen();
|
|
335
434
|
useSubmenuStyles(menuVisible, dropdownMaxHeight);
|
|
435
|
+
const closeMenu = (0, import_react.useCallback)(() => {
|
|
436
|
+
setMenuVisible(false);
|
|
437
|
+
setActiveSearchKey(null);
|
|
438
|
+
setOpenKeys(/* @__PURE__ */ new Set());
|
|
439
|
+
clearAllSearchValues();
|
|
440
|
+
}, [clearAllSearchValues]);
|
|
441
|
+
const activateSearchSubmenu = (0, import_react.useCallback)((key) => {
|
|
442
|
+
setActiveSearchKey(key);
|
|
443
|
+
setOpenKeys((prev) => {
|
|
444
|
+
if (prev.has(key)) return prev;
|
|
445
|
+
const next = new Set(prev);
|
|
446
|
+
next.add(key);
|
|
447
|
+
return next;
|
|
448
|
+
});
|
|
449
|
+
}, []);
|
|
450
|
+
const deactivateSearchSubmenu = (0, import_react.useCallback)((key) => {
|
|
451
|
+
setActiveSearchKey((prev) => prev === key ? null : prev);
|
|
452
|
+
}, []);
|
|
453
|
+
const closeActiveSearchForPath = (0, import_react.useCallback)(
|
|
454
|
+
(keyPath) => {
|
|
455
|
+
if (!activeSearchKey || keyPath === activeSearchKey || keyPath.startsWith(`${activeSearchKey}/`) || activeSearchKey.startsWith(`${keyPath}/`)) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
skipPreserveActiveSearchRef.current = true;
|
|
459
|
+
clearSearchValue(activeSearchKey);
|
|
460
|
+
setActiveSearchKey(null);
|
|
461
|
+
setOpenKeys((prev) => {
|
|
462
|
+
const next = new Set(prev);
|
|
463
|
+
next.delete(activeSearchKey);
|
|
464
|
+
return next;
|
|
465
|
+
});
|
|
466
|
+
},
|
|
467
|
+
[activeSearchKey, clearSearchValue]
|
|
468
|
+
);
|
|
336
469
|
const handleMenuOpenChange = (0, import_react.useCallback)(
|
|
337
470
|
(nextOpenKeys) => {
|
|
338
471
|
var _a, _b;
|
|
339
|
-
|
|
472
|
+
let normalized = normalizeOpenKeys(nextOpenKeys);
|
|
473
|
+
if (activeSearchKey && openKeys.has(activeSearchKey) && !normalized.includes(activeSearchKey)) {
|
|
474
|
+
if (normalized.length || skipPreserveActiveSearchRef.current) {
|
|
475
|
+
clearSearchValue(activeSearchKey);
|
|
476
|
+
setActiveSearchKey(null);
|
|
477
|
+
} else {
|
|
478
|
+
normalized = [activeSearchKey];
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
if (!normalized.length && shouldPreventClose()) {
|
|
340
482
|
(_a = dropdownMenuProps.onOpenChange) == null ? void 0 : _a.call(dropdownMenuProps, Array.from(openKeys));
|
|
483
|
+
skipPreserveActiveSearchRef.current = false;
|
|
341
484
|
return;
|
|
342
485
|
}
|
|
343
|
-
|
|
486
|
+
Array.from(openKeys).forEach((key) => {
|
|
487
|
+
if (!normalized.includes(key)) {
|
|
488
|
+
clearSearchValue(key);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
344
491
|
setOpenKeys(new Set(normalized));
|
|
345
492
|
(_b = dropdownMenuProps.onOpenChange) == null ? void 0 : _b.call(dropdownMenuProps, normalized);
|
|
493
|
+
skipPreserveActiveSearchRef.current = false;
|
|
346
494
|
},
|
|
347
|
-
[dropdownMenuProps, openKeys, shouldPreventClose]
|
|
495
|
+
[activeSearchKey, clearSearchValue, dropdownMenuProps, openKeys, shouldPreventClose]
|
|
348
496
|
);
|
|
497
|
+
(0, import_react.useEffect)(() => {
|
|
498
|
+
if (!menuVisible) return;
|
|
499
|
+
const markOutsideClick = /* @__PURE__ */ __name((event) => {
|
|
500
|
+
const target = event.target;
|
|
501
|
+
const isOutside = !(target == null ? void 0 : target.closest(".ant-dropdown, .ant-dropdown-menu, .ant-dropdown-menu-submenu-popup"));
|
|
502
|
+
closeByOutsideClickRef.current = isOutside;
|
|
503
|
+
if (isOutside) {
|
|
504
|
+
closeMenu();
|
|
505
|
+
}
|
|
506
|
+
}, "markOutsideClick");
|
|
507
|
+
document.addEventListener("pointerdown", markOutsideClick, true);
|
|
508
|
+
document.addEventListener("mousedown", markOutsideClick, true);
|
|
509
|
+
return () => {
|
|
510
|
+
document.removeEventListener("pointerdown", markOutsideClick, true);
|
|
511
|
+
document.removeEventListener("mousedown", markOutsideClick, true);
|
|
512
|
+
};
|
|
513
|
+
}, [closeMenu, menuVisible]);
|
|
349
514
|
(0, import_react.useEffect)(() => {
|
|
350
515
|
if (!persistKey) return;
|
|
351
516
|
const until = dropdownPersistRegistry.get(persistKey) || 0;
|
|
@@ -391,6 +556,7 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
391
556
|
function buildSearchChildren(children, item, keyPath, path, menuVisible2, resolve) {
|
|
392
557
|
const searchKey = keyPath;
|
|
393
558
|
const currentSearchValue = searchValues[searchKey] || "";
|
|
559
|
+
const currentInputValue = inputValues[searchKey] ?? currentSearchValue;
|
|
394
560
|
const filteredChildren = currentSearchValue ? (/* @__PURE__ */ __name(function deepFilter(items2) {
|
|
395
561
|
const searchText = currentSearchValue.toLowerCase();
|
|
396
562
|
return items2.map((child) => {
|
|
@@ -407,7 +573,16 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
407
573
|
}).filter(Boolean);
|
|
408
574
|
}, "deepFilter"))(children) : children;
|
|
409
575
|
const resolvedFiltered = resolve(filteredChildren, [...path, item.key]);
|
|
410
|
-
const searchItem = createSearchItem(
|
|
576
|
+
const searchItem = createSearchItem(
|
|
577
|
+
item,
|
|
578
|
+
searchKey,
|
|
579
|
+
currentInputValue,
|
|
580
|
+
menuVisible2,
|
|
581
|
+
t,
|
|
582
|
+
searchHandlers,
|
|
583
|
+
activateSearchSubmenu,
|
|
584
|
+
deactivateSearchSubmenu
|
|
585
|
+
);
|
|
411
586
|
const dividerItem = { key: `${keyPath}-search-divider`, type: "divider" };
|
|
412
587
|
if (currentSearchValue && resolvedFiltered.length === 0) {
|
|
413
588
|
return [searchItem, dividerItem, createEmptyItem(keyPath, t)];
|
|
@@ -466,6 +641,7 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
466
641
|
label,
|
|
467
642
|
onClick: /* @__PURE__ */ __name((info) => {
|
|
468
643
|
}, "onClick"),
|
|
644
|
+
onMouseEnter: /* @__PURE__ */ __name(() => closeActiveSearchForPath(keyPath), "onMouseEnter"),
|
|
469
645
|
children: buildSearchChildren(children, item, keyPath, path, menuVisible, resolveItems)
|
|
470
646
|
};
|
|
471
647
|
}
|
|
@@ -513,6 +689,7 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
513
689
|
onClick: /* @__PURE__ */ __name((info) => {
|
|
514
690
|
if (!itemShouldKeepOpen) handleLeafClick(info);
|
|
515
691
|
}, "onClick"),
|
|
692
|
+
onMouseEnter: /* @__PURE__ */ __name(() => closeActiveSearchForPath(keyPath), "onMouseEnter"),
|
|
516
693
|
onMouseDown: /* @__PURE__ */ __name(() => {
|
|
517
694
|
if (!itemShouldKeepOpen) {
|
|
518
695
|
return;
|
|
@@ -555,6 +732,8 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
555
732
|
...dropdownMenuProps,
|
|
556
733
|
openKeys: Array.from(openKeys),
|
|
557
734
|
items,
|
|
735
|
+
subMenuCloseDelay: dropdownMenuProps.subMenuCloseDelay ?? SUBMENU_CLOSE_DELAY,
|
|
736
|
+
motion: dropdownMenuProps.motion ?? SUBMENU_MOTION_DISABLED,
|
|
558
737
|
onClick: /* @__PURE__ */ __name(() => {
|
|
559
738
|
}, "onClick"),
|
|
560
739
|
onOpenChange: handleMenuOpenChange,
|
|
@@ -564,14 +743,19 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
564
743
|
...dropdownMenuProps == null ? void 0 : dropdownMenuProps.style
|
|
565
744
|
}
|
|
566
745
|
},
|
|
567
|
-
onOpenChange: (visible) => {
|
|
568
|
-
if (!visible &&
|
|
746
|
+
onOpenChange: (visible, info) => {
|
|
747
|
+
if (!visible && activeSearchKey && (info == null ? void 0 : info.source) === "trigger" && !closeByOutsideClickRef.current) {
|
|
569
748
|
return;
|
|
570
749
|
}
|
|
571
750
|
if (!visible && shouldPreventClose()) {
|
|
572
751
|
return;
|
|
573
752
|
}
|
|
574
|
-
|
|
753
|
+
if (!visible) {
|
|
754
|
+
closeMenu();
|
|
755
|
+
} else {
|
|
756
|
+
setMenuVisible(visible);
|
|
757
|
+
}
|
|
758
|
+
closeByOutsideClickRef.current = false;
|
|
575
759
|
}
|
|
576
760
|
},
|
|
577
761
|
props.children
|
package/lib/flowContext.js
CHANGED
|
@@ -2697,6 +2697,9 @@ const _FlowEngineContext = class _FlowEngineContext extends BaseFlowEngineContex
|
|
|
2697
2697
|
}, "get")
|
|
2698
2698
|
});
|
|
2699
2699
|
this.defineMethod("aclCheck", function(params) {
|
|
2700
|
+
if (this.skipAclCheck) {
|
|
2701
|
+
return true;
|
|
2702
|
+
}
|
|
2700
2703
|
return this.acl.aclCheck(params);
|
|
2701
2704
|
});
|
|
2702
2705
|
this.defineMethod("createResource", function(resourceType) {
|
package/lib/flowEngine.js
CHANGED
|
@@ -63,6 +63,7 @@ var import_emitter = require("./emitter");
|
|
|
63
63
|
var import_ModelOperationScheduler = __toESM(require("./scheduler/ModelOperationScheduler"));
|
|
64
64
|
var import_utils = require("./utils");
|
|
65
65
|
var _FlowEngine_instances, registerModel_fn;
|
|
66
|
+
const getFlowEngineLoggerLevel = /* @__PURE__ */ __name(() => process.env.NODE_ENV === "production" ? "warn" : "trace", "getFlowEngineLoggerLevel");
|
|
66
67
|
const _FlowEngine = class _FlowEngine {
|
|
67
68
|
/**
|
|
68
69
|
* Constructor. Initializes React view, registers default model and form scopes.
|
|
@@ -195,7 +196,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
195
196
|
MultiRecordResource: import_resources.MultiRecordResource
|
|
196
197
|
});
|
|
197
198
|
this.logger = (0, import_pino.default)({
|
|
198
|
-
level:
|
|
199
|
+
level: getFlowEngineLoggerLevel(),
|
|
199
200
|
browser: {
|
|
200
201
|
write: {
|
|
201
202
|
fatal: /* @__PURE__ */ __name((o) => console.trace(o), "fatal"),
|
|
@@ -859,7 +860,6 @@ const _FlowEngine = class _FlowEngine {
|
|
|
859
860
|
const visited = /* @__PURE__ */ new Set();
|
|
860
861
|
while (current) {
|
|
861
862
|
if (visited.has(current)) {
|
|
862
|
-
console.warn(`FlowEngine: resolveUse circular reference detected on '${current.name}'.`);
|
|
863
863
|
break;
|
|
864
864
|
}
|
|
865
865
|
visited.add(current);
|
|
@@ -960,7 +960,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
960
960
|
removeModel(uid) {
|
|
961
961
|
var _a, _b, _c;
|
|
962
962
|
if (!this._modelInstances.has(uid)) {
|
|
963
|
-
|
|
963
|
+
this.logger.debug(`FlowEngine: Model with UID '${uid}' does not exist.`);
|
|
964
964
|
return false;
|
|
965
965
|
}
|
|
966
966
|
const modelInstance = this._modelInstances.get(uid);
|
package/lib/models/flowModel.js
CHANGED
|
@@ -668,7 +668,7 @@ const _FlowModel = class _FlowModel {
|
|
|
668
668
|
}
|
|
669
669
|
const isFork = this.isFork === true;
|
|
670
670
|
const target = this;
|
|
671
|
-
|
|
671
|
+
currentFlowEngine.logger.debug(
|
|
672
672
|
`[FlowModel] applyFlow: uid=${this.uid}, flowKey=${flowKey}, isFork=${isFork}, cleanRun=${this.cleanRun}, targetIsFork=${(target == null ? void 0 : target.isFork) === true}`
|
|
673
673
|
);
|
|
674
674
|
return currentFlowEngine.executor.runFlow(target, flowKey, inputArgs, runId);
|
|
@@ -681,7 +681,7 @@ const _FlowModel = class _FlowModel {
|
|
|
681
681
|
}
|
|
682
682
|
const isFork = this.isFork === true;
|
|
683
683
|
const target = this;
|
|
684
|
-
|
|
684
|
+
currentFlowEngine.logger.debug(
|
|
685
685
|
`[FlowModel] dispatchEvent: uid=${this.uid}, event=${eventName}, isFork=${isFork}, cleanRun=${this.cleanRun}, targetIsFork=${(target == null ? void 0 : target.isFork) === true}`
|
|
686
686
|
);
|
|
687
687
|
return await currentFlowEngine.executor.dispatchEvent(target, eventName, inputArgs, options);
|
|
@@ -1056,7 +1056,7 @@ const _FlowModel = class _FlowModel {
|
|
|
1056
1056
|
}
|
|
1057
1057
|
clearForks() {
|
|
1058
1058
|
var _a;
|
|
1059
|
-
|
|
1059
|
+
this.flowEngine.logger.debug(`FlowModel ${this.uid} clearing all forks.`);
|
|
1060
1060
|
if ((_a = this.forks) == null ? void 0 : _a.size) {
|
|
1061
1061
|
this.forks.forEach((fork) => fork.dispose());
|
|
1062
1062
|
this.forks.clear();
|
|
@@ -16,6 +16,10 @@ export interface ViewParam {
|
|
|
16
16
|
/** source Id */
|
|
17
17
|
sourceId?: string;
|
|
18
18
|
}
|
|
19
|
+
export interface ParsePathnameToViewParamsOptions {
|
|
20
|
+
rootPrefix?: string;
|
|
21
|
+
basePath?: string;
|
|
22
|
+
}
|
|
19
23
|
/**
|
|
20
24
|
* 解析路径名为视图参数数组
|
|
21
25
|
*
|
|
@@ -31,4 +35,4 @@ export interface ViewParam {
|
|
|
31
35
|
* parsePathnameToViewParams('/admin/xxx/view/yyy') // [{ viewUid: 'xxx' }, { viewUid: 'yyy' }]
|
|
32
36
|
* ```
|
|
33
37
|
*/
|
|
34
|
-
export declare const parsePathnameToViewParams: (pathname: string) => ViewParam[];
|
|
38
|
+
export declare const parsePathnameToViewParams: (pathname: string, options?: ParsePathnameToViewParamsOptions) => ViewParam[];
|
|
@@ -30,20 +30,44 @@ __export(parsePathnameToViewParams_exports, {
|
|
|
30
30
|
parsePathnameToViewParams: () => parsePathnameToViewParams
|
|
31
31
|
});
|
|
32
32
|
module.exports = __toCommonJS(parsePathnameToViewParams_exports);
|
|
33
|
-
const
|
|
33
|
+
const normalizePathname = /* @__PURE__ */ __name((pathname) => {
|
|
34
|
+
if (!pathname || pathname === "/") {
|
|
35
|
+
return "/";
|
|
36
|
+
}
|
|
37
|
+
return `/${pathname.replace(/^\/+/, "").replace(/\/+$/, "")}`;
|
|
38
|
+
}, "normalizePathname");
|
|
39
|
+
const normalizeBasePath = /* @__PURE__ */ __name((basePath) => `/${basePath.replace(/^\/+/, "").replace(/\/+$/, "")}`, "normalizeBasePath");
|
|
40
|
+
const stripBasePath = /* @__PURE__ */ __name((pathname, basePath) => {
|
|
41
|
+
const normalizedPathname = normalizePathname(pathname);
|
|
42
|
+
const normalizedBasePath = normalizeBasePath(basePath);
|
|
43
|
+
if (normalizedPathname === normalizedBasePath) {
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
46
|
+
if (normalizedPathname.startsWith(`${normalizedBasePath}/`)) {
|
|
47
|
+
return normalizedPathname.slice(normalizedBasePath.length + 1);
|
|
48
|
+
}
|
|
49
|
+
return "";
|
|
50
|
+
}, "stripBasePath");
|
|
51
|
+
const parsePathnameToViewParams = /* @__PURE__ */ __name((pathname, options = {}) => {
|
|
34
52
|
if (!pathname || pathname === "/") {
|
|
35
53
|
return [];
|
|
36
54
|
}
|
|
37
|
-
const
|
|
38
|
-
|
|
55
|
+
const rootPrefix = options.rootPrefix || "admin";
|
|
56
|
+
const relativePath = options.basePath ? stripBasePath(pathname, options.basePath) : "";
|
|
57
|
+
const segments = (options.basePath ? relativePath : pathname).replace(/^\/+/, "").split("/").filter(Boolean);
|
|
58
|
+
if (segments.length < (options.basePath ? 1 : 2)) {
|
|
39
59
|
return [];
|
|
40
60
|
}
|
|
41
61
|
const result = [];
|
|
42
62
|
let currentView = null;
|
|
43
63
|
let i = 0;
|
|
64
|
+
if (options.basePath) {
|
|
65
|
+
currentView = { viewUid: segments[0] };
|
|
66
|
+
i = 1;
|
|
67
|
+
}
|
|
44
68
|
while (i < segments.length) {
|
|
45
69
|
const segment = segments[i];
|
|
46
|
-
if (segment ===
|
|
70
|
+
if (segment === rootPrefix || segment === "view") {
|
|
47
71
|
if (currentView) {
|
|
48
72
|
result.push(currentView);
|
|
49
73
|
}
|
|
@@ -11,6 +11,14 @@ import { ViewParam as SharedViewParam } from '../utils';
|
|
|
11
11
|
type ViewParams = Omit<SharedViewParam, 'viewUid'> & {
|
|
12
12
|
viewUid?: string;
|
|
13
13
|
};
|
|
14
|
+
export interface GeneratePathnameFromViewParamsOptions {
|
|
15
|
+
prefix?: string;
|
|
16
|
+
basePath?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ViewNavigationOptions {
|
|
19
|
+
basePath?: string;
|
|
20
|
+
layoutBasePath?: string;
|
|
21
|
+
}
|
|
14
22
|
/**
|
|
15
23
|
* 将 ViewParam 数组转换为 pathname
|
|
16
24
|
*
|
|
@@ -24,12 +32,13 @@ type ViewParams = Omit<SharedViewParam, 'viewUid'> & {
|
|
|
24
32
|
* generatePathnameFromViewParams([{ viewUid: 'xxx' }, { viewUid: 'yyy' }]) // '/admin/xxx/view/yyy'
|
|
25
33
|
* ```
|
|
26
34
|
*/
|
|
27
|
-
export declare function generatePathnameFromViewParams(viewParams: ViewParams[]): string;
|
|
35
|
+
export declare function generatePathnameFromViewParams(viewParams: ViewParams[], options?: GeneratePathnameFromViewParamsOptions): string;
|
|
28
36
|
export declare class ViewNavigation {
|
|
29
37
|
viewStack: ReadonlyArray<ViewParams>;
|
|
30
38
|
ctx: FlowEngineContext;
|
|
31
39
|
viewParams: ViewParams;
|
|
32
|
-
|
|
40
|
+
private readonly basePath?;
|
|
41
|
+
constructor(ctx: FlowEngineContext, viewParams: ViewParams[], options?: ViewNavigationOptions);
|
|
33
42
|
setViewStack(viewParams: ViewParams[]): void;
|
|
34
43
|
changeTo(viewParam: ViewParams): void;
|
|
35
44
|
navigateTo(viewParam: ViewParams, opts?: {
|
|
@@ -37,5 +46,6 @@ export declare class ViewNavigation {
|
|
|
37
46
|
state?: any;
|
|
38
47
|
}): void;
|
|
39
48
|
back(): void;
|
|
49
|
+
private getLayoutBasePath;
|
|
40
50
|
}
|
|
41
51
|
export {};
|
|
@@ -47,11 +47,17 @@ function hasUsableSourceId(sourceId) {
|
|
|
47
47
|
return sourceId !== void 0 && sourceId !== null && String(sourceId) !== "";
|
|
48
48
|
}
|
|
49
49
|
__name(hasUsableSourceId, "hasUsableSourceId");
|
|
50
|
-
function
|
|
50
|
+
function normalizeBasePath(basePath) {
|
|
51
|
+
const value = basePath || "/admin";
|
|
52
|
+
return `/${value.replace(/^\/+/, "").replace(/\/+$/, "")}`;
|
|
53
|
+
}
|
|
54
|
+
__name(normalizeBasePath, "normalizeBasePath");
|
|
55
|
+
function generatePathnameFromViewParams(viewParams, options = {}) {
|
|
56
|
+
const basePath = normalizeBasePath(options.basePath || options.prefix);
|
|
51
57
|
if (!viewParams || viewParams.length === 0) {
|
|
52
|
-
return
|
|
58
|
+
return basePath;
|
|
53
59
|
}
|
|
54
|
-
const segments =
|
|
60
|
+
const segments = basePath.replace(/^\/+/, "").split("/").filter(Boolean);
|
|
55
61
|
viewParams.forEach((viewParam, index) => {
|
|
56
62
|
if (index > 0) {
|
|
57
63
|
segments.push("view");
|
|
@@ -78,9 +84,11 @@ const _ViewNavigation = class _ViewNavigation {
|
|
|
78
84
|
// 只能通过 setViewStack 修改
|
|
79
85
|
ctx;
|
|
80
86
|
viewParams;
|
|
81
|
-
|
|
87
|
+
basePath;
|
|
88
|
+
constructor(ctx, viewParams, options = {}) {
|
|
82
89
|
this.setViewStack(viewParams);
|
|
83
90
|
this.ctx = ctx;
|
|
91
|
+
this.basePath = options.basePath || options.layoutBasePath;
|
|
84
92
|
(0, import_reactive.define)(this, {
|
|
85
93
|
viewParams: import_reactive.observable
|
|
86
94
|
});
|
|
@@ -96,19 +104,26 @@ const _ViewNavigation = class _ViewNavigation {
|
|
|
96
104
|
}
|
|
97
105
|
return { ...item };
|
|
98
106
|
});
|
|
99
|
-
const newPathname = generatePathnameFromViewParams(newViewStack);
|
|
107
|
+
const newPathname = generatePathnameFromViewParams(newViewStack, { basePath: this.getLayoutBasePath() });
|
|
100
108
|
this.ctx.router.navigate(newPathname, { replace: true });
|
|
101
109
|
}
|
|
102
110
|
navigateTo(viewParam, opts) {
|
|
103
|
-
const newViewPathname = generatePathnameFromViewParams([...this.viewStack, viewParam]
|
|
111
|
+
const newViewPathname = generatePathnameFromViewParams([...this.viewStack, viewParam], {
|
|
112
|
+
basePath: this.getLayoutBasePath()
|
|
113
|
+
});
|
|
104
114
|
const newPathname = newViewPathname;
|
|
105
115
|
this.ctx.router.navigate(newPathname, opts);
|
|
106
116
|
}
|
|
107
117
|
back() {
|
|
108
118
|
const prevStack = this.viewStack.slice(0, -1);
|
|
109
|
-
const prevPath = generatePathnameFromViewParams(prevStack);
|
|
119
|
+
const prevPath = generatePathnameFromViewParams(prevStack, { basePath: this.getLayoutBasePath() });
|
|
110
120
|
this.ctx.router.navigate(prevPath, { replace: true });
|
|
111
121
|
}
|
|
122
|
+
getLayoutBasePath() {
|
|
123
|
+
var _a, _b;
|
|
124
|
+
const routePath = (_a = this.ctx.layout) == null ? void 0 : _a.routePath;
|
|
125
|
+
return this.basePath || ((_b = this.ctx.layoutRoute) == null ? void 0 : _b.basePathname) || ((routePath == null ? void 0 : routePath.startsWith("/")) ? routePath : "/admin");
|
|
126
|
+
}
|
|
112
127
|
};
|
|
113
128
|
__name(_ViewNavigation, "ViewNavigation");
|
|
114
129
|
let ViewNavigation = _ViewNavigation;
|