"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 _useZoomOutModeExit = require("./use-zoom-out-mode-exit");
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 [contentHeight, setContentHeight] = (0, _element.useState)('');
  const effectContentHeight = (0, _compose.useResizeObserver)(([entry]) => {
    setContentHeight(entry.borderBoxSize[0].blockSize);
  });
  const isMobileViewport = (0, _compose.useViewportMatch)('small', '<');
  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.isPreviewMode
    };
  }, []);
  const {
    isCleanNewPost
  } = (0, _data.useSelect)(_store.store);
  const {
    hasRootPaddingAwareAlignments,
    themeHasDisabledLayoutStyles,
    themeSupportsLayout,
    isZoomedOut
  } = (0, _data.useSelect)(select => {
    const {
      getSettings,
      isZoomOut: _isZoomOut
    } = (0, _lockUnlock.unlock)(select(_blockEditor.store));
    const _settings = getSettings();
    return {
      themeHasDisabledLayoutStyles: _settings.disableLayoutStyles,
      themeSupportsLayout: _settings.supportsLayout,
      hasRootPaddingAwareAlignments: _settings.__experimentalFeatures?.useRootPaddingAwareAlignments,
      isZoomedOut: _isZoomOut()
    };
  }, []);
  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 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 &&
  // Disable resizing in zoomed-out mode.
  !isZoomedOut;
  const iframeStyles = (0, _element.useMemo)(() => {
    return [...(styles !== null && styles !== void 0 ? 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;}.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]);
  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'
  }), (0, _useZoomOutModeExit.useZoomOutModeExit)(),
  // Avoid resize listeners when not needed, these will trigger
  // unnecessary re-renders when animating the iframe width.
  enableResizing ? effectContentHeight : null]);
  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': !disableIframe
    }),
    children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_resizableEditor.default, {
      enableResizing: enableResizing,
      height: contentHeight && !forceFullHeight ? contentHeight : '100%',
      children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(BlockCanvas, {
        shouldIframe: !disableIframe,
        contentRef: contentRef,
        styles: iframeStyles,
        height: "100%",
        iframeProps: {
          ...iframeProps,
          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 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, _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.
            {
              '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, _jsxRuntime.jsx)(_editTemplateBlocksNotification.default, {
            contentRef: localRef
          })]
        })]
      })
    })
  });
}
var _default = exports.default = VisualEditor;
//# sourceMappingURL=index.js.map