Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved node resetting #2566

Merged
merged 12 commits into from
Feb 15, 2024
4 changes: 3 additions & 1 deletion src/renderer/components/node/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,9 @@ const NodeInner = memo(({ data, selected }: NodeProps) => {
targetRef={targetRef}
toggleCollapse={toggleCollapse}
validity={validity}
onContextMenu={menu.onContextMenu}
onContextMenu={(e) => {
menu.onContextMenu(e);
}}
onDragOver={onDragOver}
onDrop={onDrop}
/>
Expand Down
19 changes: 16 additions & 3 deletions src/renderer/contexts/GlobalNodeState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ interface Global {
removeEdgeById: (id: string) => void;
duplicateNodes: (nodeIds: readonly string[], withInputEdges?: boolean) => void;
toggleNodeLock: (id: string) => void;
clearNodes: (ids: readonly string[]) => void;
resetInputs: (ids: readonly string[]) => void;
resetConnections: (ids: readonly string[]) => void;
setNodeDisabled: (id: string, isDisabled: boolean) => void;
setCollidingEdge: (value: string | undefined) => void;
setCollidingNode: (value: string | undefined) => void;
Expand Down Expand Up @@ -1035,7 +1036,7 @@ export const GlobalProvider = memo(
[changeNodes, changeEdges]
);

const clearNodes = useCallback(
const resetInputs = useCallback(
(ids: readonly string[]) => {
ids.forEach((id) => {
modifyNode(id, (old) => {
Expand All @@ -1053,6 +1054,17 @@ export const GlobalProvider = memo(
[modifyNode, addInputDataChanges, outputDataActions, backend, schemata]
);

const resetConnections = useCallback(
(ids: readonly string[]) => {
changeEdges((edges) => {
return edges.filter((e) => {
return !ids.includes(e.source) && !ids.includes(e.target);
});
});
},
[changeEdges]
);

const setNodeDisabled = useCallback(
(id: string, isDisabled: boolean): void => {
modifyNode(id, (n) => {
Expand Down Expand Up @@ -1220,7 +1232,8 @@ export const GlobalProvider = memo(
setNodeOutputHeight,
setNodeWidth,
toggleNodeLock,
clearNodes,
resetInputs,
resetConnections,
removeNodesById,
removeEdgeById,
duplicateNodes,
Expand Down
7 changes: 7 additions & 0 deletions src/renderer/hooks/useContextMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface UseContextMenu {
readonly onContextMenu: MouseEventHandler;
readonly onClick: MouseEventHandler;
readonly manuallyOpenContextMenu: (pageX: number, pageY: number) => void;
readonly position: { x: number; y: number };
}

export const useContextMenu = (render: () => JSX.Element): UseContextMenu => {
Expand All @@ -18,6 +19,8 @@ export const useContextMenu = (render: () => JSX.Element): UseContextMenu => {
// eslint-disable-next-line react/hook-use-state
const [id] = useState(createUniqueId);

const [position, setPosition] = useState({ x: 0, y: 0 });

useEffect(() => {
return () => unregisterContextMenu(id);
}, [unregisterContextMenu, id]);
Expand All @@ -33,13 +36,15 @@ export const useContextMenu = (render: () => JSX.Element): UseContextMenu => {
e.stopPropagation();
e.preventDefault();
openContextMenu(id, e.pageX, e.pageY);
setPosition({ x: e.pageX, y: e.pageY });
},
[openContextMenu, id]
);

const manuallyOpenContextMenu = useCallback(
(pageX: number, pageY: number): void => {
openContextMenu(id, pageX, pageY);
setPosition({ x: pageX, y: pageY });
},
[openContextMenu, id]
);
Expand All @@ -53,6 +58,7 @@ export const useContextMenu = (render: () => JSX.Element): UseContextMenu => {
const y = e.clientY - rect.top;

openContextMenu(id, e.pageX - x + rect.width, e.pageY - y + rect.height);
setPosition({ x: e.pageX - x + rect.width, y: e.pageY - y + rect.height });
},
[openContextMenu, id]
);
Expand All @@ -62,6 +68,7 @@ export const useContextMenu = (render: () => JSX.Element): UseContextMenu => {
onContextMenu,
onClick,
manuallyOpenContextMenu,
position,
});
};

Expand Down
11 changes: 11 additions & 0 deletions src/renderer/hooks/useNodeMenu.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.useNodeMenu-child {
display: none;
}

.useNodeMenu-container:hover + .useNodeMenu-child {
display: block;
}

.useNodeMenu-child:hover {
display: block;
}
57 changes: 51 additions & 6 deletions src/renderer/hooks/useNodeMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {
ChevronRightIcon,
CloseIcon,
CopyIcon,
DeleteIcon,
LockIcon,
RepeatIcon,
UnlockIcon,
} from '@chakra-ui/icons';
import { MenuDivider, MenuItem, MenuList } from '@chakra-ui/react';
import { HStack, MenuDivider, MenuItem, MenuList, Spacer, Text } from '@chakra-ui/react';
import { useRef } from 'react';
import { BsFillJournalBookmarkFill } from 'react-icons/bs';
import { MdPlayArrow, MdPlayDisabled } from 'react-icons/md';
import { useReactFlow } from 'reactflow';
Expand All @@ -18,6 +20,8 @@ import { copyToClipboard } from '../helpers/copyAndPaste';
import { UseContextMenu, useContextMenu } from './useContextMenu';
import { UseDisabled } from './useDisabled';

import './useNodeMenu.scss';

export interface UseNodeMenuOptions {
canLock?: boolean;
reload?: () => void;
Expand All @@ -31,12 +35,14 @@ export const useNodeMenu = (
const { openNodeDocumentation } = useContext(NodeDocumentationContext);
const { id, isLocked = false, schemaId } = data;

const { removeNodesById, clearNodes, duplicateNodes, toggleNodeLock } =
const { removeNodesById, resetInputs, resetConnections, duplicateNodes, toggleNodeLock } =
useContext(GlobalContext);
const { isDirectlyDisabled, canDisable, toggleDirectlyDisabled } = useDisabled;

const { getNode, getNodes, getEdges } = useReactFlow<NodeData, EdgeData>();

const resetMenuParentRef = useRef<HTMLButtonElement>(null);

return useContextMenu(() => (
<MenuList className="nodrag">
<MenuItem
Expand All @@ -63,13 +69,52 @@ export const useNodeMenu = (
</MenuItem>
<MenuDivider />
<MenuItem
as="a"
className="useNodeMenu-container"
closeOnSelect={false}
icon={<CloseIcon />}
onClick={() => {
clearNodes([id]);
}}
ref={resetMenuParentRef}
>
Reset Node
<HStack>
<Text>Reset Node</Text>
<Spacer />
<ChevronRightIcon />
</HStack>
</MenuItem>
<div className="useNodeMenu-child">
<MenuList
left={resetMenuParentRef.current?.offsetWidth || 0}
position="absolute"
top={(resetMenuParentRef.current?.offsetHeight || 0) - 12}
>
<MenuItem
icon={<CloseIcon />}
onClick={() => {
resetInputs([id]);
}}
>
Reset Inputs
</MenuItem>
<MenuItem
icon={<CloseIcon />}
onClick={() => {
resetConnections([id]);
}}
>
Reset Connections
</MenuItem>
<MenuItem
icon={<CloseIcon />}
onClick={() => {
resetInputs([id]);
resetConnections([id]);
}}
>
Reset All
</MenuItem>
</MenuList>
</div>
<MenuDivider />
{canDisable && (
<MenuItem
icon={isDirectlyDisabled ? <MdPlayArrow /> : <MdPlayDisabled />}
Expand Down
60 changes: 53 additions & 7 deletions src/renderer/hooks/useNodesMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { CloseIcon, CopyIcon, DeleteIcon } from '@chakra-ui/icons';
import { MenuDivider, MenuItem, MenuList } from '@chakra-ui/react';
import { ChevronRightIcon, CloseIcon, CopyIcon, DeleteIcon } from '@chakra-ui/icons';
import { HStack, MenuDivider, MenuItem, MenuList, Spacer, Text } from '@chakra-ui/react';
import { useRef } from 'react';
import { Node, useReactFlow } from 'reactflow';
import { useContext } from 'use-context-selector';
import { EdgeData, NodeData } from '../../common/common-types';
import { GlobalContext } from '../contexts/GlobalNodeState';
import { copyToClipboard } from '../helpers/copyAndPaste';
import { UseContextMenu, useContextMenu } from './useContextMenu';

import './useNodeMenu.scss';

export const useNodesMenu = (nodes: Node<NodeData>[]): UseContextMenu => {
const { removeNodesById, clearNodes, duplicateNodes } = useContext(GlobalContext);
const { removeNodesById, resetInputs, resetConnections, duplicateNodes } =
useContext(GlobalContext);

const nodeIds = nodes.map((n) => n.id);

const { getNodes, getEdges } = useReactFlow<NodeData, EdgeData>();

const resetMenuParentRef = useRef<HTMLButtonElement>(null);

return useContextMenu(() => (
<MenuList className="nodrag">
<MenuItem
Expand All @@ -34,13 +40,53 @@ export const useNodesMenu = (nodes: Node<NodeData>[]): UseContextMenu => {
</MenuItem>
<MenuDivider />
<MenuItem
as="a"
className="useNodeMenu-container"
closeOnSelect={false}
icon={<CloseIcon />}
onClick={() => {
clearNodes(nodeIds);
}}
ref={resetMenuParentRef}
>
Reset Selected Nodes
<HStack>
<Text>Reset Nodes</Text>
<Spacer />
<ChevronRightIcon />
</HStack>
</MenuItem>
<div className="useNodeMenu-child">
<MenuList
className="nodrag"
left={resetMenuParentRef.current?.offsetWidth || 0}
position="absolute"
top={(resetMenuParentRef.current?.offsetHeight || 0) - 12}
>
<MenuItem
icon={<CloseIcon />}
onClick={() => {
resetInputs(nodeIds);
}}
>
Reset Inputs
</MenuItem>
<MenuItem
icon={<CloseIcon />}
onClick={() => {
resetConnections(nodeIds);
}}
>
Reset Connections
</MenuItem>
<MenuItem
icon={<CloseIcon />}
onClick={() => {
resetInputs(nodeIds);
resetConnections(nodeIds);
}}
>
Reset All
</MenuItem>
</MenuList>
</div>
<MenuDivider />
<MenuItem
icon={<DeleteIcon />}
onClick={() => {
Expand Down
Loading