UNPKG

@wordpress/editor

Version:
364 lines (354 loc) 15.7 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _clsx = _interopRequireDefault(require("clsx")); var _blockEditor = require("@wordpress/block-editor"); var _element = require("@wordpress/element"); var _data = require("@wordpress/data"); var _blocks = require("@wordpress/blocks"); var _coreData = require("@wordpress/core-data"); var _compose = require("@wordpress/compose"); var _postTitle = _interopRequireDefault(require("../post-title")); var _store = require("../../store"); var _lockUnlock = require("../../lock-unlock"); var _editTemplateBlocksNotification = _interopRequireDefault(require("./edit-template-blocks-notification")); var _resizableEditor = _interopRequireDefault(require("../resizable-editor")); var _useSelectNearestEditableBlock = _interopRequireDefault(require("./use-select-nearest-editable-block")); var _constants = require("../../store/constants"); var _jsxRuntime = require("react/jsx-runtime"); /** * External dependencies */ /** * WordPress dependencies */ /** * Internal dependencies */ const { LayoutStyle, useLayoutClasses, useLayoutStyles, ExperimentalBlockCanvas: BlockCanvas, useFlashEditableBlocks } = (0, _lockUnlock.unlock)(_blockEditor.privateApis); /** * These post types have a special editor where they don't allow you to fill the title * and they don't apply the layout styles. */ const DESIGN_POST_TYPES = [_constants.PATTERN_POST_TYPE, _constants.TEMPLATE_POST_TYPE, _constants.NAVIGATION_POST_TYPE, _constants.TEMPLATE_PART_POST_TYPE]; /** * Given an array of nested blocks, find the first Post Content * block inside it, recursing through any nesting levels, * and return its attributes. * * @param {Array} blocks A list of blocks. * * @return {Object | undefined} The Post Content block. */ 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, styles, disableIframe = false, iframeProps, contentRef, className }) { const [resizeObserver, sizes] = (0, _compose.useResizeObserver)(); const isMobileViewport = (0, _compose.useViewportMatch)('small', '<'); const isTabletViewport = (0, _compose.useViewportMatch)('medium', '<'); const { renderingMode, postContentAttributes, editedPostTemplate = {}, wrapperBlockName, wrapperUniqueId, deviceType, isFocusedEntity, isDesignPostType, postType, isPreview } = (0, _data.useSelect)(select => { const { getCurrentPostId, getCurrentPostType, getCurrentTemplateId, getEditorSettings, getRenderingMode, getDeviceType } = select(_store.store); const { getPostType, getEditedEntityRecord } = select(_coreData.store); const postTypeSlug = getCurrentPostType(); const _renderingMode = getRenderingMode(); let _wrapperBlockName; if (postTypeSlug === _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', _constants.TEMPLATE_POST_TYPE, currentTemplateId) : undefined; return { renderingMode: _renderingMode, postContentAttributes: editorSettings.postContentAttributes, isDesignPostType: 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 : undefined, wrapperBlockName: _wrapperBlockName, wrapperUniqueId: getCurrentPostId(), deviceType: getDeviceType(), isFocusedEntity: !!editorSettings.onNavigateToPreviousEntityRecord, postType: postTypeSlug, isPreview: editorSettings.__unstableIsPreviewMode }; }, []); const { isCleanNewPost } = (0, _data.useSelect)(_store.store); const { hasRootPaddingAwareAlignments, themeHasDisabledLayoutStyles, themeSupportsLayout, isZoomOutMode } = (0, _data.useSelect)(select => { const { getSettings, __unstableGetEditorMode } = select(_blockEditor.store); const _settings = getSettings(); return { themeHasDisabledLayoutStyles: _settings.disableLayoutStyles, themeSupportsLayout: _settings.supportsLayout, hasRootPaddingAwareAlignments: _settings.__experimentalFeatures?.useRootPaddingAwareAlignments, isZoomOutMode: __unstableGetEditorMode() === 'zoom-out' }; }, []); const deviceStyles = (0, _blockEditor.__experimentalUseResizeCanvas)(deviceType); const [globalLayoutSettings] = (0, _blockEditor.useSettings)('layout'); // fallbackLayout is used if there is no Post Content, // and for Post Title. const fallbackLayout = (0, _element.useMemo)(() => { if (renderingMode !== 'post-only' || isDesignPostType) { return { type: 'default' }; } if (themeSupportsLayout) { // We need to ensure support for wide and full alignments, // so we add the constrained type. return { ...globalLayoutSettings, type: 'constrained' }; } // Set default layout for classic themes so all alignments are supported. return { type: 'default' }; }, [renderingMode, themeSupportsLayout, globalLayoutSettings, isDesignPostType]); const newestPostContentAttributes = (0, _element.useMemo)(() => { if (!editedPostTemplate?.content && !editedPostTemplate?.blocks && postContentAttributes) { return postContentAttributes; } // When in template editing mode, we can access the blocks directly. if (editedPostTemplate?.blocks) { return getPostContentAttributes(editedPostTemplate?.blocks); } // If there are no blocks, we have to parse the content string. // Best double-check it's a string otherwise the parse function gets unhappy. const parseableContent = typeof editedPostTemplate?.content === 'string' ? editedPostTemplate?.content : ''; return getPostContentAttributes((0, _blocks.parse)(parseableContent)) || {}; }, [editedPostTemplate?.content, editedPostTemplate?.blocks, postContentAttributes]); const hasPostContentAtRootLevel = (0, _element.useMemo)(() => { if (!editedPostTemplate?.content && !editedPostTemplate?.blocks) { return false; } // When in template editing mode, we can access the blocks directly. if (editedPostTemplate?.blocks) { return checkForPostContentAtRootLevel(editedPostTemplate?.blocks); } // If there are no blocks, we have to parse the content string. // Best double-check it's a string otherwise the parse function gets unhappy. const parseableContent = typeof editedPostTemplate?.content === 'string' ? editedPostTemplate?.content : ''; return checkForPostContentAtRootLevel((0, _blocks.parse)(parseableContent)) || false; }, [editedPostTemplate?.content, editedPostTemplate?.blocks]); const { layout = {}, align = '' } = newestPostContentAttributes || {}; const postContentLayoutClasses = useLayoutClasses(newestPostContentAttributes, 'core/post-content'); const blockListLayoutClass = (0, _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'); // Update type for blocks using legacy layouts. const postContentLayout = (0, _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]); // If there is a Post Content block we use its layout for the block list; // if not, this must be a classic theme, in which case we use the fallback layout. const blockListLayout = postContentAttributes ? postContentLayout : fallbackLayout; const postEditorLayout = blockListLayout?.type === 'default' && !hasPostContentAtRootLevel ? fallbackLayout : blockListLayout; const observeTypingRef = (0, _blockEditor.__unstableUseTypingObserver)(); const titleRef = (0, _element.useRef)(); (0, _element.useEffect)(() => { if (!autoFocus || !isCleanNewPost()) { return; } titleRef?.current?.focus(); }, [autoFocus, isCleanNewPost]); // Add some styles for alignwide/alignfull Post Content and its children. 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 localRef = (0, _element.useRef)(); const typewriterRef = (0, _blockEditor.__unstableUseTypewriter)(); contentRef = (0, _compose.useMergeRefs)([localRef, contentRef, renderingMode === 'post-only' ? typewriterRef : null, useFlashEditableBlocks({ isEnabled: renderingMode === 'template-locked' }), (0, _useSelectNearestEditableBlock.default)({ isEnabled: renderingMode === 'template-locked' })]); const zoomOutProps = isZoomOutMode && !isTabletViewport ? { scale: 'default', frameSize: '48px' } : {}; const forceFullHeight = postType === _constants.NAVIGATION_POST_TYPE; const enableResizing = [_constants.NAVIGATION_POST_TYPE, _constants.TEMPLATE_PART_POST_TYPE, _constants.PATTERN_POST_TYPE].includes(postType) && // Disable in previews / view mode. !isPreview && // Disable resizing in mobile viewport. !isMobileViewport && // Dsiable resizing in zoomed-out mode. !isZoomOutMode; const shouldIframe = !disableIframe || ['Tablet', 'Mobile'].includes(deviceType); const iframeStyles = (0, _element.useMemo)(() => { return [...(styles !== null && styles !== void 0 ? styles : []), { css: `.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 ? 'min-height:0!important;' : ''}}` }]; }, [styles, enableResizing]); return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { className: (0, _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': shouldIframe }), children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_resizableEditor.default, { enableResizing: enableResizing, height: sizes.height && !forceFullHeight ? sizes.height : '100%', children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(BlockCanvas, { shouldIframe: shouldIframe, contentRef: contentRef, styles: iframeStyles, height: "100%", iframeProps: { ...iframeProps, ...zoomOutProps, style: { ...iframeProps?.style, ...deviceStyles } }, children: [themeSupportsLayout && !themeHasDisabledLayoutStyles && renderingMode === 'post-only' && !isDesignPostType && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, { children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(LayoutStyle, { selector: ".editor-visual-editor__post-title-wrapper", layout: fallbackLayout }), /*#__PURE__*/(0, _jsxRuntime.jsx)(LayoutStyle, { selector: ".block-editor-block-list__layout.is-root-container", layout: postEditorLayout }), align && /*#__PURE__*/(0, _jsxRuntime.jsx)(LayoutStyle, { css: alignCSS }), postContentLayoutStyles && /*#__PURE__*/(0, _jsxRuntime.jsx)(LayoutStyle, { layout: postContentLayout, css: postContentLayoutStyles })] }), renderingMode === 'post-only' && !isDesignPostType && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { className: (0, _clsx.default)('editor-visual-editor__post-title-wrapper', // The following class is only here for backward comapatibility // 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, _jsxRuntime.jsx)(_postTitle.default, { ref: titleRef }) }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_blockEditor.RecursionProvider, { blockName: wrapperBlockName, uniqueId: wrapperUniqueId, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_blockEditor.BlockList, { className: (0, _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. ), 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, _jsxRuntime.jsx)(_editTemplateBlocksNotification.default, { contentRef: localRef })] }), // Avoid resize listeners when not needed, // these will trigger unnecessary re-renders // when animating the iframe width. enableResizing && resizeObserver] }) }) }); } var _default = exports.default = VisualEditor; //# sourceMappingURL=index.js.map