From 2644b145b261baa80abc151b0cd394b38ebc3d2b Mon Sep 17 00:00:00 2001 From: Cody Bennett Date: Thu, 30 Jan 2025 06:14:06 -0600 Subject: [PATCH 1/5] fix: upstream use-measure --- example/src/demos/SVGRenderer.tsx | 2 +- packages/fiber/package.json | 3 +- packages/fiber/src/web/Canvas.tsx | 2 +- packages/fiber/src/web/use-measure.ts | 222 -------------------------- yarn.lock | 15 +- 5 files changed, 8 insertions(+), 236 deletions(-) delete mode 100644 packages/fiber/src/web/use-measure.ts diff --git a/example/src/demos/SVGRenderer.tsx b/example/src/demos/SVGRenderer.tsx index dc632ab11e..dac92110d1 100644 --- a/example/src/demos/SVGRenderer.tsx +++ b/example/src/demos/SVGRenderer.tsx @@ -9,7 +9,7 @@ import { events, ReconcilerRoot, } from '@react-three/fiber' -import { useMeasure, Options as ResizeOptions } from '../../../packages/fiber/src/web/use-measure' +import useMeasure, { Options as ResizeOptions } from 'react-use-measure' import { SVGRenderer } from 'three-stdlib' function TorusKnot() { diff --git a/packages/fiber/package.json b/packages/fiber/package.json index 703eaff8c7..a2714c7a34 100644 --- a/packages/fiber/package.json +++ b/packages/fiber/package.json @@ -43,14 +43,13 @@ }, "dependencies": { "@babel/runtime": "^7.17.8", - "@types/debounce": "^1.2.1", "@types/react-reconciler": "^0.26.7", "@types/webxr": "*", "base64-js": "^1.5.1", "buffer": "^6.0.3", - "debounce": "^1.2.1", "its-fine": "^1.0.6", "react-reconciler": "^0.27.0", + "react-use-measure": "^2.1.3", "scheduler": "^0.21.0", "suspend-react": "^0.1.3", "zustand": "^3.7.1" diff --git a/packages/fiber/src/web/Canvas.tsx b/packages/fiber/src/web/Canvas.tsx index 6384921275..82ecd8e6f2 100644 --- a/packages/fiber/src/web/Canvas.tsx +++ b/packages/fiber/src/web/Canvas.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as THREE from 'three' -import { useMeasure, Options as ResizeOptions } from './use-measure' +import useMeasure, { Options as ResizeOptions } from 'react-use-measure' import { useContextBridge, FiberProvider } from 'its-fine' import { isRef, SetBlock, Block, ErrorBoundary, useMutableCallback, useIsomorphicLayoutEffect } from '../core/utils' import { ReconcilerRoot, extend, createRoot, unmountComponentAtNode, RenderProps } from '../core' diff --git a/packages/fiber/src/web/use-measure.ts b/packages/fiber/src/web/use-measure.ts deleted file mode 100644 index cc5ffef13c..0000000000 --- a/packages/fiber/src/web/use-measure.ts +++ /dev/null @@ -1,222 +0,0 @@ -/* eslint-disable react-hooks/rules-of-hooks */ -import { useEffect, useState, useRef, useMemo } from 'react' -import createDebounce from 'debounce' - -declare type ResizeObserverCallback = (entries: any[], observer: ResizeObserver) => void -declare class ResizeObserver { - constructor(callback: ResizeObserverCallback) - observe(target: Element, options?: any): void - unobserve(target: Element): void - disconnect(): void - static toString(): string -} - -export interface RectReadOnly { - readonly x: number - readonly y: number - readonly width: number - readonly height: number - readonly top: number - readonly right: number - readonly bottom: number - readonly left: number - [key: string]: number -} - -type HTMLOrSVGElement = HTMLElement | SVGElement - -type Result = [(element: HTMLOrSVGElement | null) => void, RectReadOnly, () => void] - -type State = { - element: HTMLOrSVGElement | null - scrollContainers: HTMLOrSVGElement[] | null - resizeObserver: ResizeObserver | null - lastBounds: RectReadOnly - orientationHandler: null | (() => void) -} - -export type Options = { - debounce?: number | { scroll: number; resize: number } - scroll?: boolean - polyfill?: { new (cb: ResizeObserverCallback): ResizeObserver } - offsetSize?: boolean -} - -export function useMeasure( - { debounce, scroll, polyfill, offsetSize }: Options = { debounce: 0, scroll: false, offsetSize: false }, -): Result { - const ResizeObserver = polyfill || (typeof window !== 'undefined' && (window as any).ResizeObserver) - - const [bounds, set] = useState({ - left: 0, - top: 0, - width: 0, - height: 0, - bottom: 0, - right: 0, - x: 0, - y: 0, - }) - - // In test mode - if (!ResizeObserver) { - // @ts-ignore - bounds.width = 1280 - // @ts-ignore - bounds.height = 800 - return [() => {}, bounds, () => {}] - } - - // keep all state in a ref - const state = useRef({ - element: null, - scrollContainers: null, - resizeObserver: null, - lastBounds: bounds, - orientationHandler: null, - }) - - // set actual debounce values early, so effects know if they should react accordingly - const scrollDebounce = debounce ? (typeof debounce === 'number' ? debounce : debounce.scroll) : null - const resizeDebounce = debounce ? (typeof debounce === 'number' ? debounce : debounce.resize) : null - - // make sure to update state only as long as the component is truly mounted - const mounted = useRef(false) - useEffect(() => { - mounted.current = true - return () => void (mounted.current = false) - }) - - // memoize handlers, so event-listeners know when they should update - const [forceRefresh, resizeChange, scrollChange] = useMemo(() => { - const callback = () => { - if (!state.current.element) return - const { left, top, width, height, bottom, right, x, y } = - state.current.element.getBoundingClientRect() as unknown as RectReadOnly - - const size = { - left, - top, - width, - height, - bottom, - right, - x, - y, - } - - if (state.current.element instanceof HTMLElement && offsetSize) { - size.height = state.current.element.offsetHeight - size.width = state.current.element.offsetWidth - } - - Object.freeze(size) - if (mounted.current && !areBoundsEqual(state.current.lastBounds, size)) set((state.current.lastBounds = size)) - } - return [ - callback, - resizeDebounce ? createDebounce(callback, resizeDebounce) : callback, - scrollDebounce ? createDebounce(callback, scrollDebounce) : callback, - ] - }, [set, offsetSize, scrollDebounce, resizeDebounce]) - - // cleanup current scroll-listeners / observers - function removeListeners() { - if (state.current.scrollContainers) { - state.current.scrollContainers.forEach((element) => element.removeEventListener('scroll', scrollChange, true)) - state.current.scrollContainers = null - } - - if (state.current.resizeObserver) { - state.current.resizeObserver.disconnect() - state.current.resizeObserver = null - } - - if (state.current.orientationHandler) { - if ('orientation' in screen && 'removeEventListener' in screen.orientation) { - screen.orientation.removeEventListener('change', state.current.orientationHandler) - } else if ('onorientationchange' in window) { - window.removeEventListener('orientationchange', state.current.orientationHandler) - } - } - } - - // add scroll-listeners / observers - function addListeners() { - if (!state.current.element) return - state.current.resizeObserver = new ResizeObserver(resizeChange) - state.current.resizeObserver?.observe(state.current.element) - if (scroll && state.current.scrollContainers) { - state.current.scrollContainers.forEach((scrollContainer) => - scrollContainer.addEventListener('scroll', scrollChange, { capture: true, passive: true }), - ) - } - - // Handle orientation changes - state.current.orientationHandler = () => { - scrollChange() - } - - // Use screen.orientation if available - if ('orientation' in screen && 'addEventListener' in screen.orientation) { - screen.orientation.addEventListener('change', state.current.orientationHandler) - } else if ('onorientationchange' in window) { - // Fallback to orientationchange event - window.addEventListener('orientationchange', state.current.orientationHandler) - } - } - - // the ref we expose to the user - const ref = (node: HTMLOrSVGElement | null) => { - if (!node || node === state.current.element) return - removeListeners() - state.current.element = node - state.current.scrollContainers = findScrollContainers(node) - addListeners() - } - - // add general event listeners - useOnWindowScroll(scrollChange, Boolean(scroll)) - useOnWindowResize(resizeChange) - - // respond to changes that are relevant for the listeners - useEffect(() => { - removeListeners() - addListeners() - }, [scroll, scrollChange, resizeChange]) - - // remove all listeners when the components unmounts - useEffect(() => removeListeners, []) - return [ref, bounds, forceRefresh] -} - -// Adds native resize listener to window -function useOnWindowResize(onWindowResize: (event: Event) => void) { - useEffect(() => { - const cb = onWindowResize - window.addEventListener('resize', cb) - return () => void window.removeEventListener('resize', cb) - }, [onWindowResize]) -} -function useOnWindowScroll(onScroll: () => void, enabled: boolean) { - useEffect(() => { - if (enabled) { - const cb = onScroll - window.addEventListener('scroll', cb, { capture: true, passive: true }) - return () => void window.removeEventListener('scroll', cb, true) - } - }, [onScroll, enabled]) -} - -// Returns a list of scroll offsets -function findScrollContainers(element: HTMLOrSVGElement | null): HTMLOrSVGElement[] { - const result: HTMLOrSVGElement[] = [] - if (!element || element === document.body) return result - const { overflow, overflowX, overflowY } = window.getComputedStyle(element) - if ([overflow, overflowX, overflowY].some((prop) => prop === 'auto' || prop === 'scroll')) result.push(element) - return [...result, ...findScrollContainers(element.parentElement)] -} - -// Checks if element boundaries are equal -const keys: (keyof RectReadOnly)[] = ['x', 'y', 'top', 'bottom', 'left', 'right', 'width', 'height'] -const areBoundsEqual = (a: RectReadOnly, b: RectReadOnly): boolean => keys.every((key) => a[key] === b[key]) diff --git a/yarn.lock b/yarn.lock index 56c9a1abf3..7c4d164806 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2815,11 +2815,6 @@ dependencies: "@babel/types" "^7.3.0" -"@types/debounce@^1.2.1": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/debounce/-/debounce-1.2.4.tgz#cb7e85d9ad5ababfac2f27183e8ac8b576b2abb3" - integrity sha512-jBqiORIzKDOToaF63Fm//haOCHuwQuLa2202RK4MozpA6lh93eCBc+/8+wZn5OzjJt3ySdc+74SXWXB55Ewtyw== - "@types/draco3d@^1.4.0": version "1.4.2" resolved "https://registry.yarnpkg.com/@types/draco3d/-/draco3d-1.4.2.tgz#7faccb809db2a5e19b9efb97c5f2eb9d64d527ea" @@ -4447,11 +4442,6 @@ dayjs@^1.8.15: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.1.tgz#90b33a3dda3417258d48ad2771b415def6545eb0" integrity sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA== -debounce@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" - integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== - debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -8916,6 +8906,11 @@ react-test-renderer@^18.0.0: react-shallow-renderer "^16.13.1" scheduler "^0.21.0" +react-use-measure@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.1.3.tgz#75b7bd009c45459a431762812d23a8e170ef362e" + integrity sha512-uvtMw9shJoNn2A2ZEqa7frw+29Mh0CBa7MZ8kltuW60fpBRqeeg0SDTTM3hQc0ldfpxkUIh6GEPYl3/h1OcnTg== + react-use-refs@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/react-use-refs/-/react-use-refs-1.0.1.tgz#44cab5f4764b3fa4a112189c0058fc8752d1eb2c" From a9b6af6ed2fda98fd1b2131b785228a0a3982ac4 Mon Sep 17 00:00:00 2001 From: Cody Bennett Date: Thu, 30 Jan 2025 06:25:09 -0600 Subject: [PATCH 2/5] chore: update deps --- packages/fiber/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/fiber/package.json b/packages/fiber/package.json index a2714c7a34..74ce0d870e 100644 --- a/packages/fiber/package.json +++ b/packages/fiber/package.json @@ -49,7 +49,7 @@ "buffer": "^6.0.3", "its-fine": "^1.0.6", "react-reconciler": "^0.27.0", - "react-use-measure": "^2.1.3", + "react-use-measure": "^2.1.4", "scheduler": "^0.21.0", "suspend-react": "^0.1.3", "zustand": "^3.7.1" diff --git a/yarn.lock b/yarn.lock index 7c4d164806..d69d22d7bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8906,10 +8906,10 @@ react-test-renderer@^18.0.0: react-shallow-renderer "^16.13.1" scheduler "^0.21.0" -react-use-measure@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.1.3.tgz#75b7bd009c45459a431762812d23a8e170ef362e" - integrity sha512-uvtMw9shJoNn2A2ZEqa7frw+29Mh0CBa7MZ8kltuW60fpBRqeeg0SDTTM3hQc0ldfpxkUIh6GEPYl3/h1OcnTg== +react-use-measure@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.1.4.tgz#55a621d47170cc8d1de531adc1812a2c60c154f9" + integrity sha512-JlljMo2MEDQkDl4XoB2CnQO5T0bqlUWB4laEbwa73pZj34xLNzz9+hrWY+F+V/Z+KTb0YjvGKQ9w5YUO2EPgvA== react-use-refs@^1.0.1: version "1.0.1" From 4d24a1a0fce373e043061da7833e1b484aa0a55e Mon Sep 17 00:00:00 2001 From: Cody Bennett Date: Thu, 30 Jan 2025 06:40:08 -0600 Subject: [PATCH 3/5] chore: update deps --- packages/fiber/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/fiber/package.json b/packages/fiber/package.json index 74ce0d870e..c595134590 100644 --- a/packages/fiber/package.json +++ b/packages/fiber/package.json @@ -49,7 +49,7 @@ "buffer": "^6.0.3", "its-fine": "^1.0.6", "react-reconciler": "^0.27.0", - "react-use-measure": "^2.1.4", + "react-use-measure": "^2.1.5", "scheduler": "^0.21.0", "suspend-react": "^0.1.3", "zustand": "^3.7.1" diff --git a/yarn.lock b/yarn.lock index d69d22d7bd..529229d22b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8906,10 +8906,10 @@ react-test-renderer@^18.0.0: react-shallow-renderer "^16.13.1" scheduler "^0.21.0" -react-use-measure@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.1.4.tgz#55a621d47170cc8d1de531adc1812a2c60c154f9" - integrity sha512-JlljMo2MEDQkDl4XoB2CnQO5T0bqlUWB4laEbwa73pZj34xLNzz9+hrWY+F+V/Z+KTb0YjvGKQ9w5YUO2EPgvA== +react-use-measure@^2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.1.5.tgz#fe81eb66e0c3bcfef5ea170cf3d52144b12f48dc" + integrity sha512-o/OJti/d964e4l/wpFTO7lcFVvnt79XAEsuglEd+OsdiE3DKfYLzV/txC/mm8KyX6Ec24w/3Lzx3mHRcWGWTaQ== react-use-refs@^1.0.1: version "1.0.1" From b0538d59be80cbefd550cd24045a1bfd5f1cde48 Mon Sep 17 00:00:00 2001 From: Cody Bennett Date: Thu, 30 Jan 2025 06:47:06 -0600 Subject: [PATCH 4/5] chore: update deps --- packages/fiber/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fiber/package.json b/packages/fiber/package.json index c595134590..2fc74b4e81 100644 --- a/packages/fiber/package.json +++ b/packages/fiber/package.json @@ -49,7 +49,7 @@ "buffer": "^6.0.3", "its-fine": "^1.0.6", "react-reconciler": "^0.27.0", - "react-use-measure": "^2.1.5", + "react-use-measure": "^2.1.6", "scheduler": "^0.21.0", "suspend-react": "^0.1.3", "zustand": "^3.7.1" From 212c377a55a6d7aa7d927077bf7c3d96acb27910 Mon Sep 17 00:00:00 2001 From: Cody Bennett Date: Thu, 30 Jan 2025 06:47:11 -0600 Subject: [PATCH 5/5] chore: update lockfile --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 529229d22b..5b9b11011d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8906,10 +8906,10 @@ react-test-renderer@^18.0.0: react-shallow-renderer "^16.13.1" scheduler "^0.21.0" -react-use-measure@^2.1.5: - version "2.1.5" - resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.1.5.tgz#fe81eb66e0c3bcfef5ea170cf3d52144b12f48dc" - integrity sha512-o/OJti/d964e4l/wpFTO7lcFVvnt79XAEsuglEd+OsdiE3DKfYLzV/txC/mm8KyX6Ec24w/3Lzx3mHRcWGWTaQ== +react-use-measure@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.1.6.tgz#d0dc826b2020361da8323adcd39f72a8191f7a19" + integrity sha512-IobUVRLFrtrpUX2RMDrdAfRTaEftVRXFlTEuTdJFgtrnbxAMhueimO7nFix4/X40KI8LnJFV7LHxPnMi9Tc1Cw== react-use-refs@^1.0.1: version "1.0.1"