Skip to content

Commit

Permalink
fix nodemove
Browse files Browse the repository at this point in the history
  • Loading branch information
emilwidlund committed May 20, 2024
1 parent 1c4a47e commit f3d1276
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 138 deletions.
2 changes: 1 addition & 1 deletion apps/web/src/app/(landing)/CircuitExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const CircuitExample = () => {

return (
<StoreContext.Provider value={{ store: circuitStore }}>
<div className="relative flex flex-col justify-between h-screen md:h-auto md:aspect-video w-full cursor-[url('/cursor.svg')_4_4,auto] rounded-[2rem] overflow-hidden pointer-events-none">
<div className="relative flex flex-col justify-between h-screen md:h-auto md:aspect-video w-full cursor-[url('/cursor.svg')_4_4,auto] rounded-[2rem] overflow-hidden">
<CircuitComponent
store={circuitStore}
nodeWindowResolver={nodeWindowResolver}
Expand Down
10 changes: 10 additions & 0 deletions apps/web/src/app/(protected)/circuit/[...circuitId]/ClientPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { Menu } from '../../../../components/Menu/Menu/Menu';
import { Avatar } from '../../../../circuit/components/Avatar/Avatar';
import { KeyboardArrowDownOutlined } from '@mui/icons-material';
import { updateCircuit } from '../../../../server/mutations/updateCircuit';
import { moveNode } from '@/server/mutations/moveNode';

export const ClientPage = ({ circuit }: { circuit: ExtendedNode }) => {
const [menuOpen, setMenuOpen] = useState(false);
Expand Down Expand Up @@ -70,6 +71,15 @@ export const ClientPage = ({ circuit }: { circuit: ExtendedNode }) => {
<CircuitComponent
store={canvasStore}
nodeWindowResolver={nodeWindowResolver}
onNodeMoveStop={e => {
for (const selectedNode of canvasStore.selectedNodes ||
[]) {
moveNode(selectedNode.id, {
x: selectedNode.position.x,
y: selectedNode.position.y
});
}
}}
/>
{menuOpen && <Menu onClose={() => setMenuOpen(false)} />}
<div className="absolute left-1/2 bottom-12 -translate-x-1/2 flex flex-row justify-center">
Expand Down
262 changes: 128 additions & 134 deletions apps/web/src/circuit/components/Node/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,162 +13,156 @@ import clsx from 'clsx';
import { motion } from 'framer-motion';
import { Circuit } from '@bitspace/circuit';
import { useRouter } from 'next/navigation';
import { moveNode } from '../../../server/mutations/moveNode';
import { removeNode } from '../../../server/mutations/removeNode';

export const Node = observer(({ node, actions, window }: NodeProps) => {
const ref = React.useRef<HTMLDivElement>(null);
const { onMouseEnter, onMouseLeave, isHovered } = useHover();
const { store } = React.useContext(StoreContext);
const router = useRouter();
export const Node = observer(
({ node, actions, window, onMoveStop }: NodeProps) => {
const ref = React.useRef<HTMLDivElement>(null);
const { onMouseEnter, onMouseLeave, isHovered } = useHover();
const { store } = React.useContext(StoreContext);
const router = useRouter();

React.useEffect(() => {
if (ref.current) {
store.setNodeElement(node.id, ref.current);
React.useEffect(() => {
if (ref.current) {
store.setNodeElement(node.id, ref.current);

return () => {
store.removeNodeElement(node.id);
};
}
}, [ref]);
return () => {
store.removeNodeElement(node.id);
};
}
}, [ref]);

const handleOnClick = React.useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
if (!store.selectedNodes?.includes(node)) {
store.selectNodes([node]);
}
},
[node]
);

const handleOnClick = React.useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
const handleOnFocus = React.useCallback(() => {
if (!store.selectedNodes?.includes(node)) {
store.selectNodes([node]);
}
},
[node]
);
}, [node]);

const handleOnFocus = React.useCallback(() => {
if (!store.selectedNodes?.includes(node)) {
store.selectNodes([node]);
}
}, [node]);
const handleOnDrag: DraggableEventHandler = React.useCallback(
(e, { deltaX, deltaY }) => {
e.preventDefault();
e.stopPropagation();

const handleOnDrag: DraggableEventHandler = React.useCallback(
(e, { deltaX, deltaY }) => {
e.preventDefault();
e.stopPropagation();
for (const selectedNode of store.selectedNodes || []) {
selectedNode.incrementPosition(deltaX, -deltaY);
}
},
[node]
);

for (const selectedNode of store.selectedNodes || []) {
selectedNode.incrementPosition(deltaX, -deltaY);
}
},
[node]
);
const handleRemoveNode = React.useCallback(() => {
node.dispose();

const handleRemoveNode = React.useCallback(() => {
node.dispose();
store.removeNode(node);

store.removeNode(node);
removeNode(node.id);
}, [node]);

removeNode(node.id);
}, [node]);
const handleDoubleClick: React.MouseEventHandler<HTMLDivElement> =
React.useCallback(() => {
if (node instanceof Circuit) {
router.push(`/circuit/${store.circuit.id}/${node.id}`);
}
}, [node, store]);

const handleDoubleClick: React.MouseEventHandler<HTMLDivElement> =
React.useCallback(() => {
if (node instanceof Circuit) {
router.push(`/circuit/${store.circuit.id}/${node.id}`);
}
}, [node, store]);

const active = store.selectedNodes?.indexOf(node) !== -1;
const position = node.position || { x: 0, y: 0 };

const nodeWrapperClassNames = clsx(
`absolute flex flex-col select-none focus:outline-none w-[260px] bg-[#fcfdff] border-white border rounded-3xl transition-shadow active:shadow-2xl opacity-100`,
{
'z-10': !active,
'z-20': active,
'shadow-2xl': active
}
);
const active = store.selectedNodes?.indexOf(node) !== -1;
const position = node.position || { x: 0, y: 0 };

const nodeHeaderWrapperClassNames = clsx(
'relative flex flex-row justify-center items-center px-4 pt-4 text-xs font-medium rounded-t-xl handle',
{
'text-black': active
}
);
const nodeWrapperClassNames = clsx(
`absolute flex flex-col select-none focus:outline-none w-[260px] bg-[#fcfdff] border-white border rounded-3xl transition-shadow active:shadow-2xl opacity-100`,
{
'z-10': !active,
'z-20': active,
'shadow-2xl': active
}
);

const nodeActionsClassNames = clsx(
'absolute right-5 top-5 flex flex-row nowrap gap-x-2 align-center transition-opacity',
{
'opacity-0': !(isHovered || active),
'opacity-100': isHovered || active
}
);
const nodeHeaderWrapperClassNames = clsx(
'relative flex flex-row justify-center items-center px-4 pt-4 text-xs font-medium rounded-t-xl handle',
{
'text-black': active
}
);

const nodeContentWrapperClassNames = clsx(
`flex flex-row justify-between items-start rounded-b-xl border-b-slate-100`,
{
'mt-4': !window
}
);
const nodeActionsClassNames = clsx(
'absolute right-5 top-5 flex flex-row nowrap gap-x-2 align-center transition-opacity',
{
'opacity-0': !(isHovered || active),
'opacity-100': isHovered || active
}
);

return (
<Draggable
nodeRef={ref}
position={fromCanvasCartesianPoint(
position.x - NODE_POSITION_OFFSET_X,
position.y
)}
onDrag={handleOnDrag}
onStop={e => {
for (const selectedNode of store.selectedNodes || []) {
moveNode(selectedNode.id, {
x: selectedNode.position.x,
y: selectedNode.position.y
});
}
}}
>
<motion.div
ref={ref}
className={nodeWrapperClassNames}
onClick={handleOnClick}
onFocus={handleOnFocus}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onDoubleClick={handleDoubleClick}
tabIndex={0}
variants={{
initial: {
opacity: 0
},
animate: {
opacity: 1,
transition: {
duration: 1.5,
ease: [0.75, 0, 0.25, 1]
}
}
}}
const nodeContentWrapperClassNames = clsx(
`flex flex-row justify-between items-start rounded-b-xl border-b-slate-100`,
{
'mt-4': !window
}
);

return (
<Draggable
nodeRef={ref}
position={fromCanvasCartesianPoint(
position.x - NODE_POSITION_OFFSET_X,
position.y
)}
onDrag={handleOnDrag}
onStop={onMoveStop}
>
<div className={nodeHeaderWrapperClassNames}>
{/** @ts-ignore */}
<span>{node.constructor.displayName}</span>
<div className={nodeActionsClassNames}>
<NodeAction
color="#ff4444"
onClick={handleRemoveNode}
<motion.div
ref={ref}
className={nodeWrapperClassNames}
onClick={handleOnClick}
onFocus={handleOnFocus}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onDoubleClick={handleDoubleClick}
tabIndex={0}
variants={{
initial: {
opacity: 0
},
animate: {
opacity: 1,
transition: {
duration: 1.5,
ease: [0.75, 0, 0.25, 1]
}
}
}}
>
<div className={nodeHeaderWrapperClassNames}>
{/** @ts-ignore */}
<span>{node.constructor.displayName}</span>
<div className={nodeActionsClassNames}>
<NodeAction
color="#ff4444"
onClick={handleRemoveNode}
/>
</div>
</div>
{window}
<div className={nodeContentWrapperClassNames}>
<NodePorts ports={Object.values(node.inputs)} />
<NodePorts
ports={Object.values(node.outputs)}
isOutputWrapper={true}
/>
</div>
</div>
{window}
<div className={nodeContentWrapperClassNames}>
<NodePorts ports={Object.values(node.inputs)} />
<NodePorts
ports={Object.values(node.outputs)}
isOutputWrapper={true}
/>
</div>
</motion.div>
</Draggable>
);
});
</motion.div>
</Draggable>
);
}
);

const NodeAction = ({ color = '#fff', onClick }: NodeActionProps) => {
return (
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/circuit/components/Node/Node.types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Input, Node, Output } from '@bitspace/circuit';
import { DraggableProps } from 'react-draggable';
import { DraggableEventHandler, DraggableProps } from 'react-draggable';

export type NodeProps = {
node: Node;
window?: JSX.Element;
actions?: NodeActionProps[];
className?: string;
disabled?: boolean;
onMoveStop?: DraggableEventHandler;
} & Partial<DraggableProps>;

export type NodeActionProps = {
Expand Down
16 changes: 14 additions & 2 deletions apps/web/src/circuit/containers/Circuit/Circuit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,16 @@ import { CircuitProps, NodeWindowResolver } from './Circuit.types';
import { motion } from 'framer-motion';
import clsx from 'clsx';
import { useUploadFile } from '../../hooks/useDropzone/useDropzone';
import { DraggableEventHandler } from 'react-draggable';

const Nodes = observer(
({ windowResolver }: { windowResolver?: NodeWindowResolver }) => {
({
windowResolver,
onNodeMoveStop
}: {
windowResolver?: NodeWindowResolver;
onNodeMoveStop?: DraggableEventHandler;
}) => {
const { store } = React.useContext(StoreContext);

return (
Expand All @@ -28,6 +35,7 @@ const Nodes = observer(
key={node.id}
node={node}
window={windowResolver?.(node)}
onMoveStop={onNodeMoveStop}
/>
))}
</>
Expand Down Expand Up @@ -93,6 +101,7 @@ export const Circuit = observer(
onConnection,
onConnectionRemoval,
onNodeRemoval,
onNodeMoveStop,
onSelectionChanged
}: CircuitProps) => {
useKeyboardActions(store);
Expand Down Expand Up @@ -164,7 +173,10 @@ export const Circuit = observer(
onScroll={onScroll}
onDrop={uploadFile}
>
<Nodes windowResolver={nodeWindowResolver} />
<Nodes
windowResolver={nodeWindowResolver}
onNodeMoveStop={onNodeMoveStop}
/>
<Connections />
<Selection />
</Canvas>
Expand Down
Loading

0 comments on commit f3d1276

Please sign in to comment.