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 { X } from 'lucide-react'
import { useState, useEffect, useCallback, type ReactNode } from 'react'
import { X, Maximize2, Minimize2 } from 'lucide-react'
import { cn } from '@/lib/utils'
interface ModalProps {
@@ -10,9 +10,28 @@ interface ModalProps {
/** Optional footer content that stays fixed at bottom (doesn't scroll) */
footer?: ReactNode
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
const handleKeyDown = useCallback(
(e: KeyboardEvent) => {
@@ -61,9 +80,13 @@ export function Modal({ isOpen, onClose, title, children, footer, size = 'md' }:
<div
className={cn(
'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',
sizeClasses[size]
'animate-scale-in transition-all duration-200',
isFullScreen
? '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 */}
@@ -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">
{title}
</h2>
<button
onClick={onClose}
className={cn(
'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'
<div className="flex items-center gap-1">
{allowFullScreen && (
<button
type="button"
onClick={toggleFullScreen}
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"
>
<X className="h-5 w-5" />
</button>
<button
onClick={onClose}
className={cn(
'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>
{/* Body - Scrollable */}

View File

@@ -83,7 +83,7 @@ export function NodeEditorModal({ node, onClose, isNewNode = false }: NodeEditor
)
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 */}
<div className="mb-4 text-xs text-muted-foreground">
Node ID: <code className="rounded bg-accent px-1 py-0.5">{node.id}</code>