@willbooster/react-frame-component 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/Frame.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";const e=require("./_virtual/_rolldown/runtime.cjs"),t=require("./Context.cjs"),n=require("./Content.cjs");let r=require("react");r=e.__toESM(r,1);let i=require("react-dom");i=e.__toESM(i,1);let a=require("react/jsx-runtime");const o=`<!DOCTYPE html><html><head></head><body><div class="frame-root"></div></body></html>`;var s=class extends r.Component{constructor(...e){super(...e),this._isMounted=!1,this.nodeRef=r.createRef(),this.state={iframeLoaded:!1},this.setRef=e=>{this.nodeRef.current=e;let{forwardedRef:t}=this.props;typeof t==`function`?t(e):t&&(t.current=e)},this.handleLoad=()=>{this.state.iframeLoaded||this.setState({iframeLoaded:!0})}}componentDidMount(){this._isMounted=!0,this.getDoc()&&this.nodeRef.current?.contentWindow?.addEventListener(`DOMContentLoaded`,this.handleLoad),this.props.dangerouslyUseDocWrite&&this.handleLoad()}componentWillUnmount(){this._isMounted=!1,this.nodeRef.current?.contentWindow?.removeEventListener(`DOMContentLoaded`,this.handleLoad)}getDoc(){return this.nodeRef.current?.contentDocument??void 0}getMountTarget(){let e=this.getDoc();if(!(!e||!e.body))return this.props.mountTarget?e.querySelector(this.props.mountTarget)??void 0:e.body.children[0]}renderFrameContents(){if(!this._isMounted)return;let e=this.getDoc();if(!e)return;let{contentDidMount:r=()=>{},contentDidUpdate:s=()=>{}}=this.props,c=e.defaultView??void 0;(!this._contextValue||this._contextValue.document!==e||this._contextValue.window!==c)&&(this._contextValue={document:e,window:c});let l=(0,a.jsx)(n.default,{contentDidMount:r,contentDidUpdate:s,children:(0,a.jsx)(t.FrameContextProvider,{value:this._contextValue,children:(0,a.jsx)(`div`,{className:`frame-content`,children:this.props.children})})});this.props.dangerouslyUseDocWrite&&e.body.children.length===0&&(e.open(`text/html`,`replace`),e.write(this.props.initialContent??o),e.close());let u=this.getMountTarget();if(u)return[i.createPortal(this.props.head,e.head,`head`),i.createPortal(l,u,`contents`)]}render(){let{children:e,contentDidMount:t,contentDidUpdate:n,dangerouslyUseDocWrite:r,forwardedRef:i,head:s,initialContent:c,mountTarget:l,...u}=this.props;return r||(u.srcDoc=c??o),(0,a.jsx)(`iframe`,{...u,ref:this.setRef,onLoad:this.handleLoad,children:this.state.iframeLoaded&&this.renderFrameContents()})}},c=r.forwardRef((e,t)=>(0,a.jsx)(s,{...e,forwardedRef:t}));exports.default=c;
1
+ "use strict";const e=require("./_virtual/_rolldown/runtime.cjs"),t=require("./Context.cjs"),n=require("./Content.cjs");let r=require("react");r=e.__toESM(r,1);let i=require("react-dom");i=e.__toESM(i,1);let a=require("react/jsx-runtime");const o=`<!DOCTYPE html><html><head></head><body><div class="frame-root"></div></body></html>`,s=new WeakSet;function c(e){if(s.has(e))return;s.add(e);let t=e.removeChild.bind(e);e.removeChild=n=>n.parentNode===e?t(n):n;let n=e.insertBefore.bind(e);e.insertBefore=(t,r)=>r!==null&&r.parentNode!==e?(e.append(t),t):n(t,r)}var l=class extends r.Component{constructor(...e){super(...e),this._isMounted=!1,this.nodeRef=r.createRef(),this.state={iframeLoaded:!1},this.setRef=e=>{this.nodeRef.current=e;let{forwardedRef:t}=this.props;typeof t==`function`?t(e):t&&(t.current=e)},this.handleLoad=()=>{this.state.iframeLoaded||this.setState({iframeLoaded:!0})}}componentDidMount(){this._isMounted=!0,this.getDoc()&&this.nodeRef.current?.contentWindow?.addEventListener(`DOMContentLoaded`,this.handleLoad),this.props.dangerouslyUseDocWrite&&this.handleLoad()}componentWillUnmount(){this._isMounted=!1,this.getDoc()&&this.nodeRef.current?.contentWindow?.removeEventListener(`DOMContentLoaded`,this.handleLoad)}getDoc(){return this.nodeRef.current?.contentDocument??void 0}getMountTarget(){let e=this.getDoc();if(!(!e||!e.body))return this.props.mountTarget?e.querySelector(this.props.mountTarget)??void 0:e.body.children[0]}renderFrameContents(){if(!this._isMounted)return;let e=this.getDoc();if(!e)return;let{contentDidMount:r=()=>{},contentDidUpdate:s=()=>{}}=this.props,l=e.defaultView??void 0;(!this._contextValue||this._contextValue.document!==e||this._contextValue.window!==l)&&(this._contextValue={document:e,window:l});let u=(0,a.jsx)(n.default,{contentDidMount:r,contentDidUpdate:s,children:(0,a.jsx)(t.FrameContextProvider,{value:this._contextValue,children:(0,a.jsx)(`div`,{className:`frame-content`,children:this.props.children})})});this.props.dangerouslyUseDocWrite&&e.body.children.length===0&&(e.open(`text/html`,`replace`),e.write(this.props.initialContent??o),e.close());let d=this.getMountTarget();if(d)return c(e.head),c(d),[i.createPortal(this.props.head,e.head,`head`),i.createPortal(u,d,`contents`)]}render(){let{children:e,contentDidMount:t,contentDidUpdate:n,dangerouslyUseDocWrite:r,forwardedRef:i,head:s,initialContent:c,mountTarget:l,...u}=this.props;return r||(u.srcDoc=c??o),(0,a.jsx)(`iframe`,{...u,ref:this.setRef,onLoad:this.handleLoad,children:this.state.iframeLoaded&&this.renderFrameContents()})}},u=r.forwardRef((e,t)=>(0,a.jsx)(l,{...e,forwardedRef:t}));exports.default=u;
2
2
  //# sourceMappingURL=Frame.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Frame.cjs","names":["React","Content","FrameContextProvider","ReactDOM"],"sources":["../src/Frame.tsx"],"sourcesContent":["import * as React from 'react';\nimport * as ReactDOM from 'react-dom';\nimport type { FrameContextProps } from './Context.js';\nimport { FrameContextProvider } from './Context.js';\nimport Content from './Content.js';\n\ninterface FrameOwnProps extends React.IframeHTMLAttributes<HTMLIFrameElement> {\n head?: React.ReactNode | undefined;\n mountTarget?: string | undefined;\n initialContent?: string | undefined;\n contentDidMount?: (() => void) | undefined;\n contentDidUpdate?: (() => void) | undefined;\n dangerouslyUseDocWrite?: boolean | undefined;\n children?: React.ReactNode | undefined;\n}\n\n// The public props type accepts `ref` (forwarded to the underlying iframe element),\n// while the internal class must not declare `ref` in its props so that a ref on\n// `Frame` itself keeps referring to the component instance.\nexport type FrameComponentProps = FrameOwnProps & React.RefAttributes<HTMLIFrameElement>;\n\ninterface InternalFrameProps extends FrameOwnProps {\n forwardedRef?: React.ForwardedRef<HTMLIFrameElement> | undefined;\n}\n\ninterface FrameState {\n iframeLoaded: boolean;\n}\n\n// React warns when you render directly into the body since browser extensions\n// also inject into the body and can mess up React. For this reason\n// initialContent is expected to have a div inside of the body\n// element that we render react into.\nconst DEFAULT_INITIAL_CONTENT = '<!DOCTYPE html><html><head></head><body><div class=\"frame-root\"></div></body></html>';\n\nexport class Frame extends React.Component<InternalFrameProps, FrameState> {\n private _isMounted = false;\n private _contextValue: FrameContextProps | undefined;\n private readonly nodeRef: React.MutableRefObject<HTMLIFrameElement | null> = React.createRef();\n\n override state: FrameState = { iframeLoaded: false };\n\n override componentDidMount(): void {\n this._isMounted = true;\n\n const doc = this.getDoc();\n\n if (doc) {\n this.nodeRef.current?.contentWindow?.addEventListener('DOMContentLoaded', this.handleLoad);\n }\n\n if (this.props.dangerouslyUseDocWrite) {\n this.handleLoad();\n }\n }\n\n override componentWillUnmount(): void {\n this._isMounted = false;\n\n // The listener was added to the iframe's content window in componentDidMount.\n this.nodeRef.current?.contentWindow?.removeEventListener('DOMContentLoaded', this.handleLoad);\n }\n\n getDoc(): Document | undefined {\n return this.nodeRef.current?.contentDocument ?? undefined;\n }\n\n getMountTarget(): Element | undefined {\n const doc = this.getDoc();\n\n if (!doc || !doc.body) {\n return undefined;\n }\n\n if (this.props.mountTarget) {\n return doc.querySelector(this.props.mountTarget) ?? undefined;\n }\n\n return doc.body.children[0];\n }\n\n setRef = (node: HTMLIFrameElement | null): void => {\n this.nodeRef.current = node;\n\n const { forwardedRef } = this.props;\n if (typeof forwardedRef === 'function') {\n forwardedRef(node);\n } else if (forwardedRef) {\n forwardedRef.current = node;\n }\n };\n\n handleLoad = (): void => {\n // Bail update as some browsers will trigger on both DOMContentLoaded & onLoad ala firefox\n if (!this.state.iframeLoaded) {\n this.setState({ iframeLoaded: true });\n }\n };\n\n renderFrameContents(): React.ReactNode {\n if (!this._isMounted) {\n return undefined;\n }\n\n const doc = this.getDoc();\n\n if (!doc) {\n return undefined;\n }\n\n const { contentDidMount = () => {}, contentDidUpdate = () => {} } = this.props;\n\n const win = doc.defaultView ?? undefined;\n // Reuse the context value across renders so consumers only re-render when the\n // document or window actually changes, not on every render of Frame.\n if (!this._contextValue || this._contextValue.document !== doc || this._contextValue.window !== win) {\n this._contextValue = { document: doc, window: win };\n }\n const contents = (\n <Content contentDidMount={contentDidMount} contentDidUpdate={contentDidUpdate}>\n <FrameContextProvider value={this._contextValue}>\n <div className=\"frame-content\">{this.props.children}</div>\n </FrameContextProvider>\n </Content>\n );\n\n if (this.props.dangerouslyUseDocWrite && doc.body.children.length === 0) {\n doc.open('text/html', 'replace');\n doc.write(this.props.initialContent ?? DEFAULT_INITIAL_CONTENT);\n doc.close();\n }\n\n const mountTarget = this.getMountTarget();\n\n if (!mountTarget) {\n return undefined;\n }\n\n return [\n ReactDOM.createPortal(this.props.head, doc.head, 'head'),\n ReactDOM.createPortal(contents, mountTarget, 'contents'),\n ];\n }\n\n override render(): React.ReactElement {\n // The iframe isn't ready so we drop children from the iframe props here. #12, #17\n const {\n children: _children,\n contentDidMount: _contentDidMount,\n contentDidUpdate: _contentDidUpdate,\n dangerouslyUseDocWrite,\n forwardedRef: _forwardedRef,\n head: _head,\n initialContent,\n mountTarget: _mountTarget,\n ...iframeProps\n } = this.props;\n\n if (!dangerouslyUseDocWrite) {\n iframeProps.srcDoc = initialContent ?? DEFAULT_INITIAL_CONTENT;\n }\n\n return (\n // oxlint-disable-next-line jsx-a11y/iframe-has-title -- consumers can pass `title` via the props spread\n <iframe {...iframeProps} ref={this.setRef} onLoad={this.handleLoad}>\n {this.state.iframeLoaded && this.renderFrameContents()}\n </iframe>\n );\n }\n}\n\nexport default React.forwardRef<HTMLIFrameElement, FrameOwnProps>((props, ref) => (\n <Frame {...props} forwardedRef={ref} />\n));\n"],"mappings":"8OAiCA,MAAM,EAA0B,uFAEhC,IAAa,EAAb,cAA2BA,EAAM,SAA0C,+CACpD,gBAEwDA,EAAM,UAAU,aAEhE,CAAE,aAAc,EAAM,cAyCzC,GAAyC,CACjD,KAAK,QAAQ,QAAU,EAEvB,GAAM,CAAE,gBAAiB,KAAK,MAC1B,OAAO,GAAiB,WAC1B,EAAa,CAAI,EACR,IACT,EAAa,QAAU,EAE3B,sBAEyB,CAElB,KAAK,MAAM,cACd,KAAK,SAAS,CAAE,aAAc,EAAK,CAAC,CAExC,EAvDA,mBAAmC,CACjC,KAAK,WAAa,GAEN,KAAK,OAEX,GACJ,KAAK,QAAQ,SAAS,eAAe,iBAAiB,mBAAoB,KAAK,UAAU,EAGvF,KAAK,MAAM,wBACb,KAAK,WAAW,CAEpB,CAEA,sBAAsC,CACpC,KAAK,WAAa,GAGlB,KAAK,QAAQ,SAAS,eAAe,oBAAoB,mBAAoB,KAAK,UAAU,CAC9F,CAEA,QAA+B,CAC7B,OAAO,KAAK,QAAQ,SAAS,iBAAmB,IAAA,EAClD,CAEA,gBAAsC,CACpC,IAAM,EAAM,KAAK,OAAO,EAEpB,MAAC,GAAO,CAAC,EAAI,MAQjB,OAJI,KAAK,MAAM,YACN,EAAI,cAAc,KAAK,MAAM,WAAW,GAAK,IAAA,GAG/C,EAAI,KAAK,SAAS,EAC3B,CAoBA,qBAAuC,CACrC,GAAI,CAAC,KAAK,WACR,OAGF,IAAM,EAAM,KAAK,OAAO,EAExB,GAAI,CAAC,EACH,OAGF,GAAM,CAAE,sBAAwB,CAAC,EAAG,uBAAyB,CAAC,GAAM,KAAK,MAEnE,EAAM,EAAI,aAAe,IAAA,IAG3B,CAAC,KAAK,eAAiB,KAAK,cAAc,WAAa,GAAO,KAAK,cAAc,SAAW,KAC9F,KAAK,cAAgB,CAAE,SAAU,EAAK,OAAQ,CAAI,GAEpD,IAAM,GACJ,EAAA,EAAA,IAAA,CAACC,EAAAA,QAAD,CAA0B,kBAAmC,6BAC3D,EAAA,EAAA,IAAA,CAACC,EAAAA,qBAAD,CAAsB,MAAO,KAAK,wBAChC,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,yBAAiB,KAAK,MAAM,QAAc,CAAA,CACrC,CAAA,CACf,CAAA,EAGP,KAAK,MAAM,wBAA0B,EAAI,KAAK,SAAS,SAAW,IACpE,EAAI,KAAK,YAAa,SAAS,EAC/B,EAAI,MAAM,KAAK,MAAM,gBAAkB,CAAuB,EAC9D,EAAI,MAAM,GAGZ,IAAM,EAAc,KAAK,eAAe,EAEnC,KAIL,MAAO,CACLC,EAAS,aAAa,KAAK,MAAM,KAAM,EAAI,KAAM,MAAM,EACvDA,EAAS,aAAa,EAAU,EAAa,UAAU,CACzD,CACF,CAEA,QAAsC,CAEpC,GAAM,CACJ,SAAU,EACV,gBAAiB,EACjB,iBAAkB,EAClB,yBACA,aAAc,EACd,KAAM,EACN,iBACA,YAAa,EACb,GAAG,GACD,KAAK,MAMT,OAJK,IACH,EAAY,OAAS,GAAkB,IAKvC,EAAA,EAAA,IAAA,CAAC,SAAD,CAAQ,GAAI,EAAa,IAAK,KAAK,OAAQ,OAAQ,KAAK,oBACrD,KAAK,MAAM,cAAgB,KAAK,oBAAoB,CAC/C,CAAA,CAEZ,CACF,EAEA,EAAeH,EAAM,YAA8C,EAAO,KACxE,EAAA,EAAA,IAAA,CAAC,EAAD,CAAO,GAAI,EAAO,aAAc,CAAM,CAAA,CACvC"}
1
+ {"version":3,"file":"Frame.cjs","names":["React","Content","FrameContextProvider","ReactDOM"],"sources":["../src/Frame.tsx"],"sourcesContent":["import * as React from 'react';\nimport * as ReactDOM from 'react-dom';\nimport type { FrameContextProps } from './Context.js';\nimport { FrameContextProvider } from './Context.js';\nimport Content from './Content.js';\n\ninterface FrameOwnProps extends React.IframeHTMLAttributes<HTMLIFrameElement> {\n head?: React.ReactNode | undefined;\n mountTarget?: string | undefined;\n initialContent?: string | undefined;\n contentDidMount?: (() => void) | undefined;\n contentDidUpdate?: (() => void) | undefined;\n dangerouslyUseDocWrite?: boolean | undefined;\n children?: React.ReactNode | undefined;\n}\n\n// The public props type accepts `ref` (forwarded to the underlying iframe element),\n// while the internal class must not declare `ref` in its props so that a ref on\n// `Frame` itself keeps referring to the component instance.\nexport type FrameComponentProps = FrameOwnProps & React.RefAttributes<HTMLIFrameElement>;\n\ninterface InternalFrameProps extends FrameOwnProps {\n forwardedRef?: React.ForwardedRef<HTMLIFrameElement> | undefined;\n}\n\ninterface FrameState {\n iframeLoaded: boolean;\n}\n\n// React warns when you render directly into the body since browser extensions\n// also inject into the body and can mess up React. For this reason\n// initialContent is expected to have a div inside of the body\n// element that we render react into.\nconst DEFAULT_INITIAL_CONTENT = '<!DOCTYPE html><html><head></head><body><div class=\"frame-root\"></div></body></html>';\n\nconst resilientContainers = new WeakSet<Node>();\n\n/**\n * Makes a portal container tolerate DOM mutations performed by scripts running\n * inside the iframe document.\n *\n * Scripts in the loaded document (e.g. `initialContent` of a generated app) may\n * remove or move the nodes React rendered into the container. React would then\n * throw `NotFoundError` from `removeChild`/`insertBefore` while committing an\n * update or unmount, crashing the parent React tree. Shadowing these methods on\n * the container node itself (not on the realm's prototypes, so the embedded\n * app's own DOM code keeps standard semantics) lets React degrade gracefully.\n */\nfunction makePortalContainerResilient(container: Element): void {\n if (resilientContainers.has(container)) {\n return;\n }\n resilientContainers.add(container);\n\n const originalRemoveChild = container.removeChild.bind(container);\n container.removeChild = <T extends Node>(child: T): T => {\n // The child was already detached by the embedded document's scripts; React\n // only wants it gone from the container, so this is a successful no-op.\n if (child.parentNode !== container) {\n return child;\n }\n return originalRemoveChild(child) as T;\n };\n\n const originalInsertBefore = container.insertBefore.bind(container);\n container.insertBefore = <T extends Node>(node: T, reference: Node | null): T => {\n // The reference sibling was detached by the embedded document's scripts;\n // appending is the closest position that keeps the node in the container.\n if (reference !== null && reference.parentNode !== container) {\n container.append(node);\n return node;\n }\n return originalInsertBefore(node, reference) as T;\n };\n}\n\nexport class Frame extends React.Component<InternalFrameProps, FrameState> {\n private _isMounted = false;\n private _contextValue: FrameContextProps | undefined;\n private readonly nodeRef: React.MutableRefObject<HTMLIFrameElement | null> = React.createRef();\n\n override state: FrameState = { iframeLoaded: false };\n\n override componentDidMount(): void {\n this._isMounted = true;\n\n const doc = this.getDoc();\n\n if (doc) {\n this.nodeRef.current?.contentWindow?.addEventListener('DOMContentLoaded', this.handleLoad);\n }\n\n if (this.props.dangerouslyUseDocWrite) {\n this.handleLoad();\n }\n }\n\n override componentWillUnmount(): void {\n this._isMounted = false;\n\n // The listener was added to the iframe's content window in componentDidMount.\n // A null document means the content window is cross-origin (e.g. a sandbox\n // without allow-same-origin) — no listener was added there, and touching the\n // window would throw SecurityError and crash the unmounting React tree.\n if (this.getDoc()) {\n this.nodeRef.current?.contentWindow?.removeEventListener('DOMContentLoaded', this.handleLoad);\n }\n }\n\n getDoc(): Document | undefined {\n return this.nodeRef.current?.contentDocument ?? undefined;\n }\n\n getMountTarget(): Element | undefined {\n const doc = this.getDoc();\n\n if (!doc || !doc.body) {\n return undefined;\n }\n\n if (this.props.mountTarget) {\n return doc.querySelector(this.props.mountTarget) ?? undefined;\n }\n\n return doc.body.children[0];\n }\n\n setRef = (node: HTMLIFrameElement | null): void => {\n this.nodeRef.current = node;\n\n const { forwardedRef } = this.props;\n if (typeof forwardedRef === 'function') {\n forwardedRef(node);\n } else if (forwardedRef) {\n forwardedRef.current = node;\n }\n };\n\n handleLoad = (): void => {\n // Bail update as some browsers will trigger on both DOMContentLoaded & onLoad ala firefox\n if (!this.state.iframeLoaded) {\n this.setState({ iframeLoaded: true });\n }\n };\n\n renderFrameContents(): React.ReactNode {\n if (!this._isMounted) {\n return undefined;\n }\n\n const doc = this.getDoc();\n\n if (!doc) {\n return undefined;\n }\n\n const { contentDidMount = () => {}, contentDidUpdate = () => {} } = this.props;\n\n const win = doc.defaultView ?? undefined;\n // Reuse the context value across renders so consumers only re-render when the\n // document or window actually changes, not on every render of Frame.\n if (!this._contextValue || this._contextValue.document !== doc || this._contextValue.window !== win) {\n this._contextValue = { document: doc, window: win };\n }\n const contents = (\n <Content contentDidMount={contentDidMount} contentDidUpdate={contentDidUpdate}>\n <FrameContextProvider value={this._contextValue}>\n <div className=\"frame-content\">{this.props.children}</div>\n </FrameContextProvider>\n </Content>\n );\n\n if (this.props.dangerouslyUseDocWrite && doc.body.children.length === 0) {\n doc.open('text/html', 'replace');\n doc.write(this.props.initialContent ?? DEFAULT_INITIAL_CONTENT);\n doc.close();\n }\n\n const mountTarget = this.getMountTarget();\n\n if (!mountTarget) {\n return undefined;\n }\n\n makePortalContainerResilient(doc.head);\n makePortalContainerResilient(mountTarget);\n\n return [\n ReactDOM.createPortal(this.props.head, doc.head, 'head'),\n ReactDOM.createPortal(contents, mountTarget, 'contents'),\n ];\n }\n\n override render(): React.ReactElement {\n // The iframe isn't ready so we drop children from the iframe props here. #12, #17\n const {\n children: _children,\n contentDidMount: _contentDidMount,\n contentDidUpdate: _contentDidUpdate,\n dangerouslyUseDocWrite,\n forwardedRef: _forwardedRef,\n head: _head,\n initialContent,\n mountTarget: _mountTarget,\n ...iframeProps\n } = this.props;\n\n if (!dangerouslyUseDocWrite) {\n iframeProps.srcDoc = initialContent ?? DEFAULT_INITIAL_CONTENT;\n }\n\n return (\n // oxlint-disable-next-line jsx-a11y/iframe-has-title -- consumers can pass `title` via the props spread\n <iframe {...iframeProps} ref={this.setRef} onLoad={this.handleLoad}>\n {this.state.iframeLoaded && this.renderFrameContents()}\n </iframe>\n );\n }\n}\n\nexport default React.forwardRef<HTMLIFrameElement, FrameOwnProps>((props, ref) => (\n <Frame {...props} forwardedRef={ref} />\n));\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Frame.d.ts","sourceRoot":"","sources":["../src/Frame.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,UAAU,aAAc,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IAC3E,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,eAAe,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3C,gBAAgB,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAC5C,sBAAsB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,SAAS,CAAC;CACxC;AAKD,MAAM,MAAM,mBAAmB,GAAG,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC;AAEzF,UAAU,kBAAmB,SAAQ,aAAa;IAChD,YAAY,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,iBAAiB,CAAC,GAAG,SAAS,CAAC;CAClE;AAED,UAAU,UAAU;IAClB,YAAY,EAAE,OAAO,CAAC;CACvB;AAQD,qBAAa,KAAM,SAAQ,KAAK,CAAC,SAAS,CAAC,kBAAkB,EAAE,UAAU,CAAC;IACxE,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAgC;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuE;IAEtF,KAAK,EAAE,UAAU,CAA2B;IAE5C,iBAAiB,IAAI,IAAI,CAYjC;IAEQ,oBAAoB,IAAI,IAAI,CAKpC;IAED,MAAM,IAAI,QAAQ,GAAG,SAAS,CAE7B;IAED,cAAc,IAAI,OAAO,GAAG,SAAS,CAYpC;IAED,MAAM,SAAU,iBAAiB,GAAG,IAAI,KAAG,IAAI,CAS7C;IAEF,UAAU,QAAO,IAAI,CAKnB;IAEF,mBAAmB,IAAI,KAAK,CAAC,SAAS,CA2CrC;IAEQ,MAAM,IAAI,KAAK,CAAC,YAAY,CAwBpC;CACF"}
1
+ {"version":3,"file":"Frame.d.ts","sourceRoot":"","sources":["../src/Frame.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,UAAU,aAAc,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IAC3E,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,eAAe,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3C,gBAAgB,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAC5C,sBAAsB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,SAAS,CAAC;CACxC;AAKD,MAAM,MAAM,mBAAmB,GAAG,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC;AAEzF,UAAU,kBAAmB,SAAQ,aAAa;IAChD,YAAY,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,iBAAiB,CAAC,GAAG,SAAS,CAAC;CAClE;AAED,UAAU,UAAU;IAClB,YAAY,EAAE,OAAO,CAAC;CACvB;AAiDD,qBAAa,KAAM,SAAQ,KAAK,CAAC,SAAS,CAAC,kBAAkB,EAAE,UAAU,CAAC;IACxE,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAgC;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuE;IAEtF,KAAK,EAAE,UAAU,CAA2B;IAE5C,iBAAiB,IAAI,IAAI,CAYjC;IAEQ,oBAAoB,IAAI,IAAI,CAUpC;IAED,MAAM,IAAI,QAAQ,GAAG,SAAS,CAE7B;IAED,cAAc,IAAI,OAAO,GAAG,SAAS,CAYpC;IAED,MAAM,SAAU,iBAAiB,GAAG,IAAI,KAAG,IAAI,CAS7C;IAEF,UAAU,QAAO,IAAI,CAKnB;IAEF,mBAAmB,IAAI,KAAK,CAAC,SAAS,CA8CrC;IAEQ,MAAM,IAAI,KAAK,CAAC,YAAY,CAwBpC;CACF"}
package/dist/Frame.js CHANGED
@@ -1,2 +1,2 @@
1
- import{FrameContextProvider as e}from"./Context.js";import t from"./Content.js";import*as n from"react";import*as r from"react-dom";import{jsx as i}from"react/jsx-runtime";const a=`<!DOCTYPE html><html><head></head><body><div class="frame-root"></div></body></html>`;var o=class extends n.Component{constructor(...e){super(...e),this._isMounted=!1,this.nodeRef=n.createRef(),this.state={iframeLoaded:!1},this.setRef=e=>{this.nodeRef.current=e;let{forwardedRef:t}=this.props;typeof t==`function`?t(e):t&&(t.current=e)},this.handleLoad=()=>{this.state.iframeLoaded||this.setState({iframeLoaded:!0})}}componentDidMount(){this._isMounted=!0,this.getDoc()&&this.nodeRef.current?.contentWindow?.addEventListener(`DOMContentLoaded`,this.handleLoad),this.props.dangerouslyUseDocWrite&&this.handleLoad()}componentWillUnmount(){this._isMounted=!1,this.nodeRef.current?.contentWindow?.removeEventListener(`DOMContentLoaded`,this.handleLoad)}getDoc(){return this.nodeRef.current?.contentDocument??void 0}getMountTarget(){let e=this.getDoc();if(!(!e||!e.body))return this.props.mountTarget?e.querySelector(this.props.mountTarget)??void 0:e.body.children[0]}renderFrameContents(){if(!this._isMounted)return;let n=this.getDoc();if(!n)return;let{contentDidMount:o=()=>{},contentDidUpdate:s=()=>{}}=this.props,c=n.defaultView??void 0;(!this._contextValue||this._contextValue.document!==n||this._contextValue.window!==c)&&(this._contextValue={document:n,window:c});let l=i(t,{contentDidMount:o,contentDidUpdate:s,children:i(e,{value:this._contextValue,children:i(`div`,{className:`frame-content`,children:this.props.children})})});this.props.dangerouslyUseDocWrite&&n.body.children.length===0&&(n.open(`text/html`,`replace`),n.write(this.props.initialContent??a),n.close());let u=this.getMountTarget();if(u)return[r.createPortal(this.props.head,n.head,`head`),r.createPortal(l,u,`contents`)]}render(){let{children:e,contentDidMount:t,contentDidUpdate:n,dangerouslyUseDocWrite:r,forwardedRef:o,head:s,initialContent:c,mountTarget:l,...u}=this.props;return r||(u.srcDoc=c??a),i(`iframe`,{...u,ref:this.setRef,onLoad:this.handleLoad,children:this.state.iframeLoaded&&this.renderFrameContents()})}},s=n.forwardRef((e,t)=>i(o,{...e,forwardedRef:t}));export{s as default};
1
+ import{FrameContextProvider as e}from"./Context.js";import t from"./Content.js";import*as n from"react";import*as r from"react-dom";import{jsx as i}from"react/jsx-runtime";const a=`<!DOCTYPE html><html><head></head><body><div class="frame-root"></div></body></html>`,o=new WeakSet;function s(e){if(o.has(e))return;o.add(e);let t=e.removeChild.bind(e);e.removeChild=n=>n.parentNode===e?t(n):n;let n=e.insertBefore.bind(e);e.insertBefore=(t,r)=>r!==null&&r.parentNode!==e?(e.append(t),t):n(t,r)}var c=class extends n.Component{constructor(...e){super(...e),this._isMounted=!1,this.nodeRef=n.createRef(),this.state={iframeLoaded:!1},this.setRef=e=>{this.nodeRef.current=e;let{forwardedRef:t}=this.props;typeof t==`function`?t(e):t&&(t.current=e)},this.handleLoad=()=>{this.state.iframeLoaded||this.setState({iframeLoaded:!0})}}componentDidMount(){this._isMounted=!0,this.getDoc()&&this.nodeRef.current?.contentWindow?.addEventListener(`DOMContentLoaded`,this.handleLoad),this.props.dangerouslyUseDocWrite&&this.handleLoad()}componentWillUnmount(){this._isMounted=!1,this.getDoc()&&this.nodeRef.current?.contentWindow?.removeEventListener(`DOMContentLoaded`,this.handleLoad)}getDoc(){return this.nodeRef.current?.contentDocument??void 0}getMountTarget(){let e=this.getDoc();if(!(!e||!e.body))return this.props.mountTarget?e.querySelector(this.props.mountTarget)??void 0:e.body.children[0]}renderFrameContents(){if(!this._isMounted)return;let n=this.getDoc();if(!n)return;let{contentDidMount:o=()=>{},contentDidUpdate:c=()=>{}}=this.props,l=n.defaultView??void 0;(!this._contextValue||this._contextValue.document!==n||this._contextValue.window!==l)&&(this._contextValue={document:n,window:l});let u=i(t,{contentDidMount:o,contentDidUpdate:c,children:i(e,{value:this._contextValue,children:i(`div`,{className:`frame-content`,children:this.props.children})})});this.props.dangerouslyUseDocWrite&&n.body.children.length===0&&(n.open(`text/html`,`replace`),n.write(this.props.initialContent??a),n.close());let d=this.getMountTarget();if(d)return s(n.head),s(d),[r.createPortal(this.props.head,n.head,`head`),r.createPortal(u,d,`contents`)]}render(){let{children:e,contentDidMount:t,contentDidUpdate:n,dangerouslyUseDocWrite:r,forwardedRef:o,head:s,initialContent:c,mountTarget:l,...u}=this.props;return r||(u.srcDoc=c??a),i(`iframe`,{...u,ref:this.setRef,onLoad:this.handleLoad,children:this.state.iframeLoaded&&this.renderFrameContents()})}},l=n.forwardRef((e,t)=>i(c,{...e,forwardedRef:t}));export{l as default};
2
2
  //# sourceMappingURL=Frame.js.map
package/dist/Frame.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Frame.js","names":[],"sources":["../src/Frame.tsx"],"sourcesContent":["import * as React from 'react';\nimport * as ReactDOM from 'react-dom';\nimport type { FrameContextProps } from './Context.js';\nimport { FrameContextProvider } from './Context.js';\nimport Content from './Content.js';\n\ninterface FrameOwnProps extends React.IframeHTMLAttributes<HTMLIFrameElement> {\n head?: React.ReactNode | undefined;\n mountTarget?: string | undefined;\n initialContent?: string | undefined;\n contentDidMount?: (() => void) | undefined;\n contentDidUpdate?: (() => void) | undefined;\n dangerouslyUseDocWrite?: boolean | undefined;\n children?: React.ReactNode | undefined;\n}\n\n// The public props type accepts `ref` (forwarded to the underlying iframe element),\n// while the internal class must not declare `ref` in its props so that a ref on\n// `Frame` itself keeps referring to the component instance.\nexport type FrameComponentProps = FrameOwnProps & React.RefAttributes<HTMLIFrameElement>;\n\ninterface InternalFrameProps extends FrameOwnProps {\n forwardedRef?: React.ForwardedRef<HTMLIFrameElement> | undefined;\n}\n\ninterface FrameState {\n iframeLoaded: boolean;\n}\n\n// React warns when you render directly into the body since browser extensions\n// also inject into the body and can mess up React. For this reason\n// initialContent is expected to have a div inside of the body\n// element that we render react into.\nconst DEFAULT_INITIAL_CONTENT = '<!DOCTYPE html><html><head></head><body><div class=\"frame-root\"></div></body></html>';\n\nexport class Frame extends React.Component<InternalFrameProps, FrameState> {\n private _isMounted = false;\n private _contextValue: FrameContextProps | undefined;\n private readonly nodeRef: React.MutableRefObject<HTMLIFrameElement | null> = React.createRef();\n\n override state: FrameState = { iframeLoaded: false };\n\n override componentDidMount(): void {\n this._isMounted = true;\n\n const doc = this.getDoc();\n\n if (doc) {\n this.nodeRef.current?.contentWindow?.addEventListener('DOMContentLoaded', this.handleLoad);\n }\n\n if (this.props.dangerouslyUseDocWrite) {\n this.handleLoad();\n }\n }\n\n override componentWillUnmount(): void {\n this._isMounted = false;\n\n // The listener was added to the iframe's content window in componentDidMount.\n this.nodeRef.current?.contentWindow?.removeEventListener('DOMContentLoaded', this.handleLoad);\n }\n\n getDoc(): Document | undefined {\n return this.nodeRef.current?.contentDocument ?? undefined;\n }\n\n getMountTarget(): Element | undefined {\n const doc = this.getDoc();\n\n if (!doc || !doc.body) {\n return undefined;\n }\n\n if (this.props.mountTarget) {\n return doc.querySelector(this.props.mountTarget) ?? undefined;\n }\n\n return doc.body.children[0];\n }\n\n setRef = (node: HTMLIFrameElement | null): void => {\n this.nodeRef.current = node;\n\n const { forwardedRef } = this.props;\n if (typeof forwardedRef === 'function') {\n forwardedRef(node);\n } else if (forwardedRef) {\n forwardedRef.current = node;\n }\n };\n\n handleLoad = (): void => {\n // Bail update as some browsers will trigger on both DOMContentLoaded & onLoad ala firefox\n if (!this.state.iframeLoaded) {\n this.setState({ iframeLoaded: true });\n }\n };\n\n renderFrameContents(): React.ReactNode {\n if (!this._isMounted) {\n return undefined;\n }\n\n const doc = this.getDoc();\n\n if (!doc) {\n return undefined;\n }\n\n const { contentDidMount = () => {}, contentDidUpdate = () => {} } = this.props;\n\n const win = doc.defaultView ?? undefined;\n // Reuse the context value across renders so consumers only re-render when the\n // document or window actually changes, not on every render of Frame.\n if (!this._contextValue || this._contextValue.document !== doc || this._contextValue.window !== win) {\n this._contextValue = { document: doc, window: win };\n }\n const contents = (\n <Content contentDidMount={contentDidMount} contentDidUpdate={contentDidUpdate}>\n <FrameContextProvider value={this._contextValue}>\n <div className=\"frame-content\">{this.props.children}</div>\n </FrameContextProvider>\n </Content>\n );\n\n if (this.props.dangerouslyUseDocWrite && doc.body.children.length === 0) {\n doc.open('text/html', 'replace');\n doc.write(this.props.initialContent ?? DEFAULT_INITIAL_CONTENT);\n doc.close();\n }\n\n const mountTarget = this.getMountTarget();\n\n if (!mountTarget) {\n return undefined;\n }\n\n return [\n ReactDOM.createPortal(this.props.head, doc.head, 'head'),\n ReactDOM.createPortal(contents, mountTarget, 'contents'),\n ];\n }\n\n override render(): React.ReactElement {\n // The iframe isn't ready so we drop children from the iframe props here. #12, #17\n const {\n children: _children,\n contentDidMount: _contentDidMount,\n contentDidUpdate: _contentDidUpdate,\n dangerouslyUseDocWrite,\n forwardedRef: _forwardedRef,\n head: _head,\n initialContent,\n mountTarget: _mountTarget,\n ...iframeProps\n } = this.props;\n\n if (!dangerouslyUseDocWrite) {\n iframeProps.srcDoc = initialContent ?? DEFAULT_INITIAL_CONTENT;\n }\n\n return (\n // oxlint-disable-next-line jsx-a11y/iframe-has-title -- consumers can pass `title` via the props spread\n <iframe {...iframeProps} ref={this.setRef} onLoad={this.handleLoad}>\n {this.state.iframeLoaded && this.renderFrameContents()}\n </iframe>\n );\n }\n}\n\nexport default React.forwardRef<HTMLIFrameElement, FrameOwnProps>((props, ref) => (\n <Frame {...props} forwardedRef={ref} />\n));\n"],"mappings":"4KAiCA,MAAM,EAA0B,uFAEhC,IAAa,EAAb,cAA2B,EAAM,SAA0C,+CACpD,gBAEwD,EAAM,UAAU,aAEhE,CAAE,aAAc,EAAM,cAyCzC,GAAyC,CACjD,KAAK,QAAQ,QAAU,EAEvB,GAAM,CAAE,gBAAiB,KAAK,MAC1B,OAAO,GAAiB,WAC1B,EAAa,CAAI,EACR,IACT,EAAa,QAAU,EAE3B,sBAEyB,CAElB,KAAK,MAAM,cACd,KAAK,SAAS,CAAE,aAAc,EAAK,CAAC,CAExC,EAvDA,mBAAmC,CACjC,KAAK,WAAa,GAEN,KAAK,OAEX,GACJ,KAAK,QAAQ,SAAS,eAAe,iBAAiB,mBAAoB,KAAK,UAAU,EAGvF,KAAK,MAAM,wBACb,KAAK,WAAW,CAEpB,CAEA,sBAAsC,CACpC,KAAK,WAAa,GAGlB,KAAK,QAAQ,SAAS,eAAe,oBAAoB,mBAAoB,KAAK,UAAU,CAC9F,CAEA,QAA+B,CAC7B,OAAO,KAAK,QAAQ,SAAS,iBAAmB,IAAA,EAClD,CAEA,gBAAsC,CACpC,IAAM,EAAM,KAAK,OAAO,EAEpB,MAAC,GAAO,CAAC,EAAI,MAQjB,OAJI,KAAK,MAAM,YACN,EAAI,cAAc,KAAK,MAAM,WAAW,GAAK,IAAA,GAG/C,EAAI,KAAK,SAAS,EAC3B,CAoBA,qBAAuC,CACrC,GAAI,CAAC,KAAK,WACR,OAGF,IAAM,EAAM,KAAK,OAAO,EAExB,GAAI,CAAC,EACH,OAGF,GAAM,CAAE,sBAAwB,CAAC,EAAG,uBAAyB,CAAC,GAAM,KAAK,MAEnE,EAAM,EAAI,aAAe,IAAA,IAG3B,CAAC,KAAK,eAAiB,KAAK,cAAc,WAAa,GAAO,KAAK,cAAc,SAAW,KAC9F,KAAK,cAAgB,CAAE,SAAU,EAAK,OAAQ,CAAI,GAEpD,IAAM,EACJ,EAAC,EAAD,CAA0B,kBAAmC,4BAC3D,EAAC,EAAD,CAAsB,MAAO,KAAK,uBAChC,EAAC,MAAD,CAAK,UAAU,yBAAiB,KAAK,MAAM,QAAc,CAAA,CACrC,CAAA,CACf,CAAA,EAGP,KAAK,MAAM,wBAA0B,EAAI,KAAK,SAAS,SAAW,IACpE,EAAI,KAAK,YAAa,SAAS,EAC/B,EAAI,MAAM,KAAK,MAAM,gBAAkB,CAAuB,EAC9D,EAAI,MAAM,GAGZ,IAAM,EAAc,KAAK,eAAe,EAEnC,KAIL,MAAO,CACL,EAAS,aAAa,KAAK,MAAM,KAAM,EAAI,KAAM,MAAM,EACvD,EAAS,aAAa,EAAU,EAAa,UAAU,CACzD,CACF,CAEA,QAAsC,CAEpC,GAAM,CACJ,SAAU,EACV,gBAAiB,EACjB,iBAAkB,EAClB,yBACA,aAAc,EACd,KAAM,EACN,iBACA,YAAa,EACb,GAAG,GACD,KAAK,MAMT,OAJK,IACH,EAAY,OAAS,GAAkB,GAKvC,EAAC,SAAD,CAAQ,GAAI,EAAa,IAAK,KAAK,OAAQ,OAAQ,KAAK,oBACrD,KAAK,MAAM,cAAgB,KAAK,oBAAoB,CAC/C,CAAA,CAEZ,CACF,EAEA,EAAe,EAAM,YAA8C,EAAO,IACxE,EAAC,EAAD,CAAO,GAAI,EAAO,aAAc,CAAM,CAAA,CACvC"}
1
+ {"version":3,"file":"Frame.js","names":[],"sources":["../src/Frame.tsx"],"sourcesContent":["import * as React from 'react';\nimport * as ReactDOM from 'react-dom';\nimport type { FrameContextProps } from './Context.js';\nimport { FrameContextProvider } from './Context.js';\nimport Content from './Content.js';\n\ninterface FrameOwnProps extends React.IframeHTMLAttributes<HTMLIFrameElement> {\n head?: React.ReactNode | undefined;\n mountTarget?: string | undefined;\n initialContent?: string | undefined;\n contentDidMount?: (() => void) | undefined;\n contentDidUpdate?: (() => void) | undefined;\n dangerouslyUseDocWrite?: boolean | undefined;\n children?: React.ReactNode | undefined;\n}\n\n// The public props type accepts `ref` (forwarded to the underlying iframe element),\n// while the internal class must not declare `ref` in its props so that a ref on\n// `Frame` itself keeps referring to the component instance.\nexport type FrameComponentProps = FrameOwnProps & React.RefAttributes<HTMLIFrameElement>;\n\ninterface InternalFrameProps extends FrameOwnProps {\n forwardedRef?: React.ForwardedRef<HTMLIFrameElement> | undefined;\n}\n\ninterface FrameState {\n iframeLoaded: boolean;\n}\n\n// React warns when you render directly into the body since browser extensions\n// also inject into the body and can mess up React. For this reason\n// initialContent is expected to have a div inside of the body\n// element that we render react into.\nconst DEFAULT_INITIAL_CONTENT = '<!DOCTYPE html><html><head></head><body><div class=\"frame-root\"></div></body></html>';\n\nconst resilientContainers = new WeakSet<Node>();\n\n/**\n * Makes a portal container tolerate DOM mutations performed by scripts running\n * inside the iframe document.\n *\n * Scripts in the loaded document (e.g. `initialContent` of a generated app) may\n * remove or move the nodes React rendered into the container. React would then\n * throw `NotFoundError` from `removeChild`/`insertBefore` while committing an\n * update or unmount, crashing the parent React tree. Shadowing these methods on\n * the container node itself (not on the realm's prototypes, so the embedded\n * app's own DOM code keeps standard semantics) lets React degrade gracefully.\n */\nfunction makePortalContainerResilient(container: Element): void {\n if (resilientContainers.has(container)) {\n return;\n }\n resilientContainers.add(container);\n\n const originalRemoveChild = container.removeChild.bind(container);\n container.removeChild = <T extends Node>(child: T): T => {\n // The child was already detached by the embedded document's scripts; React\n // only wants it gone from the container, so this is a successful no-op.\n if (child.parentNode !== container) {\n return child;\n }\n return originalRemoveChild(child) as T;\n };\n\n const originalInsertBefore = container.insertBefore.bind(container);\n container.insertBefore = <T extends Node>(node: T, reference: Node | null): T => {\n // The reference sibling was detached by the embedded document's scripts;\n // appending is the closest position that keeps the node in the container.\n if (reference !== null && reference.parentNode !== container) {\n container.append(node);\n return node;\n }\n return originalInsertBefore(node, reference) as T;\n };\n}\n\nexport class Frame extends React.Component<InternalFrameProps, FrameState> {\n private _isMounted = false;\n private _contextValue: FrameContextProps | undefined;\n private readonly nodeRef: React.MutableRefObject<HTMLIFrameElement | null> = React.createRef();\n\n override state: FrameState = { iframeLoaded: false };\n\n override componentDidMount(): void {\n this._isMounted = true;\n\n const doc = this.getDoc();\n\n if (doc) {\n this.nodeRef.current?.contentWindow?.addEventListener('DOMContentLoaded', this.handleLoad);\n }\n\n if (this.props.dangerouslyUseDocWrite) {\n this.handleLoad();\n }\n }\n\n override componentWillUnmount(): void {\n this._isMounted = false;\n\n // The listener was added to the iframe's content window in componentDidMount.\n // A null document means the content window is cross-origin (e.g. a sandbox\n // without allow-same-origin) — no listener was added there, and touching the\n // window would throw SecurityError and crash the unmounting React tree.\n if (this.getDoc()) {\n this.nodeRef.current?.contentWindow?.removeEventListener('DOMContentLoaded', this.handleLoad);\n }\n }\n\n getDoc(): Document | undefined {\n return this.nodeRef.current?.contentDocument ?? undefined;\n }\n\n getMountTarget(): Element | undefined {\n const doc = this.getDoc();\n\n if (!doc || !doc.body) {\n return undefined;\n }\n\n if (this.props.mountTarget) {\n return doc.querySelector(this.props.mountTarget) ?? undefined;\n }\n\n return doc.body.children[0];\n }\n\n setRef = (node: HTMLIFrameElement | null): void => {\n this.nodeRef.current = node;\n\n const { forwardedRef } = this.props;\n if (typeof forwardedRef === 'function') {\n forwardedRef(node);\n } else if (forwardedRef) {\n forwardedRef.current = node;\n }\n };\n\n handleLoad = (): void => {\n // Bail update as some browsers will trigger on both DOMContentLoaded & onLoad ala firefox\n if (!this.state.iframeLoaded) {\n this.setState({ iframeLoaded: true });\n }\n };\n\n renderFrameContents(): React.ReactNode {\n if (!this._isMounted) {\n return undefined;\n }\n\n const doc = this.getDoc();\n\n if (!doc) {\n return undefined;\n }\n\n const { contentDidMount = () => {}, contentDidUpdate = () => {} } = this.props;\n\n const win = doc.defaultView ?? undefined;\n // Reuse the context value across renders so consumers only re-render when the\n // document or window actually changes, not on every render of Frame.\n if (!this._contextValue || this._contextValue.document !== doc || this._contextValue.window !== win) {\n this._contextValue = { document: doc, window: win };\n }\n const contents = (\n <Content contentDidMount={contentDidMount} contentDidUpdate={contentDidUpdate}>\n <FrameContextProvider value={this._contextValue}>\n <div className=\"frame-content\">{this.props.children}</div>\n </FrameContextProvider>\n </Content>\n );\n\n if (this.props.dangerouslyUseDocWrite && doc.body.children.length === 0) {\n doc.open('text/html', 'replace');\n doc.write(this.props.initialContent ?? DEFAULT_INITIAL_CONTENT);\n doc.close();\n }\n\n const mountTarget = this.getMountTarget();\n\n if (!mountTarget) {\n return undefined;\n }\n\n makePortalContainerResilient(doc.head);\n makePortalContainerResilient(mountTarget);\n\n return [\n ReactDOM.createPortal(this.props.head, doc.head, 'head'),\n ReactDOM.createPortal(contents, mountTarget, 'contents'),\n ];\n }\n\n override render(): React.ReactElement {\n // The iframe isn't ready so we drop children from the iframe props here. #12, #17\n const {\n children: _children,\n contentDidMount: _contentDidMount,\n contentDidUpdate: _contentDidUpdate,\n dangerouslyUseDocWrite,\n forwardedRef: _forwardedRef,\n head: _head,\n initialContent,\n mountTarget: _mountTarget,\n ...iframeProps\n } = this.props;\n\n if (!dangerouslyUseDocWrite) {\n iframeProps.srcDoc = initialContent ?? DEFAULT_INITIAL_CONTENT;\n }\n\n return (\n // oxlint-disable-next-line jsx-a11y/iframe-has-title -- consumers can pass `title` via the props spread\n <iframe {...iframeProps} ref={this.setRef} onLoad={this.handleLoad}>\n {this.state.iframeLoaded && this.renderFrameContents()}\n </iframe>\n );\n }\n}\n\nexport default React.forwardRef<HTMLIFrameElement, FrameOwnProps>((props, ref) => (\n <Frame {...props} forwardedRef={ref} />\n));\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@willbooster/react-frame-component",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "React component to wrap your application or component in an iFrame for encapsulation purposes",
5
5
  "keywords": [
6
6
  "React",