@open-iframe-resizer/react
Version:
Open-source modern iframe resizer component
289 lines (288 loc) • 8.81 kB
JavaScript
"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
};