UNPKG

@wordpress/editor

Version:
419 lines (418 loc) 16.7 kB
// packages/editor/src/components/visual-editor/index.js import clsx from "clsx"; import { BlockList, store as blockEditorStore, __unstableUseTypewriter as useTypewriter, __unstableUseTypingObserver as useTypingObserver, useSettings, RecursionProvider, privateApis as blockEditorPrivateApis, __experimentalUseResizeCanvas as useResizeCanvas } from "@wordpress/block-editor"; import { useEffect, useRef, useMemo } from "@wordpress/element"; import { useSelect } from "@wordpress/data"; import { parse } from "@wordpress/blocks"; import { store as coreStore } from "@wordpress/core-data"; import { useMergeRefs, useViewportMatch } from "@wordpress/compose"; import PostTitle from "../post-title/index.mjs"; import { store as editorStore } from "../../store/index.mjs"; import { unlock } from "../../lock-unlock.mjs"; import EditTemplateBlocksNotification from "./edit-template-blocks-notification.mjs"; import ResizableEditor from "../resizable-editor/index.mjs"; import useSelectNearestEditableBlock from "./use-select-nearest-editable-block.mjs"; import { NAVIGATION_POST_TYPE, PATTERN_POST_TYPE, TEMPLATE_PART_POST_TYPE, TEMPLATE_POST_TYPE, DESIGN_POST_TYPES } from "../../store/constants.mjs"; import { useZoomOutModeExit } from "./use-zoom-out-mode-exit.mjs"; import { usePaddingAppender } from "./use-padding-appender.mjs"; import { useEditContentOnlySectionExit } from "./use-edit-content-only-section-exit.mjs"; import { SyncConnectionErrorModal } from "../sync-connection-error-modal/index.mjs"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; var { LayoutStyle, useLayoutClasses, useLayoutStyles, ExperimentalBlockCanvas: BlockCanvas, useFlashEditableBlocks } = unlock(blockEditorPrivateApis); 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 = useViewportMatch("small", "<"); const { renderingMode, postContentAttributes, editedPostTemplate = {}, wrapperBlockName, wrapperUniqueId, deviceType, isFocusedEntity, isDesignPostType, postType, isPreview, styles, canvasMinHeight } = useSelect((select) => { const { getCurrentPostId, getCurrentPostType, getCurrentTemplateId, getEditorSettings, getRenderingMode, getDeviceType, getCanvasMinHeight } = unlock(select(editorStore)); const { getPostType, getEditedEntityRecord } = select(coreStore); const postTypeSlug = getCurrentPostType(); const _renderingMode = getRenderingMode(); let _wrapperBlockName; if (postTypeSlug === 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", TEMPLATE_POST_TYPE, currentTemplateId ) : void 0; 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 : void 0, wrapperBlockName: _wrapperBlockName, wrapperUniqueId: getCurrentPostId(), deviceType: getDeviceType(), isFocusedEntity: !!editorSettings.onNavigateToPreviousEntityRecord, postType: postTypeSlug, isPreview: editorSettings.isPreviewMode, styles: editorSettings.styles, canvasMinHeight: getCanvasMinHeight() }; }, []); const { isCleanNewPost } = useSelect(editorStore); const { hasRootPaddingAwareAlignments, themeHasDisabledLayoutStyles, themeSupportsLayout, isZoomedOut } = useSelect((select) => { const { getSettings, isZoomOut: _isZoomOut } = unlock( select(blockEditorStore) ); const _settings = getSettings(); return { themeHasDisabledLayoutStyles: _settings.disableLayoutStyles, themeSupportsLayout: _settings.supportsLayout, hasRootPaddingAwareAlignments: _settings.__experimentalFeatures?.useRootPaddingAwareAlignments, isZoomedOut: _isZoomOut() }; }, []); const localRef = useRef(); const deviceStyles = useResizeCanvas(deviceType); const [globalLayoutSettings] = useSettings("layout"); const fallbackLayout = useMemo(() => { if (renderingMode !== "post-only" || isDesignPostType) { return { type: "default" }; } if (themeSupportsLayout) { return { ...globalLayoutSettings, type: "constrained" }; } return { type: "default" }; }, [ renderingMode, themeSupportsLayout, globalLayoutSettings, isDesignPostType ]); const newestPostContentAttributes = 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(parse(parseableContent)) || {}; }, [ editedPostTemplate?.content, editedPostTemplate?.blocks, postContentAttributes ]); const hasPostContentAtRootLevel = 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(parse(parseableContent)) || false; }, [editedPostTemplate?.content, editedPostTemplate?.blocks]); const { layout = {}, align = "" } = newestPostContentAttributes || {}; const postContentLayoutClasses = useLayoutClasses( newestPostContentAttributes, "core/post-content" ); const blockListLayoutClass = clsx( { "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 = 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 = useTypingObserver(); const titleRef = useRef(); 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 = [ NAVIGATION_POST_TYPE, TEMPLATE_PART_POST_TYPE, 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 === NAVIGATION_POST_TYPE && isPreview; const calculatedMinHeight = 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] = usePaddingAppender( !isPreview && renderingMode === "post-only" && !isDesignPostType ); const centerContentCSS = `display:flex;align-items:center;justify-content:center;`; const iframeStyles = 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 = useTypewriter(); contentRef = useMergeRefs([ localRef, contentRef, renderingMode === "post-only" ? typewriterRef : null, useFlashEditableBlocks({ isEnabled: renderingMode === "template-locked" }), useSelectNearestEditableBlock({ isEnabled: renderingMode === "template-locked" }), useZoomOutModeExit(), paddingAppenderRef, useEditContentOnlySectionExit() ]); return /* @__PURE__ */ jsxs( "div", { className: clsx( "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__ */ jsx(SyncConnectionErrorModal, {}), /* @__PURE__ */ jsx(ResizableEditor, { enableResizing, height: "100%", children: /* @__PURE__ */ jsxs( BlockCanvas, { shouldIframe: !disableIframe, contentRef, styles: iframeStyles, height: "100%", iframeProps: { ...iframeProps, style: { ...iframeProps?.style, ...deviceStyles } }, children: [ themeSupportsLayout && !themeHasDisabledLayoutStyles && renderingMode === "post-only" && !isDesignPostType && /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx( LayoutStyle, { selector: ".editor-visual-editor__post-title-wrapper", layout: fallbackLayout } ), /* @__PURE__ */ jsx( LayoutStyle, { selector: ".block-editor-block-list__layout.is-root-container", layout: postEditorLayout } ), align && /* @__PURE__ */ jsx(LayoutStyle, { css: alignCSS }), postContentLayoutStyles && /* @__PURE__ */ jsx( LayoutStyle, { layout: postContentLayout, css: postContentLayoutStyles } ) ] }), renderingMode === "post-only" && !isDesignPostType && /* @__PURE__ */ jsx( "div", { className: clsx( "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__ */ jsx(PostTitle, { ref: titleRef }) } ), /* @__PURE__ */ jsxs( RecursionProvider, { blockName: wrapperBlockName, uniqueId: wrapperUniqueId, children: [ /* @__PURE__ */ jsx( BlockList, { className: clsx( "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__ */ jsx( EditTemplateBlocksNotification, { contentRef: localRef } ) ] } ) ] } ) }) ] } ); } var visual_editor_default = VisualEditor; export { visual_editor_default as default }; //# sourceMappingURL=index.mjs.map