fix resizeobserver loop

This commit is contained in:
grafixeyehero 2023-06-29 23:17:59 +03:00
parent 00ec92cc02
commit 5598f49c32
6 changed files with 103 additions and 18 deletions

62
package-lock.json generated
View file

@ -21,6 +21,7 @@
"@loadable/component": "5.15.3", "@loadable/component": "5.15.3",
"@mui/icons-material": "5.11.16", "@mui/icons-material": "5.11.16",
"@mui/material": "5.13.3", "@mui/material": "5.13.3",
"@react-hook/resize-observer": "1.2.6",
"@tanstack/react-query": "4.29.12", "@tanstack/react-query": "4.29.12",
"@tanstack/react-query-devtools": "4.29.12", "@tanstack/react-query-devtools": "4.29.12",
"blurhash": "2.0.5", "blurhash": "2.0.5",
@ -2980,6 +2981,11 @@
"@jridgewell/sourcemap-codec": "1.4.14" "@jridgewell/sourcemap-codec": "1.4.14"
} }
}, },
"node_modules/@juggle/resize-observer": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
},
"node_modules/@leichtgewicht/ip-codec": { "node_modules/@leichtgewicht/ip-codec": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
@ -3375,6 +3381,35 @@
"url": "https://opencollective.com/popperjs" "url": "https://opencollective.com/popperjs"
} }
}, },
"node_modules/@react-hook/latest": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@react-hook/latest/-/latest-1.0.3.tgz",
"integrity": "sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==",
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/@react-hook/passive-layout-effect": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz",
"integrity": "sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==",
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/@react-hook/resize-observer": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@react-hook/resize-observer/-/resize-observer-1.2.6.tgz",
"integrity": "sha512-DlBXtLSW0DqYYTW3Ft1/GQFZlTdKY5VAFIC4+km6IK5NiPPDFchGbEJm1j6pSgMqPRHbUQgHJX7RaR76ic1LWA==",
"dependencies": {
"@juggle/resize-observer": "^3.3.1",
"@react-hook/latest": "^1.0.2",
"@react-hook/passive-layout-effect": "^1.2.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/@remix-run/router": { "node_modules/@remix-run/router": {
"version": "1.6.1", "version": "1.6.1",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.1.tgz", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.1.tgz",
@ -22455,6 +22490,11 @@
"@jridgewell/sourcemap-codec": "1.4.14" "@jridgewell/sourcemap-codec": "1.4.14"
} }
}, },
"@juggle/resize-observer": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
},
"@leichtgewicht/ip-codec": { "@leichtgewicht/ip-codec": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
@ -22677,6 +22717,28 @@
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz",
"integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==" "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw=="
}, },
"@react-hook/latest": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@react-hook/latest/-/latest-1.0.3.tgz",
"integrity": "sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==",
"requires": {}
},
"@react-hook/passive-layout-effect": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz",
"integrity": "sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==",
"requires": {}
},
"@react-hook/resize-observer": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@react-hook/resize-observer/-/resize-observer-1.2.6.tgz",
"integrity": "sha512-DlBXtLSW0DqYYTW3Ft1/GQFZlTdKY5VAFIC4+km6IK5NiPPDFchGbEJm1j6pSgMqPRHbUQgHJX7RaR76ic1LWA==",
"requires": {
"@juggle/resize-observer": "^3.3.1",
"@react-hook/latest": "^1.0.2",
"@react-hook/passive-layout-effect": "^1.2.0"
}
},
"@remix-run/router": { "@remix-run/router": {
"version": "1.6.1", "version": "1.6.1",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.1.tgz", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.1.tgz",

View file

@ -77,6 +77,7 @@
"@loadable/component": "5.15.3", "@loadable/component": "5.15.3",
"@mui/icons-material": "5.11.16", "@mui/icons-material": "5.11.16",
"@mui/material": "5.13.3", "@mui/material": "5.13.3",
"@react-hook/resize-observer": "1.2.6",
"@tanstack/react-query": "4.29.12", "@tanstack/react-query": "4.29.12",
"@tanstack/react-query-devtools": "4.29.12", "@tanstack/react-query-devtools": "4.29.12",
"blurhash": "2.0.5", "blurhash": "2.0.5",

View file

@ -10,7 +10,6 @@ enum Direction {
} }
interface ScrollButtonsProps { interface ScrollButtonsProps {
scrollRef?: React.MutableRefObject<HTMLElement | null>;
scrollerFactoryRef: React.MutableRefObject<scrollerFactory | null>; scrollerFactoryRef: React.MutableRefObject<scrollerFactory | null>;
scrollState: { scrollState: {
scrollSize: number; scrollSize: number;

View file

@ -1,5 +1,6 @@
import React, { FC, useCallback, useEffect, useRef, useState } from 'react'; import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import useElementSize from 'hooks/useElementSize';
import layoutManager from '../../components/layoutManager'; import layoutManager from '../../components/layoutManager';
import dom from '../../scripts/dom'; import dom from '../../scripts/dom';
import browser from '../../scripts/browser'; import browser from '../../scripts/browser';
@ -32,14 +33,14 @@ const Scroller: FC<ScrollerProps> = ({
isAllowNativeSmoothScrollEnabled, isAllowNativeSmoothScrollEnabled,
children children
}) => { }) => {
const [scrollRef, size] = useElementSize();
const [showControls, setShowControls] = useState(false); const [showControls, setShowControls] = useState(false);
const [scrollState, setScrollState] = useState({ const [scrollState, setScrollState] = useState({
scrollSize: 0, scrollSize: size.width,
scrollPos: 0, scrollPos: 0,
scrollWidth: 0 scrollWidth: 0
}); });
const scrollRef = useRef<HTMLDivElement>(null);
const scrollerFactoryRef = useRef<scrollerFactory | null>(null); const scrollerFactoryRef = useRef<scrollerFactory | null>(null);
const getScrollSlider = useCallback(() => { const getScrollSlider = useCallback(() => {
@ -125,7 +126,7 @@ const Scroller: FC<ScrollerProps> = ({
}); });
}, [getScrollPosition, getScrollSize, getScrollWidth]); }, [getScrollPosition, getScrollSize, getScrollWidth]);
const initCenterFocus = useCallback((elem: EventTarget, scrollerInstance: scrollerFactory) => { const initCenterFocus = useCallback((elem, scrollerInstance: scrollerFactory) => {
dom.addEventListener(elem, 'focus', function (e: FocusEvent) { dom.addEventListener(elem, 'focus', function (e: FocusEvent) {
const focused = focusManager.focusableParent(e.target); const focused = focusManager.focusableParent(e.target);
if (focused) { if (focused) {
@ -150,15 +151,10 @@ const Scroller: FC<ScrollerProps> = ({
}, [scrollerFactoryRef]); }, [scrollerFactoryRef]);
useEffect(() => { useEffect(() => {
const scrollerElement = scrollRef.current as HTMLDivElement;
const horizontal = isHorizontalEnabled !== false; const horizontal = isHorizontalEnabled !== false;
const scrollbuttons = isScrollButtonsEnabled !== false; const scrollbuttons = isScrollButtonsEnabled !== false;
const mousewheel = isMouseWheelEnabled !== false; const mousewheel = isMouseWheelEnabled !== false;
const slider = scrollerElement.querySelector('.scrollSlider');
const scrollFrame = scrollerElement;
const enableScrollButtons = layoutManager.desktop && horizontal && scrollbuttons; const enableScrollButtons = layoutManager.desktop && horizontal && scrollbuttons;
const options = { const options = {
@ -166,7 +162,7 @@ const Scroller: FC<ScrollerProps> = ({
mouseDragging: 1, mouseDragging: 1,
mouseWheel: mousewheel, mouseWheel: mousewheel,
touchDragging: 1, touchDragging: 1,
slidee: slider, slidee: scrollRef.current?.querySelector('.scrollSlider'),
scrollBy: 200, scrollBy: 200,
speed: horizontal ? 270 : 240, speed: horizontal ? 270 : 240,
elasticBounds: 1, elasticBounds: 1,
@ -183,12 +179,12 @@ const Scroller: FC<ScrollerProps> = ({
}; };
// If just inserted it might not have any height yet - yes this is a hack // If just inserted it might not have any height yet - yes this is a hack
scrollerFactoryRef.current = new scrollerFactory(scrollFrame, options); scrollerFactoryRef.current = new scrollerFactory(scrollRef.current, options);
scrollerFactoryRef.current.init(); scrollerFactoryRef.current.init();
scrollerFactoryRef.current.reload(); scrollerFactoryRef.current.reload();
if (layoutManager.tv && isCenterFocusEnabled) { if (layoutManager.tv && isCenterFocusEnabled) {
initCenterFocus(scrollerElement, scrollerFactoryRef.current); initCenterFocus(scrollRef.current, scrollerFactoryRef.current);
} }
if (enableScrollButtons) { if (enableScrollButtons) {
@ -200,9 +196,8 @@ const Scroller: FC<ScrollerProps> = ({
} }
return () => { return () => {
const scrollerInstance = scrollerFactoryRef.current; if (scrollerFactoryRef.current) {
if (scrollerInstance) { scrollerFactoryRef.current.destroy();
scrollerInstance.destroy();
scrollerFactoryRef.current = null; scrollerFactoryRef.current = null;
} }
@ -223,7 +218,8 @@ const Scroller: FC<ScrollerProps> = ({
isScrollEventEnabled, isScrollEventEnabled,
isSkipFocusWhenVisibleEnabled, isSkipFocusWhenVisibleEnabled,
onScroll, onScroll,
removeScrollEventListener removeScrollEventListener,
scrollRef
]); ]);
return ( return (
@ -231,7 +227,6 @@ const Scroller: FC<ScrollerProps> = ({
{ {
showControls && scrollState.scrollWidth > scrollState.scrollSize + 20 showControls && scrollState.scrollWidth > scrollState.scrollSize + 20
&& <ScrollButtons && <ScrollButtons
scrollRef={scrollRef}
scrollerFactoryRef={scrollerFactoryRef} scrollerFactoryRef={scrollerFactoryRef}
scrollState={scrollState} scrollState={scrollState}
/> />

View file

@ -0,0 +1,25 @@
import { MutableRefObject, useLayoutEffect, useRef, useState } from 'react';
import useResizeObserver from '@react-hook/resize-observer';
interface Size {
width: number;
height: number;
}
export default function useElementSize<
T extends HTMLElement = HTMLDivElement
>(): [MutableRefObject<T | null>, Size] {
const target = useRef<T | null>(null);
const [size, setSize] = useState<Size>({
width: 0,
height: 0
});
useLayoutEffect(() => {
target.current && setSize(target.current.getBoundingClientRect());
}, [target]);
useResizeObserver(target, (entry) => setSize(entry.contentRect));
return [target, size];
}

View file

@ -166,6 +166,9 @@ const config = {
path.resolve(__dirname, 'node_modules/event-target-polyfill'), path.resolve(__dirname, 'node_modules/event-target-polyfill'),
path.resolve(__dirname, 'node_modules/rvfc-polyfill'), path.resolve(__dirname, 'node_modules/rvfc-polyfill'),
path.resolve(__dirname, 'node_modules/@jellyfin/sdk'), path.resolve(__dirname, 'node_modules/@jellyfin/sdk'),
path.resolve(__dirname, 'node_modules/@react-hook/latest'),
path.resolve(__dirname, 'node_modules/@react-hook/passive-layout-effect'),
path.resolve(__dirname, 'node_modules/@react-hook/resize-observer'),
path.resolve(__dirname, 'node_modules/@remix-run/router'), path.resolve(__dirname, 'node_modules/@remix-run/router'),
path.resolve(__dirname, 'node_modules/@tanstack/query-core'), path.resolve(__dirname, 'node_modules/@tanstack/query-core'),
path.resolve(__dirname, 'node_modules/@tanstack/react-query'), path.resolve(__dirname, 'node_modules/@tanstack/react-query'),