ux(modal): nao trancar - ESC e clique-fora fecham; overlay deixa clicar em novo slot/evento
- StackModal: overlay pointer-events-none + painel pointer-events-auto (agenda clicavel por baixo) - ESC fecha; pointerdown fora do painel fecha; clicar em outro slot/evento fecha o atual e abre o novo Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { createContext, useContext, useState, useCallback, ReactNode, useEffect } from 'react';
|
||||
import React, { createContext, useContext, useState, useCallback, ReactNode, useEffect, useRef } from 'react';
|
||||
import { X, ChevronLeft, Maximize2, Minimize2 } from 'lucide-react';
|
||||
|
||||
// Modal em PILHA (estilo navegação iOS): páginas deslizam da direita, com botão voltar.
|
||||
@@ -25,10 +25,24 @@ export const useStackModal = (): StackCtx => {
|
||||
export const StackModal: React.FC<{ isOpen: boolean; onClose: () => void; root: StackPage }> = ({ isOpen, onClose, root }) => {
|
||||
const [stack, setStack] = useState<StackPage[]>([root]);
|
||||
const [fullscreen, setFullscreen] = useState(false);
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Reabriu → reinicia a pilha. (Use também key={...} no pai p/ forçar remount por entidade.)
|
||||
useEffect(() => { if (isOpen) setStack([root]); /* eslint-disable-line */ }, [isOpen]);
|
||||
|
||||
// Não trancar: ESC fecha; clicar FORA do painel fecha (o overlay deixa o clique passar
|
||||
// pra agenda, então clicar em outro slot/evento fecha este e abre o novo).
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
const onDown = (e: PointerEvent) => {
|
||||
if (panelRef.current && !panelRef.current.contains(e.target as Node)) onClose();
|
||||
};
|
||||
const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };
|
||||
document.addEventListener('pointerdown', onDown, true);
|
||||
window.addEventListener('keydown', onKey);
|
||||
return () => { document.removeEventListener('pointerdown', onDown, true); window.removeEventListener('keydown', onKey); };
|
||||
}, [isOpen, onClose]);
|
||||
|
||||
const push = useCallback((p: StackPage) => setStack(s => [...s, p]), []);
|
||||
const pop = useCallback(() => setStack(s => (s.length > 1 ? s.slice(0, -1) : s)), []);
|
||||
const close = useCallback(() => onClose(), [onClose]);
|
||||
@@ -38,9 +52,10 @@ export const StackModal: React.FC<{ isOpen: boolean; onClose: () => void; root:
|
||||
const canBack = stack.length > 1;
|
||||
|
||||
return (
|
||||
<div className={`fixed inset-0 bg-black/0 z-[70] flex items-end sm:items-center justify-center ${fullscreen ? '' : 'sm:p-4'}`}>
|
||||
<div className={`fixed inset-0 bg-black/0 z-[70] pointer-events-none flex items-end sm:items-center justify-center ${fullscreen ? '' : 'sm:p-4'}`}>
|
||||
<div
|
||||
className={`bg-white shadow-2xl flex flex-col overflow-hidden transition-all duration-300 w-full ${
|
||||
ref={panelRef}
|
||||
className={`pointer-events-auto bg-white shadow-2xl flex flex-col overflow-hidden transition-all duration-300 w-full ${
|
||||
fullscreen
|
||||
? 'h-full rounded-none'
|
||||
: 'max-h-[90vh] rounded-t-2xl sm:w-[440px] sm:max-w-[95vw] sm:h-auto sm:max-h-[88vh] sm:rounded-3xl'
|
||||
|
||||
Reference in New Issue
Block a user