| import { ReactNode } from "react"; | |
| import { X, Lightbulb, CheckCircle2 } from "lucide-react"; | |
| import { AnimatePresence, motion } from "framer-motion"; | |
| export interface HelpPoint { | |
| title: string; | |
| text: string; | |
| } | |
| interface ContextHelpPanelProps { | |
| open: boolean; | |
| title: string; | |
| intro: string; | |
| steps: HelpPoint[]; | |
| tips?: HelpPoint[]; | |
| onClose: () => void; | |
| footer?: ReactNode; | |
| } | |
| export function ContextHelpPanel({ open, title, intro, steps, tips, onClose, footer }: ContextHelpPanelProps) { | |
| return ( | |
| <AnimatePresence> | |
| {open && ( | |
| <> | |
| <motion.div | |
| initial={{ opacity: 0 }} | |
| animate={{ opacity: 1 }} | |
| exit={{ opacity: 0 }} | |
| className="fixed inset-0 z-50 bg-black/40 backdrop-blur-sm" | |
| onClick={onClose} | |
| /> | |
| <motion.aside | |
| initial={{ opacity: 0, x: 40 }} | |
| animate={{ opacity: 1, x: 0 }} | |
| exit={{ opacity: 0, x: 40 }} | |
| transition={{ duration: 0.25 }} | |
| className="fixed right-4 top-4 z-50 w-[min(92vw,28rem)] overflow-hidden rounded-[1.75rem] border border-white/60 bg-white/95 shadow-[0_30px_80px_rgba(15,23,42,0.25)] backdrop-blur-xl dark:border-white/10 dark:bg-slate-950/95" | |
| > | |
| <div className="flex items-start justify-between gap-4 border-b border-gray-200 px-5 py-4 dark:border-gray-800"> | |
| <div> | |
| <div className="inline-flex items-center gap-2 rounded-full bg-primary/10 px-3 py-1 text-xs font-semibold text-primary"> | |
| <Lightbulb className="h-3.5 w-3.5" /> | |
| Quick Guide | |
| </div> | |
| <h3 className="mt-3 text-xl font-bold text-gray-900 dark:text-white">{title}</h3> | |
| <p className="mt-2 text-sm leading-relaxed text-gray-600 dark:text-gray-400">{intro}</p> | |
| </div> | |
| <button | |
| onClick={onClose} | |
| className="rounded-full p-2 text-gray-400 transition hover:bg-gray-100 hover:text-gray-700 dark:hover:bg-gray-800 dark:hover:text-gray-200" | |
| aria-label="Close help panel" | |
| > | |
| <X className="h-4 w-4" /> | |
| </button> | |
| </div> | |
| <div className="max-h-[70vh] space-y-5 overflow-y-auto px-5 py-5"> | |
| <div className="space-y-3"> | |
| {steps.map((step, index) => ( | |
| <div key={step.title} className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-800 dark:bg-white/5"> | |
| <div className="flex items-start gap-3"> | |
| <div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-gradient-to-r from-primary to-secondary text-xs font-bold text-white"> | |
| {index + 1} | |
| </div> | |
| <div> | |
| <div className="font-semibold text-gray-900 dark:text-white">{step.title}</div> | |
| <p className="mt-1 text-sm leading-relaxed text-gray-600 dark:text-gray-400">{step.text}</p> | |
| </div> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| {tips && tips.length > 0 && ( | |
| <div className="rounded-2xl border border-emerald-200 bg-emerald-50 p-4 dark:border-emerald-900/50 dark:bg-emerald-950/25"> | |
| <div className="mb-3 flex items-center gap-2 text-sm font-semibold text-emerald-700 dark:text-emerald-300"> | |
| <CheckCircle2 className="h-4 w-4" /> | |
| Helpful Tips | |
| </div> | |
| <div className="space-y-3"> | |
| {tips.map((tip) => ( | |
| <div key={tip.title}> | |
| <div className="text-sm font-medium text-emerald-800 dark:text-emerald-200">{tip.title}</div> | |
| <p className="mt-1 text-sm leading-relaxed text-emerald-700/90 dark:text-emerald-300/90">{tip.text}</p> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| )} | |
| {footer && <div>{footer}</div>} | |
| </div> | |
| </motion.aside> | |
| </> | |
| )} | |
| </AnimatePresence> | |
| ); | |
| } | |
Xet Storage Details
- Size:
- 4.28 kB
- Xet hash:
- 7f1a6e3376b72060e15ee329ede8dbcfd47ab366ad71c4da330fcac577ff9c6f
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.