tahamajs/IE / Projects /front /src /components /workspace /WebBrowserPanel.tsx
tahamajs's picture
download
raw
6.57 kB
// src/components/workspace/WebBrowserPanel.tsx
import { useState, useRef, useCallback, useEffect } from "react";
import { Search, ArrowLeft, ArrowRight, RotateCw, Home, ExternalLink, Copy, Check, BookmarkPlus, Settings } from "lucide-react";
import { toast } from "react-toastify";
interface WebBrowserPanelProps {
initialUrl?: string;
panelId?: string;
}
export function WebBrowserPanel({ initialUrl = "https://scholar.google.com", panelId }: WebBrowserPanelProps) {
const [url, setUrl] = useState(initialUrl);
const [input, setInput] = useState(initialUrl);
const [loading, setLoading] = useState(false);
const [canGoBack, setCanGoBack] = useState(false);
const [canGoForward, setCanGoForward] = useState(false);
const [copied, setCopied] = useState(false);
const iframeRef = useRef<HTMLIFrameElement>(null);
const navigate = useCallback((newUrl: string) => {
let finalUrl = newUrl.trim();
if (!/^https?:\/\//i.test(finalUrl) && !finalUrl.startsWith("about:")) {
if (finalUrl.includes(" ") || !finalUrl.includes(".")) {
finalUrl = `https://scholar.google.com/scholar?q=${encodeURIComponent(finalUrl)}`;
} else {
finalUrl = "https://" + finalUrl;
}
}
setUrl(finalUrl);
setInput(finalUrl);
setLoading(true);
}, []);
const goBack = () => {
iframeRef.current?.contentWindow?.history.back();
};
const goForward = () => {
iframeRef.current?.contentWindow?.history.forward();
};
const reload = () => {
iframeRef.current?.contentWindow?.location.reload();
setLoading(true);
};
const goHome = () => {
navigate(initialUrl);
};
const handleLoad = () => {
setLoading(false);
// Update navigation buttons state
try {
const iframe = iframeRef.current;
if (iframe?.contentWindow?.history.length) {
// Cannot reliably check back/forward due to cross-origin restrictions
}
} catch (e) {
// Cross-origin, ignore
}
};
const copyUrl = async () => {
await navigator.clipboard.writeText(url);
setCopied(true);
toast.success("URL copied to clipboard");
setTimeout(() => setCopied(false), 2000);
};
const openInNewTab = () => {
window.open(url, "_blank");
};
const handleInputKeyDown = (e: React.KeyboardEvent) => {
if (e.key === "Enter") navigate(input);
};
return (
<div className="flex flex-col h-full bg-white/40 dark:bg-slate-950/20 backdrop-blur-md">
{/* Toolbar */}
<div className="flex items-center gap-1.5 p-3 bg-white/30 dark:bg-slate-900/30 border-b border-white/10 dark:border-white/5 backdrop-blur-lg">
<button
onClick={goBack}
className="p-2 rounded-xl text-slate-650 hover:text-slate-900 dark:text-slate-300 dark:hover:text-white hover:bg-white/40 dark:hover:bg-slate-800/40 transition-all duration-200 active:scale-95"
title="Go back"
>
<ArrowLeft className="w-4 h-4" />
</button>
<button
onClick={goForward}
className="p-2 rounded-xl text-slate-650 hover:text-slate-900 dark:text-slate-300 dark:hover:text-white hover:bg-white/40 dark:hover:bg-slate-800/40 transition-all duration-200 active:scale-95"
title="Go forward"
>
<ArrowRight className="w-4 h-4" />
</button>
<button
onClick={reload}
className="p-2 rounded-xl text-slate-650 hover:text-slate-900 dark:text-slate-300 dark:hover:text-white hover:bg-white/40 dark:hover:bg-slate-800/40 transition-all duration-200 active:scale-95"
title="Reload"
>
<RotateCw className={`w-4 h-4 ${loading ? "animate-spin text-teal-500" : ""}`} />
</button>
<button
onClick={goHome}
className="p-2 rounded-xl text-slate-650 hover:text-slate-900 dark:text-slate-300 dark:hover:text-white hover:bg-white/40 dark:hover:bg-slate-800/40 transition-all duration-200 active:scale-95"
title="Home"
>
<Home className="w-4 h-4" />
</button>
<div className="flex-1 flex items-center gap-2 bg-slate-100/60 dark:bg-slate-900/60 border border-white/10 dark:border-white/5 rounded-xl px-3 py-1.5 shadow-inner">
<Search className="w-4 h-4 text-slate-400" />
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={handleInputKeyDown}
className="flex-1 bg-transparent border-none outline-none text-xs text-slate-800 dark:text-slate-200 placeholder:text-slate-500 font-medium"
placeholder="Enter URL or search term..."
/>
</div>
<button
onClick={copyUrl}
className="p-2 rounded-xl text-slate-655 hover:text-slate-900 dark:text-slate-300 dark:hover:text-white hover:bg-white/40 dark:hover:bg-slate-800/40 transition-all duration-200 active:scale-95"
title="Copy URL"
>
{copied ? <Check className="w-4 h-4 text-emerald-500" /> : <Copy className="w-4 h-4" />}
</button>
<button
onClick={openInNewTab}
className="p-2 rounded-xl text-slate-655 hover:text-slate-900 dark:text-slate-300 dark:hover:text-white hover:bg-white/40 dark:hover:bg-slate-800/40 transition-all duration-200 active:scale-95"
title="Open in new tab"
>
<ExternalLink className="w-4 h-4" />
</button>
</div>
{/* Iframe with error handling */}
<div className="flex-1 relative bg-white dark:bg-slate-950">
{loading && (
<div className="absolute inset-0 flex items-center justify-center bg-white/70 dark:bg-slate-950/70 backdrop-blur-sm z-10">
<div className="flex flex-col items-center gap-3 p-6 premium-glass rounded-2xl border border-white/10 dark:border-white/5">
<RotateCw className="w-6 h-6 animate-spin text-teal-500" />
<p className="text-xs font-bold text-slate-500 dark:text-slate-400">Loading resources...</p>
</div>
</div>
)}
<iframe
ref={iframeRef}
src={url}
className="w-full h-full border-0"
title="Web Browser"
sandbox="allow-same-origin allow-scripts allow-popups allow-forms allow-downloads allow-modals"
onLoad={handleLoad}
onError={() => {
setLoading(false);
toast.error(`Failed to load: ${url}`);
}}
/>
</div>
</div>
);
}
export default WebBrowserPanel;

Xet Storage Details

Size:
6.57 kB
·
Xet hash:
13d046d3846e4077e184b0ab237be92a35048175b68838b631b69215d40fef0b

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.