UNPKG

@open-iframe-resizer/react

Version:

Open-source modern iframe resizer component

289 lines (288 loc) 8.81 kB
"use client"; import { jsx as H } from "react/jsx-runtime"; import { forwardRef as T, useRef as v, useEffect as W, useCallback as x } from "react"; const L = () => typeof window < "u", B = () => { try { return window.self !== window.top; } catch { return !0; } }, E = (e) => e instanceof HTMLIFrameElement, z = (e) => { window.document.readyState === "complete" ? e() : window.addEventListener("load", e, { once: !0 }); }, C = (e, t) => { t(), e.addEventListener("load", t, { once: !0 }); }, F = (e, t) => { const n = e.contentWindow?.document.readyState === "complete"; e.src !== "about:blank" && e.contentWindow?.location.href !== "about:blank" && n && t(); }, U = () => ({ offsetSize: 0, checkOrigin: !0, enableLegacyLibSupport: !1 }); async function j(e) { try { return e.contentDocument?.URL === "about:blank" ? new Promise((t) => { e.addEventListener("load", () => t(e.contentDocument !== null), { once: !0 }); }) : e.contentDocument !== null; } catch { return !1; } } const q = (e) => { try { const t = new URL(e.src).origin; if (t !== "about:blank") return t; } catch { } return null; }, N = (e) => (Object.keys(e).forEach((t) => { e[t] === void 0 && delete e[t]; }), e), I = (e) => { const { height: t, width: n } = e.getBoundingClientRect(); return { height: Math.ceil(t), width: Math.ceil(n) }; }, l = (e, t) => e ? t ? e.querySelector(t) : e.documentElement : null, R = (e, t) => { e && (t.bodyPadding && (e.body.style.padding = t.bodyPadding), t.bodyMargin && (e.body.style.margin = t.bodyMargin)); }, h = (e) => e <= 100 ? 100 : e <= 120 ? 1e3 : 1e4, $ = () => "[iFrameSizer]ID:0:false:false:32:true:true::auto:::0:false:child:auto:true:::true:::false"; function G(e) { if (typeof e.data != "string" || !e.data.startsWith("[iFrameSizer]")) return null; const t = e.data.endsWith("mutationObserver") || e.data.endsWith("resizeObserver"), n = e.data.search(/:init(:[\d.]+)?/g) >= 0; if (!t && !n) return null; const [r, i] = e.data.split(":"), o = +i; return o > 0 ? o : null; } const S = Z(); let m = []; const J = async (e, t) => { if (!L()) return []; const n = { ...U(), ...N(e ?? {}) }, r = K(t), i = Q(n, r); return Promise.all( r.map(async (o) => { const s = { iframe: o, settings: n, interactionState: { isHovered: !1 }, initContext: { isInitialized: !1, retryAttempts: 0 } }, { unsubscribe: c, resize: a } = await V(s, i); return m.push(s), { unsubscribe: () => { c(), m = m.filter((u) => u.iframe !== o); }, resize: a }; }) ); }; function K(e) { return typeof e == "string" ? Array.from(document.querySelectorAll(e)).filter(E) : e ? E(e) ? [e] : [] : Array.from(document.getElementsByTagName("iframe")); } function Q(e, t) { if (Array.isArray(e.checkOrigin)) return e.checkOrigin; if (!e.checkOrigin) return []; const n = []; for (const r of t) { const i = q(r); i && n.push(i); } return n; } async function V(e, t) { const n = await j(e.iframe), { unsubscribe: r, resize: i } = n ? Y(e) : X(e, t), o = _(e); return { unsubscribe: () => { r(), o(); }, resize: i }; } function X(e, t) { const { iframe: n, initContext: r, settings: { checkOrigin: i, enableLegacyLibSupport: o, targetElementSelector: s, bodyPadding: c, bodyMargin: a } } = e, u = (d) => { const O = d.origin === "null", P = !i || O || t.includes(d.origin); if (!(n.contentWindow !== d.source || !P)) { if (d.data?.type === "iframe-resized") { const { height: f } = d.data; f && b({ newHeight: f, registeredElement: e }); return; } if (o) { const f = G(d); f !== null && b({ newHeight: f, registeredElement: e }); return; } } }; window.addEventListener("message", u); const p = o ? $() : { type: "iframe-child-init", targetElementSelector: s, bodyPadding: c, bodyMargin: a }, g = () => { C(n, () => n.contentWindow?.postMessage(p, "*")), r.retryAttempts++, r.retryTimeoutId = window.setTimeout(g, h(r.retryAttempts)); }, w = () => { r.isInitialized && (r.isInitialized = !1, r.retryAttempts = 0, g()); }; return n.addEventListener("load", w), g(), { unsubscribe: () => { window.removeEventListener("message", u), n.removeEventListener("load", w); }, resize: () => { const d = { type: "iframe-get-child-dimensions" }; n.contentWindow?.postMessage(d, "*"); } }; } function Y(e) { const { iframe: t, settings: n } = e, { targetElementSelector: r } = n; let i = 0; const o = () => { const s = l(t.contentDocument, r); if (!t.contentDocument || !s) return i++, setTimeout(o, h(i)); R(t.contentDocument, n), S().observe(s); }; return F(t, o), t.addEventListener("load", o), { unsubscribe: () => { const s = l(t.contentDocument, r); s && S().unobserve(s), t.removeEventListener("load", o); }, resize: () => M(e) }; } function _({ iframe: e, interactionState: t, settings: n }) { if (!n.onBeforeIframeResize && !n.onIframeResize) return () => { }; const r = () => { t.isHovered = !0; }, i = () => { t.isHovered = !1; }; return e.addEventListener("mouseenter", r), e.addEventListener("mouseleave", i), () => { e.removeEventListener("mouseenter", r), e.removeEventListener("mouseleave", i); }; } function Z() { let e = null; return () => { if (!e) { const t = ({ target: n }) => { const r = m.find(({ iframe: i }) => i.contentDocument === n.ownerDocument); r && M(r); }; e = new ResizeObserver((n) => n.forEach(t)); } return e; }; } function M(e) { const { iframe: t, settings: n } = e, r = l(t.contentDocument, n.targetElementSelector); if (!r) return; const { height: i } = I(r); i && b({ newHeight: i, registeredElement: e }); } function b({ registeredElement: e, newHeight: t }) { const { iframe: n, settings: r, interactionState: i, initContext: o } = e; if (o.isInitialized || (o.isInitialized = !0, clearTimeout(o.retryTimeoutId)), r.onBeforeIframeResize?.({ iframe: n, interactionState: { ...i }, settings: { ...r }, observedHeight: t }) === !1) return; const s = n.getBoundingClientRect(), c = t + r.offsetSize; if (n.style.height = `${c}px`, !r.onIframeResize) return; const a = { iframe: n, settings: { ...r }, interactionState: { ...i }, previousRenderState: { rect: s }, nextRenderState: { rect: n.getBoundingClientRect() } }; r.onIframeResize(a); } const ee = re(); let y = !1, k; te(); function te() { !L() || !B() || window.addEventListener("message", (e) => { if (e.data?.type === "iframe-child-init") return z(() => A(e)); if (e.data?.type === "iframe-get-child-dimensions") return z(() => ne(e)); }); } function A(e, t = 0) { const { targetElementSelector: n, bodyPadding: r, bodyMargin: i } = e.data, o = l(document, n); if (y || window.parent !== e.source) return; if (!o) return setTimeout(() => A(e, t + 1), h(t)); R(document, { bodyMargin: i, bodyPadding: r }), k = n; const s = ee(); s.disconnect(), s.observe(o), y = !0; } function ne(e) { const t = l(document, k); !y || window.parent !== e.source || !t || D(t); } function re() { let e = null; return () => (e || (e = new ResizeObserver((t) => { t[0].target && D(t[0].target); })), e); } const D = (e) => { const { width: t, height: n } = I(e), r = { type: "iframe-resized", width: t, height: n }; window.parent.postMessage(r, "*"); }, ie = T((e, t) => { const n = v(null), r = v([]), { settings: i, iframeAttributes: o } = oe(e); W(() => { let c = !1; if (n.current) return J(i, n.current).then((a) => { c ? a.forEach((u) => { u.unsubscribe(); }) : r.current = a; }), () => { c = !0, r.current?.forEach((a) => { a.unsubscribe(); }), r.current = []; }; }, [i]); const s = x( (c) => { n.current = c, typeof t == "function" ? t(c) : t && (t.current = c); }, [t] ); return /* @__PURE__ */ H("iframe", { ...o, ref: s }); }); ie.displayName = "IframeResizer"; function oe(e) { const { offsetSize: t, enableLegacyLibSupport: n, checkOrigin: r, onIframeResize: i, onBeforeIframeResize: o, targetElementSelector: s, bodyMargin: c, bodyPadding: a, ...u } = e; return { iframeAttributes: u, settings: { offsetSize: t, enableLegacyLibSupport: n, checkOrigin: r, onIframeResize: i, onBeforeIframeResize: o, targetElementSelector: s, bodyMargin: c, bodyPadding: a } }; } export { ie as IframeResizer };