diff --git a/.changeset/smooth-donuts-exist.md b/.changeset/smooth-donuts-exist.md new file mode 100644 index 000000000..25ff18017 --- /dev/null +++ b/.changeset/smooth-donuts-exist.md @@ -0,0 +1,5 @@ +--- +'@cube-dev/ui-kit': patch +--- + +Prevent ResizablePanel from infinite switching state loop in controllable mode. diff --git a/src/components/layout/ResizablePanel.stories.tsx b/src/components/layout/ResizablePanel.stories.tsx index ad3851b67..4e60f8820 100644 --- a/src/components/layout/ResizablePanel.stories.tsx +++ b/src/components/layout/ResizablePanel.stories.tsx @@ -44,7 +44,6 @@ const TemplateTop: StoryFn = (args) => { const TemplateControllable: StoryFn = (args) => { const [size, setSize] = useState(200); - return ( (null); - useEffect(() => { - if (ref.current) { - const offsetProp = isHorizontal ? 'offsetWidth' : 'offsetHeight'; - const containerSize = ref.current[offsetProp]; - - if (Math.abs(containerSize - size) < 1 && !isDisabled) { - setVisualSize(size); - } else { - setVisualSize(containerSize); - } - } - }, [size, isDisabled]); - let { moveProps } = useMove({ onMoveStart(e) { if (isDisabled) { @@ -258,7 +246,7 @@ function ResizablePanel( ? e.deltaX * (direction === 'right' ? 1 : -1) : e.deltaY * (direction === 'bottom' ? 1 : -1); - return clamp(size); + return size; }); }, onMoveEnd(e) { @@ -267,16 +255,44 @@ function ResizablePanel( }, }); + // Since we sync provided size and the local one in two ways + // we need a way to prevent infinite loop in some cases. + // We will run this in setTimeout and make sure it will get the most recent state. + const notifyChange = useEvent(() => { + setSize((size) => { + if (providedSize && Math.abs(providedSize - size) > 0.5) { + return providedSize; + } + + return size; + }); + }); + useEffect(() => { - if (providedSize == null || Math.abs(providedSize - size) > 0.5) { - onSizeChange?.(Math.round(size)); + if (ref.current) { + const offsetProp = isHorizontal ? 'offsetWidth' : 'offsetHeight'; + const containerSize = ref.current[offsetProp]; + + if (Math.abs(containerSize - size) < 1 && !isDisabled) { + setVisualSize(size); + } else { + setVisualSize(containerSize); + } } - }, [size]); + }, [size, isDisabled]); useEffect(() => { - if (providedSize && Math.abs(providedSize - size) > 0.5) { - setSize(clamp(providedSize)); + if ( + !isDragging && + visualSize != null && + (providedSize == null || Math.abs(providedSize - visualSize) > 0.5) + ) { + onSizeChange?.(Math.round(visualSize)); } + }, [visualSize]); + + useEffect(() => { + setTimeout(notifyChange); }, [providedSize]); const mods = useMemo(() => {