بستهٔ سفارشPackage Box

جعبهٔ مقواییِ سه‌بعدی که با کلیک نوارچسبش می‌شکافد، درهایش تا می‌خورند و برگهٔ سفارش از داخل بالا می‌آید و رو به شما می‌چرخد.

محمدجواد

ساخته شده توسط محمدجواد

سازنده فارسی‌فای، بچه دولوپر

وابستگی‌ها

motion

نوع تعامل

کلیک روی جعبه یا دکمهٔ زیرش باز/بسته‌اش می‌کند؛ بستن همان فیلمِ بازشدن است به‌عکس — کلیکِ وسطِ راه فقط جهت را عوض می‌کند و چیزی نمی‌پرد. Escape هم می‌بندد. دکمهٔ زیرِ جعبه همیشه برای صفحه‌خوان هست (با `hint={false}` فقط نامرئی می‌شود) و `aria-expanded`/`aria-controls` می‌گیرد؛ برگهٔ سفارش هم تا وقتی باز نشده `inert` است تا فوکوس به محتوای پنهان نرود. با `prefers-reduced-motion` کلِ تایم‌لاین در ۱۰ میلی‌ثانیه به مقصد می‌رسد، پس حالت درست است ولی حرکت پخش نمی‌شود.

پراپ‌ها

گزینه‌هایی که می‌تونی به کامپوننت بدی؛ ستاره‌دارها اجباری‌اند.

open
booleanپیش‌فرض: undefined

حالتِ بازِ کنترل‌شده؛ کنارش `onOpenChange` بدهید. ندهید تا خودش مدیریت کند.

defaultOpen
booleanپیش‌فرض: false

جعبه از اول باز باشد (حالتِ نام‌کنترل‌شده).

onOpenChange
(open: boolean) => voidپیش‌فرض: undefined

با هر باز/بسته شدن صدا زده می‌شود؛ با `open` می‌توانید کنترل‌شده‌اش کنید.

slip
PackageSlip

متنِ برگهٔ داخلِ جعبه: عنوان، زیرعنوان، شماره، تاریخ، گیرنده، نشانی، اقلام و مهر. هر فیلدی را ندهید، جای‌نگهدارِ فارسی‌اش می‌نشیند.

children
ReactNodeپیش‌فرض: undefined

جای برگهٔ پیش‌فرض؛ در فضای طراحی ۲۴۰×۳۴۰ رندر می‌شود (بر `slip` مقدم است).

colors
PackageBoxColorsپیش‌فرض: مقوای طبیعی + مهرِ نارنجی

`{ box, tape, paper, ink, accent }`. همهٔ وجه‌ها، سایه و بافتِ کارتون از `box` مشتق می‌شوند؛ `accent` رنگِ مهر است.

نصب

۱. این آدرس رو یک‌بار توی components.json بذار تا shadcn مخزن فارسی‌فای رو بشناسه.

components.jsonjson
{
  "registries": {
    "@farsify": "https://farsify.ir/r/{name}.json"
  }
}

۲. بعد با دستورِ زیر کامپوننت رو نصب کن.

npx shadcn@latest add @farsify/package-box

پیش‌نیاز: Tailwind CSS v4 و صفحهٔ راست‌به‌چپ dir="rtl".

نمونه

package-box.tsxtsx
"use client";

import { useState } from "react";
import { PackageBox } from "@/components/farsify/package-box";

const SLIP = {
  eyebrow: "برگهٔ بسته‌بندی",
  title: "سفارشتان بسته شد.",
  subtitle: "بسته آماده است و به‌زودی راه می‌افتد.",
  reference: "شمارهٔ ۴۸۲۱",
  date: "۱۵ آبان ۱۴۰۵",
  recipient: "سارا احمدی",
  address: "تهران، خیابانِ بهشتی، کوچهٔ ۱۲، پلاک ۴",
  items: [
    { name: "لامپِ رومیزیِ ساده", qty: 1, price: "۲٬۴۰۰٬۰۰۰" },
    { name: "دفترِ نقطه‌دار", qty: 1, price: "۱۸۵٬۰۰۰" },
  ],
  total: "۲٬۵۸۵٬۰۰۰ تومان",
  // مهر دو سه واژه بیشتر جا ندارد: دایرهٔ ۳۸px است و متنِ بلندتر از دایره می‌زند بیرون
  stamp: "بسته شد",
};

export function OrderReceived() {
  return <PackageBox slip={SLIP} />;
}

// باز/بستهٔ کنترل‌شده از بیرون
const [open, setOpen] = useState(false);
<PackageBox slip={SLIP} open={open} onOpenChange={setOpen} />

// رنگِ مقوا و مهر — تنها رنگ‌های کامپوننت
<PackageBox slip={SLIP} colors={{ box: "#b07b4a", accent: "#c2552b" }} />

// داخلِ قابِ کوتاه: سقفِ ارتفاع + رزروِ فضا تا صفحه نپرد
<PackageBox slip={SLIP} maxWidth={320} maxHeight={420} reserveSpace />

// بدونِ نوارچسب یا بدونِ نشانه‌های حمل
<PackageBox slip={SLIP} tape={false} symbols={false} />

نکته‌ها

  • این کامپوننت یک شیءِ فیزیکی است، نه چیدمان: محورهای چرخش و مختصاتِ وجهِ آن آینه نمی‌شوند. متنِ برگه اما کاملاً راست‌به‌چپ است و با `text-start` و `justify-between` خودش برمی‌گردد.
  • ارتفاعِ صحنه از ۳۹۲px (بسته) تا ۶۵۰px (باز) در فضای طراحی می‌رود. اگر داخلِ قابی کوتاه (کارت، پنل) می‌گذارید `maxHeight` را هم بدهید وگرنه جعبهٔ باز از قاب بیرون می‌زند؛ `reserveSpace` هم از پرشِ صفحه جلوگیری می‌کند.
  • برای متنِ برگه، رقم‌ها را فارسی بنویسید و ستون‌های عددی را `tabular-fa` کنید تا با تغییرِ مقدار نلرزند. کدِ لاتین (مثلِ SKU) را با `tabular-en` ایزوله کنید.
  • `box` را خیلی روشن یا خیلی تیره ندهید: سایه و نورِ هر وجه از همین رنگ مشتق می‌شود و در دو سرِ طیف، جعبه تخت دیده می‌شود. مقواهای خاکی و قهوه‌ای بهترین نتیجه را می‌دهند.
  • روی موبایل، هاورِ شناورِ جعبه و `whileTap` هر دو به `prefers-reduced-motion` وابسته‌اند؛ چیزی برای خاموش‌کردن دستی لازم نیست.
  • متنِ برگه داخلِ `role="document"` با `aria-label` می‌نشیند و تا باز نشدن `inert` است؛ اگر محتوای تعاملی داخلِ برگه می‌گذارید، دکمهٔ راهنما را با `hint={false}` از سرِ راه بردارید تا دو کنترل برای یک کار نداشته باشید.

مجوز

کد بعد از نصب مالِ خودته؛ هر تغییری خواستی بده و توی پروژه‌ی خودت نگهش دار. برای پروژه‌های شخصی و تجاری آزاده، حتی اگه کدت بسته باشه. فقط اسم و لینک فارسی‌فای رو یه جا توی پروژه نگه دار.