summaryrefslogtreecommitdiff
path: root/apps/web/src/preview/popout-store.ts
blob: 1b91f6a255fab89de218fa5221c96202955751a3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import { create } from "zustand";

interface PreviewPopoutState {
	pipWindow: Window | null;
	container: HTMLElement | null;
	openPopout: () => Promise<void>;
	dockPopout: () => void;
}

function copyStylesInto({ pipWindow }: { pipWindow: Window }) {
	const styleNodes = document.head.querySelectorAll(
		"style, link[rel='stylesheet']",
	);
	for (const node of styleNodes) {
		if (node instanceof HTMLStyleElement) {
			pipWindow.document.head.appendChild(node.cloneNode(true));
		} else if (node instanceof HTMLLinkElement) {
			const link = pipWindow.document.createElement("link");
			link.rel = "stylesheet";
			link.href = node.href;
			pipWindow.document.head.appendChild(link);
		}
	}
}

function preparePipDocument({ pipWindow }: { pipWindow: Window }) {
	pipWindow.document.documentElement.className =
		document.documentElement.className;
	pipWindow.document.documentElement.style.height = "100%";
	pipWindow.document.body.className = document.body.className;
	pipWindow.document.body.style.margin = "0";
	pipWindow.document.body.style.height = "100%";
	pipWindow.document.body.style.overflow = "hidden";
	pipWindow.document.title = "Preview";
	copyStylesInto({ pipWindow });
}

export const usePreviewPopoutStore = create<PreviewPopoutState>()(
	(set, get) => ({
		pipWindow: null,
		container: null,
		openPopout: async () => {
			if (get().container !== null) {
				return;
			}
			if (!("documentPictureInPicture" in window)) {
				return;
			}
			const pip = window.documentPictureInPicture;
			if (!pip) {
				return;
			}
			const pipWindow = await pip.requestWindow();
			preparePipDocument({ pipWindow });
			const container = pipWindow.document.createElement("div");
			container.style.width = "100%";
			container.style.height = "100%";
			pipWindow.document.body.appendChild(container);
			pipWindow.addEventListener(
				"pagehide",
				() => {
					set({ pipWindow: null, container: null });
				},
				{ once: true },
			);
			set({ pipWindow, container });
		},
		dockPopout: () => {
			const { pipWindow } = get();
			if (pipWindow && !pipWindow.closed) {
				pipWindow.close();
			}
			set({ pipWindow: null, container: null });
		},
	}),
);