feat: add fullscreen toggle to Modal, enable in NodeEditorModal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-02-18 01:16:10 -05:00
parent d295ae5d88
commit bee7f45459
2 changed files with 55 additions and 17 deletions

View File

@@ -1,5 +1,5 @@
import { useEffect, useCallback, type ReactNode } from 'react' import { useState, useEffect, useCallback, type ReactNode } from 'react'
import { X } from 'lucide-react' import { X, Maximize2, Minimize2 } from 'lucide-react'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
interface ModalProps { interface ModalProps {
@@ -10,9 +10,28 @@ interface ModalProps {
/** Optional footer content that stays fixed at bottom (doesn't scroll) */ /** Optional footer content that stays fixed at bottom (doesn't scroll) */
footer?: ReactNode footer?: ReactNode
size?: 'sm' | 'md' | 'lg' | 'xl' size?: 'sm' | 'md' | 'lg' | 'xl'
/** If true, a fullscreen toggle button appears in the modal header */
allowFullScreen?: boolean
} }
export function Modal({ isOpen, onClose, title, children, footer, size = 'md' }: ModalProps) { export function Modal({ isOpen, onClose, title, children, footer, size = 'md', allowFullScreen = false }: ModalProps) {
const [isFullScreen, setIsFullScreen] = useState(() => {
if (!allowFullScreen) return false
try {
return localStorage.getItem('rf-editor-fullscreen') === 'true'
} catch {
return false
}
})
const toggleFullScreen = () => {
const next = !isFullScreen
setIsFullScreen(next)
try {
localStorage.setItem('rf-editor-fullscreen', String(next))
} catch {}
}
// Close on Escape key // Close on Escape key
const handleKeyDown = useCallback( const handleKeyDown = useCallback(
(e: KeyboardEvent) => { (e: KeyboardEvent) => {
@@ -61,9 +80,13 @@ export function Modal({ isOpen, onClose, title, children, footer, size = 'md' }:
<div <div
className={cn( className={cn(
'relative flex w-full flex-col border border-border bg-card shadow-lg', 'relative flex w-full flex-col border border-border bg-card shadow-lg',
'max-h-[100vh] rounded-t-2xl sm:max-h-[85vh] sm:rounded-2xl', 'animate-scale-in transition-all duration-200',
'animate-scale-in', isFullScreen
sizeClasses[size] ? 'fixed inset-4 max-w-none w-auto h-auto rounded-2xl'
: cn(
'max-h-[100vh] rounded-t-2xl sm:max-h-[85vh] sm:rounded-2xl',
sizeClasses[size]
)
)} )}
> >
{/* Header - Fixed at top */} {/* Header - Fixed at top */}
@@ -71,17 +94,32 @@ export function Modal({ isOpen, onClose, title, children, footer, size = 'md' }:
<h2 id="modal-title" className="text-lg font-semibold text-foreground"> <h2 id="modal-title" className="text-lg font-semibold text-foreground">
{title} {title}
</h2> </h2>
<button <div className="flex items-center gap-1">
onClick={onClose} {allowFullScreen && (
className={cn( <button
'rounded-md p-1.5 text-muted-foreground transition-colors sm:p-1', type="button"
'hover:bg-accent hover:text-foreground', onClick={toggleFullScreen}
'focus:outline-none focus:ring-2 focus:ring-primary/20' className="rounded-md p-1 text-muted-foreground hover:bg-accent hover:text-foreground"
title={isFullScreen ? 'Exit full screen' : 'Full screen'}
>
{isFullScreen
? <Minimize2 className="h-4 w-4" />
: <Maximize2 className="h-4 w-4" />
}
</button>
)} )}
aria-label="Close modal" <button
> onClick={onClose}
<X className="h-5 w-5" /> className={cn(
</button> 'rounded-md p-1.5 text-muted-foreground transition-colors sm:p-1',
'hover:bg-accent hover:text-foreground',
'focus:outline-none focus:ring-2 focus:ring-primary/20'
)}
aria-label="Close modal"
>
<X className="h-5 w-5" />
</button>
</div>
</div> </div>
{/* Body - Scrollable */} {/* Body - Scrollable */}

View File

@@ -83,7 +83,7 @@ export function NodeEditorModal({ node, onClose, isNewNode = false }: NodeEditor
) )
return ( return (
<Modal isOpen={true} onClose={onClose} title={getTitle()} size="lg" footer={footerContent}> <Modal isOpen={true} onClose={onClose} title={getTitle()} size="lg" footer={footerContent} allowFullScreen={true}>
{/* Node ID display */} {/* Node ID display */}
<div className="mb-4 text-xs text-muted-foreground"> <div className="mb-4 text-xs text-muted-foreground">
Node ID: <code className="rounded bg-accent px-1 py-0.5">{node.id}</code> Node ID: <code className="rounded bg-accent px-1 py-0.5">{node.id}</code>