@wordpress/block-editor
Version:
213 lines (176 loc) • 5.65 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _element = require("@wordpress/element");
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _i18n = require("@wordpress/i18n");
var _compose = require("@wordpress/compose");
var _components = require("@wordpress/components");
/**
* WordPress dependencies
*/
const BODY_CLASS_NAME = 'editor-styles-wrapper';
const BLOCK_PREFIX = 'wp-block';
/**
* Clones stylesheets targetting the editor canvas to the given document. A
* stylesheet is considered targetting the editor a canvas if it contains the
* `editor-styles-wrapper`, `wp-block`, or `wp-block-*` class selectors.
*
* Ideally, this hook should be removed in the future and styles should be added
* explicitly as editor styles.
*
* @param {Document} doc The document to append cloned stylesheets to.
*/
function styleSheetsCompat(doc) {
// Search the document for stylesheets targetting the editor canvas.
Array.from(document.styleSheets).forEach(styleSheet => {
try {
// May fail for external styles.
// eslint-disable-next-line no-unused-expressions
styleSheet.cssRules;
} catch (e) {
return;
}
const {
ownerNode,
cssRules
} = styleSheet;
if (!cssRules) {
return;
}
const isMatch = Array.from(cssRules).find(({
selectorText
}) => selectorText && (selectorText.includes(`.${BODY_CLASS_NAME}`) || selectorText.includes(`.${BLOCK_PREFIX}`)));
if (isMatch && !doc.getElementById(ownerNode.id)) {
doc.head.appendChild(ownerNode.cloneNode(true));
}
});
}
/**
* Bubbles some event types (keydown, keypress, and dragover) to parent document
* document to ensure that the keyboard shortcuts and drag and drop work.
*
* Ideally, we should remove event bubbling in the future. Keyboard shortcuts
* should be context dependent, e.g. actions on blocks like Cmd+A should not
* work globally outside the block editor.
*
* @param {Document} doc Document to attach listeners to.
*/
function bubbleEvents(doc) {
const {
defaultView
} = doc;
const {
frameElement
} = defaultView;
function bubbleEvent(event) {
const prototype = Object.getPrototypeOf(event);
const constructorName = prototype.constructor.name;
const Constructor = window[constructorName];
const init = {};
for (const key in event) {
init[key] = event[key];
}
if (event instanceof defaultView.MouseEvent) {
const rect = frameElement.getBoundingClientRect();
init.clientX += rect.left;
init.clientY += rect.top;
}
const newEvent = new Constructor(event.type, init);
const cancelled = !frameElement.dispatchEvent(newEvent);
if (cancelled) {
event.preventDefault();
}
}
const eventTypes = ['keydown', 'keypress', 'dragover'];
for (const name of eventTypes) {
doc.addEventListener(name, bubbleEvent);
}
}
/**
* Sets the document direction.
*
* Sets the `editor-styles-wrapper` class name on the body.
*
* Copies the `admin-color-*` class name to the body so that the admin color
* scheme applies to components in the iframe.
*
* @param {Document} doc Document to add class name to.
*/
function setBodyClassName(doc) {
doc.dir = document.dir;
doc.body.className = BODY_CLASS_NAME;
for (const name of document.body.classList) {
if (name.startsWith('admin-color-')) {
doc.body.classList.add(name);
}
}
}
/**
* Sets the document head and default styles.
*
* @param {Document} doc Document to set the head for.
* @param {string} head HTML to set as the head.
*/
function setHead(doc, head) {
doc.head.innerHTML = // Body margin must be overridable by themes.
'<style>body{margin:0}</style>' + head;
}
function Iframe({
contentRef,
children,
head,
headHTML,
...props
}, ref) {
const [iframeDocument, setIframeDocument] = (0, _element.useState)();
const setRef = (0, _element.useCallback)(node => {
if (!node) {
return;
}
function setDocumentIfReady() {
const {
contentDocument
} = node;
const {
readyState,
body
} = contentDocument;
if (readyState !== 'interactive' && readyState !== 'complete') {
return false;
}
if (typeof contentRef === 'function') {
contentRef(body);
} else if (contentRef) {
contentRef.current = body;
}
setHead(contentDocument, headHTML);
setBodyClassName(contentDocument);
styleSheetsCompat(contentDocument);
bubbleEvents(contentDocument);
setBodyClassName(contentDocument);
setIframeDocument(contentDocument);
return true;
}
if (setDocumentIfReady()) {
return;
} // Document is not immediately loaded in Firefox.
node.addEventListener('load', () => {
setDocumentIfReady();
});
}, []);
return (0, _element.createElement)("iframe", (0, _extends2.default)({}, props, {
ref: (0, _compose.useMergeRefs)([ref, setRef]),
tabIndex: "0",
title: (0, _i18n.__)('Editor canvas'),
name: "editor-canvas"
}), iframeDocument && (0, _element.createPortal)((0, _element.createElement)(_components.__experimentalStyleProvider, {
document: iframeDocument
}, children), iframeDocument.body), iframeDocument && (0, _element.createPortal)(head, iframeDocument.head));
}
var _default = (0, _element.forwardRef)(Iframe);
exports.default = _default;
//# sourceMappingURL=index.js.map