PluginProbe
Extendify / 3.1.5
Extendify v3.1.5
3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 0.7.0 All 126 releases
extendify / src / Launch / hooks / usePreviewIframe.js

usePreviewIframe.js in Extendify 3.1.5, at src/Launch/hooks/usePreviewIframe.js

120 lines 3.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { useIsMounted } from '@launch/hooks/useIsMounted';
2 import {
3 useCallback,
4 useLayoutEffect,
5 useRef,
6 useState,
7 } from '@wordpress/element';
8 import { debounce } from 'lodash';
9
10 const originalHeights = new WeakMap();
11
12 export const usePreviewIframe = ({ container, onLoad, ready, loadDelay }) => {
13 const isMounted = useIsMounted();
14 const [waitForIframe, setWaitForIframe] = useState(0);
15 const [iFrame, setIFrame] = useState(null);
16 const [maybeOk, setMaybeOk] = useState(false);
17 const isUpdating = useRef(false);
18
19 const updateCoverBlocks = useCallback((frame, cntnr) => {
20 if (!frame) return;
21
22 const ft = frame.getBoundingClientRect().top;
23 const ct = cntnr.getBoundingClientRect().top;
24 // If they have scrolled, don't mess with it
25 if (ft < ct) return;
26
27 isUpdating.current = true;
28
29 // WP scales the iframe to fit the container
30 // Get the scale value from the transform property
31 let scale = cntnr
32 .querySelector('[style*="scale"]')
33 ?.style?.transform?.match(/scale\((.*?)\)/)?.[1];
34 scale = scale ? parseFloat(scale) : null;
35 // Get the height of the container and scale it
36 const cntnrHScaled = cntnr.offsetHeight / (scale ?? 1);
37 frame.style.setProperty('max-height', `${cntnrHScaled}px`, 'important');
38
39 const coverBlocks =
40 frame?.contentDocument?.querySelectorAll('.wp-block-cover');
41 for (const el of coverBlocks) {
42 if (!originalHeights.has(el)) {
43 // Cache the original 'vh' value
44 originalHeights.set(el, el.style.minHeight);
45 }
46 // Reapply the original 'vh' value so it can be used in computations
47 el.style.minHeight = originalHeights.get(el);
48 }
49
50 for (const el of coverBlocks) {
51 if (!frame.contentDocument?.defaultView) return;
52 // Get the computed height in px and use it for your calculation
53 const computedHeight = parseFloat(
54 frame.contentDocument.defaultView.getComputedStyle(el).height,
55 );
56 el.offsetHeight; // Force a reflow
57 el.style.minHeight =
58 computedHeight > 784 ? '784px' : `${computedHeight}px`;
59 }
60
61 frame.style.setProperty('max-height', 'none', 'important');
62 isUpdating.current = false;
63 }, []);
64
65 useLayoutEffect(() => {
66 if (!ready) return;
67 // continuously check for iframe
68 const interval = setTimeout(() => {
69 const frame = container?.querySelector('iframe[title]');
70 // If not found, retry by updating state
71 if (!frame) return setWaitForIframe((prev) => prev + 1);
72 setIFrame(frame);
73 requestAnimationFrame(() => onLoad(frame, container));
74 }, 100);
75 return () => clearTimeout(interval);
76 }, [iFrame, ready, waitForIframe, container, onLoad]);
77
78 useLayoutEffect(() => {
79 setMaybeOk(false);
80 // After the iFrame is found, wait for it to load
81 // Note: using load event is not reliable
82 if (!iFrame?.contentDocument) return;
83
84 const observerConfig = {
85 attributes: false,
86 childList: true,
87 subtree: true,
88 };
89
90 const loaded = debounce(() => {
91 if (!isMounted.current || isUpdating.current) return;
92 // Allow the skeleton loader to be removed
93 setTimeout(() => setMaybeOk(true), loadDelay);
94 updateCoverBlocks(iFrame, container);
95 }, 50);
96
97 // Run once in case the iframe is already loaded
98 const firstRun = requestAnimationFrame(() => loaded(iFrame, container));
99 // Allow only for a max 2s wait time in case the mutation observer gets stuck
100 const longestWait = setTimeout(() => {
101 if (isMounted.current) {
102 m.disconnect();
103 loaded();
104 }
105 }, 2000);
106 // Continuously check for changes
107 const m = new MutationObserver(loaded);
108 m.observe(iFrame.contentDocument, observerConfig);
109
110 return () => {
111 cancelAnimationFrame(firstRun);
112 clearTimeout(longestWait);
113 loaded.cancel();
114 m?.disconnect();
115 };
116 }, [iFrame, container, isMounted, ready, updateCoverBlocks, loadDelay]);
117
118 return { loading: !iFrame, ready: maybeOk };
119 };
120