CategoriesList: apply draggable effects

This commit is contained in:
2025-10-11 20:51:21 +03:00
parent 563d44d0ca
commit 1cc254063d
3 changed files with 97 additions and 7 deletions

View File

@@ -120,6 +120,25 @@
gap: 0.5rem; gap: 0.5rem;
padding: 0.5rem 1rem 1rem 1rem; padding: 0.5rem 1rem 1rem 1rem;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
cursor: grab;
user-select: none;
}
.categoriesContainer:active {
cursor: grabbing;
}
.categoriesContainer::-webkit-scrollbar {
display: none;
}
/* Smooth scrolling for drag interactions */
.categoriesContainer.dragging {
scroll-behavior: auto;
}
.categoriesContainer.dragging * {
pointer-events: none;
} }
/* Enhanced responsive categories container */ /* Enhanced responsive categories container */

View File

@@ -2,7 +2,7 @@ import { Card, Grid, Space } from "antd";
import ImageWithFallback from "components/ImageWithFallback"; import ImageWithFallback from "components/ImageWithFallback";
import ProText from "components/ProText"; import ProText from "components/ProText";
import { useScrollHandler } from "contexts/ScrollHandlerContext"; import { useScrollHandler } from "contexts/ScrollHandlerContext";
import { useCallback, useEffect } from "react"; import { useCallback, useEffect, useRef } from "react";
import { useAppSelector } from "redux/hooks"; import { useAppSelector } from "redux/hooks";
import { colors } from "ThemeConstants"; import { colors } from "ThemeConstants";
import { default_image } from "utils/constants"; import { default_image } from "utils/constants";
@@ -25,6 +25,11 @@ export function CategoriesList({ categories }: CategoriesListProps) {
setActiveCategory, setActiveCategory,
} = useScrollHandler(); } = useScrollHandler();
const { isRTL } = useAppSelector((state) => state.locale); const { isRTL } = useAppSelector((state) => state.locale);
// Drag scroll functionality
const isDragging = useRef(false);
const startX = useRef(0);
const scrollLeft = useRef(0);
const getCategoryCardStyle = useCallback(() => { const getCategoryCardStyle = useCallback(() => {
if (xs) { if (xs) {
@@ -55,11 +60,11 @@ export function CategoriesList({ categories }: CategoriesListProps) {
// Function to scroll category card into view when active category changes automatically // Function to scroll category card into view when active category changes automatically
const scrollCategoryCardIntoView = useCallback((categoryId: number) => { const scrollCategoryCardIntoView = useCallback((categoryId: number) => {
const categoryCard = document.querySelector( const categoryCard = document.querySelector(
`[data-category-id="${categoryId}"]` `[data-category-id="${categoryId}"]`,
); );
if (categoryCard) { if (categoryCard) {
const categoriesContainer = categoryCard.closest( const categoriesContainer = categoryCard.closest(
`.${styles.categoriesContainer}` `.${styles.categoriesContainer}`,
); );
if (categoriesContainer) { if (categoriesContainer) {
const cardElement = categoryCard as HTMLElement; const cardElement = categoryCard as HTMLElement;
@@ -93,6 +98,60 @@ export function CategoriesList({ categories }: CategoriesListProps) {
} }
}, [activeCategory, scrollCategoryCardIntoView]); }, [activeCategory, scrollCategoryCardIntoView]);
// Drag scroll event handlers
const handleMouseDown = useCallback((e: React.MouseEvent) => {
const container = categoriesContainerRef?.current;
if (!container) return;
isDragging.current = true;
startX.current = e.pageX - container.offsetLeft;
scrollLeft.current = container.scrollLeft;
// Prevent text selection during drag
e.preventDefault();
}, [categoriesContainerRef]);
const handleMouseMove = useCallback((e: React.MouseEvent) => {
const container = categoriesContainerRef?.current;
if (!container || !isDragging.current) return;
e.preventDefault();
const x = e.pageX - container.offsetLeft;
const walk = (x - startX.current) * 2; // Multiply for faster scrolling
container.scrollLeft = scrollLeft.current - walk;
}, [categoriesContainerRef]);
const handleMouseUp = useCallback(() => {
isDragging.current = false;
}, []);
const handleMouseLeave = useCallback(() => {
isDragging.current = false;
}, []);
// Touch event handlers for mobile
const handleTouchStart = useCallback((e: React.TouchEvent) => {
const container = categoriesContainerRef?.current;
if (!container) return;
isDragging.current = true;
startX.current = e.touches[0].pageX - container.offsetLeft;
scrollLeft.current = container.scrollLeft;
}, [categoriesContainerRef]);
const handleTouchMove = useCallback((e: React.TouchEvent) => {
const container = categoriesContainerRef?.current;
if (!container || !isDragging.current) return;
const x = e.touches[0].pageX - container.offsetLeft;
const walk = (x - startX.current) * 2;
container.scrollLeft = scrollLeft.current - walk;
}, [categoriesContainerRef]);
const handleTouchEnd = useCallback(() => {
isDragging.current = false;
}, []);
return ( return (
<> <>
<div <div
@@ -101,12 +160,24 @@ export function CategoriesList({ categories }: CategoriesListProps) {
isCategoriesSticky ? styles.categoriesSticky : "" isCategoriesSticky ? styles.categoriesSticky : ""
}`} }`}
style={!isCategoriesSticky ? { paddingTop: "1rem" } : {}} style={!isCategoriesSticky ? { paddingTop: "1rem" } : {}}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
onMouseLeave={handleMouseLeave}
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
> >
{categories?.map((category) => ( {categories?.map((category) => (
<div key={category.id}> <div key={category.id}>
<Card <Card
key={category.id} key={category.id}
onClick={() => { onClick={(e) => {
// Prevent click if we were dragging
if (isDragging.current) {
e.preventDefault();
return;
}
scrollToCategory(category.id); scrollToCategory(category.id);
}} }}
data-category-id={category.id} data-category-id={category.id}
@@ -146,8 +217,8 @@ export function CategoriesList({ categories }: CategoriesListProps) {
xs xs
? styles.categoryMenuItemImageMobile ? styles.categoryMenuItemImageMobile
: md : md
? styles.categoryMenuItemImageTablet ? styles.categoryMenuItemImageTablet
: styles.categoryMenuItemImageDesktop : styles.categoryMenuItemImageDesktop
}`} }`}
// {...getCategoryImageStyle()} // {...getCategoryImageStyle()}
width={105} width={105}

View File

@@ -56,7 +56,7 @@ function MenuPage() {
orderType={orderType || ""} orderType={orderType || ""}
/> />
{!isLoading ? ( {isLoading ? (
<MenuSkeleton categoryCount={10} itemCount={30} variant="default" /> <MenuSkeleton categoryCount={10} itemCount={30} variant="default" />
) : ( ) : (
<div className={styles.menuContainer}> <div className={styles.menuContainer}>