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