| // @ts-nocheck | |
| // src/components/workspace/FloatingWindow.tsx | |
| import { useState, useRef, useEffect } from "react"; | |
| import { motion, AnimatePresence } from "framer-motion"; | |
| import Draggable from "react-draggable"; | |
| import { Resizable } from "react-resizable"; | |
| import "react-resizable/css/styles.css"; | |
| import { X, Minimize2, Maximize2, Square, GripVertical } from "lucide-react"; | |
| interface FloatingWindowProps { | |
| id: string; | |
| title: string; | |
| icon?: React.ReactNode; | |
| children: React.ReactNode; | |
| isOpen: boolean; | |
| isMinimized: boolean; | |
| isMaximized: boolean; | |
| isActive?: boolean; | |
| onClose: () => void; | |
| onMinimize: () => void; | |
| onMaximize: () => void; | |
| onFocus: () => void; | |
| zIndex: number; | |
| defaultPosition?: { x: number; y: number }; | |
| defaultSize?: { width: number; height: number }; | |
| minWidth?: number; | |
| minHeight?: number; | |
| maxWidth?: number; | |
| maxHeight?: number; | |
| } | |
| export function FloatingWindow({ | |
| id, | |
| title, | |
| icon, | |
| children, | |
| isOpen, | |
| isMinimized, | |
| isMaximized, | |
| isActive = false, | |
| onClose, | |
| onMinimize, | |
| onMaximize, | |
| onFocus, | |
| zIndex, | |
| defaultPosition = { x: 100, y: 100 }, | |
| defaultSize = { width: 600, height: 400 }, | |
| minWidth = 300, | |
| minHeight = 200, | |
| maxWidth = 1400, | |
| maxHeight = 900, | |
| }: FloatingWindowProps) { | |
| const [position, setPosition] = useState(defaultPosition); | |
| const [size, setSize] = useState(defaultSize); | |
| const nodeRef = useRef<HTMLDivElement>(null); | |
| // Reset position when maximized toggled off | |
| useEffect(() => { | |
| if (!isMaximized && !isMinimized) { | |
| // Keep position | |
| } | |
| }, [isMaximized]); | |
| if (!isOpen) return null; | |
| const windowContent = ( | |
| <div | |
| className={`premium-glass rounded-[1.5rem] overflow-hidden flex flex-col shadow-2xl transition-shadow duration-200 border ${ | |
| isActive | |
| ? "ring-2 ring-teal-500/70 border-teal-500/30 dark:ring-teal-400/70 dark:border-teal-400/30 shadow-[0_0_30px_rgba(20,184,166,0.25)]" | |
| : "border-white/10 dark:border-white/5" | |
| }`} | |
| style={{ | |
| width: isMaximized ? "100vw" : size.width, | |
| height: isMaximized ? "calc(100vh - 80px)" : size.height, | |
| maxWidth: "100vw", | |
| maxHeight: "calc(100vh - 80px)", | |
| }} | |
| onMouseDown={onFocus} | |
| > | |
| {/* Window Header */} | |
| <div | |
| className="flex items-center justify-between px-4 py-3 bg-white/20 dark:bg-slate-900/20 border-b border-white/10 dark:border-white/5 cursor-grab active:cursor-grabbing select-none" | |
| > | |
| <div className="flex items-center gap-2"> | |
| <GripVertical className="w-3.5 h-3.5 text-slate-400 dark:text-slate-500" /> | |
| {icon && <span className="text-sm">{icon}</span>} | |
| <span className="font-bold text-sm text-slate-800 dark:text-slate-200">{title}</span> | |
| </div> | |
| {/* MacOS-style Window controls */} | |
| <div className="flex items-center gap-2"> | |
| <button | |
| onClick={(e) => { e.stopPropagation(); onMinimize(); }} | |
| className="p-1 rounded-full hover:bg-amber-500/20 text-slate-500 hover:text-amber-500 dark:hover:text-amber-400 transition" | |
| title="Minimize" | |
| > | |
| <Minimize2 className="w-3.5 h-3.5" /> | |
| </button> | |
| <button | |
| onClick={(e) => { e.stopPropagation(); onMaximize(); }} | |
| className="p-1 rounded-full hover:bg-emerald-500/20 text-slate-500 hover:text-emerald-500 dark:hover:text-emerald-400 transition" | |
| title={isMaximized ? "Restore" : "Maximize"} | |
| > | |
| {isMaximized ? <Square className="w-3.5 h-3.5" /> : <Maximize2 className="w-3.5 h-3.5" />} | |
| </button> | |
| <button | |
| onClick={(e) => { e.stopPropagation(); onClose(); }} | |
| className="p-1 rounded-full hover:bg-rose-500/20 text-slate-500 hover:text-rose-500 dark:hover:text-rose-450 transition" | |
| title="Close" | |
| > | |
| <X className="w-3.5 h-3.5" /> | |
| </button> | |
| </div> | |
| </div> | |
| {/* Window Content */} | |
| <div className="flex-1 overflow-auto bg-white/60 dark:bg-slate-950/40 min-h-0"> | |
| {children} | |
| </div> | |
| </div> | |
| ); | |
| if (isMinimized) return null; | |
| if (isMaximized) { | |
| return ( | |
| <div | |
| className="fixed inset-0 z-50 flex items-center justify-center bg-black/20 backdrop-blur-sm" | |
| style={{ zIndex }} | |
| onClick={(e) => e.target === e.currentTarget && onFocus()} | |
| > | |
| {windowContent} | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <Draggable | |
| nodeRef={nodeRef} | |
| handle=".cursor-grab" | |
| position={position} | |
| onStop={(_, data) => setPosition({ x: data.x, y: data.y })} | |
| bounds="body" | |
| > | |
| <div ref={nodeRef} className="fixed" style={{ zIndex }}> | |
| <Resizable | |
| width={size.width} | |
| height={size.height} | |
| onResize={(_, data) => setSize({ width: data.size.width, height: data.size.height })} | |
| minConstraints={[minWidth, minHeight]} | |
| maxConstraints={[maxWidth, maxHeight]} | |
| > | |
| <div style={{ width: size.width, height: size.height }} className="resize-handles"> | |
| {windowContent} | |
| </div> | |
| </Resizable> | |
| </div> | |
| </Draggable> | |
| ); | |
| } | |
Xet Storage Details
- Size:
- 5.21 kB
- Xet hash:
- 9f15d48542790dcf62a79af760fc987bda8a65e498d3cbd01dc45768e9e714db
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.