@nocobase/flow-engine 2.1.27 → 2.1.29
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.
|
@@ -347,19 +347,15 @@ const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchV
|
|
|
347
347
|
e.stopPropagation();
|
|
348
348
|
},
|
|
349
349
|
onChange: (e) => {
|
|
350
|
-
var _a;
|
|
351
350
|
e.stopPropagation();
|
|
352
351
|
const value = e.target.value;
|
|
353
352
|
if (shouldActivateSearchSubmenu) {
|
|
354
353
|
activateSearchSubmenu(searchKey);
|
|
355
354
|
}
|
|
356
|
-
if (
|
|
355
|
+
if (e.nativeEvent.isComposing || searchHandlers.isComposing(searchKey)) {
|
|
357
356
|
searchHandlers.updateInputValue(searchKey, value);
|
|
358
357
|
return;
|
|
359
358
|
}
|
|
360
|
-
if (!value && shouldActivateSearchSubmenu) {
|
|
361
|
-
deactivateSearchSubmenu(searchKey);
|
|
362
|
-
}
|
|
363
359
|
searchHandlers.updateSearchValue(searchKey, value);
|
|
364
360
|
},
|
|
365
361
|
onCompositionStart: (e) => {
|
|
@@ -373,11 +369,7 @@ const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchV
|
|
|
373
369
|
e.stopPropagation();
|
|
374
370
|
const value = e.currentTarget.value;
|
|
375
371
|
if (shouldActivateSearchSubmenu) {
|
|
376
|
-
|
|
377
|
-
activateSearchSubmenu(searchKey);
|
|
378
|
-
} else {
|
|
379
|
-
deactivateSearchSubmenu(searchKey);
|
|
380
|
-
}
|
|
372
|
+
activateSearchSubmenu(searchKey);
|
|
381
373
|
}
|
|
382
374
|
searchHandlers.endComposition(searchKey, value);
|
|
383
375
|
},
|
|
@@ -385,10 +377,20 @@ const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchV
|
|
|
385
377
|
e.stopPropagation();
|
|
386
378
|
},
|
|
387
379
|
onKeyDown: (e) => {
|
|
380
|
+
if (e.key === "Escape" || e.key === "Tab") {
|
|
381
|
+
deactivateSearchSubmenu(searchKey);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (shouldActivateSearchSubmenu) {
|
|
385
|
+
activateSearchSubmenu(searchKey);
|
|
386
|
+
}
|
|
388
387
|
e.stopPropagation();
|
|
389
388
|
},
|
|
390
389
|
onMouseDown: (e) => {
|
|
391
390
|
e.stopPropagation();
|
|
391
|
+
if (shouldActivateSearchSubmenu) {
|
|
392
|
+
activateSearchSubmenu(searchKey);
|
|
393
|
+
}
|
|
392
394
|
},
|
|
393
395
|
size: "small",
|
|
394
396
|
style: {
|
|
@@ -409,7 +411,7 @@ const KEEP_OPEN_LABEL_STYLE = {
|
|
|
409
411
|
width: "100%"
|
|
410
412
|
};
|
|
411
413
|
const DROPDOWN_PERSIST_TTL_MS = 350;
|
|
412
|
-
const
|
|
414
|
+
const MENU_CLOSE_DELAY = 0.3;
|
|
413
415
|
const SUBMENU_MOTION_DISABLED = {
|
|
414
416
|
motionEnter: false,
|
|
415
417
|
motionLeave: false
|
|
@@ -419,9 +421,9 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
419
421
|
const engine = (0, import_provider.useFlowEngine)();
|
|
420
422
|
const [menuVisible, setMenuVisible] = (0, import_react.useState)(false);
|
|
421
423
|
const [openKeys, setOpenKeys] = (0, import_react.useState)(/* @__PURE__ */ new Set());
|
|
422
|
-
const [activeSearchKey, setActiveSearchKey] = (0, import_react.useState)(null);
|
|
423
424
|
const [rootItems, setRootItems] = (0, import_react.useState)([]);
|
|
424
425
|
const [rootLoading, setRootLoading] = (0, import_react.useState)(false);
|
|
426
|
+
const activeSearchKeyRef = (0, import_react.useRef)(null);
|
|
425
427
|
const closeByOutsideClickRef = (0, import_react.useRef)(false);
|
|
426
428
|
const skipPreserveActiveSearchRef = (0, import_react.useRef)(false);
|
|
427
429
|
const dropdownMaxHeight = useNiceDropdownMaxHeight();
|
|
@@ -440,12 +442,12 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
440
442
|
useSubmenuStyles(menuVisible, dropdownMaxHeight);
|
|
441
443
|
const closeMenu = (0, import_react.useCallback)(() => {
|
|
442
444
|
setMenuVisible(false);
|
|
443
|
-
|
|
445
|
+
activeSearchKeyRef.current = null;
|
|
444
446
|
setOpenKeys(/* @__PURE__ */ new Set());
|
|
445
447
|
clearAllSearchValues();
|
|
446
448
|
}, [clearAllSearchValues]);
|
|
447
449
|
const activateSearchSubmenu = (0, import_react.useCallback)((key) => {
|
|
448
|
-
|
|
450
|
+
activeSearchKeyRef.current = key;
|
|
449
451
|
setOpenKeys((prev) => {
|
|
450
452
|
if (prev.has(key)) return prev;
|
|
451
453
|
const next = new Set(prev);
|
|
@@ -454,34 +456,38 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
454
456
|
});
|
|
455
457
|
}, []);
|
|
456
458
|
const deactivateSearchSubmenu = (0, import_react.useCallback)((key) => {
|
|
457
|
-
|
|
459
|
+
if (activeSearchKeyRef.current === key) {
|
|
460
|
+
activeSearchKeyRef.current = null;
|
|
461
|
+
}
|
|
458
462
|
}, []);
|
|
459
463
|
const closeActiveSearchForPath = (0, import_react.useCallback)(
|
|
460
464
|
(keyPath) => {
|
|
465
|
+
const activeSearchKey = activeSearchKeyRef.current;
|
|
461
466
|
if (!activeSearchKey || keyPath === activeSearchKey || keyPath.startsWith(`${activeSearchKey}/`) || activeSearchKey.startsWith(`${keyPath}/`)) {
|
|
462
467
|
return;
|
|
463
468
|
}
|
|
464
469
|
skipPreserveActiveSearchRef.current = true;
|
|
465
470
|
clearSearchValue(activeSearchKey);
|
|
466
|
-
|
|
471
|
+
activeSearchKeyRef.current = null;
|
|
467
472
|
setOpenKeys((prev) => {
|
|
468
473
|
const next = new Set(prev);
|
|
469
474
|
next.delete(activeSearchKey);
|
|
470
475
|
return next;
|
|
471
476
|
});
|
|
472
477
|
},
|
|
473
|
-
[
|
|
478
|
+
[clearSearchValue]
|
|
474
479
|
);
|
|
475
480
|
const handleMenuOpenChange = (0, import_react.useCallback)(
|
|
476
481
|
(nextOpenKeys) => {
|
|
477
482
|
var _a, _b;
|
|
478
483
|
let normalized = normalizeOpenKeys(nextOpenKeys);
|
|
479
|
-
|
|
480
|
-
|
|
484
|
+
const activeSearchKey = activeSearchKeyRef.current;
|
|
485
|
+
if (activeSearchKey && !normalized.includes(activeSearchKey)) {
|
|
486
|
+
if (skipPreserveActiveSearchRef.current) {
|
|
481
487
|
clearSearchValue(activeSearchKey);
|
|
482
|
-
|
|
488
|
+
activeSearchKeyRef.current = null;
|
|
483
489
|
} else {
|
|
484
|
-
normalized =
|
|
490
|
+
normalized = Array.from(openKeys);
|
|
485
491
|
}
|
|
486
492
|
}
|
|
487
493
|
if (!normalized.length && shouldPreventClose()) {
|
|
@@ -498,7 +504,7 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
498
504
|
(_b = dropdownMenuProps.onOpenChange) == null ? void 0 : _b.call(dropdownMenuProps, normalized);
|
|
499
505
|
skipPreserveActiveSearchRef.current = false;
|
|
500
506
|
},
|
|
501
|
-
[
|
|
507
|
+
[clearSearchValue, dropdownMenuProps, openKeys, shouldPreventClose]
|
|
502
508
|
);
|
|
503
509
|
(0, import_react.useEffect)(() => {
|
|
504
510
|
if (!menuVisible) return;
|
|
@@ -734,13 +740,14 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
734
740
|
...props,
|
|
735
741
|
open: menuVisible,
|
|
736
742
|
destroyPopupOnHide: true,
|
|
743
|
+
mouseLeaveDelay: props.mouseLeaveDelay ?? MENU_CLOSE_DELAY,
|
|
737
744
|
overlayClassName,
|
|
738
745
|
placement: "bottomLeft",
|
|
739
746
|
menu: {
|
|
740
747
|
...dropdownMenuProps,
|
|
741
748
|
openKeys: Array.from(openKeys),
|
|
742
749
|
items,
|
|
743
|
-
subMenuCloseDelay: dropdownMenuProps.subMenuCloseDelay ??
|
|
750
|
+
subMenuCloseDelay: dropdownMenuProps.subMenuCloseDelay ?? MENU_CLOSE_DELAY,
|
|
744
751
|
motion: dropdownMenuProps.motion ?? SUBMENU_MOTION_DISABLED,
|
|
745
752
|
onClick: /* @__PURE__ */ __name(() => {
|
|
746
753
|
}, "onClick"),
|
|
@@ -752,7 +759,7 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
752
759
|
}
|
|
753
760
|
},
|
|
754
761
|
onOpenChange: (visible, info) => {
|
|
755
|
-
if (!visible &&
|
|
762
|
+
if (!visible && activeSearchKeyRef.current && (info == null ? void 0 : info.source) === "trigger" && !closeByOutsideClickRef.current) {
|
|
756
763
|
return;
|
|
757
764
|
}
|
|
758
765
|
if (!visible && shouldPreventClose()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/flow-engine",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.29",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A standalone flow engine for NocoBase, managing workflows, models, and actions.",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@formily/antd-v5": "1.x",
|
|
10
10
|
"@formily/reactive": "2.x",
|
|
11
|
-
"@nocobase/sdk": "2.1.
|
|
12
|
-
"@nocobase/shared": "2.1.
|
|
11
|
+
"@nocobase/sdk": "2.1.29",
|
|
12
|
+
"@nocobase/shared": "2.1.29",
|
|
13
13
|
"ahooks": "^3.7.2",
|
|
14
14
|
"axios": "^1.7.0",
|
|
15
15
|
"dayjs": "^1.11.9",
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
],
|
|
38
38
|
"author": "NocoBase Team",
|
|
39
39
|
"license": "Apache-2.0",
|
|
40
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "d9848c5df07cef806cb938bb14b3f4ef5f3d0129"
|
|
41
41
|
}
|
|
@@ -484,13 +484,10 @@ const createSearchItem = (
|
|
|
484
484
|
if (shouldActivateSearchSubmenu) {
|
|
485
485
|
activateSearchSubmenu(searchKey);
|
|
486
486
|
}
|
|
487
|
-
if ((e.nativeEvent as
|
|
487
|
+
if ((e.nativeEvent as InputEvent).isComposing || searchHandlers.isComposing(searchKey)) {
|
|
488
488
|
searchHandlers.updateInputValue(searchKey, value);
|
|
489
489
|
return;
|
|
490
490
|
}
|
|
491
|
-
if (!value && shouldActivateSearchSubmenu) {
|
|
492
|
-
deactivateSearchSubmenu(searchKey);
|
|
493
|
-
}
|
|
494
491
|
searchHandlers.updateSearchValue(searchKey, value);
|
|
495
492
|
}}
|
|
496
493
|
onCompositionStart={(e) => {
|
|
@@ -504,11 +501,7 @@ const createSearchItem = (
|
|
|
504
501
|
e.stopPropagation();
|
|
505
502
|
const value = e.currentTarget.value;
|
|
506
503
|
if (shouldActivateSearchSubmenu) {
|
|
507
|
-
|
|
508
|
-
activateSearchSubmenu(searchKey);
|
|
509
|
-
} else {
|
|
510
|
-
deactivateSearchSubmenu(searchKey);
|
|
511
|
-
}
|
|
504
|
+
activateSearchSubmenu(searchKey);
|
|
512
505
|
}
|
|
513
506
|
searchHandlers.endComposition(searchKey, value);
|
|
514
507
|
}}
|
|
@@ -516,11 +509,21 @@ const createSearchItem = (
|
|
|
516
509
|
e.stopPropagation();
|
|
517
510
|
}}
|
|
518
511
|
onKeyDown={(e) => {
|
|
512
|
+
if (e.key === 'Escape' || e.key === 'Tab') {
|
|
513
|
+
deactivateSearchSubmenu(searchKey);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
if (shouldActivateSearchSubmenu) {
|
|
517
|
+
activateSearchSubmenu(searchKey);
|
|
518
|
+
}
|
|
519
519
|
e.stopPropagation();
|
|
520
520
|
}}
|
|
521
521
|
onMouseDown={(e) => {
|
|
522
522
|
// 防止菜单聚焦丢失或页面滚动
|
|
523
523
|
e.stopPropagation();
|
|
524
|
+
if (shouldActivateSearchSubmenu) {
|
|
525
|
+
activateSearchSubmenu(searchKey);
|
|
526
|
+
}
|
|
524
527
|
}}
|
|
525
528
|
size="small"
|
|
526
529
|
style={{
|
|
@@ -552,7 +555,7 @@ const KEEP_OPEN_LABEL_STYLE: React.CSSProperties = {
|
|
|
552
555
|
|
|
553
556
|
// 短暂保持打开状态的注册表(用于跨父节点快速重建时的恢复)
|
|
554
557
|
const DROPDOWN_PERSIST_TTL_MS = 350;
|
|
555
|
-
const
|
|
558
|
+
const MENU_CLOSE_DELAY = 0.3;
|
|
556
559
|
const SUBMENU_MOTION_DISABLED = {
|
|
557
560
|
motionEnter: false,
|
|
558
561
|
motionLeave: false,
|
|
@@ -563,9 +566,9 @@ const LazyDropdown: React.FC<Omit<DropdownProps, 'menu'> & { menu: LazyDropdownM
|
|
|
563
566
|
const engine = useFlowEngine();
|
|
564
567
|
const [menuVisible, setMenuVisible] = useState(false);
|
|
565
568
|
const [openKeys, setOpenKeys] = useState<Set<string>>(new Set());
|
|
566
|
-
const [activeSearchKey, setActiveSearchKey] = useState<string | null>(null);
|
|
567
569
|
const [rootItems, setRootItems] = useState<Item[]>([]);
|
|
568
570
|
const [rootLoading, setRootLoading] = useState(false);
|
|
571
|
+
const activeSearchKeyRef = useRef<string | null>(null);
|
|
569
572
|
const closeByOutsideClickRef = useRef(false);
|
|
570
573
|
const skipPreserveActiveSearchRef = useRef(false);
|
|
571
574
|
const dropdownMaxHeight = useNiceDropdownMaxHeight();
|
|
@@ -589,13 +592,13 @@ const LazyDropdown: React.FC<Omit<DropdownProps, 'menu'> & { menu: LazyDropdownM
|
|
|
589
592
|
|
|
590
593
|
const closeMenu = useCallback(() => {
|
|
591
594
|
setMenuVisible(false);
|
|
592
|
-
|
|
595
|
+
activeSearchKeyRef.current = null;
|
|
593
596
|
setOpenKeys(new Set());
|
|
594
597
|
clearAllSearchValues();
|
|
595
598
|
}, [clearAllSearchValues]);
|
|
596
599
|
|
|
597
600
|
const activateSearchSubmenu = useCallback((key: string) => {
|
|
598
|
-
|
|
601
|
+
activeSearchKeyRef.current = key;
|
|
599
602
|
setOpenKeys((prev) => {
|
|
600
603
|
if (prev.has(key)) return prev;
|
|
601
604
|
const next = new Set(prev);
|
|
@@ -605,11 +608,14 @@ const LazyDropdown: React.FC<Omit<DropdownProps, 'menu'> & { menu: LazyDropdownM
|
|
|
605
608
|
}, []);
|
|
606
609
|
|
|
607
610
|
const deactivateSearchSubmenu = useCallback((key: string) => {
|
|
608
|
-
|
|
611
|
+
if (activeSearchKeyRef.current === key) {
|
|
612
|
+
activeSearchKeyRef.current = null;
|
|
613
|
+
}
|
|
609
614
|
}, []);
|
|
610
615
|
|
|
611
616
|
const closeActiveSearchForPath = useCallback(
|
|
612
617
|
(keyPath: string) => {
|
|
618
|
+
const activeSearchKey = activeSearchKeyRef.current;
|
|
613
619
|
if (
|
|
614
620
|
!activeSearchKey ||
|
|
615
621
|
keyPath === activeSearchKey ||
|
|
@@ -621,25 +627,26 @@ const LazyDropdown: React.FC<Omit<DropdownProps, 'menu'> & { menu: LazyDropdownM
|
|
|
621
627
|
|
|
622
628
|
skipPreserveActiveSearchRef.current = true;
|
|
623
629
|
clearSearchValue(activeSearchKey);
|
|
624
|
-
|
|
630
|
+
activeSearchKeyRef.current = null;
|
|
625
631
|
setOpenKeys((prev) => {
|
|
626
632
|
const next = new Set(prev);
|
|
627
633
|
next.delete(activeSearchKey);
|
|
628
634
|
return next;
|
|
629
635
|
});
|
|
630
636
|
},
|
|
631
|
-
[
|
|
637
|
+
[clearSearchValue],
|
|
632
638
|
);
|
|
633
639
|
|
|
634
640
|
const handleMenuOpenChange = useCallback(
|
|
635
641
|
(nextOpenKeys: string[]) => {
|
|
636
642
|
let normalized = normalizeOpenKeys(nextOpenKeys);
|
|
637
|
-
|
|
638
|
-
|
|
643
|
+
const activeSearchKey = activeSearchKeyRef.current;
|
|
644
|
+
if (activeSearchKey && !normalized.includes(activeSearchKey)) {
|
|
645
|
+
if (skipPreserveActiveSearchRef.current) {
|
|
639
646
|
clearSearchValue(activeSearchKey);
|
|
640
|
-
|
|
647
|
+
activeSearchKeyRef.current = null;
|
|
641
648
|
} else {
|
|
642
|
-
normalized =
|
|
649
|
+
normalized = Array.from(openKeys);
|
|
643
650
|
}
|
|
644
651
|
}
|
|
645
652
|
|
|
@@ -658,7 +665,7 @@ const LazyDropdown: React.FC<Omit<DropdownProps, 'menu'> & { menu: LazyDropdownM
|
|
|
658
665
|
dropdownMenuProps.onOpenChange?.(normalized);
|
|
659
666
|
skipPreserveActiveSearchRef.current = false;
|
|
660
667
|
},
|
|
661
|
-
[
|
|
668
|
+
[clearSearchValue, dropdownMenuProps, openKeys, shouldPreventClose],
|
|
662
669
|
);
|
|
663
670
|
|
|
664
671
|
useEffect(() => {
|
|
@@ -957,13 +964,14 @@ const LazyDropdown: React.FC<Omit<DropdownProps, 'menu'> & { menu: LazyDropdownM
|
|
|
957
964
|
{...props}
|
|
958
965
|
open={menuVisible}
|
|
959
966
|
destroyPopupOnHide
|
|
967
|
+
mouseLeaveDelay={props.mouseLeaveDelay ?? MENU_CLOSE_DELAY}
|
|
960
968
|
overlayClassName={overlayClassName}
|
|
961
969
|
placement="bottomLeft"
|
|
962
970
|
menu={{
|
|
963
971
|
...dropdownMenuProps,
|
|
964
972
|
openKeys: Array.from(openKeys),
|
|
965
973
|
items: items,
|
|
966
|
-
subMenuCloseDelay: dropdownMenuProps.subMenuCloseDelay ??
|
|
974
|
+
subMenuCloseDelay: dropdownMenuProps.subMenuCloseDelay ?? MENU_CLOSE_DELAY,
|
|
967
975
|
motion: dropdownMenuProps.motion ?? SUBMENU_MOTION_DISABLED,
|
|
968
976
|
onClick: () => {},
|
|
969
977
|
onOpenChange: handleMenuOpenChange,
|
|
@@ -974,7 +982,7 @@ const LazyDropdown: React.FC<Omit<DropdownProps, 'menu'> & { menu: LazyDropdownM
|
|
|
974
982
|
},
|
|
975
983
|
}}
|
|
976
984
|
onOpenChange={(visible, info) => {
|
|
977
|
-
if (!visible &&
|
|
985
|
+
if (!visible && activeSearchKeyRef.current && info?.source === 'trigger' && !closeByOutsideClickRef.current) {
|
|
978
986
|
return;
|
|
979
987
|
}
|
|
980
988
|
|
|
@@ -411,7 +411,7 @@ describe('transformItems - searchable flags', () => {
|
|
|
411
411
|
await waitFor(() => expect(screen.getAllByText('No data').length).toBeGreaterThan(0));
|
|
412
412
|
});
|
|
413
413
|
|
|
414
|
-
it('
|
|
414
|
+
it('keeps searchable submenu open while interacting with its input', async () => {
|
|
415
415
|
const engine = new FlowEngine();
|
|
416
416
|
await engine.flowSettings.forceEnable();
|
|
417
417
|
class Parent extends FlowModel {}
|
|
@@ -451,7 +451,11 @@ describe('transformItems - searchable flags', () => {
|
|
|
451
451
|
await user.click(screen.getByRole('textbox'));
|
|
452
452
|
fireEvent.mouseLeave(screen.getByText('Fields'));
|
|
453
453
|
|
|
454
|
-
await
|
|
454
|
+
await act(async () => {
|
|
455
|
+
await new Promise((resolve) => setTimeout(resolve, 350));
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
expect(screen.getByText('Field 1')).toBeInTheDocument();
|
|
455
459
|
});
|
|
456
460
|
|
|
457
461
|
it('closes active searchable submenu after outside click', async () => {
|