diff --git a/src/admin/AdminBatchUploadActions.tsx b/src/admin/AdminBatchUploadActions.tsx index a2b6e8bd..f6dbacb3 100644 --- a/src/admin/AdminBatchUploadActions.tsx +++ b/src/admin/AdminBatchUploadActions.tsx @@ -161,7 +161,7 @@ export default function AdminBatchUploadActions({ readOnly={isAdding} /> setFormData(data => ({ ...data, favorite }))} diff --git a/src/components/MaskedScroll.tsx b/src/components/MaskedScroll.tsx index a83550c2..ebbef434 100644 --- a/src/components/MaskedScroll.tsx +++ b/src/components/MaskedScroll.tsx @@ -9,14 +9,14 @@ export default function MaskedScroll({ setMaxSize, hideScrollbar, updateMaskOnEvents, - updateMaskAfterDelay, scrollToEndOnMount, style, children, ...props -}: HTMLAttributes & -Omit[0], 'ref'> & -{ ref?: RefObject }) { +}: { + ref?: RefObject +} & HTMLAttributes +& Omit[0], 'ref'>) { const refInternal = useRef(null); const ref = refProp ?? refInternal; @@ -28,7 +28,6 @@ Omit[0], 'ref'> & setMaxSize, hideScrollbar, updateMaskOnEvents, - updateMaskAfterDelay, scrollToEndOnMount, }); diff --git a/src/components/SelectMenu.tsx b/src/components/SelectMenu.tsx index 6d60da75..9156b62e 100644 --- a/src/components/SelectMenu.tsx +++ b/src/components/SelectMenu.tsx @@ -140,7 +140,7 @@ export default function SelectMenu({ tabIndex={tabIndex} className={clsx( 'cursor-pointer control pl-1.5 py-2', - 'flex items-center w-full h-9.5', + 'flex items-center w-full h-10', 'focus:outline-2 -outline-offset-2 focus:outline-blue-600', 'select-none', Boolean(error) && 'error', diff --git a/src/components/useMaskedScroll.ts b/src/components/useMaskedScroll.ts index b2d431de..6ad40eaa 100644 --- a/src/components/useMaskedScroll.ts +++ b/src/components/useMaskedScroll.ts @@ -1,3 +1,4 @@ +import useElementHeight from '@/utility/useElementHeight'; import { CSSProperties, RefObject, @@ -5,6 +6,7 @@ import { useEffect, useMemo, } from 'react'; +import { useDebouncedCallback } from 'use-debounce'; const CSS_VAR_MASK_COLOR_START = '--mask-color-start'; const CSS_VAR_MASK_COLOR_END = '--mask-color-end'; @@ -18,7 +20,6 @@ export default function useMaskedScroll({ hideScrollbar = true, // Disable when calling 'updateMask' explicitly updateMaskOnEvents = true, - updateMaskAfterDelay = 0, scrollToEndOnMount, }: { ref: RefObject @@ -28,12 +29,13 @@ export default function useMaskedScroll({ animationDuration?: number setMaxSize?: boolean hideScrollbar?: boolean - updateMaskAfterDelay?: number scrollToEndOnMount?: boolean }) { const isVertical = direction === 'vertical'; - const updateMask = useCallback(() => { + const containerHeight = useElementHeight(containerRef); + + const _updateMask = useCallback(() => { const ref = containerRef?.current; if (ref) { const start = isVertical @@ -51,7 +53,9 @@ export default function useMaskedScroll({ } }, [containerRef, isVertical]); - // Conditionally track events + const updateMask = useDebouncedCallback(_updateMask, 50, { leading: true }); + + // Update on scroll/resize useEffect(() => { const ref = containerRef?.current; if (ref && updateMaskOnEvents) { @@ -64,18 +68,19 @@ export default function useMaskedScroll({ } }, [containerRef, updateMask, updateMaskOnEvents]); - // Update on mount + // Update on container height change useEffect(() => { - updateMask(); - }, [updateMask]); - - // Update after delay - useEffect(() => { - if (updateMaskAfterDelay) { - const timeout = setTimeout(updateMask, updateMaskAfterDelay); - return () => clearTimeout(timeout); + if (updateMaskOnEvents) { + updateMask(); } - }, [containerRef, updateMask, updateMaskAfterDelay]); + }, [containerHeight, updateMaskOnEvents, updateMask]); + + // Update on mount when not responding to events + useEffect(() => { + if (!updateMaskOnEvents) { + updateMask(); + } + }, [updateMask, updateMaskOnEvents]); useEffect(() => { const ref = containerRef?.current; diff --git a/src/photo/PhotoGridPageClient.tsx b/src/photo/PhotoGridPageClient.tsx index 7dfab9b1..a2b765aa 100644 --- a/src/photo/PhotoGridPageClient.tsx +++ b/src/photo/PhotoGridPageClient.tsx @@ -55,7 +55,6 @@ export default function PhotoGridPageClient({ )} fadeSize={100} setMaxSize={false} - updateMaskAfterDelay={500} > , + shouldDebounce = true, ) { const [height, setHeight] = useState(ref.current?.clientHeight); + const setHeightDebounced = + useDebouncedCallback(setHeight, 250, { leading: true }); + useEffect(() => { - const handleResize = () => setHeight(ref.current?.clientHeight); - handleResize(); - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); - }, [ref]); + if (ref.current) { + const observer = new ResizeObserver(e => { + if (shouldDebounce) { + setHeightDebounced(e[0].contentRect.height); + } else { + setHeight(e[0].contentRect.height); + } + }); + observer.observe(ref.current); + return () => observer.disconnect(); + } + }, [ref, setHeightDebounced, shouldDebounce]); return height; }