UNPKG

react-iframe

Version:

A zero-dependency, typed, ref-forwarding iframe component for React 18+

190 lines (186 loc) • 5.11 kB
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