Decouple faded scroll component from behavior
This commit is contained in:
parent
fa9b62f34b
commit
29c3c7f167
@ -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<HTMLDivElement> & {
|
||||
@ -24,52 +19,22 @@ export default function FadedScroll({
|
||||
classNameContent?: string
|
||||
hideScrollbar?: boolean
|
||||
}) {
|
||||
const contentRef = useRef<HTMLDivElement>(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 <div
|
||||
{...props}
|
||||
ref={containerRef}
|
||||
ref={ref}
|
||||
className={clsx(
|
||||
isVertical
|
||||
direction === 'vertical'
|
||||
? 'overflow-y-hidden'
|
||||
: 'overflow-x-hidden',
|
||||
className,
|
||||
)}
|
||||
style={{ maskImage }}
|
||||
style={{ maskImage, ...style }}
|
||||
>
|
||||
<div
|
||||
ref={contentRef}
|
||||
className={clsx(
|
||||
isVertical
|
||||
direction === 'vertical'
|
||||
? 'max-h-full overflow-y-auto'
|
||||
: 'max-w-full overflow-x-auto',
|
||||
hideScrollbar && '[scrollbar-width:none]',
|
||||
|
||||
@ -155,7 +155,7 @@ export default function CommandKClient({
|
||||
|
||||
const ref = useRef<HTMLInputElement>(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({
|
||||
</span>}
|
||||
</div>
|
||||
</div>
|
||||
<Command.List className={clsx(
|
||||
'relative overflow-y-auto',
|
||||
'mx-3 pt-3',
|
||||
)} style={{
|
||||
maxHeight,
|
||||
// eslint-disable-next-line max-len
|
||||
maskImage: 'linear-gradient(to bottom, transparent, black 20px, black calc(100% - 20px), transparent)',
|
||||
}}>
|
||||
<Command.List
|
||||
className={clsx(
|
||||
'overflow-y-auto',
|
||||
'mx-3 pt-3',
|
||||
)} style={{
|
||||
maxHeight,
|
||||
// eslint-disable-next-line max-len
|
||||
maskImage: 'linear-gradient(to bottom, transparent, black 20px, black calc(100% - 20px), transparent)',
|
||||
}}
|
||||
>
|
||||
<div className="pb-1 md:pb-2">
|
||||
<Command.Empty className="mt-1 pl-3 text-dim pb-4">
|
||||
{isLoading ? 'Searching ...' : 'No results found'}
|
||||
|
||||
38
src/components/useFadedScroll.ts
Normal file
38
src/components/useFadedScroll.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { RefObject, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
export default function useFadedScroll(
|
||||
containerRef?: RefObject<HTMLDivElement | null>,
|
||||
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 };
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user