From c983ae65cc0c0ee83667ee9c712e5e6383a1545e Mon Sep 17 00:00:00 2001 From: Mahyar Khanbolooki Date: Fri, 18 Jul 2025 21:29:04 +0330 Subject: [PATCH] add: dynamic fixed categories list to menu page --- src/app/[name]/page.tsx | 73 +++++++++++++++++-- .../listview/CategorySmallItemRenderer.tsx | 20 +++++ 2 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 src/components/listview/CategorySmallItemRenderer.tsx diff --git a/src/app/[name]/page.tsx b/src/app/[name]/page.tsx index 3ded9d5..f0eb180 100644 --- a/src/app/[name]/page.tsx +++ b/src/app/[name]/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import SearchBox from "@/components/input/SearchBox"; import CategoryItemRenderer from "@/components/listview/CategoryItemRenderer"; import HorizontalScrollView from "@/components/listview/HorizontalScrollView"; @@ -18,6 +18,9 @@ import { TicketPercentIcon } from "lucide-react"; import { MedalStar } from 'iconsax-react' import { useQueryState } from 'next-usequerystate' import ComboBox, { DropdownOption } from "@/components/combobox/ComboBox"; +import clsx from "clsx"; +import CategorySmallItemRenderer from "@/components/listview/CategorySmallItemRenderer"; +import { motion } from "framer-motion"; const categories = new Array(13).fill({ title: "خوراک", icon: "" }); @@ -148,15 +151,40 @@ const MenuIndex = () => { const [selectedCategory, setSelectedCategory] = useQueryState('category', { defaultValue: '0' }); const [selectedContentId, setSelectedContentId] = useQueryState('contentType', { defaultValue: '0' }); const [selectedShippingId, setSelectedShippingId] = useQueryState('shippingType', { defaultValue: '0' }); + const smallCategoriesRef: React.RefObject = useRef(null); + const wrapperRef: React.RefObject = useRef(null); + const [smallCategoriesVisible, setSmallCategoriesVisibility] = useState(false); + + useEffect(() => { + let m_wrapperRef = null; + if (wrapperRef.current) { + m_wrapperRef = wrapperRef; + wrapperRef.current?.parentElement?.addEventListener('scroll', onScroll); + } + + return () => { + if (m_wrapperRef?.current) + m_wrapperRef.current?.parentElement?.removeEventListener('scroll', onScroll) + } + }) + + const onScroll = () => { + if (!wrapperRef?.current?.parentElement?.scrollTop || !smallCategoriesRef.current?.offsetTop) return; + if (wrapperRef?.current?.parentElement?.scrollTop >= smallCategoriesRef.current?.offsetTop) { + setSmallCategoriesVisibility(true); + } else { + setSmallCategoriesVisibility(false); + } + } const updateSearch = useCallback((e: React.ChangeEvent) => { setSearch(e.target.value); - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const updateCategory = useCallback((id: number) => { setSelectedCategory(String(id)); - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const toggleFilterModal = useCallback(() => { @@ -190,13 +218,15 @@ const MenuIndex = () => { return ( -
+
{categories.map((item, index) => ( updateCategory(index)} > {
-
+ +
{categories[+selectedCategory]?.title} @@ -227,6 +258,36 @@ const MenuIndex = () => {
+ + + {categories.map((item, index) => ( + updateCategory(index)} + > + category image + {item.title} + + ))} + + + {filteredReceiptItems.map((food) => ( diff --git a/src/components/listview/CategorySmallItemRenderer.tsx b/src/components/listview/CategorySmallItemRenderer.tsx new file mode 100644 index 0000000..20ac4e7 --- /dev/null +++ b/src/components/listview/CategorySmallItemRenderer.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +type Props = { + children: React.ReactNode; +} & React.HTMLAttributes; + +function CategorySmallItemRenderer({ children, ...rest }: Props) { + return ( +
+
+ {children} +
+
+ ); +} + +export default CategorySmallItemRenderer;