feat: dropdown-style animation on content — glass stays during exit via onExitComplete
This commit is contained in:
@@ -240,6 +240,7 @@ export default function UnifiedSettingsModal() {
|
|||||||
const [hasPendingAction, setHasPendingAction] = useState(false)
|
const [hasPendingAction, setHasPendingAction] = useState(false)
|
||||||
const saveHandlerRef = useRef<(() => Promise<void>) | null>(null)
|
const saveHandlerRef = useRef<(() => Promise<void>) | null>(null)
|
||||||
const [saving, setSaving] = useState(false)
|
const [saving, setSaving] = useState(false)
|
||||||
|
const [showGlass, setShowGlass] = useState(false)
|
||||||
|
|
||||||
const handleDirtyChange = useCallback((dirty: boolean) => {
|
const handleDirtyChange = useCallback((dirty: boolean) => {
|
||||||
isDirtyRef.current = dirty
|
isDirtyRef.current = dirty
|
||||||
@@ -305,6 +306,7 @@ export default function UnifiedSettingsModal() {
|
|||||||
isDirtyRef.current = false
|
isDirtyRef.current = false
|
||||||
pendingActionRef.current = null
|
pendingActionRef.current = null
|
||||||
setHasPendingAction(false)
|
setHasPendingAction(false)
|
||||||
|
setShowGlass(true)
|
||||||
}
|
}
|
||||||
}, [isOpen])
|
}, [isOpen])
|
||||||
|
|
||||||
@@ -382,19 +384,26 @@ export default function UnifiedSettingsModal() {
|
|||||||
onClick={handleBackdropClick}
|
onClick={handleBackdropClick}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Glass panel — always mounted, instant show/hide, blur always composited */}
|
{/* Glass panel — always mounted, blur always composited */}
|
||||||
<div
|
<div
|
||||||
className={`fixed inset-0 z-[61] flex items-center justify-center p-4 ${
|
className={`fixed inset-0 z-[61] flex items-center justify-center p-4 ${
|
||||||
isOpen ? 'visible pointer-events-auto' : 'invisible pointer-events-none'
|
isOpen || showGlass ? 'visible pointer-events-auto' : 'invisible pointer-events-none'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="w-full max-w-3xl h-[85vh] bg-neutral-900/65 backdrop-blur-3xl backdrop-saturate-150 supports-[backdrop-filter]:bg-neutral-900/60 border border-white/[0.08] rounded-2xl shadow-xl shadow-black/20 flex flex-col overflow-hidden"
|
className="w-full max-w-3xl h-[85vh] bg-neutral-900/65 backdrop-blur-3xl backdrop-saturate-150 supports-[backdrop-filter]:bg-neutral-900/60 border border-white/[0.08] rounded-2xl shadow-xl shadow-black/20 flex flex-col overflow-hidden"
|
||||||
onClick={e => e.stopPropagation()}
|
onClick={e => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
{/* Content fades in/out independently — glass stays solid */}
|
{/* Content animates in/out — glass panel stays visible during exit */}
|
||||||
{isOpen && (
|
<AnimatePresence onExitComplete={() => setShowGlass(false)}>
|
||||||
<div className="flex flex-col h-full animate-fade-in">
|
{isOpen && (
|
||||||
|
<motion.div
|
||||||
|
className="flex flex-col h-full"
|
||||||
|
initial={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||||
|
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||||
|
exit={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||||
|
transition={{ duration: 0.15 }}
|
||||||
|
>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="shrink-0 px-6 pt-5 pb-4 border-b border-white/[0.06]">
|
<div className="shrink-0 px-6 pt-5 pb-4 border-b border-white/[0.06]">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
@@ -468,8 +477,9 @@ export default function UnifiedSettingsModal() {
|
|||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user