diff --git a/packages/editor/src/components/editor-canvas/edit-template-blocks-notification.js b/packages/editor/src/components/editor-canvas/edit-template-blocks-notification.js deleted file mode 100644 index 566311e20cadc2..00000000000000 --- a/packages/editor/src/components/editor-canvas/edit-template-blocks-notification.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * WordPress dependencies - */ -import { useSelect, useDispatch } from '@wordpress/data'; -import { useEffect, useState, useRef } from '@wordpress/element'; -import { store as noticesStore } from '@wordpress/notices'; -import { __ } from '@wordpress/i18n'; -import { __experimentalConfirmDialog as ConfirmDialog } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { store as editorStore } from '../../store'; - -/** - * Component that: - * - * - Displays a 'Edit your template to edit this block' notification when the - * user is focusing on editing page content and clicks on a disabled template - * block. - * - Displays a 'Edit your template to edit this block' dialog when the user - * is focusing on editing page conetnt and double clicks on a disabled - * template block. - * - * @param {Object} props - * @param {import('react').RefObject} props.contentRef Ref to the block - * editor iframe canvas. - */ -export default function EditTemplateBlocksNotification( { contentRef } ) { - const renderingMode = useSelect( - ( select ) => select( editorStore ).getRenderingMode(), - [] - ); - const { getNotices } = useSelect( noticesStore ); - - const { createInfoNotice, removeNotice } = useDispatch( noticesStore ); - const { setRenderingMode } = useDispatch( editorStore ); - - const [ isDialogOpen, setIsDialogOpen ] = useState( false ); - - const lastNoticeId = useRef( 0 ); - - useEffect( () => { - const handleClick = async ( event ) => { - if ( renderingMode !== 'template-locked' ) { - return; - } - if ( ! event.target.classList.contains( 'is-root-container' ) ) { - return; - } - const isNoticeAlreadyShowing = getNotices().some( - ( notice ) => notice.id === lastNoticeId.current - ); - if ( isNoticeAlreadyShowing ) { - return; - } - const { notice } = await createInfoNotice( - __( 'Edit your template to edit this block.' ), - { - isDismissible: true, - type: 'snackbar', - actions: [ - { - label: __( 'Edit template' ), - onClick: () => setRenderingMode( 'template-only' ), - }, - ], - } - ); - lastNoticeId.current = notice.id; - }; - - const handleDblClick = ( event ) => { - if ( renderingMode !== 'template-locked' ) { - return; - } - if ( ! event.target.classList.contains( 'is-root-container' ) ) { - return; - } - if ( lastNoticeId.current ) { - removeNotice( lastNoticeId.current ); - } - setIsDialogOpen( true ); - }; - - const canvas = contentRef.current; - canvas?.addEventListener( 'click', handleClick ); - canvas?.addEventListener( 'dblclick', handleDblClick ); - return () => { - canvas?.removeEventListener( 'click', handleClick ); - canvas?.removeEventListener( 'dblclick', handleDblClick ); - }; - }, [ lastNoticeId, renderingMode, contentRef.current ] ); - - return ( - { - setIsDialogOpen( false ); - setRenderingMode( 'template-only' ); - } } - onCancel={ () => setIsDialogOpen( false ) } - > - { __( 'Edit your template to edit this block.' ) } - - ); -} diff --git a/packages/editor/src/components/editor-canvas/index.js b/packages/editor/src/components/editor-canvas/index.js index d6084ef922805e..c9da78c23f4e59 100644 --- a/packages/editor/src/components/editor-canvas/index.js +++ b/packages/editor/src/components/editor-canvas/index.js @@ -28,7 +28,7 @@ import { useMergeRefs } from '@wordpress/compose'; import PostTitle from '../post-title'; import { store as editorStore } from '../../store'; import { unlock } from '../../lock-unlock'; -import EditTemplateBlocksNotification from './edit-template-blocks-notification'; +import TemplateSelection from './template-selection'; const { LayoutStyle, @@ -293,94 +293,100 @@ function EditorCanvas( { ] ); return ( - - { themeSupportsLayout && - ! themeHasDisabledLayoutStyles && - renderingMode === 'post-only' && ( - <> - - - { align && } - { postContentLayoutStyles && ( + <> + + { themeSupportsLayout && + ! themeHasDisabledLayoutStyles && + renderingMode === 'post-only' && ( + <> + + { align && } + { postContentLayoutStyles && ( + + ) } + + ) } + { renderingMode === 'post-only' && ( +
+ contentEditable={ false } + ref={ observeTypingRef } + style={ { + // This is using inline styles + // so it's applied for both iframed and non iframed editors. + marginTop: '4rem', + } } + > + +
) } - { renderingMode === 'post-only' && ( -
- -
- ) } - - + { renderingMode === 'template-locked' && ( + ) } - 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 - } - renderAppender={ renderAppender } - /> - - - { children } -
+ + { children } +
+ ); } diff --git a/packages/editor/src/components/editor-canvas/template-selection.js b/packages/editor/src/components/editor-canvas/template-selection.js new file mode 100644 index 00000000000000..d072273c200624 --- /dev/null +++ b/packages/editor/src/components/editor-canvas/template-selection.js @@ -0,0 +1,90 @@ +/** + * WordPress dependencies + */ +import { useState, useEffect } from '@wordpress/element'; +import { Fill } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import TemplateToolbar from '../template-toolbar'; + +// This CSS is adapted from block-list/content.scss in the block-editor package. +// TODO: Should we re-use the existing CSS there by allowing it to target .is-root-container? +const HOVERING_CSS = ` +.is-root-container::after { + content: ""; + position: absolute; + pointer-events: none; + top: 1px; + left: 1px; + right: 1px; + bottom: 1px; + box-shadow: 0 0 0 1px var(--wp-block-synced-color); + border-radius: 1px; +} +`; +const SELECTED_CSS = ` +.is-root-container::after { + content: ""; + position: absolute; + pointer-events: none; + top: 1px; + left: 1px; + right: 1px; + bottom: 1px; + box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-block-synced-color); + border-radius: 1px; +} +`; + +export default function TemplateSelection( { canvasRef } ) { + const [ isHovering, setIsHovering ] = useState( false ); + const [ isSelected, setIsSelected ] = useState( false ); + + useEffect( () => { + const handleMouseOver = ( event ) => { + setIsHovering( + event.target.classList.contains( 'is-root-container' ) + ); + }; + + const handleMouseOut = () => { + setIsHovering( false ); + }; + + const handleClick = async ( event ) => { + setIsSelected( + event.target.classList.contains( 'is-root-container' ) + ); + }; + + const canvas = canvasRef.current; + canvas?.addEventListener( 'mouseover', handleMouseOver ); + canvas?.addEventListener( 'mouseout', handleMouseOut ); + canvas?.addEventListener( 'click', handleClick ); + return () => { + canvas?.removeEventListener( 'mouseover', handleMouseOver ); + canvas?.removeEventListener( 'mouseout', handleMouseOut ); + canvas?.removeEventListener( 'click', handleClick ); + }; + }, [ canvasRef, isSelected ] ); + + let css = ''; + if ( isSelected ) { + css = SELECTED_CSS; + } else if ( isHovering ) { + css = HOVERING_CSS; + } + + return ( + <> + + { isSelected && ( + + + + ) } + + ); +} diff --git a/packages/editor/src/components/template-toolbar/index.js b/packages/editor/src/components/template-toolbar/index.js new file mode 100644 index 00000000000000..8b630f12e7bb09 --- /dev/null +++ b/packages/editor/src/components/template-toolbar/index.js @@ -0,0 +1,58 @@ +/** + * WordPress dependencies + */ +import { + ToolbarButton, + ToolbarGroup, + __experimentalHStack as HStack, + __experimentalText as Text, +} from '@wordpress/components'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { NavigableToolbar, BlockIcon } from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { store as editorStore } from '../../store'; + +export default function TemplateToolbar() { + const { icon: templateIcon, title: templateTitle } = useSelect( + ( select ) => { + const templateId = select( editorStore ).getCurrentTemplateId(); + const record = select( coreStore ).getEditedEntityRecord( + 'postType', + 'wp_template', + templateId + ); + return select( editorStore ).__experimentalGetTemplateInfo( + record + ); + } + ); + const { setRenderingMode } = useDispatch( editorStore ); + return ( + + + + + { templateTitle } + + + + setRenderingMode( 'template-only' ) } + > + { __( 'Edit template' ) } + + + + ); +} diff --git a/packages/editor/src/components/template-toolbar/style.scss b/packages/editor/src/components/template-toolbar/style.scss new file mode 100644 index 00000000000000..783178ba570d8e --- /dev/null +++ b/packages/editor/src/components/template-toolbar/style.scss @@ -0,0 +1,13 @@ +.editor-template-toolbar { + position: absolute; + left: $grid-unit-20; + top: $grid-unit-20; +} + +.editor-template-toolbar__title { + fill: var(--wp-block-synced-color); + + .components-text { + color: var(--wp-block-synced-color); + } +} diff --git a/packages/editor/src/style.scss b/packages/editor/src/style.scss index 450c61fd0bb7e6..5adedc27a9f19b 100644 --- a/packages/editor/src/style.scss +++ b/packages/editor/src/style.scss @@ -29,4 +29,5 @@ @import "./components/post-trash/style.scss"; @import "./components/preview-dropdown/style.scss"; @import "./components/table-of-contents/style.scss"; +@import "./components/template-toolbar/style.scss"; @import "./components/template-validation-notice/style.scss";