diff --git a/lib/Paneset/PaneResizeContainer.js b/lib/Paneset/PaneResizeContainer.js index e959244af..3efb481fc 100644 --- a/lib/Paneset/PaneResizeContainer.js +++ b/lib/Paneset/PaneResizeContainer.js @@ -1,4 +1,4 @@ -import React, { useState, useRef, useEffect, useCallback } from 'react'; +import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react'; import PropTypes from 'prop-types'; import cloneDeep from 'lodash/cloneDeep'; import isEqual from 'lodash/isEqual'; @@ -13,7 +13,7 @@ const PaneResizeContainer = ({ isRoot, children, onElementResize, parentElement, const [cursorX, setCursorX] = useState(0); const [handles, setHandles] = useState([]); const [activeHandle, setActiveHandle] = useState(null); - const [update, setUpdate] = useState(0); // eslint-disable-line no-unused-vars + const [update, setUpdate] = useState(0); const [blocking, setBlocking] = useState(null); let container = useRef(); if (resizeContainerRef) { @@ -142,9 +142,8 @@ const PaneResizeContainer = ({ isRoot, children, onElementResize, parentElement, document.removeEventListener('mousemove', updateCursor); document.removeEventListener('mouseup', handleMouseUp); document.body.style.userSelect = 'auto'; - document.body.style.webkitUserSelect = 'auto'; - document.body.style.msUserSelect = 'auto'; document.body.style.msUserSelect = 'auto'; + document.body.style.mozUserSelect = 'auto'; setDragging(false); }, [updateCursor]); @@ -158,7 +157,6 @@ const PaneResizeContainer = ({ isRoot, children, onElementResize, parentElement, document.addEventListener('mousemove', updateCursor); document.addEventListener('mouseup', handleMouseUp); document.body.style.userSelect = 'none'; - document.body.style.webkitUserSelect = 'none'; document.body.style.msUserSelect = 'none'; document.body.style.mozUserSelect = 'none'; }, [handleMouseUp, handles, updateCursor]); @@ -184,37 +182,48 @@ const PaneResizeContainer = ({ isRoot, children, onElementResize, parentElement, }); }; - const renderHandles = useCallback(() => { + // Reads the DOM (forces layout) to locate each handle. Must only rerun when the handle set, + // container/window size, or blocking state changes - never merely because cursorX/dragging/ + // activeHandle changed during a drag, since handles don't move until the drag ends. + const computeHandlePositions = useCallback(() => { handleData.current = []; - const handleElements = handles.map((h) => { + return handles.map((h) => { const rect = h.getRef()?.current?.getBoundingClientRect(); - if (rect) { - if (rect.left === 0) { - handleData.current.push({ id: h.id, elementId: h.elementId, x: 0, y: 50 }); - return false; - } + if (!rect) return null; - if (!containerRect.current) { - containerRect.current = container.current?.getBoundingClientRect(); - } - const newLeft = rect.left - (containerRect.current ? containerRect.current.left : 0); - const top = rect.top - (containerRect.current ? containerRect.current.top : 0); - handleData.current.push({ id: h.id, elementId: h.elementId, x: newLeft, y: top }); - return ( - - ); + if (rect.left === 0) { + handleData.current.push({ id: h.id, elementId: h.elementId, x: 0, y: 50 }); + return { id: h.id, render: false }; + } + + if (!containerRect.current) { + containerRect.current = container.current?.getBoundingClientRect(); } - return null; + const newLeft = rect.left - (containerRect.current ? containerRect.current.left : 0); + const top = rect.top - (containerRect.current ? containerRect.current.top : 0); + handleData.current.push({ id: h.id, elementId: h.elementId, x: newLeft, y: top }); + return { id: h.id, x: newLeft, render: true }; }); + }, [handles]); // eslint-disable-line react-hooks/exhaustive-deps + + const handlePositions = useMemo( + () => (blocking ? [] : computeHandlePositions()), + [computeHandlePositions, update, blocking] + ); - return handleElements; - }, [activeHandle, handleHandleMouseDown, handles]); + // No DOM reads here, so this is cheap to rerun on every render (mousedown/mousemove/mouseup). + const renderHandles = () => handlePositions.map((pos) => { + if (!pos || !pos.render) return null; + return ( + + ); + }); // this is the equivalent of `forceUpdate` for functional components. const updateHandle = () => { diff --git a/lib/Paneset/Paneset.js b/lib/Paneset/Paneset.js index 838dbbd75..330403c2e 100644 --- a/lib/Paneset/Paneset.js +++ b/lib/Paneset/Paneset.js @@ -374,33 +374,7 @@ class Paneset extends React.Component { ]); } - setStyle = () => { - if (this.isThisMounted()) { - const { - paneset, - isRoot - } = this.props; - - // for nested, non-root panesets, we resize their element to the edge of the screen. - // this resolves a cropping behavior (impetus for STCOM-953) and allows - // paneset to accurately resize its children (overflow: hidden) - if (paneset && !isRoot) { - const containerBounds = this.container.current?.getBoundingClientRect(); - if (containerBounds.right < window.offsetWidth) { - this.setState((curState) => { - const { - left, right - } = containerBounds; - const newStyle = { flex: `0 0 ${right - left}px` }; - return { - changeType: 'nested-resize-end', - style: { ...curState.style, ...newStyle } - }; - }); - } - } - } - } + setStyle = () => {} resizePanes = (panes, widths) => { if (widths !== null) {