desktop: keep drag/resize working over embedded iframes + add embed loading spinner
This commit is contained in:
parent
e740a0061b
commit
bc738541cf
5 changed files with 65 additions and 3 deletions
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { type CSSProperties, type PointerEvent as ReactPointerEvent } from "react";
|
||||
import { useState, type CSSProperties, type PointerEvent as ReactPointerEvent } from "react";
|
||||
import type { AppDef } from "./apps";
|
||||
|
||||
export interface WinState {
|
||||
|
|
@ -49,6 +49,10 @@ export default function WinWindow({
|
|||
onMove,
|
||||
onResize,
|
||||
}: Props) {
|
||||
// While a drag/resize is in flight we disarm the embedded iframe (see the
|
||||
// `.interacting` rule in skeuo.css). Otherwise a service window swallows the
|
||||
// pointer as soon as it passes over the site and the gesture freezes.
|
||||
const [interacting, setInteracting] = useState(false);
|
||||
const centered = win.centered && !win.maximized;
|
||||
const style: CSSProperties = win.maximized
|
||||
? { left: 0, top: 0, width: "100%", height: "100%", zIndex: win.z }
|
||||
|
|
@ -62,6 +66,7 @@ export default function WinWindow({
|
|||
if ((e.target as HTMLElement).closest(".win-controls")) return;
|
||||
|
||||
e.preventDefault();
|
||||
setInteracting(true);
|
||||
let ox = win.x;
|
||||
let oy = win.y;
|
||||
if (win.centered) {
|
||||
|
|
@ -79,6 +84,7 @@ export default function WinWindow({
|
|||
onMove(ox + (ev.clientX - sx), oy + (ev.clientY - sy));
|
||||
};
|
||||
const handleUp = () => {
|
||||
setInteracting(false);
|
||||
window.removeEventListener("pointermove", handleMove);
|
||||
window.removeEventListener("pointerup", handleUp);
|
||||
window.removeEventListener("pointercancel", handleUp);
|
||||
|
|
@ -95,6 +101,7 @@ export default function WinWindow({
|
|||
if (win.maximized) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setInteracting(true);
|
||||
|
||||
const node = e.currentTarget.closest(".win-window") as HTMLElement | null;
|
||||
const rect = node?.getBoundingClientRect();
|
||||
|
|
@ -137,6 +144,7 @@ export default function WinWindow({
|
|||
onResize(Math.round(x), Math.round(y), Math.round(w), Math.round(h));
|
||||
};
|
||||
const handleUp = () => {
|
||||
setInteracting(false);
|
||||
window.removeEventListener("pointermove", handleMove);
|
||||
window.removeEventListener("pointerup", handleUp);
|
||||
window.removeEventListener("pointercancel", handleUp);
|
||||
|
|
@ -156,7 +164,8 @@ export default function WinWindow({
|
|||
(win.minimized ? " minimized" : "") +
|
||||
(win.maximized ? " maximized" : "") +
|
||||
(closing ? " closing" : "") +
|
||||
(minimizing ? " minimizing" : "")
|
||||
(minimizing ? " minimizing" : "") +
|
||||
(interacting ? " interacting" : "")
|
||||
}
|
||||
style={style}
|
||||
onPointerDown={onFocus}
|
||||
|
|
|
|||
|
|
@ -818,12 +818,20 @@ function WebsiteApp({
|
|||
|
||||
/** Renders a site in an iframe. Only usable for sites that allow framing. */
|
||||
function EmbeddedSite({ title, url }: { title: string; url: string }) {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
return (
|
||||
<div className="website-embed">
|
||||
{!loaded && (
|
||||
<div className="website-embed-loading" role="status">
|
||||
<span className="website-embed-spinner" aria-hidden="true" />
|
||||
<span>Loading {title}…</span>
|
||||
</div>
|
||||
)}
|
||||
<iframe
|
||||
className="website-embed-frame"
|
||||
src={url}
|
||||
title={title}
|
||||
onLoad={() => setLoaded(true)}
|
||||
allow="autoplay; fullscreen; encrypted-media; clipboard-read; clipboard-write"
|
||||
allowFullScreen
|
||||
/>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue