روزنامهٔ موبایل
پیشنمایش تمامصفحهنامِ نشریه درشت، روزهای هفته با دایرهٔ تاریخ، خبرِ تمامعرض با نوارِ استوری و خبرِ بعدی، پربازدیدها با خلاصه و ناوبریِ پایین.
۱. نصب کامپوننتها
این بلاک از ۶ آیتم دیگ استفاده میکند: bottom-navigationbuttoncontrol-sizeiconspersiantoggle-group
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/bottom-navigation.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/toggle-group.jsonpnpm dlx digdesign@latest add https://docs.digdesign.ir/r/bottom-navigation.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/toggle-group.jsonyarn dlx digdesign@latest add https://docs.digdesign.ir/r/bottom-navigation.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/toggle-group.jsonbunx --bun digdesign@latest add https://docs.digdesign.ir/r/bottom-navigation.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/toggle-group.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
mobile-news-editorial.tsx
"use client"
import * as React from "react"
import { BellIcon, DashboardIcon, GlobeIcon, HeartIcon, UserIcon } from "@/components/icons"
import { BottomNavigation, BottomNavigationItem } from "@/components/ui/bottom-navigation"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { toFaDigits } from "@/lib/persian"
import { stockImages, type StockImageName } from "@/lib/stock-images"
const DAYS = [
{ value: "14", day: 14, name: "دوشنبه" },
{ value: "15", day: 15, name: "سهشنبه" },
{ value: "16", day: 16, name: "چهارشنبه" },
{ value: "17", day: 17, name: "پنجشنبه" },
{ value: "18", day: 18, name: "جمعه" },
]
const STORIES: { id: string; title: string; time: string; image: StockImageName }[] = [
{ id: "s1", title: "طرحِ تازهٔ حملونقلِ عمومی؛ هفت شهر خطِ اتوبوسِ برقی میگیرند", time: "۱۸ مهر · ۱۵:۳۰", image: "planeWindow" },
{ id: "s2", title: "کتابخوانی در کافهها دوباره رونق گرفته است", time: "۱۸ مهر · ۱۲:۱۰", image: "restaurant" },
{ id: "s3", title: "کلاسهای شبانهٔ مهارت برای جوانها رایگان شد", time: "۱۸ مهر · ۰۹:۴۵", image: "classroom" },
]
const POPULAR: { id: string; title: string; time: string; excerpt: string; image: StockImageName }[] = [
{ id: "p1", title: "مجلس طرحِ اشتغالِ جوانان را تصویب کرد", time: "۱۸ مهر · ۰۹:۱۵", excerpt: "نمایندگان طرحی را تصویب کردند که به کارفرمایانِ کوچک معافیتِ بیمه میدهد.", image: "studentStudy" },
{ id: "p2", title: "شفافیتِ بودجهٔ شهری؛ درخواستِ تازهٔ شوراها", time: "۱۷ مهر · ۱۱:۲۰", excerpt: "شوراها میخواهند هزینههای عمرانی ماهبهماه منتشر شود.", image: "coverWorkspace" },
{ id: "p3", title: "باشگاههای محلهای ساعتِ کارشان را بیشتر کردند", time: "۱۷ مهر · ۰۸:۰۵", excerpt: "از این هفته باشگاهها تا ۲۳ باز هستند.", image: "gym" },
]
/**
* روزنامهٔ موبایل با حالِوهوای مطبوعاتی: نامِ نشریه درشت با خطِ جداکننده، ردیفِ روزهای هفته
* با دایرههای تاریخ (انتخابِ یک روز)، خبرِ تمامعرض با نوارِ قطعهقطعهٔ «استوری» و دکمهٔ «بعدی»،
* و «پربازدیدها» با عکس، تیتر، زمان و یک خط خلاصه. ناوبریِ پایین با چهار مقصد.
*/
export default function MobileNewsEditorialBlock() {
const [day, setDay] = React.useState("18")
const [story, setStory] = React.useState(0)
const [tab, setTab] = React.useState("feed")
const current = STORIES[story]
return (
<ControlSizeProvider size="xl" asChild>
<div className="mx-auto flex min-h-svh w-full max-w-md flex-col bg-background">
<header className="flex items-end justify-between gap-4 border-b border-border px-4 pt-6 pb-4">
<h1 className="text-[1.75rem] leading-snug font-black">
روزنامهٔ
<br />
آوند
</h1>
<Button size="icon" aria-label="اعلانها" className="rounded-lg">
<BellIcon />
</Button>
</header>
<main className="flex flex-1 flex-col gap-6 pt-5 pb-6">
<section aria-labelledby="week-title" className="flex flex-col gap-3">
<div className="flex items-center justify-between px-4">
<h2 id="week-title" className="font-bold">
مرورِ هفته
</h2>
<span className="text-sm text-muted-foreground">مهر ۱۴۰۵</span>
</div>
<div className="overflow-x-auto px-4 [scrollbar-width:none]">
<ToggleGroup type="single" separated value={day} onValueChange={(value) => value && setDay(value as string)} aria-label="روزِ هفته" className="w-max gap-2.5">
{DAYS.map((item) => (
<ToggleGroupItem
key={item.value}
value={item.value}
aria-label={`${item.name} ${toFaDigits(item.day)} مهر`}
className="group h-auto flex-col gap-1.5 bg-transparent p-0 hover:bg-transparent data-[state=on]:bg-transparent"
>
<span className="flex size-14 items-center justify-center rounded-full border border-border text-xl font-black tabular-nums group-data-[state=on]:border-foreground group-data-[state=on]:bg-foreground group-data-[state=on]:text-background">
{toFaDigits(item.day)}
</span>
<span className="text-xs text-muted-foreground group-data-[state=on]:font-semibold group-data-[state=on]:text-foreground">{item.name}</span>
</ToggleGroupItem>
))}
</ToggleGroup>
</div>
</section>
{/* خبرِ تمامعرض با نوارِ قطعهای؛ لمسِ «بعدی» خبرِ بعدی را میآورد. */}
<section aria-label="خبرهای امروز" aria-roledescription="استوری" className="relative isolate overflow-hidden text-[var(--dig-white)]">
{/* eslint-disable-next-line @next/next/no-img-element -- عکس از کتابخانهٔ تصویرِ دیگ */}
<img key={current.image} src={stockImages[current.image].src} alt="" className="aspect-[4/3] w-full object-cover" />
<span aria-hidden className="absolute inset-0 bg-linear-to-t from-[var(--dig-slate-950)] via-[color-mix(in_oklab,var(--dig-slate-950)_30%,transparent)] via-50% to-transparent" />
<div className="absolute inset-x-0 bottom-0 flex flex-col gap-3 p-4" aria-live="polite">
<h2 className="text-xl leading-snug font-bold text-balance">
<a href="#">{current.title}</a>
</h2>
<div className="flex items-center justify-between gap-3">
<span className="text-xs text-[var(--dig-slate-200)]">{current.time}</span>
<ControlSizeProvider size="lg" asChild>
<Button onClick={() => setStory((value) => (value + 1) % STORIES.length)} className="rounded-full bg-[var(--dig-white)] px-4 text-sm text-[var(--dig-slate-900)] hover:bg-[var(--dig-slate-100)]">
خبرِ بعدی
</Button>
</ControlSizeProvider>
</div>
<div aria-hidden className="flex gap-1.5">
{STORIES.map((item, index) => (
<span key={item.id} className={`h-0.5 flex-1 rounded-full ${index === story ? "bg-[var(--dig-white)]" : "bg-[color-mix(in_oklab,var(--dig-white)_35%,transparent)]"}`} />
))}
</div>
</div>
</section>
<section aria-labelledby="popular-title" className="flex flex-col gap-1 px-4">
<h2 id="popular-title" className="font-bold">
پربازدیدها
</h2>
<ul className="flex flex-col divide-y divide-border">
{POPULAR.map((item) => (
<li key={item.id} className="relative flex gap-3 py-4">
{/* eslint-disable-next-line @next/next/no-img-element -- عکس از کتابخانهٔ تصویرِ دیگ */}
<img src={stockImages[item.image].src} alt="" loading="lazy" className="size-24 shrink-0 object-cover" />
<div className="flex min-w-0 flex-col gap-1">
<h3 className="line-clamp-2 leading-6 font-bold">
<a href="#" className="after:absolute after:inset-0">
{item.title}
</a>
</h3>
<span className="text-xs text-muted-foreground">{item.time}</span>
<p className="line-clamp-1 text-sm text-muted-foreground">{item.excerpt}</p>
</div>
</li>
))}
</ul>
</section>
</main>
<BottomNavigation value={tab} onValueChange={setTab} indicator="pill" fixed={false} className="sticky bottom-0">
<BottomNavigationItem value="feed" icon={<DashboardIcon />} label="روزنامه" />
<BottomNavigationItem value="explore" icon={<GlobeIcon />} label="کاوش" />
<BottomNavigationItem value="saved" icon={<HeartIcon />} label="ذخیرهها" />
<BottomNavigationItem value="account" icon={<UserIcon />} label="حساب" />
</BottomNavigation>
</div>
</ControlSizeProvider>
)
}