react-iframe
Version:
A zero-dependency, typed, ref-forwarding iframe component for React 18+
190 lines (186 loc) • 5.11 kB
JavaScript
import { forwardRef, useRef, useLayoutEffect, useCallback, useEffect } from 'react';
import { jsx } from 'react/jsx-runtime';
// src/iframe.tsx
// src/allow.ts
function mergeAllow(allow, extras = []) {
const tokens = (allow ?? "").split(";").map((part) => part.trim()).filter(Boolean);
const names = new Set(
tokens.map((token) => token.split(/\s+/)[0]).filter((name) => Boolean(name))
);
const prefix = [];
for (const extra of extras) {
const name = extra.split(/\s+/)[0];
if (name && !names.has(name)) {
prefix.push(extra);
names.add(name);
}
}
const merged = [...prefix, ...tokens];
return merged.length > 0 ? merged.join("; ") : void 0;
}
// src/sandbox.ts
function normalizeSandbox(sandbox) {
if (sandbox == null) {
return void 0;
}
if (typeof sandbox === "string") {
return sandbox;
}
return sandbox.join(" ");
}
function assignRef(ref, value) {
if (typeof ref === "function") {
ref(value);
} else if (ref) {
ref.current = value;
}
}
function mergeStyle(shortcuts, styles, style) {
const merged = { ...shortcuts, ...styles, ...style };
return Object.keys(merged).length > 0 ? merged : void 0;
}
var Iframe = forwardRef(function Iframe2({
url,
src,
srcDoc,
styles,
style,
sandbox,
allow,
allowFullScreen,
display,
position,
overflow,
frameBorder,
referrerPolicy,
referrerpolicy,
fetchPriority,
importance,
allowpaymentrequest,
credentialless,
onError,
...rest
}, ref) {
const nodeRef = useRef(null);
const onErrorRef = useRef(onError);
useLayoutEffect(() => {
onErrorRef.current = onError;
}, [onError]);
const setRef = useCallback((node) => {
nodeRef.current = node;
}, []);
useLayoutEffect(() => {
assignRef(ref, nodeRef.current);
return () => assignRef(ref, null);
}, [ref]);
useLayoutEffect(() => {
const node = nodeRef.current;
if (!node) {
return;
}
const listener = (event) => {
onErrorRef.current?.(event);
};
node.addEventListener("error", listener);
return () => node.removeEventListener("error", listener);
}, []);
const extras = [];
if (allowFullScreen) {
extras.push("fullscreen");
}
if (allowpaymentrequest) {
extras.push("payment");
}
const shortcutStyle = {};
if (position != null) {
shortcutStyle.position = position;
}
if (display != null) {
shortcutStyle.display = display;
}
if (overflow != null) {
shortcutStyle.overflow = overflow;
}
if (frameBorder != null && Number(frameBorder) >= 0) {
shortcutStyle.border = frameBorder;
}
const resolvedSrcDoc = srcDoc;
const resolvedSrc = resolvedSrcDoc ? void 0 : src ?? url;
const resolvedSandbox = normalizeSandbox(sandbox);
const resolvedAllow = mergeAllow(allow, extras);
const resolvedStyle = mergeStyle(shortcutStyle, styles, style);
const resolvedReferrerPolicy = referrerPolicy ?? referrerpolicy;
const resolvedFetchPriority = fetchPriority ?? importance;
const iframeProps = {
src: resolvedSrc,
srcDoc: resolvedSrcDoc,
sandbox: resolvedSandbox,
allow: resolvedAllow,
allowFullScreen: allowFullScreen || void 0,
referrerPolicy: resolvedReferrerPolicy,
style: resolvedStyle,
...rest
};
if (resolvedFetchPriority) {
iframeProps.fetchPriority = resolvedFetchPriority;
}
if (credentialless) {
iframeProps.credentialless = "";
}
return /* @__PURE__ */ jsx("iframe", { ref: setRef, ...iframeProps });
});
Iframe.displayName = "Iframe";
function windowFromSource(source) {
if (source == null) {
return void 0;
}
if (typeof HTMLIFrameElement !== "undefined" && source instanceof HTMLIFrameElement) {
return source.contentWindow;
}
if (typeof source === "object" && "current" in source) {
const node = source.current;
if (node == null) {
return null;
}
if (typeof HTMLIFrameElement !== "undefined" && node instanceof HTMLIFrameElement) {
return node.contentWindow;
}
}
return source;
}
function useIframeMessage(handler, options = {}) {
const { origin, source } = options;
const handlerRef = useRef(handler);
const sourceRef = useRef(source);
useEffect(() => {
handlerRef.current = handler;
}, [handler]);
useEffect(() => {
sourceRef.current = source;
}, [source]);
useEffect(() => {
const onMessage = (event) => {
if (origin && event.origin !== origin) {
return;
}
const expected = windowFromSource(sourceRef.current);
if (expected !== void 0 && event.source !== expected) {
return;
}
handlerRef.current(event);
};
window.addEventListener("message", onMessage);
return () => window.removeEventListener("message", onMessage);
}, [origin]);
}
function postToIframe(iframe, message, targetOrigin) {
const target = iframe?.contentWindow;
if (!target) {
return false;
}
target.postMessage(message, targetOrigin);
return true;
}
export { Iframe, Iframe as default, postToIframe, useIframeMessage };
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map