|
| 1 | +'use client' |
| 2 | + |
| 3 | +import { useState, useEffect, useCallback, useRef } from 'react' |
| 4 | +import { createPortal } from 'react-dom' |
| 5 | +import { Quiz } from './Quiz.jsx' |
| 6 | + |
| 7 | +export function PostActionsSheet({ postId }) { |
| 8 | + const [open, setOpen] = useState(null) // 'practicar' | 'evaluar' | null |
| 9 | + const [mounted, setMounted] = useState(false) |
| 10 | + const sheetRef = useRef(null) |
| 11 | + |
| 12 | + useEffect(() => { |
| 13 | + setMounted(true) |
| 14 | + }, []) |
| 15 | + |
| 16 | + const close = useCallback(() => setOpen(null), []) |
| 17 | + |
| 18 | + useEffect(() => { |
| 19 | + if (!open) return |
| 20 | + const onKey = e => { |
| 21 | + if (e.key === 'Escape') close() |
| 22 | + } |
| 23 | + window.addEventListener('keydown', onKey) |
| 24 | + return () => window.removeEventListener('keydown', onKey) |
| 25 | + }, [open, close]) |
| 26 | + |
| 27 | + useEffect(() => { |
| 28 | + if (!open) return |
| 29 | + const handleClickOutside = e => { |
| 30 | + if (sheetRef.current && !sheetRef.current.contains(e.target)) { |
| 31 | + close() |
| 32 | + } |
| 33 | + } |
| 34 | + document.addEventListener('mousedown', handleClickOutside) |
| 35 | + return () => document.removeEventListener('mousedown', handleClickOutside) |
| 36 | + }, [open, close]) |
| 37 | + |
| 38 | + const openSheet = type => setOpen(type) |
| 39 | + |
| 40 | + const baseBtn = |
| 41 | + 'border dark:border-white uppercase mix rounded-[4px] font-bold inline-block px-2 py-[3px] text-[10px] bg-white dark:bg-secondry hover:bg-blue-50 dark:hover:bg-blue-900 transition-colors' |
| 42 | + |
| 43 | + const sheet = ( |
| 44 | + <div |
| 45 | + aria-hidden={!open} |
| 46 | + className={`fixed inset-0 z-40 ${open ? 'pointer-events-auto' : 'pointer-events-none'}`} |
| 47 | + > |
| 48 | + <div |
| 49 | + className={`absolute inset-0 bg-black/30 backdrop-blur-sm transition-opacity ${open ? 'opacity-100' : 'opacity-0'}`} |
| 50 | + onClick={close} |
| 51 | + /> |
| 52 | + <div |
| 53 | + ref={sheetRef} |
| 54 | + role='dialog' |
| 55 | + aria-modal='true' |
| 56 | + aria-label={open === 'practicar' ? 'Prácticar' : 'Evaluar'} |
| 57 | + className={`absolute bottom-0 left-0 right-0 mx-auto max-w-3xl rounded-t-xl border border-blue-200 dark:border-blue-800 bg-white dark:bg-secondry shadow-xl p-6 transition-transform duration-300 ${open ? 'translate-y-0' : 'translate-y-full'}`} |
| 58 | + > |
| 59 | + <div className='flex items-start justify-between pb-4'> |
| 60 | + <h2 className='text-base font-bold text-blue-900 dark:text-blue-100'> |
| 61 | + {open === 'practicar' ? 'Prácticar' : 'Evaluar'} |
| 62 | + </h2> |
| 63 | + <button |
| 64 | + onClick={close} |
| 65 | + className='text-xs font-semibold px-2 py-1 rounded hover:bg-blue-100 dark:hover:bg-blue-900' |
| 66 | + > |
| 67 | + Cerrar |
| 68 | + </button> |
| 69 | + </div> |
| 70 | + <div className='prose dark:prose-invert max-w-none text-sm md:text-base'> |
| 71 | + {open === 'practicar' && ( |
| 72 | + <div className='space-y-4'> |
| 73 | + <p> |
| 74 | + Próximamente: área de práctica con mini retos o tarjetas |
| 75 | + (flashcards). |
| 76 | + </p> |
| 77 | + <p className='text-xs opacity-60'> |
| 78 | + Indica qué dinámica quieres y la añado. |
| 79 | + </p> |
| 80 | + </div> |
| 81 | + )} |
| 82 | + {open === 'evaluar' && ( |
| 83 | + <div className='mt-2'> |
| 84 | + <Quiz slug={postId} /> |
| 85 | + </div> |
| 86 | + )} |
| 87 | + </div> |
| 88 | + </div> |
| 89 | + </div> |
| 90 | + ) |
| 91 | + |
| 92 | + return ( |
| 93 | + <div className='pt-4 flex gap-2 justify-center'> |
| 94 | + {/* <button onClick={() => openSheet('practicar')} className={baseBtn}> |
| 95 | + prácticar |
| 96 | + </button> */} |
| 97 | + <button onClick={() => openSheet('evaluar')} className={baseBtn}> |
| 98 | + evaluar |
| 99 | + </button> |
| 100 | + {mounted && createPortal(sheet, document.body)} |
| 101 | + </div> |
| 102 | + ) |
| 103 | +} |
0 commit comments