tahamajs/IE / Projects /front /src /components /workspace /FloatingWindow.tsx
tahamajs's picture
download
raw
5.21 kB
// @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.