فیلتر در باتمشیت
پیشنمایش تمامصفحهفیلتر و مرتبسازی در شیتِ پایینیِ اپ: مرتبسازیِ سگمنت، دستهها، بازهٔ قیمت، فقط موجود، پاک کردن و نمایشِ نتایجِ چسبان؛ شمارِ فیلترهای فعال روی دکمه.
۱. نصب کامپوننتها
این بلاک از ۱۲ آیتم دیگ استفاده میکند: app-barbadgebuttoncheckboxcontrol-sizedrawericonslabelpersiansliderswitchtoggle-group
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/app-bar.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/checkbox.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/drawer.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/slider.json https://docs.digdesign.ir/r/switch.json https://docs.digdesign.ir/r/toggle-group.jsonpnpm dlx digdesign@latest add https://docs.digdesign.ir/r/app-bar.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/checkbox.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/drawer.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/slider.json https://docs.digdesign.ir/r/switch.json https://docs.digdesign.ir/r/toggle-group.jsonyarn dlx digdesign@latest add https://docs.digdesign.ir/r/app-bar.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/checkbox.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/drawer.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/slider.json https://docs.digdesign.ir/r/switch.json https://docs.digdesign.ir/r/toggle-group.jsonbunx --bun digdesign@latest add https://docs.digdesign.ir/r/app-bar.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/checkbox.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/drawer.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/slider.json https://docs.digdesign.ir/r/switch.json https://docs.digdesign.ir/r/toggle-group.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
mobile-filters.tsx
"use client"
import * as React from "react"
import { ChevronRightIcon, FilterIcon } from "@/components/icons"
import { AppBar, AppBarLeading, AppBarTitle } from "@/components/ui/app-bar"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Drawer, DrawerContent, DrawerFooter, DrawerHeader, DrawerTitle } from "@/components/ui/drawer"
import { Label } from "@/components/ui/label"
import { Slider } from "@/components/ui/slider"
import { Switch } from "@/components/ui/switch"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { formatCurrency, toFaDigits } from "@/lib/persian"
import { stockImages } from "@/lib/stock-images"
const CATEGORIES = ["گلدان", "ظرف", "فنجان", "جاشمعی"]
const SORTS = [
{ value: "popular", label: "پرفروش" },
{ value: "new", label: "تازه" },
{ value: "cheap", label: "ارزان" },
]
/**
* فیلتر در باتمشیت (اپ): دکمهٔ «فیلتر» با شمارِ فیلترهای فعال، شیتِ پایینی با مرتبسازی
* سگمنت، دستهها، بازهٔ قیمت و «فقط موجود»؛ «پاک کردن» و «نمایشِ نتایج» چسبیده به پایینِ شیت.
*/
export default function MobileFiltersBlock() {
const [open, setOpen] = React.useState(true)
const [sort, setSort] = React.useState("popular")
const [categories, setCategories] = React.useState<string[]>(["گلدان"])
const [price, setPrice] = React.useState([300_000, 1_500_000])
const [inStock, setInStock] = React.useState(true)
const active = categories.length + (inStock ? 1 : 0) + (price[0] > 0 || price[1] < 3_000_000 ? 1 : 0)
function reset() {
setSort("popular")
setCategories([])
setPrice([0, 3_000_000])
setInStock(false)
}
return (
<ControlSizeProvider size="xl" asChild>
<div className="mx-auto flex min-h-svh w-full max-w-md flex-col bg-background">
<AppBar sticky>
<AppBarLeading>
<Button variant="ghost" size="icon" aria-label="بازگشت">
<ChevronRightIcon className="ltr:rotate-180" />
</Button>
</AppBarLeading>
<AppBarTitle>سفال و سرامیک</AppBarTitle>
</AppBar>
<main className="flex flex-1 flex-col gap-4 p-4">
<div className="flex items-center justify-between gap-3">
<span className="text-sm text-muted-foreground">{toFaDigits(48)} کالا</span>
<Button variant="outline" onClick={() => setOpen(true)}>
<FilterIcon />
فیلتر
{active > 0 && <Badge color="primary">{toFaDigits(active)}</Badge>}
</Button>
</div>
<div className="grid grid-cols-2 gap-3">
{[stockImages.productVase, stockImages.coverMinimalRoom, stockImages.studioSunlit, stockImages.coverNotebook].map((image) => (
// eslint-disable-next-line @next/next/no-img-element -- عکس از کتابخانهٔ تصویرِ دیگ
<img key={image.src} src={image.src} alt={image.alt} className="aspect-square w-full rounded-xl object-cover" />
))}
</div>
</main>
<Drawer open={open} onOpenChange={setOpen}>
<ControlSizeProvider size="xl" asChild>
<DrawerContent className="max-h-[85svh]">
<DrawerHeader className="flex-row items-center justify-between">
<DrawerTitle>فیلتر و مرتبسازی</DrawerTitle>
<Button variant="ghost" onClick={reset}>
پاک کردن
</Button>
</DrawerHeader>
<div className="flex flex-col gap-8 overflow-y-auto px-4 pb-4">
<div className="flex flex-col gap-3">
<span id="mf-sort" className="text-sm font-medium">
مرتبسازی
</span>
<ToggleGroup type="single" variant="segmented" value={sort} onValueChange={(value) => value && setSort(value as string)} aria-labelledby="mf-sort" className="w-full">
{SORTS.map((item) => (
<ToggleGroupItem key={item.value} value={item.value} className="flex-1">
{item.label}
</ToggleGroupItem>
))}
</ToggleGroup>
</div>
<fieldset className="flex flex-col gap-1">
<legend className="mb-2 text-sm font-medium">دسته</legend>
{CATEGORIES.map((category) => (
<div key={category} className="flex min-h-12 items-center gap-3">
<Checkbox
id={`mf-${category}`}
checked={categories.includes(category)}
onCheckedChange={(checked) => setCategories((list) => (checked ? [...list, category] : list.filter((item) => item !== category)))}
/>
<Label htmlFor={`mf-${category}`} className="flex-1">
{category}
</Label>
</div>
))}
</fieldset>
<div className="flex flex-col gap-4">
<div className="flex items-baseline justify-between gap-2">
<span id="mf-price" className="text-sm font-medium">
قیمت
</span>
<span className="text-sm text-muted-foreground tabular-nums">
{formatCurrency(price[0])} تا {formatCurrency(price[1])}
</span>
</div>
<Slider aria-labelledby="mf-price" value={price} onValueChange={setPrice} min={0} max={3_000_000} step={100_000} />
</div>
<div className="flex min-h-12 items-center justify-between gap-3">
<Label htmlFor="mf-stock">فقط کالاهای موجود</Label>
<Switch id="mf-stock" checked={inStock} onCheckedChange={setInStock} />
</div>
</div>
<DrawerFooter className="border-t border-border pb-[max(1rem,env(safe-area-inset-bottom))]">
<Button fullWidth onClick={() => setOpen(false)}>
نمایشِ {toFaDigits(48)} نتیجه
</Button>
</DrawerFooter>
</DrawerContent>
</ControlSizeProvider>
</Drawer>
</div>
</ControlSizeProvider>
)
}