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
JavaScript
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