mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-04-29 02:42:47 -04:00
Refactor category scroll handlers (#1069)
* state enum * combine some useEffects * combine all useeffects * formatting
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import clsx from 'clsx';
|
||||
import { Suspense, useRef } from 'react';
|
||||
import { Suspense, useMemo, useRef } from 'react';
|
||||
import { Navigate, Outlet } from 'react-router-dom';
|
||||
import {
|
||||
ClientContextProvider,
|
||||
@@ -31,6 +31,8 @@ const Layout = () => {
|
||||
|
||||
usePlausiblePageViewMonitor({ currentPath: rawPath });
|
||||
|
||||
const ctxValue = useMemo(() => ({ ref: layoutRef }), [layoutRef]);
|
||||
|
||||
if (library === null && libraries.data) {
|
||||
const firstLibrary = libraries.data[0];
|
||||
|
||||
@@ -39,7 +41,7 @@ const Layout = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<LayoutContext.Provider value={{ ref: layoutRef }}>
|
||||
<LayoutContext.Provider value={ctxValue}>
|
||||
<div
|
||||
ref={layoutRef}
|
||||
className={clsx(
|
||||
|
||||
@@ -2,7 +2,7 @@ import { getIcon } from '@sd/assets/util';
|
||||
import clsx from 'clsx';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ArrowLeft, ArrowRight } from 'phosphor-react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { RefObject, useEffect, useRef, useState } from 'react';
|
||||
import { useDraggable } from 'react-use-draggable-scroll';
|
||||
import { Category, useLibraryQuery } from '@sd/client';
|
||||
import { useIsDark } from '~/hooks';
|
||||
@@ -31,18 +31,16 @@ const CategoryList = [
|
||||
] as Category[];
|
||||
|
||||
export const Categories = (props: { selected: Category; onSelectedChanged(c: Category): void }) => {
|
||||
const layout = useLayoutContext();
|
||||
const isDark = useIsDark();
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const { events } = useDraggable(ref as React.MutableRefObject<HTMLDivElement>);
|
||||
|
||||
const { scroll, mouseState } = useMouseHandlers({ ref });
|
||||
|
||||
const categories = useLibraryQuery(['categories.list']);
|
||||
|
||||
const [scroll, setScroll] = useState(0);
|
||||
const [lastCategoryVisible, setLastCategoryVisible] = useState(false);
|
||||
const [mousedown, setMousedown] = useState(false);
|
||||
const [dragging, setDragging] = useState(false);
|
||||
|
||||
const handleArrowOnClick = (direction: 'right' | 'left') => {
|
||||
const element = ref.current;
|
||||
@@ -58,62 +56,6 @@ export const Categories = (props: { selected: Category; onSelectedChanged(c: Cat
|
||||
index === CategoryList.length - 1 && setLastCategoryVisible((prev) => !prev);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const element = ref.current;
|
||||
if (!element) return;
|
||||
|
||||
const handleWheel = (event: WheelEvent) => {
|
||||
event.preventDefault();
|
||||
const { deltaX, deltaY } = event;
|
||||
const scrollAmount = Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : deltaY;
|
||||
element.scrollTo({ left: element.scrollLeft + scrollAmount });
|
||||
};
|
||||
|
||||
element.addEventListener('wheel', handleWheel);
|
||||
return () => element.removeEventListener('wheel', handleWheel);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const element = ref.current;
|
||||
if (!element) return;
|
||||
|
||||
const onScroll = () => {
|
||||
setScroll(element.scrollLeft);
|
||||
if (mousedown && !dragging) {
|
||||
setDragging(true);
|
||||
if (layout.ref.current) {
|
||||
layout.ref.current.style.cursor = 'grabbing';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
element.addEventListener('scroll', onScroll);
|
||||
return () => element.removeEventListener('scroll', onScroll);
|
||||
}, [mousedown, dragging, layout.ref]);
|
||||
|
||||
useEffect(() => {
|
||||
const element = ref.current;
|
||||
if (!element) return;
|
||||
|
||||
const onMouseDown = () => setMousedown(true);
|
||||
|
||||
element.addEventListener('mousedown', onMouseDown);
|
||||
return () => element.removeEventListener('mousedown', onMouseDown);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const onMouseUp = () => {
|
||||
setMousedown(false);
|
||||
setDragging(false);
|
||||
if (layout.ref.current) {
|
||||
layout.ref.current.style.cursor = '';
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('mouseup', onMouseUp);
|
||||
return () => window.removeEventListener('mouseup', onMouseUp);
|
||||
}, [layout.ref]);
|
||||
|
||||
return (
|
||||
<div className="sticky top-0 z-10 mt-2 flex bg-app/90 backdrop-blur">
|
||||
<div
|
||||
@@ -149,7 +91,10 @@ export const Categories = (props: { selected: Category; onSelectedChanged(c: Cat
|
||||
// WARNING: Edge breaks if the values are not postfixed with px or %
|
||||
margin: '0% -120px 0% 0%'
|
||||
}}
|
||||
className={clsx('min-w-fit', !dragging && '!cursor-default')}
|
||||
className={clsx(
|
||||
'min-w-fit',
|
||||
mouseState !== 'dragging' && '!cursor-default'
|
||||
)}
|
||||
key={category}
|
||||
>
|
||||
<CategoryButton
|
||||
@@ -177,3 +122,59 @@ export const Categories = (props: { selected: Category; onSelectedChanged(c: Cat
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const useMouseHandlers = ({ ref }: { ref: RefObject<HTMLDivElement> }) => {
|
||||
const layout = useLayoutContext();
|
||||
|
||||
const [scroll, setScroll] = useState(0);
|
||||
|
||||
type MouseState = 'idle' | 'mousedown' | 'dragging';
|
||||
const [mouseState, setMouseState] = useState<MouseState>('idle');
|
||||
|
||||
useEffect(() => {
|
||||
const element = ref.current;
|
||||
if (!element) return;
|
||||
|
||||
const onScroll = () => {
|
||||
setScroll(element.scrollLeft);
|
||||
|
||||
setMouseState((s) => {
|
||||
if (s !== 'mousedown') return s;
|
||||
|
||||
if (layout.ref.current) layout.ref.current.style.cursor = 'grabbing';
|
||||
|
||||
return 'dragging';
|
||||
});
|
||||
};
|
||||
const onWheel = (event: WheelEvent) => {
|
||||
event.preventDefault();
|
||||
const { deltaX, deltaY } = event;
|
||||
const scrollAmount = Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : deltaY;
|
||||
element.scrollTo({ left: element.scrollLeft + scrollAmount });
|
||||
};
|
||||
const onMouseDown = () => setMouseState('mousedown');
|
||||
|
||||
const onMouseUp = () => {
|
||||
setMouseState('idle');
|
||||
if (layout.ref.current) {
|
||||
layout.ref.current.style.cursor = '';
|
||||
}
|
||||
};
|
||||
|
||||
element.addEventListener('scroll', onScroll);
|
||||
element.addEventListener('wheel', onWheel);
|
||||
element.addEventListener('mousedown', onMouseDown);
|
||||
|
||||
window.addEventListener('mouseup', onMouseUp);
|
||||
|
||||
return () => {
|
||||
element.removeEventListener('scroll', onScroll);
|
||||
element.removeEventListener('wheel', onWheel);
|
||||
element.removeEventListener('mousedown', onMouseDown);
|
||||
|
||||
window.removeEventListener('mouseup', onMouseUp);
|
||||
};
|
||||
}, [ref, layout.ref]);
|
||||
|
||||
return { scroll, mouseState };
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user