flow-lib-creomnia 1.0.17 → 1.0.19-dev.2
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/modules/connection/components/AddButton/index.d.ts +2 -4
- package/lib/modules/connection/components/AddButton/index.js +21 -2
- package/lib/modules/connection/components/AddButton/styled.d.ts +2 -1
- package/lib/modules/connection/components/AddButton/styled.js +14 -6
- package/lib/modules/connection/components/ConnectionWithButton/index.js +16 -17
- package/lib/modules/connection/components/ConnectionWithButton/styled.js +1 -1
- package/lib/modules/flow/components/NodesFlow/index.js +3 -2
- package/lib/modules/flow/components/Space/index.js +4 -0
- package/lib/modules/flow/context/FlowActions/index.d.ts +8 -0
- package/lib/modules/flow/context/FlowActions/index.js +9 -0
- package/lib/modules/flow/types/index.d.ts +1 -0
- package/lib/modules/node/components/StartNode/index.js +5 -1
- package/lib/modules/node/contexts/SettingsContext/index.js +7 -2
- package/package.json +1 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import React, { Ref } from "react";
|
|
2
1
|
import { PointType } from "../../../common/types";
|
|
3
2
|
import { NodeType } from "../../../node/types";
|
|
4
3
|
import { ConnectionCurveEnum } from "../../enums";
|
|
@@ -9,8 +8,7 @@ interface IProps {
|
|
|
9
8
|
childNodeId: string;
|
|
10
9
|
node: NodeType;
|
|
11
10
|
curveType?: ConnectionCurveEnum;
|
|
12
|
-
|
|
13
|
-
innerRef: Ref<any>;
|
|
11
|
+
onOpen?: (anchorRect: DOMRect) => void;
|
|
14
12
|
}
|
|
15
|
-
export declare const AddButton: ({ endPoint, startPoint, node, curveType, parentId, childNodeId,
|
|
13
|
+
export declare const AddButton: ({ endPoint, startPoint, node, curveType, parentId, childNodeId, onOpen, }: IProps) => import("react/jsx-runtime").JSX.Element;
|
|
16
14
|
export {};
|
|
@@ -10,7 +10,7 @@ const ConnectionWithButton_1 = require("../ConnectionWithButton");
|
|
|
10
10
|
const enums_1 = require("../../enums");
|
|
11
11
|
const colors_1 = require("../../../../config/colors");
|
|
12
12
|
const material_1 = require("@mui/material");
|
|
13
|
-
const AddButton = ({ endPoint, startPoint, node, curveType, parentId, childNodeId,
|
|
13
|
+
const AddButton = ({ endPoint, startPoint, node, curveType, parentId, childNodeId, onOpen, }) => {
|
|
14
14
|
const theme = (0, material_1.useTheme)();
|
|
15
15
|
const background = theme.palette?.background?.default || colors_1.Colors.DIRTY_WHITE;
|
|
16
16
|
const positionIntoEmptyChoiceNode = !curveType ? 0 : curveType === enums_1.ConnectionCurveEnum.RIGHT ? 2 : 1;
|
|
@@ -31,15 +31,34 @@ const AddButton = ({ endPoint, startPoint, node, curveType, parentId, childNodeI
|
|
|
31
31
|
const yPosition = y + ConnectionWithButton_1.nodeHeightByType[node.type] / 2 + config_1.Config.ADD_BUTTON_SHIFT;
|
|
32
32
|
const PlusButton = (0, styled_1.createAddButton)({
|
|
33
33
|
background,
|
|
34
|
+
});
|
|
35
|
+
const PlusButtonWrapper = (0, styled_1.createAddButtonWrapper)({
|
|
34
36
|
x: xPosition,
|
|
35
37
|
y: yPosition,
|
|
36
38
|
});
|
|
37
39
|
const Placeholder = (0, styled_1.createPlaceholder)(isOver, canDrop);
|
|
38
40
|
const xDropZone = xPosition - (config_1.Config.NODE_WIDTH / 2 - 5);
|
|
39
41
|
const yDropZone = yPosition - (config_1.Config.NODE_HEIGHT / 2 - config_1.Config.ADD_BUTTON_WIDTH / 2);
|
|
42
|
+
// The add button lives inside the zoom/pan surface. Without
|
|
43
|
+
// stopping the initial press event, the canvas can start a pan
|
|
44
|
+
// gesture first, and the browser may never emit the follow-up
|
|
45
|
+
// click that opens the nodes menu. This shows up most clearly in
|
|
46
|
+
// the tool-flow modal, where the nested sandbox is rendered inside
|
|
47
|
+
// a Dialog on top of another live canvas.
|
|
48
|
+
const stopCanvasGesture = (event) => {
|
|
49
|
+
event.stopPropagation();
|
|
50
|
+
};
|
|
51
|
+
const handlePressStart = (event) => {
|
|
52
|
+
// Keep the canvas from treating the press as pan/scroll start,
|
|
53
|
+
// then open the menu immediately instead of waiting for a full
|
|
54
|
+
// click sequence that may never fire once a gesture begins.
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
stopCanvasGesture(event);
|
|
57
|
+
onOpen?.(event.currentTarget.getBoundingClientRect());
|
|
58
|
+
};
|
|
40
59
|
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Placeholder, { ref: drop, sx: {
|
|
41
60
|
top: yDropZone,
|
|
42
61
|
left: xDropZone,
|
|
43
|
-
} }), (0, jsx_runtime_1.jsx)(
|
|
62
|
+
} }), (0, jsx_runtime_1.jsx)(PlusButtonWrapper, { "data-flow-interactive": 'true', onMouseDown: handlePressStart, onPointerDown: handlePressStart, onTouchStart: handlePressStart, onClick: stopCanvasGesture, children: (0, jsx_runtime_1.jsx)(PlusButton, {}) })] }));
|
|
44
63
|
};
|
|
45
64
|
exports.AddButton = AddButton;
|
|
@@ -4,7 +4,8 @@ type AddButtonPropsType = {
|
|
|
4
4
|
x: number;
|
|
5
5
|
y: number;
|
|
6
6
|
};
|
|
7
|
-
export declare const
|
|
7
|
+
export declare const createAddButtonWrapper: ({ x, y, }: Omit<AddButtonPropsType, 'background'>) => import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<import("@mui/system").Theme>, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
8
|
+
export declare const createAddButton: ({ background, }: Omit<AddButtonPropsType, 'x' | 'y'>) => import("@emotion/styled").StyledComponent<{
|
|
8
9
|
children?: import("react").ReactNode;
|
|
9
10
|
classes?: Partial<import("@mui/material").SvgIconClasses> | undefined;
|
|
10
11
|
color?: import("@mui/types").OverridableStringUnion<"error" | "inherit" | "action" | "disabled" | "success" | "info" | "warning" | "primary" | "secondary", import("@mui/material").SvgIconPropsColorOverrides> | undefined;
|
|
@@ -3,24 +3,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.createPlaceholder = exports.createAddButton = void 0;
|
|
6
|
+
exports.createPlaceholder = exports.createAddButton = exports.createAddButtonWrapper = void 0;
|
|
7
7
|
const system_1 = require("@mui/system");
|
|
8
8
|
const Add_1 = __importDefault(require("@mui/icons-material/Add"));
|
|
9
9
|
const config_1 = require("../../../../config");
|
|
10
10
|
const colors_1 = require("../../../../config/colors");
|
|
11
11
|
const material_1 = require("@mui/material");
|
|
12
|
-
const
|
|
13
|
-
height: ${config_1.Config.ADD_BUTTON_WIDTH}px;
|
|
14
|
-
width: ${config_1.Config.ADD_BUTTON_WIDTH}px;
|
|
12
|
+
const createAddButtonWrapper = ({ x, y, }) => (0, system_1.styled)('div') `
|
|
15
13
|
position: absolute;
|
|
16
14
|
top: ${y}px;
|
|
17
15
|
left: ${x}px;
|
|
16
|
+
width: ${config_1.Config.ADD_BUTTON_WIDTH}px;
|
|
17
|
+
height: ${config_1.Config.ADD_BUTTON_WIDTH}px;
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
z-index: 1000;
|
|
23
|
+
`;
|
|
24
|
+
exports.createAddButtonWrapper = createAddButtonWrapper;
|
|
25
|
+
const createAddButton = ({ background = colors_1.Colors.WHITE, }) => (0, system_1.styled)(Add_1.default) `
|
|
26
|
+
height: ${config_1.Config.ADD_BUTTON_WIDTH}px;
|
|
27
|
+
width: ${config_1.Config.ADD_BUTTON_WIDTH}px;
|
|
18
28
|
font-size: ${config_1.Config.ADD_BUTTON_WIDTH}px;
|
|
19
29
|
background: ${background};
|
|
20
30
|
color: ${colors_1.Colors.NAVY_BLUE};
|
|
21
31
|
border-radius: 50%;
|
|
22
|
-
cursor: pointer;
|
|
23
|
-
z-index: 1000;
|
|
24
32
|
:hover {
|
|
25
33
|
background: ${colors_1.Colors.BLUE};
|
|
26
34
|
color: ${colors_1.Colors.WHITE};
|
|
@@ -24,9 +24,7 @@ exports.nodeHeightByType = {
|
|
|
24
24
|
[enums_1.NodeTypeEnum.ParallelNode]: 0,
|
|
25
25
|
};
|
|
26
26
|
const ConnectionWithButton = ({ parentId, menuTabs, startPoint, endPoint, addNewNodeCallback, node, connectionIndex, curveType, isProcessed, childNodeId, emptyMenuItemsComponent, searchPlaceholder }) => {
|
|
27
|
-
const
|
|
28
|
-
const [anchorEl, setAnchorEl] = (0, react_1.useState)(false);
|
|
29
|
-
const [styles, setStyles] = (0, react_1.useState)({});
|
|
27
|
+
const [anchorRect, setAnchorRect] = (0, react_1.useState)(null);
|
|
30
28
|
const ConnectionLine = (0, react_1.useMemo)(() => {
|
|
31
29
|
const { width, height, x, y, top, bottom, left, right } = connectionParametersServices_1.connectionParametersServices.calculateConnectionParameters({
|
|
32
30
|
endPoint,
|
|
@@ -48,19 +46,18 @@ const ConnectionWithButton = ({ parentId, menuTabs, startPoint, endPoint, addNew
|
|
|
48
46
|
});
|
|
49
47
|
}, [curveType, endPoint, node, startPoint]);
|
|
50
48
|
const handleClose = () => {
|
|
51
|
-
|
|
49
|
+
setAnchorRect(null);
|
|
52
50
|
};
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
const left = rect?.left || 0;
|
|
56
|
-
const top = rect?.top || 0;
|
|
57
|
-
const style = {
|
|
58
|
-
transform: `translate(${left - 400}px, ${top - 228}px)`,
|
|
59
|
-
overflow: 'visible',
|
|
60
|
-
};
|
|
61
|
-
setStyles(style);
|
|
62
|
-
setAnchorEl(true);
|
|
51
|
+
const handleOpen = (nextAnchorRect) => {
|
|
52
|
+
setAnchorRect(nextAnchorRect);
|
|
63
53
|
};
|
|
54
|
+
const anchorEl = (0, react_1.useMemo)(() => {
|
|
55
|
+
if (!anchorRect)
|
|
56
|
+
return null;
|
|
57
|
+
return {
|
|
58
|
+
getBoundingClientRect: () => anchorRect,
|
|
59
|
+
};
|
|
60
|
+
}, [anchorRect]);
|
|
64
61
|
const isOpen = Boolean(anchorEl);
|
|
65
62
|
const props = (0, web_1.useSpring)({
|
|
66
63
|
borderColor: isProcessed ? colors_1.Colors.GREEN : colors_1.Colors.NAVY_BLUE,
|
|
@@ -76,9 +73,11 @@ const ConnectionWithButton = ({ parentId, menuTabs, startPoint, endPoint, addNew
|
|
|
76
73
|
curveType,
|
|
77
74
|
parentId,
|
|
78
75
|
childNodeId,
|
|
79
|
-
|
|
80
|
-
innerRef: popperRef
|
|
76
|
+
onOpen: handleOpen,
|
|
81
77
|
};
|
|
82
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(AddButton_1.AddButton, { ...buttonProps }), (0, jsx_runtime_1.jsx)(AnimatedConnectionLine, { style: props }), (0, jsx_runtime_1.jsx)(material_1.ClickAwayListener, { onClickAway: () => isOpen && handleClose(), mouseEvent: "onMouseDown", touchEvent: "onTouchStart", children: (0, jsx_runtime_1.jsx)(styled_1.StyledPopper, { placement: 'left', open: isOpen,
|
|
78
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(AddButton_1.AddButton, { ...buttonProps }), (0, jsx_runtime_1.jsx)(AnimatedConnectionLine, { style: props }), (0, jsx_runtime_1.jsx)(material_1.ClickAwayListener, { onClickAway: () => isOpen && handleClose(), mouseEvent: "onMouseDown", touchEvent: "onTouchStart", children: (0, jsx_runtime_1.jsx)(styled_1.StyledPopper, { anchorEl: anchorEl, placement: 'left', open: isOpen, modifiers: [
|
|
79
|
+
{ name: 'arrow', enabled: true },
|
|
80
|
+
{ name: 'offset', options: { offset: [0, 0] } },
|
|
81
|
+
], children: (0, jsx_runtime_1.jsx)(NodesMenu_1.default, { addNewNodeCallback: addNewNodeCallback, data: { parentId, connectionIndex }, menuTabs: menuTabs, onCloseMenu: handleClose, emptyMenuItemsComponent: emptyMenuItemsComponent, searchPlaceholder: searchPlaceholder }) }) })] }));
|
|
83
82
|
};
|
|
84
83
|
exports.default = (0, react_1.memo)(ConnectionWithButton);
|
|
@@ -49,7 +49,7 @@ const createAddButton = ({ background = colors_1.Colors.WHITE, x, y, }) => (0, s
|
|
|
49
49
|
`;
|
|
50
50
|
exports.createAddButton = createAddButton;
|
|
51
51
|
exports.StyledPopper = (0, system_1.styled)(material_1.Popper) `
|
|
52
|
-
|
|
52
|
+
z-index: 1400;
|
|
53
53
|
overflow: hidden;
|
|
54
54
|
border-radius: 16px;
|
|
55
55
|
`;
|
|
@@ -13,10 +13,11 @@ const ThemeContext_1 = require("../../../common/contexts/ThemeContext");
|
|
|
13
13
|
const expandCoefficientServices_1 = require("../../../node/services/expandCoefficientServices");
|
|
14
14
|
const material_1 = require("@mui/material");
|
|
15
15
|
const FlowPosition_1 = require("../../context/FlowPosition");
|
|
16
|
+
const FlowActions_1 = require("../../context/FlowActions");
|
|
16
17
|
const react_dnd_html5_backend_1 = require("react-dnd-html5-backend");
|
|
17
18
|
const react_dnd_1 = require("react-dnd");
|
|
18
19
|
const colors_1 = require("../../../../config/colors");
|
|
19
|
-
const NodesFlow = ({ theme, nodes, menuTabs, addNewNodeCallback, permissions, drawerProps, processedNodeIds, failedNodeId, failedNodeColor, onRedirect, onUpdateNodePosition, emptyMenuItemsComponent, searchPlaceholder, }) => {
|
|
20
|
+
const NodesFlow = ({ theme, nodes, menuTabs, addNewNodeCallback, permissions, drawerProps, processedNodeIds, failedNodeId, failedNodeColor, onRedirect, onUpdateNodePosition, emptyMenuItemsComponent, searchPlaceholder, onStartClick, }) => {
|
|
20
21
|
const nodeDrawers = (0, react_1.useMemo)(() => {
|
|
21
22
|
const { startNodeList, nodeListWithEndNode } = nodeServices_1.nodeServices.createListOfStartNodesAndEndNodes(nodes);
|
|
22
23
|
const nodeListWithExpandCoefficients = expandCoefficientServices_1.expandCoefficientServices.calculateNodeExpandCoefficient(nodeListWithEndNode);
|
|
@@ -31,6 +32,6 @@ const NodesFlow = ({ theme, nodes, menuTabs, addNewNodeCallback, permissions, dr
|
|
|
31
32
|
addNewNodeCallback,
|
|
32
33
|
}), [nodeDrawers, menuTabs, addNewNodeCallback, processedNodeIds]);
|
|
33
34
|
const containerRef = (0, react_1.useRef)(null);
|
|
34
|
-
return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: permissions.canRead && ((0, jsx_runtime_1.jsx)(ThemeContext_1.ThemeContextProvider, { themContextValue: theme, children: (0, jsx_runtime_1.jsx)(react_dnd_1.DndProvider, { backend: react_dnd_html5_backend_1.HTML5Backend, children: (0, jsx_runtime_1.jsx)(material_1.Box, { ref: containerRef, style: { position: "relative", height: "100%", width: "100%", background: theme?.palette.background.default || colors_1.Colors.DIRTY_WHITE }, children: (0, jsx_runtime_1.jsx)(SettingsContext_1.NodeSettingsProvider, { permissions: permissions, drawerProps: drawerProps, children: (0, jsx_runtime_1.jsx)(FlowPosition_1.FlowPositionProvider, { children: (0, jsx_runtime_1.jsx)(FlowSandbox_1.default, { permissions: permissions, connectionDrawers: connectionDrawers, nodeDrawers: nodeDrawers, containerRef: containerRef, processedNodeIds: processedNodeIds, failedNodeId: failedNodeId, failedNodeColor: failedNodeColor, onRedirect: onRedirect, onUpdateNodePosition: onUpdateNodePosition, emptyMenuItemsComponent: emptyMenuItemsComponent, searchPlaceholder: searchPlaceholder }) }) }) }) }) })) }));
|
|
35
|
+
return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: permissions.canRead && ((0, jsx_runtime_1.jsx)(ThemeContext_1.ThemeContextProvider, { themContextValue: theme, children: (0, jsx_runtime_1.jsx)(react_dnd_1.DndProvider, { backend: react_dnd_html5_backend_1.HTML5Backend, children: (0, jsx_runtime_1.jsx)(material_1.Box, { ref: containerRef, style: { position: "relative", height: "100%", width: "100%", background: theme?.palette.background.default || colors_1.Colors.DIRTY_WHITE }, children: (0, jsx_runtime_1.jsx)(SettingsContext_1.NodeSettingsProvider, { permissions: permissions, drawerProps: drawerProps, children: (0, jsx_runtime_1.jsx)(FlowActions_1.FlowActionsProvider, { onStartClick: onStartClick, children: (0, jsx_runtime_1.jsx)(FlowPosition_1.FlowPositionProvider, { children: (0, jsx_runtime_1.jsx)(FlowSandbox_1.default, { permissions: permissions, connectionDrawers: connectionDrawers, nodeDrawers: nodeDrawers, containerRef: containerRef, processedNodeIds: processedNodeIds, failedNodeId: failedNodeId, failedNodeColor: failedNodeColor, onRedirect: onRedirect, onUpdateNodePosition: onUpdateNodePosition, emptyMenuItemsComponent: emptyMenuItemsComponent, searchPlaceholder: searchPlaceholder }) }) }) }) }) }) })) }));
|
|
35
36
|
};
|
|
36
37
|
exports.default = NodesFlow;
|
|
@@ -11,6 +11,10 @@ const Space = ({ children }) => {
|
|
|
11
11
|
const [scrollLeft, setScrollLeft] = (0, react_1.useState)(0);
|
|
12
12
|
const [scrollTop, setScrollTop] = (0, react_1.useState)(0);
|
|
13
13
|
const handleMouseDown = (event) => {
|
|
14
|
+
const target = event.target;
|
|
15
|
+
if (target?.closest('[data-flow-interactive="true"]')) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
14
18
|
setIsDragging(true);
|
|
15
19
|
setStartX(event.pageX - spaceRef.current.offsetLeft);
|
|
16
20
|
setStartY(event.pageY - spaceRef.current.offsetTop);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React, { FC, PropsWithChildren } from 'react';
|
|
2
|
+
type FlowActionsValue = {
|
|
3
|
+
onStartClick?: () => void;
|
|
4
|
+
};
|
|
5
|
+
declare const FlowActionsContext: React.Context<FlowActionsValue>;
|
|
6
|
+
type ProviderProps = PropsWithChildren & FlowActionsValue;
|
|
7
|
+
declare const FlowActionsProvider: FC<ProviderProps>;
|
|
8
|
+
export { FlowActionsContext, FlowActionsProvider };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FlowActionsProvider = exports.FlowActionsContext = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const FlowActionsContext = (0, react_1.createContext)({});
|
|
7
|
+
exports.FlowActionsContext = FlowActionsContext;
|
|
8
|
+
const FlowActionsProvider = ({ children, onStartClick }) => ((0, jsx_runtime_1.jsx)(FlowActionsContext.Provider, { value: { onStartClick }, children: children }));
|
|
9
|
+
exports.FlowActionsProvider = FlowActionsProvider;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
+
const react_1 = require("react");
|
|
4
5
|
const styled_1 = require("./styled");
|
|
6
|
+
const FlowActions_1 = require("../../../flow/context/FlowActions");
|
|
5
7
|
const StartNode = () => {
|
|
6
|
-
|
|
8
|
+
const { onStartClick } = (0, react_1.useContext)(FlowActions_1.FlowActionsContext);
|
|
9
|
+
const clickable = !!onStartClick;
|
|
10
|
+
return ((0, jsx_runtime_1.jsx)(styled_1.IconContainer, { onClick: onStartClick, role: clickable ? "button" : undefined, style: clickable ? { cursor: "pointer" } : undefined, children: (0, jsx_runtime_1.jsx)(styled_1.Icon, {}) }));
|
|
7
11
|
};
|
|
8
12
|
exports.default = StartNode;
|
|
@@ -47,8 +47,13 @@ const NodeSettingsProvider = ({ children, permissions: { canUpdate, canDelete },
|
|
|
47
47
|
setInternalNodeBase(null);
|
|
48
48
|
}, config_1.Config.CLEAR_NODE_DELAY);
|
|
49
49
|
};
|
|
50
|
-
const onSaveNode = () => {
|
|
51
|
-
|
|
50
|
+
const onSaveNode = async () => {
|
|
51
|
+
// `onSave` is allowed to be sync OR async — host apps that wrap a
|
|
52
|
+
// server call in an async handler need to surface validation errors
|
|
53
|
+
// BEFORE the drawer closes. Awaiting handles both cases: a sync
|
|
54
|
+
// `false` short-circuits, a `Promise<false>` resolves and short-
|
|
55
|
+
// circuits, anything else closes the drawer.
|
|
56
|
+
const result = await node?.settings?.footer?.onSave?.(node?.id);
|
|
52
57
|
if (result === false)
|
|
53
58
|
return;
|
|
54
59
|
handleClose();
|