add: dynamic fixed categories list to menu page
This commit is contained in:
+67
-6
@@ -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<HTMLDivElement | null> = useRef(null);
|
||||
const wrapperRef: React.RefObject<HTMLDivElement | null> = 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<HTMLInputElement>) => {
|
||||
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 (
|
||||
<div className="flex flex-col gap-4 items-center">
|
||||
<div className="flex flex-col gap-4 items-center" ref={wrapperRef}>
|
||||
<SearchBox value={search} onChange={updateSearch} />
|
||||
<HorizontalScrollView className="w-full noscrollbar py-4!">
|
||||
{categories.map((item, index) => (
|
||||
<CategoryItemRenderer
|
||||
key={index}
|
||||
className={`${index === +selectedCategory ? "bg-white!" : ""}`}
|
||||
className={clsx(
|
||||
index === +selectedCategory && "bg-white!",
|
||||
)}
|
||||
onClick={() => updateCategory(index)}
|
||||
>
|
||||
<Image
|
||||
@@ -212,7 +242,8 @@ const MenuIndex = () => {
|
||||
</HorizontalScrollView>
|
||||
|
||||
<section className="w-full">
|
||||
<div className="flex justify-between items-center">
|
||||
|
||||
<div className="flex justify-between items-center relative" ref={smallCategoriesRef} >
|
||||
<span className="text-base font-medium">
|
||||
{categories[+selectedCategory]?.title}
|
||||
</span>
|
||||
@@ -227,6 +258,36 @@ const MenuIndex = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial={{ y: -200 }}
|
||||
animate={{ y: smallCategoriesVisible ? 0 : -200, opacity: smallCategoriesVisible ? 1 : 0 }}
|
||||
transition={{ duration: .1 }}
|
||||
className={clsx(
|
||||
'fixed left-0 z-10 top-0 px-4 pt-16 bg-[#F4F5F9CC] backdrop-blur-[44px] right-0',
|
||||
``
|
||||
)}>
|
||||
<HorizontalScrollView className="w-full noscrollbar py-4!">
|
||||
{categories.map((item, index) => (
|
||||
<CategorySmallItemRenderer
|
||||
key={index}
|
||||
className={clsx(
|
||||
index === +selectedCategory && "bg-white!",
|
||||
)}
|
||||
onClick={() => updateCategory(index)}
|
||||
>
|
||||
<Image
|
||||
priority
|
||||
src="/assets/images/food-image.png"
|
||||
width={24}
|
||||
height={24}
|
||||
alt="category image"
|
||||
/>
|
||||
<span className="text-xs text-black">{item.title}</span>
|
||||
</CategorySmallItemRenderer>
|
||||
))}
|
||||
</HorizontalScrollView>
|
||||
</motion.div>
|
||||
|
||||
<VerticalScrollView className="mt-5! overflow-y-auto h-full">
|
||||
{filteredReceiptItems.map((food) => (
|
||||
<MenuItemRenderer key={food.id}>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode;
|
||||
} & React.HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
function CategorySmallItemRenderer({ children, ...rest }: Props) {
|
||||
return (
|
||||
<div className={`${rest.className} cursor-pointer transition-all duration-200 ease-out border-2 border-solid border-white overflow-hidden rounded-xl backdrop-blur-md bg-white/40`}>
|
||||
<div
|
||||
{...rest}
|
||||
className="rounded-normal h-[44px] flex flex-row justify-center items-center p-2.5 gap-2"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CategorySmallItemRenderer;
|
||||
@@ -5,7 +5,8 @@ type Props = {
|
||||
isPending: boolean
|
||||
} & Omit<React.FormHTMLAttributes<HTMLFormElement>, "id">;
|
||||
|
||||
function AuthFormWrapper({ children, ...restProps }: Props) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function AuthFormWrapper({ children, isPending, ...restProps }: Props) {
|
||||
return (
|
||||
<form {...restProps} className='p-6 lg:py-[75px] w-full flex items-center justify-center py-4 lg:items-center lg:px-10 px-4 drop-shadow-black h-full'>
|
||||
<div className='relative h-full w-full px-4 sm:px-6 lg:p-0 flex flex-col max-h-[812px] max-w-[1200px] bg-container rounded-container overflow-clip lg:flex-row justify-between'>
|
||||
|
||||
Reference in New Issue
Block a user