diff --git a/packages/block-editor/src/components/block-selection-clearer/index.js b/packages/block-editor/src/components/block-selection-clearer/index.js index b97b8895b5a9bb..8501d3179eacd1 100644 --- a/packages/block-editor/src/components/block-selection-clearer/index.js +++ b/packages/block-editor/src/components/block-selection-clearer/index.js @@ -2,8 +2,9 @@ * WordPress dependencies */ import { useSelect, useDispatch } from '@wordpress/data'; +import { useEffect, useRef } from '@wordpress/element'; -function useBlockSelectionClearer() { +export function useBlockSelectionClearer( ref ) { const hasSelection = useSelect( ( select ) => { const { hasSelectedBlock, hasMultiSelection } = select( 'core/block-editor' @@ -13,14 +14,25 @@ function useBlockSelectionClearer() { } ); const { clearSelectedBlock } = useDispatch( 'core/block-editor' ); - return ( event ) => { - if ( event.target === event.currentTarget && hasSelection ) { + useEffect( () => { + if ( ! hasSelection ) { + return; + } + + function onFocus() { clearSelectedBlock(); } - }; + + ref.current.addEventListener( 'focus', onFocus ); + + return () => { + ref.current.removeEventListener( 'focus', onFocus ); + }; + }, [ hasSelection, clearSelectedBlock ] ); } export default function BlockSelectionClearer( props ) { - const onFocus = useBlockSelectionClearer(); - return
; + const ref = useRef(); + useBlockSelectionClearer( ref ); + return
; } diff --git a/packages/block-editor/src/components/copy-handler/index.js b/packages/block-editor/src/components/copy-handler/index.js index 808c9cedf24179..887005b48488f5 100644 --- a/packages/block-editor/src/components/copy-handler/index.js +++ b/packages/block-editor/src/components/copy-handler/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useCallback, useRef } from '@wordpress/element'; +import { useCallback, useEffect, useRef } from '@wordpress/element'; import { serialize, pasteHandler } from '@wordpress/blocks'; import { documentHasSelection, @@ -71,97 +71,99 @@ export function useNotifyCopy() { }, [] ); } -function CopyHandler( { children } ) { - const containerRef = useRef(); - +export function useClipboardHandler( ref ) { const { getBlocksByClientId, getSelectedBlockClientIds, hasMultiSelection, getSettings, } = useSelect( ( select ) => select( 'core/block-editor' ), [] ); - const { flashBlock, removeBlocks, replaceBlocks } = useDispatch( 'core/block-editor' ); - const notifyCopy = useNotifyCopy(); - const { - __experimentalCanUserUseUnfilteredHTML: canUserUseUnfilteredHTML, - } = getSettings(); + useEffect( () => { + function handler( event ) { + const selectedBlockClientIds = getSelectedBlockClientIds(); - const handler = ( event ) => { - const selectedBlockClientIds = getSelectedBlockClientIds(); + if ( selectedBlockClientIds.length === 0 ) { + return; + } - if ( selectedBlockClientIds.length === 0 ) { - return; - } + // Always handle multiple selected blocks. + if ( ! hasMultiSelection() ) { + const { target } = event; + const { ownerDocument } = target; + // If copying, only consider actual text selection as selection. + // Otherwise, any focus on an input field is considered. + const hasSelection = + event.type === 'copy' || event.type === 'cut' + ? documentHasUncollapsedSelection( ownerDocument ) + : documentHasSelection( ownerDocument ); + + // Let native copy behaviour take over in input fields. + if ( hasSelection ) { + return; + } + } - // Always handle multiple selected blocks. - if ( ! hasMultiSelection() ) { - const { target } = event; - const { ownerDocument } = target; - // If copying, only consider actual text selection as selection. - // Otherwise, any focus on an input field is considered. - const hasSelection = - event.type === 'copy' || event.type === 'cut' - ? documentHasUncollapsedSelection( ownerDocument ) - : documentHasSelection( ownerDocument ); - - // Let native copy behaviour take over in input fields. - if ( hasSelection ) { + if ( ! ref.current.contains( event.target ) ) { return; } - } + event.preventDefault(); + + if ( event.type === 'copy' || event.type === 'cut' ) { + if ( selectedBlockClientIds.length === 1 ) { + flashBlock( selectedBlockClientIds[ 0 ] ); + } + notifyCopy( event.type, selectedBlockClientIds ); + const blocks = getBlocksByClientId( selectedBlockClientIds ); + const serialized = serialize( blocks ); + + event.clipboardData.setData( 'text/plain', serialized ); + event.clipboardData.setData( 'text/html', serialized ); + } - if ( ! containerRef.current.contains( event.target ) ) { - return; + if ( event.type === 'cut' ) { + removeBlocks( selectedBlockClientIds ); + } else if ( event.type === 'paste' ) { + const { + __experimentalCanUserUseUnfilteredHTML: canUserUseUnfilteredHTML, + } = getSettings(); + const { plainText, html } = getPasteEventData( event ); + const blocks = pasteHandler( { + HTML: html, + plainText, + mode: 'BLOCKS', + canUserUseUnfilteredHTML, + } ); + + replaceBlocks( + selectedBlockClientIds, + blocks, + blocks.length - 1, + -1 + ); + } } - event.preventDefault(); - if ( event.type === 'copy' || event.type === 'cut' ) { - if ( selectedBlockClientIds.length === 1 ) { - flashBlock( selectedBlockClientIds[ 0 ] ); - } - notifyCopy( event.type, selectedBlockClientIds ); - const blocks = getBlocksByClientId( selectedBlockClientIds ); - const serialized = serialize( blocks ); + ref.current.addEventListener( 'copy', handler ); + ref.current.addEventListener( 'cut', handler ); + ref.current.addEventListener( 'paste', handler ); - event.clipboardData.setData( 'text/plain', serialized ); - event.clipboardData.setData( 'text/html', serialized ); - } + return () => { + ref.current.removeEventListener( 'copy', handler ); + ref.current.removeEventListener( 'cut', handler ); + ref.current.removeEventListener( 'paste', handler ); + }; + }, [] ); +} - if ( event.type === 'cut' ) { - removeBlocks( selectedBlockClientIds ); - } else if ( event.type === 'paste' ) { - const { plainText, html } = getPasteEventData( event ); - const blocks = pasteHandler( { - HTML: html, - plainText, - mode: 'BLOCKS', - canUserUseUnfilteredHTML, - } ); - - replaceBlocks( - selectedBlockClientIds, - blocks, - blocks.length - 1, - -1 - ); - } - }; - - return ( -
- { children } -
- ); +function CopyHandler( { children } ) { + const ref = useRef(); + useClipboardHandler( ref ); + return
{ children }
; } export default CopyHandler; diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index e8a079a3af1652..40c6510d292c9f 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -83,24 +83,39 @@ export { } from './block-list/block-wrapper'; export { default as BlockMover } from './block-mover'; export { default as BlockPreview } from './block-preview'; -export { default as BlockSelectionClearer } from './block-selection-clearer'; +export { + default as BlockSelectionClearer, + useBlockSelectionClearer as __unstableUseBlockSelectionClearer, +} from './block-selection-clearer'; export { default as BlockSettingsMenu } from './block-settings-menu'; export { default as BlockSettingsMenuControls } from './block-settings-menu-controls'; export { default as BlockTitle } from './block-title'; export { default as BlockToolbar } from './block-toolbar'; -export { default as CopyHandler } from './copy-handler'; +export { + default as CopyHandler, + useClipboardHandler as __unstableUseClipboardHandler, +} from './copy-handler'; export { default as DefaultBlockAppender } from './default-block-appender'; export { default as __unstableEditorStyles } from './editor-styles'; export { default as Inserter } from './inserter'; export { default as __experimentalLibrary } from './inserter/library'; export { default as __experimentalSearchForm } from './inserter/search-form'; export { default as BlockEditorKeyboardShortcuts } from './keyboard-shortcuts'; -export { default as MultiSelectScrollIntoView } from './multi-select-scroll-into-view'; +export { + default as MultiSelectScrollIntoView, + useScrollMultiSelectionIntoView as __unstableUseScrollMultiSelectionIntoView, +} from './multi-select-scroll-into-view'; export { default as NavigableToolbar } from './navigable-toolbar'; -export { default as ObserveTyping } from './observe-typing'; +export { + default as ObserveTyping, + useTypingObserver as __unstableUseTypingObserver, +} from './observe-typing'; export { default as PreserveScrollInReorder } from './preserve-scroll-in-reorder'; export { default as SkipToSelectedBlock } from './skip-to-selected-block'; -export { default as Typewriter } from './typewriter'; +export { + default as Typewriter, + useTypewriter as __unstableUseTypewriter, +} from './typewriter'; export { default as Warning } from './warning'; export { default as WritingFlow } from './writing-flow'; diff --git a/packages/block-editor/src/components/multi-select-scroll-into-view/index.js b/packages/block-editor/src/components/multi-select-scroll-into-view/index.js index fb142b7d0ef009..fca97d6621f833 100644 --- a/packages/block-editor/src/components/multi-select-scroll-into-view/index.js +++ b/packages/block-editor/src/components/multi-select-scroll-into-view/index.js @@ -15,32 +15,29 @@ import { getScrollContainer } from '@wordpress/dom'; */ import { getBlockDOMNode } from '../../utils/dom'; -/** - * Scrolls the multi block selection end into view if not in view already. This - * is important to do after selection by keyboard. - */ -export default function MultiSelectScrollIntoView() { - const selector = ( select ) => { +export function useScrollMultiSelectionIntoView( ref ) { + const selectionEnd = useSelect( ( select ) => { const { getBlockSelectionEnd, hasMultiSelection, isMultiSelecting, } = select( 'core/block-editor' ); - return { - selectionEnd: getBlockSelectionEnd(), - isMultiSelection: hasMultiSelection(), - isMultiSelecting: isMultiSelecting(), - }; - }; - const { isMultiSelection, selectionEnd, isMultiSelecting } = useSelect( - selector, - [] - ); - const ref = useRef(); + const blockSelectionEnd = getBlockSelectionEnd(); + + if ( + ! blockSelectionEnd || + isMultiSelecting() || + ! hasMultiSelection() + ) { + return; + } + + return blockSelectionEnd; + }, [] ); useEffect( () => { - if ( ! selectionEnd || isMultiSelecting || ! isMultiSelection ) { + if ( ! selectionEnd ) { return; } @@ -62,7 +59,15 @@ export default function MultiSelectScrollIntoView() { scrollIntoView( extentNode, scrollContainer, { onlyScrollIfNeeded: true, } ); - }, [ isMultiSelection, selectionEnd, isMultiSelecting ] ); + }, [ selectionEnd ] ); +} +/** + * Scrolls the multi block selection end into view if not in view already. This + * is important to do after selection by keyboard. + */ +export default function MultiSelectScrollIntoView() { + const ref = useRef(); + useScrollMultiSelectionIntoView( ref ); return
; } diff --git a/packages/block-editor/src/components/observe-typing/index.js b/packages/block-editor/src/components/observe-typing/index.js index 269eaebf11aa5c..e33628c92b374a 100644 --- a/packages/block-editor/src/components/observe-typing/index.js +++ b/packages/block-editor/src/components/observe-typing/index.js @@ -43,7 +43,7 @@ function isKeyDownEligibleForStartTyping( event ) { return ! shiftKey && KEY_DOWN_ELIGIBLE_KEY_CODES.has( keyCode ); } -function useTypingObserver( ref ) { +export function useTypingObserver( ref ) { const isTyping = useSelect( ( select ) => select( 'core/block-editor' ).isTyping() ); diff --git a/packages/block-editor/src/components/typewriter/index.js b/packages/block-editor/src/components/typewriter/index.js index d0a05729a13964..522509f8e67561 100644 --- a/packages/block-editor/src/components/typewriter/index.js +++ b/packages/block-editor/src/components/typewriter/index.js @@ -10,8 +10,7 @@ const isIE = window.navigator.userAgent.indexOf( 'Trident' ) !== -1; const arrowKeyCodes = new Set( [ UP, DOWN, LEFT, RIGHT ] ); const initialTriggerPercentage = 0.75; -function Typewriter( { children } ) { - const ref = useRef(); +export function useTypewriter( ref ) { const hasSelectedBlock = useSelect( ( select ) => select( 'core/block-editor' ).hasSelectedBlock() ); @@ -242,7 +241,11 @@ function Typewriter( { children } ) { defaultView.cancelAnimationFrame( onKeyDownRafId ); }; }, [ hasSelectedBlock ] ); +} +function Typewriter( { children } ) { + const ref = useRef(); + useTypewriter( ref ); return (
{ children } diff --git a/packages/block-editor/src/components/writing-flow/index.js b/packages/block-editor/src/components/writing-flow/index.js index 428e904194c669..d48428a80cf9c9 100644 --- a/packages/block-editor/src/components/writing-flow/index.js +++ b/packages/block-editor/src/components/writing-flow/index.js @@ -708,7 +708,7 @@ export default function WritingFlow( { children } ) { // bubbling events from children to determine focus transition intents. /* eslint-disable jsx-a11y/no-static-element-interactions */ return ( -
+ <>
@@ -751,7 +752,7 @@ export default function WritingFlow( { children } ) { onClick={ focusLastTextField } className="block-editor-writing-flow__click-redirect" /> -
+ ); /* eslint-enable jsx-a11y/no-static-element-interactions */ } diff --git a/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js b/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js index 99a9be8268fe1a..730af28bdb10a0 100644 --- a/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js @@ -110,7 +110,7 @@ describe( 'Order of block keyboard navigation', () => { // Clear the selected block and put focus in front of the block list. await page.evaluate( () => { - document.querySelector( '.edit-post-visual-editor' ).focus(); + document.querySelector( '.editor-styles-wrapper' ).focus(); } ); await page.keyboard.press( 'Tab' ); @@ -145,7 +145,7 @@ describe( 'Order of block keyboard navigation', () => { // Clear the selected block and put focus behind the block list. await page.evaluate( () => { - document.querySelector( '.edit-post-visual-editor' ).focus(); + document.querySelector( '.editor-styles-wrapper' ).focus(); document .querySelector( '.interface-interface-skeleton__sidebar' ) .focus(); diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index 026acf6654bb51..a177981e04f175 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -7,16 +7,17 @@ import { } from '@wordpress/editor'; import { WritingFlow, - Typewriter, - ObserveTyping, BlockList, - CopyHandler, - BlockSelectionClearer, - MultiSelectScrollIntoView, + __unstableUseBlockSelectionClearer as useBlockSelectionClearer, + __unstableUseTypewriter as useTypewriter, + __unstableUseClipboardHandler as useClipboardHandler, + __unstableUseTypingObserver as useTypingObserver, + __unstableUseScrollMultiSelectionIntoView as useScrollMultiSelectionIntoView, __experimentalBlockSettingsMenuFirstItem, __experimentalUseResizeCanvas as useResizeCanvas, } from '@wordpress/block-editor'; import { Popover } from '@wordpress/components'; +import { useRef } from '@wordpress/element'; /** * Internal dependencies @@ -24,40 +25,41 @@ import { Popover } from '@wordpress/components'; import BlockInspectorButton from './block-inspector-button'; import { useSelect } from '@wordpress/data'; -function VisualEditor() { +export default function VisualEditor() { + const ref = useRef(); const deviceType = useSelect( ( select ) => { return select( 'core/edit-post' ).__experimentalGetPreviewDeviceType(); }, [] ); - const inlineStyles = useResizeCanvas( deviceType ); + useScrollMultiSelectionIntoView( ref ); + useBlockSelectionClearer( ref ); + useTypewriter( ref ); + useClipboardHandler( ref ); + useTypingObserver( ref ); + return ( - +
- - - - - -
- -
- -
-
-
-
+
+ +
+ +
+ +
+
<__experimentalBlockSettingsMenuFirstItem> { ( { onClose } ) => ( ) } - +
); } - -export default VisualEditor; diff --git a/packages/edit-post/src/components/visual-editor/style.scss b/packages/edit-post/src/components/visual-editor/style.scss index a9a261a417eb8c..4534cdefe6886c 100644 --- a/packages/edit-post/src/components/visual-editor/style.scss +++ b/packages/edit-post/src/components/visual-editor/style.scss @@ -1,6 +1,5 @@ .edit-post-visual-editor { position: relative; - padding-top: 50px; // Default background color so that grey .edit-post-editor-regions__content color doesn't show through. background-color: $white; @@ -28,10 +27,8 @@ } } -.edit-post-visual-editor > .block-editor__typewriter, -.edit-post-visual-editor > .block-editor__typewriter > div, -.edit-post-visual-editor > .block-editor__typewriter > div > .block-editor-writing-flow, -.edit-post-visual-editor > .block-editor__typewriter > div > .block-editor-writing-flow > .block-editor-writing-flow__click-redirect { +.editor-styles-wrapper, +.editor-styles-wrapper > .block-editor-writing-flow__click-redirect { height: 100%; }