From c14f9bc05a3b704a116754227f06e0cd6460bb6e Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 15 Apr 2022 12:42:25 +0100 Subject: [PATCH 01/21] Add a built-in exploded mode instead of a separate view --- .../data/data-core-block-editor.md | 2 +- .../src/components/block-list/index.js | 9 +- .../src/components/block-list/style.scss | 10 ++ .../use-focus-first-element.js | 4 +- .../use-block-props/use-is-hovered.js | 10 +- .../src/components/iframe/index.js | 91 ++++++++++++++++--- .../src/components/inner-blocks/index.js | 5 +- .../src/components/tool-selector/index.js | 20 ++-- packages/block-editor/src/store/actions.js | 19 +++- packages/block-editor/src/store/reducer.js | 14 +-- packages/block-editor/src/store/selectors.js | 13 ++- .../edit-site/src/components/header/index.js | 22 ++++- 12 files changed, 171 insertions(+), 48 deletions(-) diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index e0e9695a0036a..f38cc49a87c0a 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -1492,7 +1492,7 @@ Action that enables or disables the navigation mode. _Parameters_ -- _isNavigationMode_ `string`: Enable/Disable navigation mode. +- _isNavigationMode_ `boolean`: Enable/Disable navigation mode. ### setTemplateValidity diff --git a/packages/block-editor/src/components/block-list/index.js b/packages/block-editor/src/components/block-list/index.js index b97bb03c5c142..db91eb7c13d3e 100644 --- a/packages/block-editor/src/components/block-list/index.js +++ b/packages/block-editor/src/components/block-list/index.js @@ -34,16 +34,16 @@ export const IntersectionObserver = createContext(); function Root( { className, ...settings } ) { const [ element, setElement ] = useState(); const isLargeViewport = useViewportMatch( 'medium' ); - const { isOutlineMode, isFocusMode, isNavigationMode } = useSelect( + const { isOutlineMode, isFocusMode, editorMode } = useSelect( ( select ) => { - const { getSettings, isNavigationMode: _isNavigationMode } = select( + const { getSettings, __unstableGetEditorMode } = select( blockEditorStore ); const { outlineMode, focusMode } = getSettings(); return { isOutlineMode: outlineMode, isFocusMode: focusMode, - isNavigationMode: _isNavigationMode(), + editorMode: __unstableGetEditorMode(), }; }, [] @@ -75,7 +75,8 @@ function Root( { className, ...settings } ) { className: classnames( 'is-root-container', className, { 'is-outline-mode': isOutlineMode, 'is-focus-mode': isFocusMode && isLargeViewport, - 'is-navigate-mode': isNavigationMode, + 'is-navigate-mode': editorMode === 'navigation', + 'is-exploded-mode': editorMode === 'exploded', } ), }, settings diff --git a/packages/block-editor/src/components/block-list/style.scss b/packages/block-editor/src/components/block-list/style.scss index da02c6fd4b801..50d532f6f930b 100644 --- a/packages/block-editor/src/components/block-list/style.scss +++ b/packages/block-editor/src/components/block-list/style.scss @@ -408,3 +408,13 @@ margin-bottom: auto; } } + +/** Exploded mode styles **/ +.is-root-container.is-exploded-mode { + margin-top: 100px; +} + +.is-root-container.is-exploded-mode > .wp-block { + transform: scale(0.8); + transform-origin: top center; +} diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js b/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js index 8b95f19f3c9ff..d9b6b3831f1a4 100644 --- a/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js +++ b/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js @@ -36,7 +36,7 @@ function useInitialPosition( clientId ) { ( select ) => { const { getSelectedBlocksInitialCaretPosition, - isNavigationMode, + __unstableGetEditorMode, isBlockSelected, } = select( blockEditorStore ); @@ -44,7 +44,7 @@ function useInitialPosition( clientId ) { return; } - if ( isNavigationMode() ) { + if ( __unstableGetEditorMode() !== 'edit' ) { return; } diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-is-hovered.js b/packages/block-editor/src/components/block-list/use-block-props/use-is-hovered.js index 26fc68c21ce2f..b2635d3170909 100644 --- a/packages/block-editor/src/components/block-list/use-block-props/use-is-hovered.js +++ b/packages/block-editor/src/components/block-list/use-block-props/use-is-hovered.js @@ -26,8 +26,14 @@ function listener( event ) { */ export function useIsHovered() { const isEnabled = useSelect( ( select ) => { - const { isNavigationMode, getSettings } = select( blockEditorStore ); - return isNavigationMode() || getSettings().outlineMode; + const { __unstableGetEditorMode, getSettings } = select( + blockEditorStore + ); + return ( + __unstableGetEditorMode() === 'navigation' || + __unstableGetEditorMode() === 'exploded' || + getSettings().outlineMode + ); }, [] ); return useRefEffect( diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index 5596aa0032578..d5821f8133e96 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -16,12 +16,14 @@ import { import { __ } from '@wordpress/i18n'; import { useMergeRefs, useRefEffect } from '@wordpress/compose'; import { __experimentalStyleProvider as StyleProvider } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ import { useBlockSelectionClearer } from '../block-selection-clearer'; import { useWritingFlow } from '../writing-flow'; +import { store as blockEditorStore } from '../../store'; const BODY_CLASS_NAME = 'editor-styles-wrapper'; const BLOCK_PREFIX = 'wp-block'; @@ -164,10 +166,65 @@ async function loadScript( head, { id, src } ) { } ); } +function useExplodedModeBackgroundStyles( isExplodedMode, deps = [] ) { + return useRefEffect( + ( node ) => { + if ( ! isExplodedMode ) { + return; + } + + let bodyStyleElement = node.querySelector( + '#body-reset-background' + ); + if ( ! bodyStyleElement ) { + bodyStyleElement = node.ownerDocument.createElement( 'style' ); + bodyStyleElement.id = 'body-reset-background'; + node.prepend( bodyStyleElement ); + } + bodyStyleElement.textContent = ''; + + let copiedStylesElement = node.querySelector( + '#body-background-copied-styles' + ); + if ( ! copiedStylesElement ) { + copiedStylesElement = node.ownerDocument.createElement( + 'style' + ); + copiedStylesElement.id = 'body-background-copied-styles'; + node.prepend( copiedStylesElement ); + } + copiedStylesElement.textContent = ''; + + const styles = window.getComputedStyle( node.ownerDocument.body ); + const newBackgroundStyles = Object.values( styles ) + .filter( ( propertyName ) => { + return propertyName.indexOf( 'background' ) === 0; + } ) + .map( + ( propertyName ) => + `${ propertyName }:${ styles.getPropertyValue( + propertyName + ) };` + ) + .join( '' ); + + copiedStylesElement.innerHTML = `:where( .is-root-container.is-exploded-mode > .wp-block ) { ${ newBackgroundStyles } }`; + bodyStyleElement.innerHTML = + 'body { background: gray !important; }'; + }, + [ ...deps, isExplodedMode ] + ); +} + function Iframe( { contentRef, children, head, tabIndex = 0, assets, ...props }, ref ) { + const isExplodedMode = useSelect( + ( select ) => + select( blockEditorStore ).__unstableGetEditorMode() === 'exploded', + [] + ); const [ , forceRender ] = useReducer( () => ( {} ) ); const [ iframeDocument, setIframeDocument ] = useState(); const [ bodyClasses, setBodyClasses ] = useState( [] ); @@ -212,19 +269,27 @@ function Iframe( return () => node.removeEventListener( 'load', setDocumentIfReady ); }, [] ); - const headRef = useRefEffect( ( element ) => { - scripts - .reduce( - ( promise, script ) => - promise.then( () => loadScript( element, script ) ), - Promise.resolve() - ) - .finally( () => { - // When script are loaded, re-render blocks to allow them - // to initialise. - forceRender(); - } ); - }, [] ); + + const headBackgroundStylesRef = useExplodedModeBackgroundStyles( + isExplodedMode, + [ head ] + ); + const headRef = useMergeRefs( [ + useRefEffect( ( element ) => { + scripts + .reduce( + ( promise, script ) => + promise.then( () => loadScript( element, script ) ), + Promise.resolve() + ) + .finally( () => { + // When script are loaded, re-render blocks to allow them + // to initialise. + forceRender(); + } ); + }, [] ), + headBackgroundStylesRef, + ] ); const bodyRef = useMergeRefs( [ contentRef, clearerRef, writingFlowRef ] ); const styleCompatibilityRef = useStylesCompatibility(); diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index 8a75060b434b6..a0d5c89cec99e 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -155,10 +155,11 @@ export function useInnerBlocksProps( props = {}, options = {} ) { getBlockName, isBlockSelected, hasSelectedInnerBlock, - isNavigationMode, + __unstableGetEditorMode, } = select( blockEditorStore ); const blockName = getBlockName( clientId ); - const enableClickThrough = isNavigationMode() || isSmallScreen; + const enableClickThrough = + __unstableGetEditorMode() === 'navigation' || isSmallScreen; return { __experimentalCaptureToolbars: select( blocksStore diff --git a/packages/block-editor/src/components/tool-selector/index.js b/packages/block-editor/src/components/tool-selector/index.js index df43a5e172e28..3da6ebc5a9c46 100644 --- a/packages/block-editor/src/components/tool-selector/index.js +++ b/packages/block-editor/src/components/tool-selector/index.js @@ -31,15 +31,11 @@ const selectIcon = ( ); function ToolSelector( props, ref ) { - const isNavigationTool = useSelect( - ( select ) => select( blockEditorStore ).isNavigationMode(), + const mode = useSelect( + ( select ) => select( blockEditorStore ).__unstableGetEditorMode(), [] ); - const { setNavigationMode } = useDispatch( blockEditorStore ); - - const onSwitchMode = ( mode ) => { - setNavigationMode( mode === 'edit' ? false : true ); - }; + const { __unstableSetEditorMode } = useDispatch( blockEditorStore ); return ( { selectIcon } diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index afb93c40c5d92..ee565cc83604d 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -1427,25 +1427,36 @@ export const __unstableMarkAutomaticChange = () => ( { dispatch } ) => { /** * Action that enables or disables the navigation mode. * - * @param {string} isNavigationMode Enable/Disable navigation mode. + * @param {boolean} isNavigationMode Enable/Disable navigation mode. */ export const setNavigationMode = ( isNavigationMode = true ) => ( { dispatch, } ) => { - dispatch( { type: 'SET_NAVIGATION_MODE', isNavigationMode } ); + dispatch.setMode( isNavigationMode ? 'navigation' : 'edit' ); +}; + +/** + * Action that sets the editor mode + * + * @param {string} mode Editor mode + */ +export const __unstableSetEditorMode = ( mode ) => ( { dispatch } ) => { + dispatch( { type: 'SET_EDITOR_MODE', mode } ); - if ( isNavigationMode ) { + if ( mode === 'navigation' ) { speak( __( 'You are currently in navigation mode. Navigate blocks using the Tab key and Arrow keys. Use Left and Right Arrow keys to move between nesting levels. To exit navigation mode and edit the selected block, press Enter.' ) ); - } else { + } else if ( mode === 'edit' ) { speak( __( 'You are currently in edit mode. To return to the navigation mode, press Escape.' ) ); + } else if ( mode === 'exploded' ) { + speak( __( 'You are currently in exploded mode.' ) ); } }; diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 5df8ab6a148de..dc5ba83b054b5 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1593,14 +1593,14 @@ export const blockListSettings = ( state = {}, action ) => { * * @return {string} Updated state. */ -export function isNavigationMode( state = false, action ) { +export function editorMode( state = 'edit', action ) { // Let inserting block always trigger Edit mode. - if ( action.type === 'INSERT_BLOCKS' ) { - return false; + if ( action.type === 'INSERT_BLOCKS' && state === 'navigation' ) { + return 'edit'; } - if ( action.type === 'SET_NAVIGATION_MODE' ) { - return action.isNavigationMode; + if ( action.type === 'SET_EDITOR_MODE' ) { + return action.mode; } return state; @@ -1621,7 +1621,7 @@ export function hasBlockMovingClientId( state = null, action ) { return action.hasBlockMovingClientId; } - if ( action.type === 'SET_NAVIGATION_MODE' ) { + if ( action.type === 'SET_EDITOR_MODE' ) { return null; } @@ -1769,7 +1769,7 @@ export default combineReducers( { settings, preferences, lastBlockAttributesChange, - isNavigationMode, + editorMode, hasBlockMovingClientId, automaticChangeStatus, highlightedBlock, diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 4a1c0ca135448..6a571498021d2 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2541,7 +2541,18 @@ function getReusableBlocks( state ) { * @return {boolean} Is navigation mode enabled. */ export function isNavigationMode( state ) { - return state.isNavigationMode; + return state.editorMode === 'navigation'; +} + +/** + * Returns the current editor mode. + * + * @param {Object} state Editor state. + * + * @return {string} the editor mode. + */ +export function __unstableGetEditorMode( state ) { + return state.editorMode; } /** diff --git a/packages/edit-site/src/components/header/index.js b/packages/edit-site/src/components/header/index.js index 6257fa70a33be..791ab100ede39 100644 --- a/packages/edit-site/src/components/header/index.js +++ b/packages/edit-site/src/components/header/index.js @@ -6,11 +6,12 @@ import { useViewportMatch } from '@wordpress/compose'; import { ToolSelector, __experimentalPreviewOptions as PreviewOptions, + store as blockEditorStore, } from '@wordpress/block-editor'; import { useSelect, useDispatch } from '@wordpress/data'; import { PinnedItems } from '@wordpress/interface'; import { _x, __ } from '@wordpress/i18n'; -import { listView, plus } from '@wordpress/icons'; +import { listView, plus, stack } from '@wordpress/icons'; import { Button, ToolbarItem } from '@wordpress/components'; import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; import { store as editorStore } from '@wordpress/editor'; @@ -47,6 +48,7 @@ export default function Header( { listViewShortcut, isLoaded, isVisualMode, + blockEditorMode, } = useSelect( ( select ) => { const { __experimentalGetPreviewDeviceType, @@ -61,6 +63,7 @@ export default function Header( { editorStore ); const { getShortcutRepresentation } = select( keyboardShortcutsStore ); + const { __unstableGetEditorMode } = select( blockEditorStore ); const postType = getEditedPostType(); const postId = getEditedPostId(); @@ -79,6 +82,7 @@ export default function Header( { 'core/edit-site/toggle-list-view' ), isVisualMode: getEditorMode() === 'visual', + blockEditorMode: __unstableGetEditorMode(), }; }, [] ); @@ -87,6 +91,7 @@ export default function Header( { setIsInserterOpened, setIsListViewOpened, } = useDispatch( editSiteStore ); + const { __unstableSetEditorMode } = useDispatch( blockEditorStore ); const isLargeViewport = useViewportMatch( 'medium' ); @@ -145,6 +150,21 @@ export default function Header( { onClick={ toggleListView } shortcut={ listViewShortcut } /> +