From 29c3c7f1676925cdc2eed7ee95370b6260e912ec Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Wed, 26 Mar 2025 22:34:50 -0500 Subject: [PATCH] Decouple faded scroll component from behavior --- src/components/FadedScroll.tsx | 53 +++++--------------------- src/components/cmdk/CommandKClient.tsx | 20 +++++----- src/components/useFadedScroll.ts | 38 ++++++++++++++++++ 3 files changed, 58 insertions(+), 53 deletions(-) create mode 100644 src/components/useFadedScroll.ts diff --git a/src/components/FadedScroll.tsx b/src/components/FadedScroll.tsx index 4e940027..d16fd9b3 100644 --- a/src/components/FadedScroll.tsx +++ b/src/components/FadedScroll.tsx @@ -1,20 +1,15 @@ import clsx from 'clsx/lite'; -import { - HTMLAttributes, - RefObject, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { HTMLAttributes, RefObject } from 'react'; +import useFadedScroll from './useFadedScroll'; export default function FadedScroll({ - ref: containerRef, + ref, direction = 'vertical', fadeHeight = 24, hideScrollbar, className, classNameContent, + style, children, ...props }: HTMLAttributes & { @@ -24,52 +19,22 @@ export default function FadedScroll({ classNameContent?: string hideScrollbar?: boolean }) { - const contentRef = useRef(null); - - const [position, setPosition] = useState<'start' | 'middle' | 'end'>('start'); - - const isVertical = direction === 'vertical'; - - useEffect(() => { - const ref = contentRef.current; - if (ref) { - const handleScroll = () => { - const isStart = isVertical - ? ref.scrollTop === 0 - : ref.scrollLeft === 0; - const isEnd = isVertical - ? ref.scrollHeight - ref.scrollTop === ref.clientHeight - : ref.scrollWidth - ref.scrollLeft === ref.clientWidth; - setPosition(isStart ? 'start' : isEnd ? 'end' : 'middle'); - }; - ref.addEventListener('scroll', handleScroll); - return () => ref.removeEventListener('scroll', handleScroll); - } - }, [isVertical]); - - const maskImage = useMemo(() => { - // eslint-disable-next-line max-len - let mask = `linear-gradient(to ${isVertical ? 'bottom' : 'right'}, transparent, black `; - mask += `${position !== 'start' ? fadeHeight : 0}px, black calc(100% - `; - mask += `${position !== 'end' ? fadeHeight : 0}px), transparent)`; - return mask; - }, [fadeHeight, isVertical, position]); + const { maskImage } = useFadedScroll(ref, direction, fadeHeight); return
(null); const mobileViewportHeight = useVisualViewportHeight(); - const heightMinimum = '20rem'; + const heightMinimum = '18rem'; const maxHeight = useMemo(() => { const positionY = ref.current?.getBoundingClientRect().y; return mobileViewportHeight && positionY @@ -573,14 +573,16 @@ export default function CommandKClient({ }
- +
{isLoading ? 'Searching ...' : 'No results found'} diff --git a/src/components/useFadedScroll.ts b/src/components/useFadedScroll.ts new file mode 100644 index 00000000..7e9bd50a --- /dev/null +++ b/src/components/useFadedScroll.ts @@ -0,0 +1,38 @@ +import { RefObject, useEffect, useMemo, useState } from 'react'; + +export default function useFadedScroll( + containerRef?: RefObject, + direction: 'vertical' | 'horizontal' = 'vertical', + fadeHeight = 24, +) { + const [position, setPosition] = useState<'start' | 'middle' | 'end'>('start'); + + const isVertical = direction === 'vertical'; + + const ref = containerRef?.current?.children[0] as HTMLElement; + useEffect(() => { + if (ref) { + const handleScroll = () => { + const isStart = isVertical + ? ref.scrollTop === 0 + : ref.scrollLeft === 0; + const isEnd = isVertical + ? ref.scrollHeight - ref.scrollTop === ref.clientHeight + : ref.scrollWidth - ref.scrollLeft === ref.clientWidth; + setPosition(isStart ? 'start' : isEnd ? 'end' : 'middle'); + }; + ref.addEventListener('scroll', handleScroll); + return () => ref.removeEventListener('scroll', handleScroll); + } + }, [ref, isVertical]); + + const maskImage = useMemo(() => { + // eslint-disable-next-line max-len + let mask = `linear-gradient(to ${isVertical ? 'bottom' : 'right'}, transparent, black `; + mask += `${position !== 'start' ? fadeHeight : 0}px, black calc(100% - `; + mask += `${position !== 'end' ? fadeHeight : 0}px), transparent)`; + return mask; + }, [fadeHeight, isVertical, position]); + + return { maskImage }; +}