@wordpress/editor
Version:
Enhanced block editor for WordPress posts.
435 lines (433 loc) • 19.4 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// packages/editor/src/components/visual-editor/index.js
var visual_editor_exports = {};
__export(visual_editor_exports, {
default: () => visual_editor_default
});
module.exports = __toCommonJS(visual_editor_exports);
var import_clsx = __toESM(require("clsx"));
var import_block_editor = require("@wordpress/block-editor");
var import_element = require("@wordpress/element");
var import_data = require("@wordpress/data");
var import_blocks = require("@wordpress/blocks");
var import_core_data = require("@wordpress/core-data");
var import_compose = require("@wordpress/compose");
var import_post_title = __toESM(require("../post-title/index.cjs"));
var import_store = require("../../store/index.cjs");
var import_lock_unlock = require("../../lock-unlock.cjs");
var import_edit_template_blocks_notification = __toESM(require("./edit-template-blocks-notification.cjs"));
var import_resizable_editor = __toESM(require("../resizable-editor/index.cjs"));
var import_use_select_nearest_editable_block = __toESM(require("./use-select-nearest-editable-block.cjs"));
var import_constants = require("../../store/constants.cjs");
var import_use_zoom_out_mode_exit = require("./use-zoom-out-mode-exit.cjs");
var import_use_padding_appender = require("./use-padding-appender.cjs");
var import_use_edit_content_only_section_exit = require("./use-edit-content-only-section-exit.cjs");
var import_sync_connection_error_modal = require("../sync-connection-error-modal/index.cjs");
var import_jsx_runtime = require("react/jsx-runtime");
var {
LayoutStyle,
useLayoutClasses,
useLayoutStyles,
ExperimentalBlockCanvas: BlockCanvas,
useFlashEditableBlocks
} = (0, import_lock_unlock.unlock)(import_block_editor.privateApis);
function getPostContentAttributes(blocks) {
for (let i = 0; i < blocks.length; i++) {
if (blocks[i].name === "core/post-content") {
return blocks[i].attributes;
}
if (blocks[i].innerBlocks.length) {
const nestedPostContent = getPostContentAttributes(
blocks[i].innerBlocks
);
if (nestedPostContent) {
return nestedPostContent;
}
}
}
}
function checkForPostContentAtRootLevel(blocks) {
for (let i = 0; i < blocks.length; i++) {
if (blocks[i].name === "core/post-content") {
return true;
}
}
return false;
}
function VisualEditor({
// Ideally as we unify post and site editors, we won't need these props.
autoFocus,
disableIframe = false,
iframeProps,
contentRef,
className
}) {
const isMobileViewport = (0, import_compose.useViewportMatch)("small", "<");
const {
renderingMode,
postContentAttributes,
editedPostTemplate = {},
wrapperBlockName,
wrapperUniqueId,
deviceType,
isFocusedEntity,
isDesignPostType,
postType,
isPreview,
styles,
canvasMinHeight
} = (0, import_data.useSelect)((select) => {
const {
getCurrentPostId,
getCurrentPostType,
getCurrentTemplateId,
getEditorSettings,
getRenderingMode,
getDeviceType,
getCanvasMinHeight
} = (0, import_lock_unlock.unlock)(select(import_store.store));
const { getPostType, getEditedEntityRecord } = select(import_core_data.store);
const postTypeSlug = getCurrentPostType();
const _renderingMode = getRenderingMode();
let _wrapperBlockName;
if (postTypeSlug === import_constants.PATTERN_POST_TYPE) {
_wrapperBlockName = "core/block";
} else if (_renderingMode === "post-only") {
_wrapperBlockName = "core/post-content";
}
const editorSettings = getEditorSettings();
const supportsTemplateMode = editorSettings.supportsTemplateMode;
const postTypeObject = getPostType(postTypeSlug);
const currentTemplateId = getCurrentTemplateId();
const template = currentTemplateId ? getEditedEntityRecord(
"postType",
import_constants.TEMPLATE_POST_TYPE,
currentTemplateId
) : void 0;
return {
renderingMode: _renderingMode,
postContentAttributes: editorSettings.postContentAttributes,
isDesignPostType: import_constants.DESIGN_POST_TYPES.includes(postTypeSlug),
// Post template fetch returns a 404 on classic themes, which
// messes with e2e tests, so check it's a block theme first.
editedPostTemplate: postTypeObject?.viewable && supportsTemplateMode ? template : void 0,
wrapperBlockName: _wrapperBlockName,
wrapperUniqueId: getCurrentPostId(),
deviceType: getDeviceType(),
isFocusedEntity: !!editorSettings.onNavigateToPreviousEntityRecord,
postType: postTypeSlug,
isPreview: editorSettings.isPreviewMode,
styles: editorSettings.styles,
canvasMinHeight: getCanvasMinHeight()
};
}, []);
const { isCleanNewPost } = (0, import_data.useSelect)(import_store.store);
const {
hasRootPaddingAwareAlignments,
themeHasDisabledLayoutStyles,
themeSupportsLayout,
isZoomedOut
} = (0, import_data.useSelect)((select) => {
const { getSettings, isZoomOut: _isZoomOut } = (0, import_lock_unlock.unlock)(
select(import_block_editor.store)
);
const _settings = getSettings();
return {
themeHasDisabledLayoutStyles: _settings.disableLayoutStyles,
themeSupportsLayout: _settings.supportsLayout,
hasRootPaddingAwareAlignments: _settings.__experimentalFeatures?.useRootPaddingAwareAlignments,
isZoomedOut: _isZoomOut()
};
}, []);
const localRef = (0, import_element.useRef)();
const deviceStyles = (0, import_block_editor.__experimentalUseResizeCanvas)(deviceType);
const [globalLayoutSettings] = (0, import_block_editor.useSettings)("layout");
const fallbackLayout = (0, import_element.useMemo)(() => {
if (renderingMode !== "post-only" || isDesignPostType) {
return { type: "default" };
}
if (themeSupportsLayout) {
return { ...globalLayoutSettings, type: "constrained" };
}
return { type: "default" };
}, [
renderingMode,
themeSupportsLayout,
globalLayoutSettings,
isDesignPostType
]);
const newestPostContentAttributes = (0, import_element.useMemo)(() => {
if (!editedPostTemplate?.content && !editedPostTemplate?.blocks && postContentAttributes) {
return postContentAttributes;
}
if (editedPostTemplate?.blocks) {
return getPostContentAttributes(editedPostTemplate?.blocks);
}
const parseableContent = typeof editedPostTemplate?.content === "string" ? editedPostTemplate?.content : "";
return getPostContentAttributes((0, import_blocks.parse)(parseableContent)) || {};
}, [
editedPostTemplate?.content,
editedPostTemplate?.blocks,
postContentAttributes
]);
const hasPostContentAtRootLevel = (0, import_element.useMemo)(() => {
if (!editedPostTemplate?.content && !editedPostTemplate?.blocks) {
return false;
}
if (editedPostTemplate?.blocks) {
return checkForPostContentAtRootLevel(editedPostTemplate?.blocks);
}
const parseableContent = typeof editedPostTemplate?.content === "string" ? editedPostTemplate?.content : "";
return checkForPostContentAtRootLevel((0, import_blocks.parse)(parseableContent)) || false;
}, [editedPostTemplate?.content, editedPostTemplate?.blocks]);
const { layout = {}, align = "" } = newestPostContentAttributes || {};
const postContentLayoutClasses = useLayoutClasses(
newestPostContentAttributes,
"core/post-content"
);
const blockListLayoutClass = (0, import_clsx.default)(
{
"is-layout-flow": !themeSupportsLayout
},
themeSupportsLayout && postContentLayoutClasses,
align && `align${align}`
);
const postContentLayoutStyles = useLayoutStyles(
newestPostContentAttributes,
"core/post-content",
".block-editor-block-list__layout.is-root-container"
);
const postContentLayout = (0, import_element.useMemo)(() => {
return layout && (layout?.type === "constrained" || layout?.inherit || layout?.contentSize || layout?.wideSize) ? { ...globalLayoutSettings, ...layout, type: "constrained" } : { ...globalLayoutSettings, ...layout, type: "default" };
}, [
layout?.type,
layout?.inherit,
layout?.contentSize,
layout?.wideSize,
globalLayoutSettings
]);
const blockListLayout = postContentAttributes ? postContentLayout : fallbackLayout;
const postEditorLayout = blockListLayout?.type === "default" && !hasPostContentAtRootLevel ? fallbackLayout : blockListLayout;
const observeTypingRef = (0, import_block_editor.__unstableUseTypingObserver)();
const titleRef = (0, import_element.useRef)();
(0, import_element.useEffect)(() => {
if (!autoFocus || !isCleanNewPost()) {
return;
}
titleRef?.current?.focus();
}, [autoFocus, isCleanNewPost]);
const alignCSS = `.is-root-container.alignwide { max-width: var(--wp--style--global--wide-size); margin-left: auto; margin-right: auto;}
.is-root-container.alignwide:where(.is-layout-flow) > :not(.alignleft):not(.alignright) { max-width: var(--wp--style--global--wide-size);}
.is-root-container.alignfull { max-width: none; margin-left: auto; margin-right: auto;}
.is-root-container.alignfull:where(.is-layout-flow) > :not(.alignleft):not(.alignright) { max-width: none;}`;
const enableResizing = [
import_constants.NAVIGATION_POST_TYPE,
import_constants.TEMPLATE_PART_POST_TYPE,
import_constants.PATTERN_POST_TYPE
].includes(postType) && // Disable in previews / view mode.
!isPreview && // Disable resizing in mobile viewport.
!isMobileViewport && // Disable resizing in zoomed-out mode.
!isZoomedOut;
const isNavigationPreview = postType === import_constants.NAVIGATION_POST_TYPE && isPreview;
const calculatedMinHeight = (0, import_element.useMemo)(() => {
if (!localRef.current) {
return canvasMinHeight;
}
const { ownerDocument } = localRef.current;
const scrollTop = ownerDocument.documentElement.scrollTop || ownerDocument.body.scrollTop;
return canvasMinHeight + scrollTop;
}, [canvasMinHeight]);
const [paddingAppenderRef, paddingStyle] = (0, import_use_padding_appender.usePaddingAppender)(
!isPreview && renderingMode === "post-only" && !isDesignPostType
);
const centerContentCSS = `display:flex;align-items:center;justify-content:center;`;
const iframeStyles = (0, import_element.useMemo)(() => {
return [
...styles ?? [],
{
// Ensures margins of children are contained so that the body background paints behind them.
// Otherwise, the background of html (when zoomed out) would show there and appear broken. It's
// important mostly for post-only views yet conceivably an issue in templated views too.
css: `:where(.block-editor-iframe__body){display:flow-root;${calculatedMinHeight ? `min-height:${calculatedMinHeight}px;` : ""}}.is-root-container{display:flow-root;${// Some themes will have `min-height: 100vh` for the root container,
// which isn't a requirement in auto resize mode.
enableResizing || isNavigationPreview ? "min-height:0!important;" : ""}}
${paddingStyle ? paddingStyle : ""}
${enableResizing ? `.block-editor-iframe__html{background:var(--wp-editor-canvas-background);min-height:100vh;${centerContentCSS}}.block-editor-iframe__body{width:100%;}` : ""}${isNavigationPreview ? `.block-editor-iframe__body{${centerContentCSS}padding:var(--wp--style--block-gap,2em);}` : ""}`
// The CSS for enableResizing centers the body content vertically when resizing is enabled and applies a background
// color to the iframe HTML element to match the background color of the editor canvas.
// The CSS for isNavigationPreview centers the body content vertically and horizontally when the navigation is in preview mode.
}
];
}, [
styles,
enableResizing,
isNavigationPreview,
calculatedMinHeight,
paddingStyle
]);
const typewriterRef = (0, import_block_editor.__unstableUseTypewriter)();
contentRef = (0, import_compose.useMergeRefs)([
localRef,
contentRef,
renderingMode === "post-only" ? typewriterRef : null,
useFlashEditableBlocks({
isEnabled: renderingMode === "template-locked"
}),
(0, import_use_select_nearest_editable_block.default)({
isEnabled: renderingMode === "template-locked"
}),
(0, import_use_zoom_out_mode_exit.useZoomOutModeExit)(),
paddingAppenderRef,
(0, import_use_edit_content_only_section_exit.useEditContentOnlySectionExit)()
]);
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
"div",
{
className: (0, import_clsx.default)(
"editor-visual-editor",
// this class is here for backward compatibility reasons.
"edit-post-visual-editor",
className,
{
"has-padding": isFocusedEntity || enableResizing,
"is-resizable": enableResizing,
"is-iframed": !disableIframe
}
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sync_connection_error_modal.SyncConnectionErrorModal, {}),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_resizable_editor.default, { enableResizing, height: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
BlockCanvas,
{
shouldIframe: !disableIframe,
contentRef,
styles: iframeStyles,
height: "100%",
iframeProps: {
...iframeProps,
style: {
...iframeProps?.style,
...deviceStyles
}
},
children: [
themeSupportsLayout && !themeHasDisabledLayoutStyles && renderingMode === "post-only" && !isDesignPostType && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
LayoutStyle,
{
selector: ".editor-visual-editor__post-title-wrapper",
layout: fallbackLayout
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
LayoutStyle,
{
selector: ".block-editor-block-list__layout.is-root-container",
layout: postEditorLayout
}
),
align && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LayoutStyle, { css: alignCSS }),
postContentLayoutStyles && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
LayoutStyle,
{
layout: postContentLayout,
css: postContentLayoutStyles
}
)
] }),
renderingMode === "post-only" && !isDesignPostType && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"div",
{
className: (0, import_clsx.default)(
"editor-visual-editor__post-title-wrapper",
// The following class is only here for backward compatibility
// some themes might be using it to style the post title.
"edit-post-visual-editor__post-title-wrapper",
{
"has-global-padding": hasRootPaddingAwareAlignments
}
),
contentEditable: false,
ref: observeTypingRef,
style: {
// This is using inline styles
// so it's applied for both iframed and non iframed editors.
marginTop: "4rem"
},
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_post_title.default, { ref: titleRef })
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
import_block_editor.RecursionProvider,
{
blockName: wrapperBlockName,
uniqueId: wrapperUniqueId,
children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_block_editor.BlockList,
{
className: (0, import_clsx.default)(
"is-" + deviceType.toLowerCase() + "-preview",
renderingMode !== "post-only" || isDesignPostType ? "wp-site-blocks" : `${blockListLayoutClass} wp-block-post-content`,
// Ensure root level blocks receive default/flow blockGap styling rules.
{
"has-global-padding": renderingMode === "post-only" && !isDesignPostType && hasRootPaddingAwareAlignments
}
),
layout: blockListLayout,
dropZoneElement: (
// When iframed, pass in the html element of the iframe to
// ensure the drop zone extends to the edges of the iframe.
disableIframe ? localRef.current : localRef.current?.parentNode
),
__unstableDisableDropZone: (
// In template preview mode, disable drop zones at the root of the template.
renderingMode === "template-locked" ? true : false
)
}
),
renderingMode === "template-locked" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_edit_template_blocks_notification.default,
{
contentRef: localRef
}
)
]
}
)
]
}
) })
]
}
);
}
var visual_editor_default = VisualEditor;
//# sourceMappingURL=index.cjs.map