copy base dmenu to dkala

This commit is contained in:
hamid zarghami
2026-02-07 15:31:22 +03:30
commit c9e37f6177
521 changed files with 57786 additions and 0 deletions
@@ -0,0 +1,95 @@
'use client';
import Image from "next/image";
import clsx from "clsx";
import HorizontalScrollView from "@/components/listview/HorizontalScrollView";
import CategoryItemRenderer from "@/components/listview/CategoryItemRenderer";
import CategorySmallItemRenderer from "@/components/listview/CategorySmallItemRenderer";
import { Category } from "@/app/[name]/(Main)/types/Types";
type Variant = "large" | "small";
const variantConfig: Record<
Variant,
{
renderer: typeof CategoryItemRenderer;
imageSize: number;
}
> = {
large: {
renderer: CategoryItemRenderer,
imageSize: 32,
},
small: {
renderer: CategorySmallItemRenderer,
imageSize: 24,
},
};
type Props = {
categories: Category[];
selectedCategory: string;
onSelect: (categoryId: string) => void;
variant?: Variant;
className?: string;
};
const CategoryScroll = ({
categories,
selectedCategory,
onSelect,
variant = "large",
className,
}: Props) => {
const { renderer: Renderer, imageSize } = variantConfig[variant];
const handleSelect = (categoryId: string) => () => onSelect(categoryId);
return (
<HorizontalScrollView
className={clsx(
"w-full noscrollbar py-4!",
variant === "large" && "mt-4!",
className
)}
>
{/* <Renderer
key="all"
className={clsx(selectedCategory === "0" && "bg-container!")}
onClick={handleSelect(0)}
>
<Image
priority
src="/assets/images/food-image.png"
width={imageSize}
height={imageSize}
alt="category image"
/>
<span className="text-xs text-foreground">همه</span>
</Renderer> */}
{categories.map((item) => {
const isSelected = item.id === selectedCategory;
return (
<Renderer
key={item.id}
className={clsx(isSelected && "bg-container!")}
onClick={handleSelect(item.id)}
>
<Image
priority
src={item.avatarUrl || "/assets/images/food-image.png"}
width={imageSize}
height={imageSize}
alt="category image"
/>
<span className="text-xs text-foreground text-center">{item.title}</span>
</Renderer>
);
})}
</HorizontalScrollView>
);
};
export default CategoryScroll;
@@ -0,0 +1,138 @@
'use client';
import React, { useState, useEffect, useMemo } from "react";
import Button from "@/components/button/PrimaryButton";
import AnimatedBottomSheet from "@/components/bottomsheet/AnimatedBottomSheet";
import ComboBox, { ComboboxOption } from "@/components/combobox/Combobox";
import { Switch } from "@/components/ui/switch";
import { TicketPercentIcon } from "lucide-react";
type MenuFilterDrawerProps = {
visible: boolean;
onClose: () => void;
selectedIngredients: string;
selectedDeliveryId: string;
onIngredientsChange: (value: string) => void;
onDeliveryChange: (value: string) => void;
onApply: () => void;
tMenu: (key: string) => string;
};
const MenuFilterDrawer = ({
visible,
onClose,
selectedIngredients,
selectedDeliveryId,
onIngredientsChange,
onDeliveryChange,
onApply,
tMenu,
}: MenuFilterDrawerProps) => {
const [tempIngredients, setTempIngredients] = useState(selectedIngredients);
const [tempDeliveryId, setTempDeliveryId] = useState(selectedDeliveryId);
useEffect(() => {
if (visible) {
setTempIngredients(selectedIngredients);
setTempDeliveryId(selectedDeliveryId);
}
}, [visible, selectedIngredients, selectedDeliveryId]);
const serviceOptions: Array<ComboboxOption> = useMemo(() => {
return [
{
id: "0",
title: tMenu("MenuFilterDrawer.SelectDelivery.Options.All") || "همه",
label: "",
},
{
id: "pickup",
title: "بیرون بر",
label: "",
},
{
id: "inPlace",
title: "سرو در رستوران",
label: "",
},
];
}, [tMenu]);
const handleIngredientsChange = (
e: React.ChangeEvent<HTMLInputElement>
) => {
setTempIngredients(e.target.value);
};
const handleDeliveryChange = (
e: React.MouseEvent<HTMLDivElement, MouseEvent>,
index: number
) => {
e.stopPropagation();
setTempDeliveryId(serviceOptions[index]?.id ?? serviceOptions[0].id);
};
const handleApply = () => {
onIngredientsChange(tempIngredients);
onDeliveryChange(tempDeliveryId);
onApply();
onClose();
};
const handleCancel = () => {
setTempIngredients(selectedIngredients);
setTempDeliveryId(selectedDeliveryId);
onClose();
};
return (
<AnimatedBottomSheet
title={tMenu("MenuFilterDrawer.Heading")}
visible={visible}
outDelay={150}
onClick={onClose}
>
<div className="px-6 mt-5">
<div className="flex flex-col gap-2">
<label className="text-sm2 text-foreground">
{tMenu("MenuFilterDrawer.SelectContent.Label")}
</label>
<input
type="text"
value={tempIngredients}
onChange={handleIngredientsChange}
placeholder={tMenu("MenuFilterDrawer.SelectContent.Placeholder") || "محتویات را وارد کنید..."}
className="w-full h-11 px-3 py-2.5 rounded-normal border border-border bg-container/29 focus:outline-none focus:ring-2 focus:ring-black text-xs!"
/>
</div>
<ComboBox
className="relative mt-9.5"
title={tMenu("MenuFilterDrawer.SelectDelivery.Label")}
options={serviceOptions}
selectedId={tempDeliveryId}
onSelectionChange={handleDeliveryChange}
/>
<div className="flex w-full mt-[23px] h-11 items-center justify-between gap-2 px-3 py-4 relative bg-container/29 rounded-xl border border-solid border-border cursor-pointer focus:outline-none focus:ring-2 focus:ring-black">
<span className="inline-flex items-center gap-2.5 text-sm2">
<TicketPercentIcon size={16} />
{tMenu("MenuFilterDrawer.DiscountState")}
</span>
<Switch />
</div>
</div>
<hr className="text-white/40 mt-12" />
<div className="px-9 pt-6 flex justify-between gap-[22px]">
<div className="w-full">
<Button className="dark:bg-white! dark:text-black!" onClick={handleApply}>{tMenu("MenuFilterDrawer.ButtonOk")}</Button>
</div>
<div className="w-full">
<Button className="bg-disabled! text-disabled-text!" onClick={handleCancel}>
{tMenu("MenuFilterDrawer.ButtonCancel")}
</Button>
</div>
</div>
</AnimatedBottomSheet>
);
};
export default MenuFilterDrawer;
@@ -0,0 +1,55 @@
'use client';
import { Skeleton } from "@/components/ui/skeleton";
import VerticalScrollView from "@/components/listview/VerticalScrollView";
import MenuItemRenderer from "@/components/listview/MenuItemRenderer";
const MenuSkeleton = () => {
return (
<div className="flex flex-col gap-4 items-center pt-8 mb-8">
<div className="w-full">
<Skeleton className="h-12 w-full rounded-xl mb-4" />
<div className="flex gap-3 overflow-x-hidden py-4">
{[...Array(5)].map((_, index) => (
<Skeleton key={index} className="h-24 w-24 rounded-xl shrink-0" />
))}
</div>
</div>
<section className="w-full">
<div className="flex justify-between items-center mb-5">
<Skeleton className="h-5 w-20 rounded-md" />
<div className="flex gap-2">
<Skeleton className="h-8 w-24 rounded-xl" />
<Skeleton className="h-8 w-28 rounded-xl" />
</div>
</div>
<VerticalScrollView className="mt-5! overflow-y-auto h-full">
{[...Array(6)].map((_, index) => (
<MenuItemRenderer key={index}>
<div className="flex gap-4 w-full h-full items-center">
<Skeleton className="min-w-28 w-28 h-28 rounded-xl" />
<div className="w-full inline-flex flex-col justify-between">
<div>
<Skeleton className="h-5 w-32 rounded-md mb-2" />
<Skeleton className="h-4 w-full rounded-md mb-1" />
<Skeleton className="h-4 w-3/4 rounded-md" />
</div>
<div className="inline-flex mt-2 gap-2 justify-between w-full items-center">
<div className="w-full flex flex-col gap-1">
<Skeleton className="h-4 w-16 rounded-md" />
</div>
<Skeleton className="max-w-[115px] w-full h-8 rounded-md" />
</div>
</div>
</div>
</MenuItemRenderer>
))}
</VerticalScrollView>
</section>
</div>
);
};
export default MenuSkeleton;
@@ -0,0 +1,54 @@
'use client';
import AnimatedBottomSheet from "@/components/bottomsheet/AnimatedBottomSheet";
import clsx from "clsx";
import React from "react";
type MenuSortingDrawerProps = {
visible: boolean;
onClose: () => void;
sortings: ReadonlyArray<string>;
activeIndex: number;
onSelect: (index: number) => void;
tMenu: (key: string) => string;
};
const MenuSortingDrawer = ({
visible,
onClose,
sortings,
activeIndex,
onSelect,
tMenu,
}: MenuSortingDrawerProps) => {
return (
<AnimatedBottomSheet
title={tMenu("MenuSortingDrawer.Heading")}
visible={visible}
inDelay={150}
onClick={onClose}
>
<div className="px-8.5 py-10 flex flex-col gap-2">
{sortings.map((value, index) => {
const isActive = index === activeIndex;
return (
<button
key={value}
type="button"
onClick={() => onSelect(index)}
className={clsx(
"flex items-center justify-between py-3 text-sm2 transition-colors",
isActive ? "text-primary font-semibold" : "text-foreground"
)}
>
<span>{tMenu(`MenuSortingDrawer.Options.${value}`)}</span>
</button>
);
})}
</div>
</AnimatedBottomSheet>
);
};
export default MenuSortingDrawer;