react-iframe
Version:
A zero-dependency, typed, ref-forwarding iframe component for React 18+
1 lines • 16.4 kB
Source Map (JSON)
{"version":3,"sources":["../src/allow.ts","../src/sandbox.ts","../src/iframe.tsx","../src/use-iframe-message.ts"],"names":["forwardRef","Iframe","useRef","useLayoutEffect","useCallback","jsx","useEffect"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Merge Permissions-Policy `allow` tokens.\n * Feature names are de-duplicated; extras (e.g. \"fullscreen\") are prepended.\n */\nexport function mergeAllow(\n\tallow: string | undefined,\n\textras: readonly string[] = [],\n): string | undefined {\n\tconst tokens = (allow ?? \"\")\n\t\t.split(\";\")\n\t\t.map((part) => part.trim())\n\t\t.filter(Boolean)\n\n\tconst names = new Set(\n\t\ttokens.map((token) => token.split(/\\s+/)[0]).filter((name): name is string => Boolean(name)),\n\t)\n\n\tconst prefix: string[] = []\n\tfor (const extra of extras) {\n\t\tconst name = extra.split(/\\s+/)[0]\n\t\tif (name && !names.has(name)) {\n\t\t\tprefix.push(extra)\n\t\t\tnames.add(name)\n\t\t}\n\t}\n\n\tconst merged = [...prefix, ...tokens]\n\treturn merged.length > 0 ? merged.join(\"; \") : undefined\n}\n","export type SandboxToken =\n\t| \"allow-downloads\"\n\t| \"allow-downloads-without-user-activation\"\n\t| \"allow-forms\"\n\t| \"allow-modals\"\n\t| \"allow-orientation-lock\"\n\t| \"allow-pointer-lock\"\n\t| \"allow-popups\"\n\t| \"allow-popups-to-escape-sandbox\"\n\t| \"allow-presentation\"\n\t| \"allow-same-origin\"\n\t| \"allow-scripts\"\n\t| \"allow-storage-access-by-user-activation\"\n\t| \"allow-top-navigation\"\n\t| \"allow-top-navigation-by-user-activation\"\n\t| \"allow-top-navigation-to-custom-protocols\"\n\nexport type SandboxValue = SandboxToken | (string & {})\n\n/**\n * Normalize `sandbox` so a string is left intact (spreading a string into\n * characters was a 1.x bug) and an array is joined with spaces.\n *\n * An empty string is preserved: it enables the sandbox with no permissions.\n */\nexport function normalizeSandbox(\n\tsandbox: SandboxValue | readonly SandboxValue[] | undefined,\n): string | undefined {\n\tif (sandbox == null) {\n\t\treturn undefined\n\t}\n\tif (typeof sandbox === \"string\") {\n\t\treturn sandbox\n\t}\n\treturn sandbox.join(\" \")\n}\n","import {\n\tforwardRef,\n\tuseCallback,\n\tuseLayoutEffect,\n\tuseRef,\n\ttype CSSProperties,\n\ttype IframeHTMLAttributes,\n\ttype Ref,\n} from \"react\"\n\nimport { mergeAllow } from \"./allow\"\nimport { normalizeSandbox, type SandboxValue } from \"./sandbox\"\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n\tif (typeof ref === \"function\") {\n\t\tref(value)\n\t} else if (ref) {\n\t\tref.current = value\n\t}\n}\n\ntype FetchPriority = \"high\" | \"low\" | \"auto\"\n\ntype NativeIframeProps = Omit<\n\tIframeHTMLAttributes<HTMLIFrameElement>,\n\t\"sandbox\" | \"src\" | \"srcDoc\" | \"onError\"\n>\n\ntype LayoutShortcuts = {\n\t/** Applied as `style.display` when set. */\n\tdisplay?: CSSProperties[\"display\"]\n\t/** Applied as `style.position` when set. */\n\tposition?: CSSProperties[\"position\"]\n\t/** Applied as `style.overflow` when set. */\n\toverflow?: CSSProperties[\"overflow\"]\n\t/**\n\t * Applied as `style.border` when set and neither `style.border` nor\n\t * `styles.border` is provided. Prefer CSS `style={{ border: 0 }}`.\n\t */\n\tframeBorder?: number | string\n}\n\ntype Conveniences = LayoutShortcuts & {\n\t/**\n\t * Alias for `src`. Ignored when `src` or `srcDoc` is set.\n\t * Kept for 1.x compatibility.\n\t */\n\turl?: string\n\tsrc?: string\n\tsrcDoc?: string\n\t/** Merged into `style`. Inline `style` wins over `styles` over layout shortcuts. */\n\tstyles?: CSSProperties\n\tsandbox?: SandboxValue | readonly SandboxValue[]\n\tfetchPriority?: FetchPriority\n\t/**\n\t * Load the frame in a credentialless (ephemeral) context.\n\t * React does not yet treat this as a boolean attribute, so `true` is\n\t * emitted as the empty HTML boolean `credentialless=\"\"`.\n\t */\n\tcredentialless?: boolean\n\t/**\n\t * @deprecated Use `referrerPolicy`.\n\t */\n\treferrerpolicy?: IframeHTMLAttributes<HTMLIFrameElement>[\"referrerPolicy\"]\n\t/**\n\t * @deprecated Use `fetchPriority`.\n\t */\n\timportance?: FetchPriority\n\t/**\n\t * @deprecated Use `allow=\"payment\"`.\n\t */\n\tallowpaymentrequest?: boolean\n\t/**\n\t * Native `error` event. React does not emit iframe `onError` as a\n\t * synthetic event (only img / link / media).\n\t */\n\tonError?: (event: Event) => void\n}\n\ntype WithUrl = { url: string; src?: string; srcDoc?: string }\ntype WithSrc = { src: string; url?: string; srcDoc?: string }\ntype WithSrcDoc = { srcDoc: string; url?: string; src?: string }\n\nexport type IframeProps = NativeIframeProps & Conveniences & (WithUrl | WithSrc | WithSrcDoc)\n\nfunction mergeStyle(\n\tshortcuts: CSSProperties,\n\tstyles: CSSProperties | undefined,\n\tstyle: CSSProperties | undefined,\n): CSSProperties | undefined {\n\tconst merged: CSSProperties = { ...shortcuts, ...styles, ...style }\n\treturn Object.keys(merged).length > 0 ? merged : undefined\n}\n\nexport const Iframe = forwardRef<HTMLIFrameElement, IframeProps>(function Iframe(\n\t{\n\t\turl,\n\t\tsrc,\n\t\tsrcDoc,\n\t\tstyles,\n\t\tstyle,\n\t\tsandbox,\n\t\tallow,\n\t\tallowFullScreen,\n\t\tdisplay,\n\t\tposition,\n\t\toverflow,\n\t\tframeBorder,\n\t\treferrerPolicy,\n\t\treferrerpolicy,\n\t\tfetchPriority,\n\t\timportance,\n\t\tallowpaymentrequest,\n\t\tcredentialless,\n\t\tonError,\n\t\t...rest\n\t},\n\tref,\n) {\n\tconst nodeRef = useRef<HTMLIFrameElement | null>(null)\n\tconst onErrorRef = useRef(onError)\n\n\tuseLayoutEffect(() => {\n\t\tonErrorRef.current = onError\n\t}, [onError])\n\n\t// Keep the host ref callback stable so React does not call it with\n\t// `null` on every parent render when `ref` is an inline function.\n\tconst setRef = useCallback((node: HTMLIFrameElement | null) => {\n\t\tnodeRef.current = node\n\t}, [])\n\n\tuseLayoutEffect(() => {\n\t\tassignRef(ref, nodeRef.current)\n\t\treturn () => assignRef(ref, null)\n\t}, [ref])\n\n\tuseLayoutEffect(() => {\n\t\tconst node = nodeRef.current\n\t\tif (!node) {\n\t\t\treturn\n\t\t}\n\t\t// React attaches iframe `load` but not `error` (img/link/media only).\n\t\tconst listener = (event: Event) => {\n\t\t\tonErrorRef.current?.(event)\n\t\t}\n\t\tnode.addEventListener(\"error\", listener)\n\t\treturn () => node.removeEventListener(\"error\", listener)\n\t}, [])\n\n\tconst extras: string[] = []\n\tif (allowFullScreen) {\n\t\textras.push(\"fullscreen\")\n\t}\n\tif (allowpaymentrequest) {\n\t\textras.push(\"payment\")\n\t}\n\n\tconst shortcutStyle: CSSProperties = {}\n\tif (position != null) {\n\t\tshortcutStyle.position = position\n\t}\n\tif (display != null) {\n\t\tshortcutStyle.display = display\n\t}\n\tif (overflow != null) {\n\t\tshortcutStyle.overflow = overflow\n\t}\n\tif (frameBorder != null && Number(frameBorder) >= 0) {\n\t\tshortcutStyle.border = frameBorder as CSSProperties[\"border\"]\n\t}\n\n\tconst resolvedSrcDoc = srcDoc\n\tconst resolvedSrc = resolvedSrcDoc ? undefined : (src ?? url)\n\tconst resolvedSandbox = normalizeSandbox(sandbox)\n\tconst resolvedAllow = mergeAllow(allow, extras)\n\tconst resolvedStyle = mergeStyle(shortcutStyle, styles, style)\n\tconst resolvedReferrerPolicy = referrerPolicy ?? referrerpolicy\n\tconst resolvedFetchPriority = fetchPriority ?? importance\n\n\tconst iframeProps: IframeHTMLAttributes<HTMLIFrameElement> & {\n\t\tfetchPriority?: FetchPriority\n\t\tcredentialless?: string\n\t} = {\n\t\tsrc: resolvedSrc,\n\t\tsrcDoc: resolvedSrcDoc,\n\t\tsandbox: resolvedSandbox,\n\t\tallow: resolvedAllow,\n\t\tallowFullScreen: allowFullScreen || undefined,\n\t\treferrerPolicy: resolvedReferrerPolicy,\n\t\tstyle: resolvedStyle,\n\t\t...rest,\n\t}\n\n\tif (resolvedFetchPriority) {\n\t\tiframeProps.fetchPriority = resolvedFetchPriority\n\t}\n\tif (credentialless) {\n\t\tiframeProps.credentialless = \"\"\n\t}\n\n\treturn <iframe ref={setRef} {...(iframeProps as IframeHTMLAttributes<HTMLIFrameElement>)} />\n})\n\nIframe.displayName = \"Iframe\"\n","import { useEffect, useRef, type RefObject } from \"react\"\n\nexport type IframeMessageSource =\n\tWindow | HTMLIFrameElement | RefObject<HTMLIFrameElement | null | undefined>\n\nexport type UseIframeMessageOptions = {\n\t/**\n\t * When set, messages from other origins are ignored.\n\t * A `srcDoc` frame has origin `\"null\"` (the string).\n\t */\n\torigin?: string\n\t/**\n\t * When set, messages whose `event.source` is not this frame's\n\t * `contentWindow` are ignored. Prefer this over origin-only filtering\n\t * when several frames share an origin.\n\t */\n\tsource?: IframeMessageSource | null\n}\n\nfunction windowFromSource(\n\tsource: IframeMessageSource | null | undefined,\n): Window | null | undefined {\n\tif (source == null) {\n\t\treturn undefined\n\t}\n\tif (typeof HTMLIFrameElement !== \"undefined\" && source instanceof HTMLIFrameElement) {\n\t\treturn source.contentWindow\n\t}\n\tif (typeof source === \"object\" && \"current\" in source) {\n\t\tconst node = source.current\n\t\tif (node == null) {\n\t\t\treturn null\n\t\t}\n\t\tif (typeof HTMLIFrameElement !== \"undefined\" && node instanceof HTMLIFrameElement) {\n\t\t\treturn node.contentWindow\n\t\t}\n\t}\n\treturn source as Window\n}\n\n/**\n * Subscribe to `window` `\"message\"` events from framed documents.\n * Pair with `postToIframe` or `ref.current.contentWindow.postMessage`.\n */\nexport function useIframeMessage(\n\thandler: (event: MessageEvent) => void,\n\toptions: UseIframeMessageOptions = {},\n): void {\n\tconst { origin, source } = options\n\tconst handlerRef = useRef(handler)\n\tconst sourceRef = useRef(source)\n\n\tuseEffect(() => {\n\t\thandlerRef.current = handler\n\t}, [handler])\n\n\tuseEffect(() => {\n\t\tsourceRef.current = source\n\t}, [source])\n\n\tuseEffect(() => {\n\t\tconst onMessage = (event: MessageEvent) => {\n\t\t\tif (origin && event.origin !== origin) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst expected = windowFromSource(sourceRef.current)\n\t\t\tif (expected !== undefined && event.source !== expected) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\thandlerRef.current(event)\n\t\t}\n\n\t\twindow.addEventListener(\"message\", onMessage)\n\t\treturn () => window.removeEventListener(\"message\", onMessage)\n\t}, [origin])\n}\n\n/**\n * Post a message to an iframe's `contentWindow`.\n * Returns `false` when the iframe is missing or not yet ready.\n *\n * `targetOrigin` is required. Use the child's origin. For a `srcDoc`\n * frame the origin is opaque and the browser rejects `\"null\"` — pass `\"*\"`.\n */\nexport function postToIframe(\n\tiframe: HTMLIFrameElement | null | undefined,\n\tmessage: unknown,\n\ttargetOrigin: string,\n): boolean {\n\tconst target = iframe?.contentWindow\n\tif (!target) {\n\t\treturn false\n\t}\n\ttarget.postMessage(message, targetOrigin)\n\treturn true\n}\n"]}