UNPKG

react-frame-component

Version:

React component to wrap your application or component in an iFrame for encapsulation purposes

151 lines (150 loc) 4.67 kB
import React, { Children, Component } from "react"; import ReactDOM from "react-dom"; import PropTypes from "prop-types"; import { jsx } from "react/jsx-runtime"; //#region src/Context.jsx var doc; var win; if (typeof document !== "undefined") doc = document; if (typeof window !== "undefined") win = window; var FrameContext = React.createContext({ document: doc, window: win }); var useFrame = () => React.useContext(FrameContext); var { Provider: FrameContextProvider, Consumer: FrameContextConsumer } = FrameContext; //#endregion //#region src/Content.jsx var Content = class extends Component { static propTypes = { children: PropTypes.element.isRequired, contentDidMount: PropTypes.func.isRequired, contentDidUpdate: PropTypes.func.isRequired }; componentDidMount() { this.props.contentDidMount(); } componentDidUpdate() { this.props.contentDidUpdate(); } render() { return Children.only(this.props.children); } }; //#endregion //#region src/Frame.jsx var Frame = class extends Component { static propTypes = { style: PropTypes.object, head: PropTypes.node, initialContent: PropTypes.string, mountTarget: PropTypes.string, dangerouslyUseDocWrite: PropTypes.bool, contentDidMount: PropTypes.func, contentDidUpdate: PropTypes.func, children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]) }; static defaultProps = { style: {}, head: null, children: void 0, mountTarget: void 0, dangerouslyUseDocWrite: false, contentDidMount: () => {}, contentDidUpdate: () => {}, initialContent: "<!DOCTYPE html><html><head></head><body><div class=\"frame-root\"></div></body></html>" }; constructor(props, context) { super(props, context); this._isMounted = false; this.nodeRef = React.createRef(); this.state = { iframeLoaded: false }; } componentDidMount() { this._isMounted = true; if (this.getDoc()) this.nodeRef.current.contentWindow.addEventListener("DOMContentLoaded", this.handleLoad); if (this.props.dangerouslyUseDocWrite) this.handleLoad(); } componentWillUnmount() { this._isMounted = false; this.nodeRef.current.removeEventListener("DOMContentLoaded", this.handleLoad); } getDoc() { return this.nodeRef.current ? this.nodeRef.current.contentDocument : null; } getMountTarget() { const doc = this.getDoc(); if (!doc || !doc.body) return null; if (this.props.mountTarget) return doc.querySelector(this.props.mountTarget); return doc.body.children[0]; } setRef = (node) => { this.nodeRef.current = node; const { forwardedRef } = this.props; if (typeof forwardedRef === "function") forwardedRef(node); else if (forwardedRef) forwardedRef.current = node; }; handleLoad = () => { clearInterval(this.loadCheck); if (!this.state.iframeLoaded) this.setState({ iframeLoaded: true }); }; loadCheck = () => setInterval(() => { this.handleLoad(); }, 500); renderFrameContents() { if (!this._isMounted) return null; const doc = this.getDoc(); if (!doc) return null; const contentDidMount = this.props.contentDidMount; const contentDidUpdate = this.props.contentDidUpdate; const contents = /* @__PURE__ */ jsx(Content, { contentDidMount, contentDidUpdate, children: /* @__PURE__ */ jsx(FrameContextProvider, { value: { document: doc, window: doc.defaultView || doc.parentView }, children: /* @__PURE__ */ jsx("div", { className: "frame-content", children: this.props.children }) }) }); if (this.props.dangerouslyUseDocWrite && doc.body.children.length < 1) { doc.open("text/html", "replace"); doc.write(this.props.initialContent); doc.close(); } const mountTarget = this.getMountTarget(); if (!mountTarget) return null; return [ReactDOM.createPortal(this.props.head, this.getDoc().head), ReactDOM.createPortal(contents, mountTarget)]; } render() { const props = { ...this.props, children: void 0 }; if (!this.props.dangerouslyUseDocWrite) props.srcDoc = this.props.initialContent; delete props.head; delete props.initialContent; delete props.mountTarget; delete props.dangerouslyUseDocWrite; delete props.contentDidMount; delete props.contentDidUpdate; delete props.forwardedRef; return /* @__PURE__ */ jsx("iframe", { ...props, ref: this.setRef, onLoad: this.handleLoad, children: this.state.iframeLoaded && this.renderFrameContents() }); } }; var Frame_default = React.forwardRef((props, ref) => /* @__PURE__ */ jsx(Frame, { ...props, forwardedRef: ref })); //#endregion export { FrameContext, FrameContextConsumer, Frame_default as default, useFrame }; //# sourceMappingURL=react-frame-component.esm.js.map