From baf7961ceac0b7b180efe66465ccb1148692a98f Mon Sep 17 00:00:00 2001 From: Evan Simkowitz Date: Mon, 22 Jul 2024 17:00:31 -0700 Subject: [PATCH] Fix resize getting out of sync (#126) This PR fixes an issue where the resize state could fall out of sync. I've noticed that there is still some drift in other elements when the resize changes. I am trying to figure out what is causing this, but I don't view this as blocking since this is already a big improvement over the previous experience --- frontend/faraday/lib/TileLayout.tsx | 98 ++++++++++++++--------------- frontend/faraday/lib/layoutNode.ts | 4 ++ 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/frontend/faraday/lib/TileLayout.tsx b/frontend/faraday/lib/TileLayout.tsx index 245bf863..cc61fee3 100644 --- a/frontend/faraday/lib/TileLayout.tsx +++ b/frontend/faraday/lib/TileLayout.tsx @@ -21,7 +21,7 @@ import { DropTargetMonitor, useDrag, useDragLayer, useDrop } from "react-dnd"; import { debounce, throttle } from "throttle-debounce"; import { useDevicePixelRatio } from "use-device-pixel-ratio"; import { globalLayoutTransformsMap } from "./layoutAtom"; -import { findNode } from "./layoutNode"; +import { findNode, totalChildrenSize } from "./layoutNode"; import { layoutTreeStateReducer } from "./layoutState"; import { ContentRenderer, @@ -600,7 +600,7 @@ const OverlayNode = ({ const generateChildren = () => { if (Array.isArray(layoutNode.children)) { - const totalSize = layoutNode.children.reduce((partialSum, child) => partialSum + child.size, 0); + const totalSize = totalChildrenSize(layoutNode); return layoutNode.children .map((childItem, i) => { return [ @@ -620,7 +620,6 @@ const OverlayNode = ({ index={i} dispatch={dispatch} nodeRefsAtom={nodeRefsAtom} - siblingSize={totalSize} />, ]; }) @@ -657,10 +656,9 @@ interface ResizeHandleProps { index: number; dispatch: (action: LayoutTreeAction) => void; nodeRefsAtom: PrimitiveAtom; - siblingSize: number; } -const ResizeHandle = ({ parentNode, index, dispatch, nodeRefsAtom, siblingSize }: ResizeHandleProps) => { +const ResizeHandle = ({ parentNode, index, dispatch, nodeRefsAtom }: ResizeHandleProps) => { const resizeHandleRef = useRef(null); // The pointer currently captured, or undefined. @@ -668,61 +666,59 @@ const ResizeHandle = ({ parentNode, index, dispatch, nodeRefsAtom, siblingSi const nodeRefs = useAtomValue(nodeRefsAtom); // Cached values set in startResize - const [parentRect, setParentRect] = useState(); const [combinedNodesRect, setCombinedNodesRect] = useState(); const [gapSize, setGapSize] = useState(0); const [pixelToSizeRatio, setPixelToSizeRatio] = useState(0); // Precompute some values that will be needed by the handlePointerMove function - const startResize = useCallback( - throttle(30, () => { - const parentRef = nodeRefs.get(parentNode.id); - const node1Ref = nodeRefs.get(parentNode.children![index].id); - const node2Ref = nodeRefs.get(parentNode.children![index + 1].id); - if (parentRef?.current && node1Ref?.current && node2Ref?.current) { - const parentIsRow = parentNode.flexDirection === FlexDirection.Row; - const parentRectNew = parentRef.current.getBoundingClientRect(); - setParentRect(parentRectNew); - const node1Rect = node1Ref.current.getBoundingClientRect(); - const node2Rect = node2Ref.current.getBoundingClientRect(); - const gapSize = parentIsRow - ? node2Rect.left - (node1Rect.left + node1Rect.width) - : node2Rect.top - (node1Rect.top + node1Rect.height); - setGapSize(gapSize); - const parentPixelsMinusGap = - (parentIsRow ? parentRectNew.width : parentRectNew.height) - - (gapSize * parentNode.children!.length - 1); - const newPixelToSizeRatio = siblingSize / parentPixelsMinusGap; - // console.log("newPixelToSizeRatio", newPixelToSizeRatio, siblingSize, parentPixelsMinusGap); - setPixelToSizeRatio(newPixelToSizeRatio); - const newCombinedNodesRect: Dimensions = { - top: node1Rect.top, - left: node1Rect.left, - height: parentIsRow ? node1Rect.height : node1Rect.height + node2Rect.height + gapSize, - width: parentIsRow ? node1Rect.width + node2Rect.width + gapSize : node1Rect.width, - }; - setCombinedNodesRect(newCombinedNodesRect); - // console.log( - // "startResize", - // parentNode, - // index, - // parentIsRow, - // gapSize, - // parentRectNew, - // node1Rect, - // node2Rect, - // newCombinedNodesRect - // ); - } - }), - [parentNode, nodeRefs] - ); + const startResize = () => { + const parentRef = nodeRefs.get(parentNode.id); + const node1Ref = nodeRefs.get(parentNode.children![index].id); + const node2Ref = nodeRefs.get(parentNode.children![index + 1].id); + if (parentRef?.current && node1Ref?.current && node2Ref?.current) { + const parentIsRow = parentNode.flexDirection === FlexDirection.Row; + const parentRectNew = parentRef.current.getBoundingClientRect(); + const node1Rect = node1Ref.current.getBoundingClientRect(); + const node2Rect = node2Ref.current.getBoundingClientRect(); + const totalSiblingSize = totalChildrenSize(parentNode); + const gapSize = parentIsRow + ? node2Rect.left - (node1Rect.left + node1Rect.width) + : node2Rect.top - (node1Rect.top + node1Rect.height); + setGapSize(gapSize); + const parentPixelsMinusGap = + (parentIsRow ? parentRectNew.width : parentRectNew.height) - + (gapSize * parentNode.children!.length - 1); + const newPixelToSizeRatio = totalSiblingSize / parentPixelsMinusGap; + // console.log("newPixelToSizeRatio", newPixelToSizeRatio, siblingSize, parentPixelsMinusGap); + setPixelToSizeRatio(newPixelToSizeRatio); + const newCombinedNodesRect: Dimensions = { + top: node1Rect.top, + left: node1Rect.left, + height: parentIsRow ? node1Rect.height : node1Rect.height + node2Rect.height + gapSize, + width: parentIsRow ? node1Rect.width + node2Rect.width + gapSize : node1Rect.width, + }; + setCombinedNodesRect(newCombinedNodesRect); + // console.log( + // "startResize", + // parentNode, + // index, + // parentIsRow, + // gapSize, + // parentRectNew, + // node1Rect, + // node2Rect, + // newCombinedNodesRect, + // newPixelToSizeRatio + // ); + } + }; // Calculates the new size of the two nodes on either side of the handle, based on the position of the cursor const handlePointerMove = useCallback( throttle(10, (event: React.PointerEvent) => { if (trackingPointer === event.pointerId) { const { clientX, clientY } = event; + // console.log("handlePointerMove", [clientX, clientY], combinedNodesRect, pixelToSizeRatio); const parentIsRow = parentNode.flexDirection === FlexDirection.Row; const combinedStart = parentIsRow ? combinedNodesRect.left : combinedNodesRect.top; const combinedEnd = parentIsRow @@ -756,12 +752,13 @@ const ResizeHandle = ({ parentNode, index, dispatch, nodeRefsAtom, siblingSi } } }), - [dispatch, trackingPointer, parentNode, parentRect, combinedNodesRect, pixelToSizeRatio, gapSize] + [parentNode, trackingPointer, dispatch, gapSize, combinedNodesRect, pixelToSizeRatio, index] ); // We want to use pointer capture so the operation continues even if the pointer leaves the bounds of the handle function onPointerDown(event: React.PointerEvent) { resizeHandleRef.current?.setPointerCapture(event.pointerId); + startResize(); } // This indicates that we're ready to start tracking the resize operation via the pointer @@ -791,7 +788,6 @@ const ResizeHandle = ({ parentNode, index, dispatch, nodeRefsAtom, siblingSi onGotPointerCapture={onPointerCapture} onLostPointerCapture={onPointerRelease} onPointerMove={handlePointerMove} - onPointerEnter={startResize} >
diff --git a/frontend/faraday/lib/layoutNode.ts b/frontend/faraday/lib/layoutNode.ts index e66e7bcf..ecb8908e 100644 --- a/frontend/faraday/lib/layoutNode.ts +++ b/frontend/faraday/lib/layoutNode.ts @@ -245,3 +245,7 @@ function findNextInsertLocationHelper( .sort((a, b) => Math.pow(a.depth, a.index + maxChildren) - Math.pow(b.depth, b.index + maxChildren)); return insertLocs[0]; } + +export function totalChildrenSize(node: LayoutNode): number { + return parseFloat(node.children?.reduce((partialSum, child) => partialSum + child.size, 0).toPrecision(5)); +}