From 596453072e2430840e9fc172e1abdc60703d33e2 Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Sat, 22 Jun 2024 15:30:42 +0200 Subject: [PATCH] fix cursor bug --- .../scenes/frame/panels/Diagram/AppNode.tsx | 3 +- .../scenes/frame/panels/Diagram/CodeNode.tsx | 3 +- .../scenes/frame/panels/Diagram/EventNode.tsx | 7 +- .../frame/panels/Diagram/appNodeLogic.ts | 8 +++ .../frame/panels/Diagram/diagramLogic.tsx | 64 +++++++++++++++++-- 5 files changed, 75 insertions(+), 10 deletions(-) diff --git a/frontend/src/scenes/frame/panels/Diagram/AppNode.tsx b/frontend/src/scenes/frame/panels/Diagram/AppNode.tsx index 7c43f5f2..da2062d2 100644 --- a/frontend/src/scenes/frame/panels/Diagram/AppNode.tsx +++ b/frontend/src/scenes/frame/panels/Diagram/AppNode.tsx @@ -18,7 +18,7 @@ import { CodeArg } from './CodeArg' import { newNodePickerLogic } from './newNodePickerLogic' import { FieldTypeTag } from '../../../../components/FieldTypeTag' -export function AppNode({ data, id, isConnectable }: NodeProps): JSX.Element { +export function AppNode({ id, isConnectable }: NodeProps): JSX.Element { const { frameId, sceneId, sceneOptions } = useValues(diagramLogic) const { updateNodeConfig, copyAppJSON, deleteApp } = useActions(diagramLogic) const { editApp } = useActions(panelsLogic) @@ -40,6 +40,7 @@ export function AppNode({ data, id, isConnectable }: NodeProps>({}) diff --git a/frontend/src/scenes/frame/panels/Diagram/CodeNode.tsx b/frontend/src/scenes/frame/panels/Diagram/CodeNode.tsx index 20f02949..5cb6bd4d 100644 --- a/frontend/src/scenes/frame/panels/Diagram/CodeNode.tsx +++ b/frontend/src/scenes/frame/panels/Diagram/CodeNode.tsx @@ -11,11 +11,12 @@ import { NodeCache } from './NodeCache' import { CodeArg } from './CodeArg' import { newNodePickerLogic } from './newNodePickerLogic' -export function CodeNode({ data, id, isConnectable }: NodeProps): JSX.Element { +export function CodeNode({ id, isConnectable }: NodeProps): JSX.Element { const { frameId, sceneId } = useValues(diagramLogic) const { updateNodeData, copyAppJSON, deleteApp } = useActions(diagramLogic) const appNodeLogicProps = { frameId, sceneId, nodeId: id } const { isSelected, node, nodeEdges } = useValues(appNodeLogic(appNodeLogicProps)) + const data: CodeNodeData = (node?.data as CodeNodeData) ?? ({ code: '' } satisfies CodeNodeData) const { select, editCodeField } = useActions(appNodeLogic(appNodeLogicProps)) const { openNewNodePicker } = useActions(newNodePickerLogic({ sceneId, frameId })) diff --git a/frontend/src/scenes/frame/panels/Diagram/EventNode.tsx b/frontend/src/scenes/frame/panels/Diagram/EventNode.tsx index 3a667b74..5f2a14fa 100644 --- a/frontend/src/scenes/frame/panels/Diagram/EventNode.tsx +++ b/frontend/src/scenes/frame/panels/Diagram/EventNode.tsx @@ -3,7 +3,7 @@ import { NodeProps, Handle, Position } from 'reactflow' import clsx from 'clsx' import { diagramLogic } from './diagramLogic' import copy from 'copy-to-clipboard' -import { FrameEvent, StateField } from '../../../../types' +import { EventNodeData, FrameEvent, StateField } from '../../../../types' import { stateFieldAccess } from '../../../../utils/fieldTypes' import _events from '../../../../../schema/events.json' @@ -21,14 +21,15 @@ const events: FrameEvent[] = _events as any export function EventNode(props: NodeProps): JSX.Element { const { frameId, sceneId } = useValues(diagramLogic) - const { data, id } = props + const { id } = props const { width, height } = useValues(frameLogic) const { selectedNodeId, scene } = useValues(diagramLogic) const { selectNode } = useActions(diagramLogic) - const { keyword } = data const appNodeLogicProps = { frameId, sceneId, nodeId: id } const { node, nodeEdges } = useValues(appNodeLogic(appNodeLogicProps)) + const data: EventNodeData = (node?.data as EventNodeData) ?? ({ keyword: '' } satisfies EventNodeData) + const keyword = data.keyword const { openNewNodePicker } = useActions(newNodePickerLogic({ sceneId, frameId })) const isEventWithStateFields = keyword === 'init' || keyword === 'setSceneState' || keyword === 'render' diff --git a/frontend/src/scenes/frame/panels/Diagram/appNodeLogic.ts b/frontend/src/scenes/frame/panels/Diagram/appNodeLogic.ts index 80b79b5c..f55f2d3e 100644 --- a/frontend/src/scenes/frame/panels/Diagram/appNodeLogic.ts +++ b/frontend/src/scenes/frame/panels/Diagram/appNodeLogic.ts @@ -18,6 +18,7 @@ import type { Edge } from '@reactflow/core/dist/esm/types/edges' import type { Node } from '@reactflow/core/dist/esm/types/nodes' import _events from '../../../../../schema/events.json' +import equal from 'fast-deep-equal' const events: FrameEvent[] = _events as any export interface AppNodeLogicProps extends DiagramLogicProps { @@ -55,6 +56,7 @@ export const appNodeLogic = kea([ nodeConfig: [ (s) => [s.node], (node): Record => (node && 'config' in node?.data ? node?.data.config ?? {} : {}), + { resultEqualityCheck: equal }, ], codeArgs: [ (s) => [s.nodeEdges, s.nodeId], @@ -67,6 +69,7 @@ export const appNodeLogic = kea([ edge.targetHandle?.startsWith('fieldInput/') ) .map((edge) => edge.targetHandle?.replace('fieldInput/', '') ?? ''), + { resultEqualityCheck: equal }, ], fieldInputFields: [ (s) => [s.nodeEdges, s.nodeId], @@ -79,6 +82,7 @@ export const appNodeLogic = kea([ edge.targetHandle?.startsWith('fieldInput/') ) .map((edge) => edge.targetHandle?.replace('fieldInput/', '') ?? ''), + { resultEqualityCheck: equal }, ], nodeOutputFields: [ (s) => [s.nodeEdges, s.nodeId], @@ -88,6 +92,7 @@ export const appNodeLogic = kea([ (edge) => edge.sourceHandle?.startsWith('field/') && nodeId == edge.source && edge.targetHandle === 'prev' ) .map((edge) => edge.sourceHandle?.replace('field/', '') ?? ''), + { resultEqualityCheck: equal }, ], isSelected: [(s) => [s.selectedNodeId, s.nodeId], (selectedNodeId, nodeId) => selectedNodeId === nodeId], sources: [ @@ -139,6 +144,7 @@ export const appNodeLogic = kea([ } return null }, + { resultEqualityCheck: equal }, ], event: [ (s) => [s.node], @@ -218,6 +224,7 @@ export const appNodeLogic = kea([ } return realFields }, + { resultEqualityCheck: equal }, ], allDefaultValues: [ (s) => [s.allFields], @@ -366,6 +373,7 @@ export const appNodeLogic = kea([ }) ?? null ) }, + { resultEqualityCheck: equal }, ], }), listeners(({ actions, values, props }) => ({ diff --git a/frontend/src/scenes/frame/panels/Diagram/diagramLogic.tsx b/frontend/src/scenes/frame/panels/Diagram/diagramLogic.tsx index a3824698..c82ad75d 100644 --- a/frontend/src/scenes/frame/panels/Diagram/diagramLogic.tsx +++ b/frontend/src/scenes/frame/panels/Diagram/diagramLogic.tsx @@ -1,4 +1,16 @@ -import { actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import { + actions, + afterMount, + connect, + kea, + key, + listeners, + path, + props, + reducers, + selectors, + sharedListeners, +} from 'kea' import { framesModel } from '../../../../models/framesModel' import { applyEdgeChanges, applyNodeChanges, addEdge } from 'reactflow' import { v4 as uuidv4 } from 'uuid' @@ -206,8 +218,11 @@ export const diagramLogic = kea([ { resultEqualityCheck: equal }, ], }), - subscriptions(({ actions, values, props }) => ({ - nodes: (nodes: DiagramNode[], oldNodes: DiagramNode[]) => { + sharedListeners(({ selectors, actions, values, props }) => ({ + nodesChanged: (_, __, ___, previousState) => { + const nodes = values.nodes + const oldNodes = selectors.nodes(previousState) + // Upon first render of a new scene, the nodes will have x = -9999, y = -9999, width = undefined, height = undefined // Upon second render, the width and height will have been set, but x and y will still be -9999 for all nodes // If we detect that case, automatically rearrange the scene. @@ -233,6 +248,39 @@ export const diagramLogic = kea([ }) } }, + })), + listeners(({ sharedListeners, props, values, actions }) => ({ + setNodes: sharedListeners.nodesChanged, + onNodesChange: sharedListeners.nodesChanged, + selectNode: sharedListeners.nodesChanged, + deselectNode: sharedListeners.nodesChanged, + updateNodeData: sharedListeners.nodesChanged, + deleteApp: sharedListeners.nodesChanged, + updateNodeConfig: ({ id, field, value }) => { + actions.setFrameFormValues({ + scenes: values.editingFrame.scenes?.map((scene) => + scene.id === props.sceneId && !equal(scene.nodes, nodes) + ? // set the nodes on the scene's form, and remove the selected flag from all + ({ + ...scene, + nodes: values.nodes.map((node) => + node.id === id + ? { + ...node, + data: { + ...(node.data ?? {}), + config: { ...('config' in node.data ? node.data?.config ?? {} : {}), [field]: value }, + }, + } + : node + ), + } satisfies FrameScene) + : scene + ), + }) + }, + })), + subscriptions(({ actions, values, props }) => ({ edges: (edges: Edge[], oldEdges: Edge[]) => { // Do not update on first render if (typeof oldEdges !== 'undefined' && edges && !equal(edges, oldEdges)) { @@ -250,12 +298,18 @@ export const diagramLogic = kea([ if (scene && !equal(scene.nodes, oldScene?.nodes)) { // nodes changed on the form, update our local state, but retain the selected flag const selectedNodeId = values.selectedNodeId - actions.setNodes(scene.nodes.map((n) => (n.id === selectedNodeId ? { ...n, selected: true } : n))) + const newNodes = scene.nodes.map((n) => (n.id === selectedNodeId ? { ...n, selected: true } : n)) + if (!equal(newNodes, values.nodes)) { + actions.setNodes(newNodes) + } } if (scene && !equal(scene.edges, oldScene?.edges)) { // edges changed on the form, update our local state, but retain the selected flag const selectedEdgeId = values.selectedEdgeId - actions.setEdges(scene.edges.map((e) => (e.id === selectedEdgeId ? { ...e, selected: true } : e))) + const newEdges = scene.edges.map((e) => (e.id === selectedEdgeId ? { ...e, selected: true } : e)) + if (!equal(newEdges, values.edges)) { + actions.setEdges(newEdges) + } } }, })),