Improve masked scroll in form controls
This commit is contained in:
parent
91562e6523
commit
781ff098b1
@ -161,7 +161,7 @@ export default function AdminBatchUploadActions({
|
||||
readOnly={isAdding}
|
||||
/>
|
||||
<FieldsetFavs
|
||||
className="my-6"
|
||||
className="pt-2.5 pb-2"
|
||||
value={formData.favorite ?? 'false'}
|
||||
onChange={favorite =>
|
||||
setFormData(data => ({ ...data, favorite }))}
|
||||
|
||||
@ -9,14 +9,14 @@ export default function MaskedScroll({
|
||||
setMaxSize,
|
||||
hideScrollbar,
|
||||
updateMaskOnEvents,
|
||||
updateMaskAfterDelay,
|
||||
scrollToEndOnMount,
|
||||
style,
|
||||
children,
|
||||
...props
|
||||
}: HTMLAttributes<HTMLDivElement> &
|
||||
Omit<Parameters<typeof useMaskedScroll>[0], 'ref'> &
|
||||
{ ref?: RefObject<HTMLDivElement | null> }) {
|
||||
}: {
|
||||
ref?: RefObject<HTMLDivElement | null>
|
||||
} & HTMLAttributes<HTMLDivElement>
|
||||
& Omit<Parameters<typeof useMaskedScroll>[0], 'ref'>) {
|
||||
const refInternal = useRef<HTMLDivElement>(null);
|
||||
const ref = refProp ?? refInternal;
|
||||
|
||||
@ -28,7 +28,6 @@ Omit<Parameters<typeof useMaskedScroll>[0], 'ref'> &
|
||||
setMaxSize,
|
||||
hideScrollbar,
|
||||
updateMaskOnEvents,
|
||||
updateMaskAfterDelay,
|
||||
scrollToEndOnMount,
|
||||
});
|
||||
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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<HTMLDivElement | null>
|
||||
@ -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;
|
||||
|
||||
@ -55,7 +55,6 @@ export default function PhotoGridPageClient({
|
||||
)}
|
||||
fadeSize={100}
|
||||
setMaxSize={false}
|
||||
updateMaskAfterDelay={500}
|
||||
>
|
||||
<PhotoGridSidebar {...{
|
||||
...categories,
|
||||
|
||||
@ -1,16 +1,28 @@
|
||||
import { useState, RefObject, useEffect } from 'react';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
|
||||
export default function useElementHeight(
|
||||
ref: RefObject<HTMLElement | null>,
|
||||
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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user