import { useEffect, useState, type ButtonHTMLAttributes, type CSSProperties, type ReactNode } from 'react'; import { ChevronRight, RotateCw } from 'lucide-react'; import { SeyarMascot } from '@/mascots/SeyarMascot'; import type { SeyarChar, SeyarMood } from '@/mascots/seyar'; import { friendlyError } from '@/lib/errors'; import { useNav } from '@/nav/Navigator'; import { onWhite, shade } from '@/lib/color'; import { useCompanion } from '@/state/companion'; /* ─── سربرگِ رنگیِ صفحه ──────────────────────────────────────────────────── */ export function ScreenHeader({ title, subtitle, color, back = false, mascot, exact = false, children, }: { title: ReactNode; subtitle?: ReactNode; color: string; /** دکمهی بازگشت؛ true یعنی صفحهی قبل در پشته. */ back?: boolean | (() => void); /** بی `char` همراهِ انتخابیِ کاربر است؛ همهی صفحهها همین را میخواهند. */ mascot?: { char?: SeyarChar; mood?: SeyarMood; size?: number }; /** رنگ را همانطور که هست بگذار (بیتیرهکردن برای کنتراست)؛ انتخابِ مالک برای چند صفحه. */ exact?: boolean; children?: ReactNode; }) { const nav = useNav(); const companion = useCompanion().char; const onBack = typeof back === 'function' ? back : nav.back; const bg = exact ? color : onWhite(color); return ( {/* نمادهای ریاضی که آرام شناورند؛ فقط transform/opacity و بیتصویر، پس ارزاناند. */} π ∑ √ {back && (typeof back === 'function' || nav.canGoBack) && ( )} {title} {subtitle && {subtitle}} {mascot && } {children} ); } /* ─── دکمهها ──────────────────────────────────────────────────────────────── */ export function IconButton({ label, className = '', children, ...rest }: ButtonHTMLAttributes & { label: string }) { return ( {children} ); } export function PrimaryButton({ color = '#ED6956', loading = false, className = '', style, children, disabled, ...rest }: ButtonHTMLAttributes & { color?: string; loading?: boolean }) { return ( {loading ? : children} ); } export function SecondaryButton({ className = '', children, ...rest }: ButtonHTMLAttributes) { return ( {children} ); } export function Spinner({ size = 20, className = '' }: { size?: number; className?: string }) { return ( ); } /* ─── حالتهای بارگذاری / خطا / خالی ───────────────────────────────────────── */ function StateBox({ char, mood, text, children }: { char?: SeyarChar; mood: SeyarMood; text: ReactNode; children?: ReactNode }) { const companion = useCompanion().char; return ( {text} {children} ); } /** * لودر فقط بعد از `delayMs` دیده میشود. پاسخِ API معمولاً زیرِ ۳۰۰ms میرسد و نشان دادنِ لودر برای * همان چند فریم، پرشِ ریزِ «لودر ← محتوا» میساخت؛ حالا تا آن لحظه جای خالی میماند. اگر پاسخ * کند باشد کاربر باز هم لودر را میبیند. در مرورگر با شبکهی کند آزموده نشده. */ export function LoadingState({ text = 'یه لحظه صبر کن…', char, delayMs = 300 }: { text?: string; char?: SeyarChar; delayMs?: number }) { const [show, setShow] = useState(delayMs <= 0); useEffect(() => { if (delayMs <= 0) return; const t = setTimeout(() => setShow(true), delayMs); return () => clearTimeout(t); }, [delayMs]); return show ? : null; } export function ErrorState({ error, onRetry, text }: { error?: unknown; onRetry?: () => void; text?: string }) { return ( {onRetry && ( دوباره امتحان کن )} ); } export function EmptyState({ text, char }: { text: string; char?: SeyarChar }) { return ; } /** * جداکنندهی میانِ دو تکهی متن. «·» کنارِ صفرِ فارسی (که خودش یک نقطه است) * خوانده نمیشد: «فصلِ ۱ · ۰ از ۴» شبیهِ «۱۰۰» دیده میشد. */ export function Sep() { return ; } export function InlineError({ children }: { children: ReactNode }) { return ( {children} ); } /* ─── چیزهای کوچک ──────────────────────────────────────────────────────────── */ export function ProgressBar({ percent, color, track = '#eae7ff', height = 8 }: { percent: number; color: string; track?: string; height?: number }) { return ( ); } export function Card({ className = '', style, children, onClick }: { className?: string; style?: CSSProperties; children: ReactNode; onClick?: () => void }) { const Tag = onClick ? 'button' : 'div'; return ( {children} ); } /** بدنهی اسکرولشوندهی صفحه، زیرِ سربرگ. */ export function ScreenBody({ className = '', children }: { className?: string; children: ReactNode }) { return {children}; } export function Screen({ className = 'bg-page', children }: { className?: string; children: ReactNode }) { return {children}; }
{text}