From a7151edee5fec4ad6c694a16537f422cab78f05c Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 25 Aug 2023 12:12:34 +0100 Subject: [PATCH 01/11] Make the shortcut provider optional --- .../src/components/customize-widgets/index.js | 28 ++++++------- .../test/index.js | 9 ++--- packages/edit-post/src/editor.js | 37 ++++++++---------- .../edit-site/src/components/app/index.js | 21 +++++----- .../index.js | 29 +++++++------- packages/keyboard-shortcuts/README.md | 2 +- .../src/components/shortcut-provider.js | 2 + packages/keyboard-shortcuts/src/context.js | 9 ++++- storybook/stories/playground/index.story.js | 39 +++++++++---------- .../helpers/integration-test-editor.js | 29 +++++++------- 10 files changed, 96 insertions(+), 109 deletions(-) diff --git a/packages/customize-widgets/src/components/customize-widgets/index.js b/packages/customize-widgets/src/components/customize-widgets/index.js index 3306e438f80e9..d206108398283 100644 --- a/packages/customize-widgets/src/components/customize-widgets/index.js +++ b/packages/customize-widgets/src/components/customize-widgets/index.js @@ -3,7 +3,6 @@ */ import { useState, useEffect, useRef, createPortal } from '@wordpress/element'; import { SlotFillProvider, Popover } from '@wordpress/components'; -import { ShortcutProvider } from '@wordpress/keyboard-shortcuts'; /** * Internal dependencies @@ -68,21 +67,16 @@ export default function CustomizeWidgets( { ); return ( - - - - - { activeSidebar } - { popover } - - - - + + + + { activeSidebar } + { popover } + + + ); } diff --git a/packages/edit-post/src/components/keyboard-shortcut-help-modal/test/index.js b/packages/edit-post/src/components/keyboard-shortcut-help-modal/test/index.js index 6d8982501d137..0380e648a1733 100644 --- a/packages/edit-post/src/components/keyboard-shortcut-help-modal/test/index.js +++ b/packages/edit-post/src/components/keyboard-shortcut-help-modal/test/index.js @@ -7,7 +7,6 @@ import { render, screen } from '@testing-library/react'; * WordPress dependencies */ import { EditorKeyboardShortcutsRegister } from '@wordpress/editor'; -import { ShortcutProvider } from '@wordpress/keyboard-shortcuts'; /** * Internal dependencies @@ -19,10 +18,10 @@ const noop = () => {}; describe( 'KeyboardShortcutHelpModal', () => { it( 'should match snapshot when the modal is active', () => { render( - + <> - + ); expect( @@ -34,13 +33,13 @@ describe( 'KeyboardShortcutHelpModal', () => { it( 'should not render the modal when inactive', () => { render( - + <> - + ); expect( diff --git a/packages/edit-post/src/editor.js b/packages/edit-post/src/editor.js index 00bc911dd7626..6668001b76773 100644 --- a/packages/edit-post/src/editor.js +++ b/packages/edit-post/src/editor.js @@ -12,7 +12,6 @@ import { import { useMemo } from '@wordpress/element'; import { SlotFillProvider } from '@wordpress/components'; import { store as coreStore } from '@wordpress/core-data'; -import { ShortcutProvider } from '@wordpress/keyboard-shortcuts'; import { store as preferencesStore } from '@wordpress/preferences'; import { CommandMenu } from '@wordpress/commands'; import { privateApis as coreCommandsPrivateApis } from '@wordpress/core-commands'; @@ -156,25 +155,23 @@ function Editor( { postId, postType, settings, initialEdits, ...props } ) { } return ( - - - - - - - - - - - - + + + + + + + + + + ); } diff --git a/packages/edit-site/src/components/app/index.js b/packages/edit-site/src/components/app/index.js index ca1893c3c1775..cad76b3ea1fb8 100644 --- a/packages/edit-site/src/components/app/index.js +++ b/packages/edit-site/src/components/app/index.js @@ -3,7 +3,6 @@ */ import { SlotFillProvider } from '@wordpress/components'; import { UnsavedChangesWarning } from '@wordpress/editor'; -import { ShortcutProvider } from '@wordpress/keyboard-shortcuts'; import { store as noticesStore } from '@wordpress/notices'; import { useDispatch } from '@wordpress/data'; import { __, sprintf } from '@wordpress/i18n'; @@ -35,16 +34,14 @@ export default function App() { } return ( - - - - - - - - - - - + + + + + + + + + ); } diff --git a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js index 899cc02766480..f3a7b84d5ded6 100644 --- a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js +++ b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js @@ -15,7 +15,6 @@ import { privateApis as blockEditorPrivateApis, } from '@wordpress/block-editor'; import { privateApis as editPatternsPrivateApis } from '@wordpress/patterns'; -import { ShortcutProvider } from '@wordpress/keyboard-shortcuts'; import { store as preferencesStore } from '@wordpress/preferences'; /** @@ -98,21 +97,19 @@ export default function WidgetAreasBlockEditorProvider( { ); return ( - + - - - { children } - - - - + + { children } + + + ); } diff --git a/packages/keyboard-shortcuts/README.md b/packages/keyboard-shortcuts/README.md index 4138b44a8e685..281ef698526e7 100644 --- a/packages/keyboard-shortcuts/README.md +++ b/packages/keyboard-shortcuts/README.md @@ -18,7 +18,7 @@ _This package assumes that your code will run in an **ES2015+** environment. If ### ShortcutProvider -Handles callbacks added to context by `useShortcut`. +Handles callbacks added to context by `useShortcut`. Adding a provider allows to register contextual shortcuts that are only active when a certain part of the UI is focused. _Parameters_ diff --git a/packages/keyboard-shortcuts/src/components/shortcut-provider.js b/packages/keyboard-shortcuts/src/components/shortcut-provider.js index bebad586268a7..9fabf392738f6 100644 --- a/packages/keyboard-shortcuts/src/components/shortcut-provider.js +++ b/packages/keyboard-shortcuts/src/components/shortcut-provider.js @@ -12,6 +12,8 @@ const { Provider } = context; /** * Handles callbacks added to context by `useShortcut`. + * Adding a provider allows to register contextual shortcuts + * that are only active when a certain part of the UI is focused. * * @param {Object} props Props to pass to `div`. * diff --git a/packages/keyboard-shortcuts/src/context.js b/packages/keyboard-shortcuts/src/context.js index bb885af5c721f..32a535e9c1f06 100644 --- a/packages/keyboard-shortcuts/src/context.js +++ b/packages/keyboard-shortcuts/src/context.js @@ -3,4 +3,11 @@ */ import { createContext } from '@wordpress/element'; -export const context = createContext(); +const globalShortcuts = { current: new Set() }; +document.body.addEventListener( 'keydown', ( event ) => { + for ( const keyboardShortcut of globalShortcuts.current ) { + keyboardShortcut( event ); + } +} ); + +export const context = createContext( globalShortcuts ); diff --git a/storybook/stories/playground/index.story.js b/storybook/stories/playground/index.story.js index 380a95f4b79a9..c95da2b4c365b 100644 --- a/storybook/stories/playground/index.story.js +++ b/storybook/stories/playground/index.story.js @@ -10,7 +10,6 @@ import { WritingFlow, } from '@wordpress/block-editor'; import { registerCoreBlocks } from '@wordpress/block-library'; -import { ShortcutProvider } from '@wordpress/keyboard-shortcuts'; import '@wordpress/format-library'; /** @@ -35,26 +34,24 @@ function App() { return (
- - -
- -
-
- -
- - - -
-
-
-
-
+ +
+ +
+
+ +
+ + + +
+
+
+
); } diff --git a/test/integration/helpers/integration-test-editor.js b/test/integration/helpers/integration-test-editor.js index 7c2fba15060b4..c8694f88f1fb5 100644 --- a/test/integration/helpers/integration-test-editor.js +++ b/test/integration/helpers/integration-test-editor.js @@ -16,7 +16,6 @@ import { WritingFlow, } from '@wordpress/block-editor'; import { registerCoreBlocks } from '@wordpress/block-library'; -import { ShortcutProvider } from '@wordpress/keyboard-shortcuts'; import '@wordpress/format-library'; import { createBlock, @@ -66,21 +65,19 @@ export function Editor( { testBlocks, settings = {} } ) { }, [] ); return ( - - - - - - - - - - + + + + + + + + ); } From 88b69b23ca848549f17d5d8c31f9daf7d6d9020c Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 25 Aug 2023 16:38:24 +0100 Subject: [PATCH 02/11] Bubble keydown events --- packages/block-editor/src/components/iframe/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js index 19c1b61058016..14139547ca947 100644 --- a/packages/block-editor/src/components/iframe/index.js +++ b/packages/block-editor/src/components/iframe/index.js @@ -70,7 +70,7 @@ function bubbleEvents( doc ) { } } - const eventTypes = [ 'dragover', 'mousemove' ]; + const eventTypes = [ 'dragover', 'mousemove', 'keydown' ]; for ( const name of eventTypes ) { doc.addEventListener( name, bubbleEvent ); From ab7c07c217299d5fc14b1948de25e213783e594e Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 25 Aug 2023 17:22:17 +0100 Subject: [PATCH 03/11] Prevent double region navigation --- .../components/src/higher-order/navigate-regions/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/components/src/higher-order/navigate-regions/index.tsx b/packages/components/src/higher-order/navigate-regions/index.tsx index f2b9c31bfbe95..320f1f7d67908 100644 --- a/packages/components/src/higher-order/navigate-regions/index.tsx +++ b/packages/components/src/higher-order/navigate-regions/index.tsx @@ -70,7 +70,6 @@ export function useNavigateRegions( shortcuts: Shortcuts = defaultShortcuts ) { nextIndex = nextIndex === regions.length ? 0 : nextIndex; nextRegion = regions[ nextIndex ]; } - nextRegion.focus(); setIsFocusingRegions( true ); } @@ -99,12 +98,14 @@ export function useNavigateRegions( shortcuts: Shortcuts = defaultShortcuts ) { return isKeyboardEvent[ modifier ]( event, character ); } ) ) { + event.stopPropagation(); focusRegion( -1 ); } else if ( shortcuts.next.some( ( { modifier, character } ) => { return isKeyboardEvent[ modifier ]( event, character ); } ) ) { + event.stopPropagation(); focusRegion( 1 ); } }, From 2a6247cbddb8f81442eed73e0131cb642490fc3b Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 25 Aug 2023 17:53:37 +0100 Subject: [PATCH 04/11] Prevent bubbling when navigation mode is handled --- packages/block-editor/src/components/writing-flow/use-tab-nav.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-editor/src/components/writing-flow/use-tab-nav.js b/packages/block-editor/src/components/writing-flow/use-tab-nav.js index 616da1bc75813..0089488e21278 100644 --- a/packages/block-editor/src/components/writing-flow/use-tab-nav.js +++ b/packages/block-editor/src/components/writing-flow/use-tab-nav.js @@ -89,6 +89,7 @@ export default function useTabNav() { if ( event.keyCode === ESCAPE && ! hasMultiSelection() ) { event.preventDefault(); + event.stopPropagation(); setNavigationMode( true ); return; } From a94eb2dde45d2006fdb7c47d0b3d6d505542f76f Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 28 Aug 2023 09:54:01 +0100 Subject: [PATCH 05/11] Wait to register the event handler --- packages/keyboard-shortcuts/src/context.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/keyboard-shortcuts/src/context.js b/packages/keyboard-shortcuts/src/context.js index 32a535e9c1f06..dfc9a34db2fcc 100644 --- a/packages/keyboard-shortcuts/src/context.js +++ b/packages/keyboard-shortcuts/src/context.js @@ -4,10 +4,12 @@ import { createContext } from '@wordpress/element'; const globalShortcuts = { current: new Set() }; -document.body.addEventListener( 'keydown', ( event ) => { - for ( const keyboardShortcut of globalShortcuts.current ) { - keyboardShortcut( event ); - } +document.addEventListener( 'DOMContentLoaded', () => { + document.body.addEventListener( 'keydown', ( event ) => { + for ( const keyboardShortcut of globalShortcuts.current ) { + keyboardShortcut( event ); + } + } ); } ); export const context = createContext( globalShortcuts ); From 0138f1367e4b6cfd864192c4539638dfa63c4280 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 29 Aug 2023 10:43:33 +0100 Subject: [PATCH 06/11] SSR support for keyboard shortcuts --- packages/keyboard-shortcuts/src/context.js | 31 +++++++++++++++------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/keyboard-shortcuts/src/context.js b/packages/keyboard-shortcuts/src/context.js index dfc9a34db2fcc..5954e74405939 100644 --- a/packages/keyboard-shortcuts/src/context.js +++ b/packages/keyboard-shortcuts/src/context.js @@ -3,13 +3,26 @@ */ import { createContext } from '@wordpress/element'; -const globalShortcuts = { current: new Set() }; -document.addEventListener( 'DOMContentLoaded', () => { - document.body.addEventListener( 'keydown', ( event ) => { - for ( const keyboardShortcut of globalShortcuts.current ) { - keyboardShortcut( event ); - } - } ); -} ); +const globalShortcuts = new Set(); +const globalListener = ( event ) => { + for ( const keyboardShortcut of globalShortcuts ) { + keyboardShortcut( event ); + } +}; -export const context = createContext( globalShortcuts ); +export const context = createContext( { + current: { + add: ( shortcut ) => { + if ( globalShortcuts.size === 0 ) { + document.addEventListener( 'keydown', globalListener ); + } + globalShortcuts.add( shortcut ); + }, + remove: ( shortcut ) => { + globalShortcuts.delete( shortcut ); + if ( globalShortcuts.size === 0 ) { + document.removeEventListener( 'keydown', globalListener ); + } + }, + }, +} ); From 93537de3ad04d6ce4fd4987662568112ab3333c9 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 29 Aug 2023 14:48:59 +0100 Subject: [PATCH 07/11] Small improvements --- packages/keyboard-shortcuts/src/context.js | 2 +- packages/keyboard-shortcuts/src/hooks/use-shortcut.js | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/keyboard-shortcuts/src/context.js b/packages/keyboard-shortcuts/src/context.js index 5954e74405939..f11990a510dbb 100644 --- a/packages/keyboard-shortcuts/src/context.js +++ b/packages/keyboard-shortcuts/src/context.js @@ -18,7 +18,7 @@ export const context = createContext( { } globalShortcuts.add( shortcut ); }, - remove: ( shortcut ) => { + delete: ( shortcut ) => { globalShortcuts.delete( shortcut ); if ( globalShortcuts.size === 0 ) { document.removeEventListener( 'keydown', globalListener ); diff --git a/packages/keyboard-shortcuts/src/hooks/use-shortcut.js b/packages/keyboard-shortcuts/src/hooks/use-shortcut.js index c60f84cc58da7..231dae6ed9c98 100644 --- a/packages/keyboard-shortcuts/src/hooks/use-shortcut.js +++ b/packages/keyboard-shortcuts/src/hooks/use-shortcut.js @@ -17,11 +17,18 @@ import { context } from '../context'; * @param {Object} options Shortcut options. * @param {boolean} options.isDisabled Whether to disable to shortut. */ -export default function useShortcut( name, callback, { isDisabled } = {} ) { +export default function useShortcut( + name, + callback, + { isDisabled = false } = {} +) { const shortcuts = useContext( context ); const isMatch = useShortcutEventMatch(); const callbackRef = useRef(); - callbackRef.current = callback; + + useEffect( () => { + callbackRef.current = callback; + } ); useEffect( () => { if ( isDisabled ) { From 82a856bbaf1854b49d1167ca690a790db21d4e07 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 29 Aug 2023 14:50:30 +0100 Subject: [PATCH 08/11] Small improvements --- packages/keyboard-shortcuts/src/context.js | 24 +++++++++---------- .../src/hooks/use-shortcut.js | 6 ++--- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/keyboard-shortcuts/src/context.js b/packages/keyboard-shortcuts/src/context.js index f11990a510dbb..f2f8846751291 100644 --- a/packages/keyboard-shortcuts/src/context.js +++ b/packages/keyboard-shortcuts/src/context.js @@ -11,18 +11,16 @@ const globalListener = ( event ) => { }; export const context = createContext( { - current: { - add: ( shortcut ) => { - if ( globalShortcuts.size === 0 ) { - document.addEventListener( 'keydown', globalListener ); - } - globalShortcuts.add( shortcut ); - }, - delete: ( shortcut ) => { - globalShortcuts.delete( shortcut ); - if ( globalShortcuts.size === 0 ) { - document.removeEventListener( 'keydown', globalListener ); - } - }, + add: ( shortcut ) => { + if ( globalShortcuts.size === 0 ) { + document.addEventListener( 'keydown', globalListener ); + } + globalShortcuts.add( shortcut ); + }, + delete: ( shortcut ) => { + globalShortcuts.delete( shortcut ); + if ( globalShortcuts.size === 0 ) { + document.removeEventListener( 'keydown', globalListener ); + } }, } ); diff --git a/packages/keyboard-shortcuts/src/hooks/use-shortcut.js b/packages/keyboard-shortcuts/src/hooks/use-shortcut.js index 231dae6ed9c98..9cac7edc92e9d 100644 --- a/packages/keyboard-shortcuts/src/hooks/use-shortcut.js +++ b/packages/keyboard-shortcuts/src/hooks/use-shortcut.js @@ -41,9 +41,9 @@ export default function useShortcut( } } - shortcuts.current.add( _callback ); + shortcuts.add( _callback ); return () => { - shortcuts.current.delete( _callback ); + shortcuts.delete( _callback ); }; - }, [ name, isDisabled ] ); + }, [ name, isDisabled, shortcuts ] ); } From 687c6532ec06d8fc9cbccca3d7a946fca5dcf782 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 29 Aug 2023 15:26:40 +0100 Subject: [PATCH 09/11] Small update --- packages/keyboard-shortcuts/src/hooks/use-shortcut.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/keyboard-shortcuts/src/hooks/use-shortcut.js b/packages/keyboard-shortcuts/src/hooks/use-shortcut.js index 9cac7edc92e9d..b6252ebc3680c 100644 --- a/packages/keyboard-shortcuts/src/hooks/use-shortcut.js +++ b/packages/keyboard-shortcuts/src/hooks/use-shortcut.js @@ -28,7 +28,7 @@ export default function useShortcut( useEffect( () => { callbackRef.current = callback; - } ); + }, [ callback ] ); useEffect( () => { if ( isDisabled ) { From 96d46840458466a8b23a95041bb083aa166307af Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 29 Aug 2023 16:54:28 +0100 Subject: [PATCH 10/11] Fix e2e tests --- packages/block-editor/src/components/block-tools/index.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/block-editor/src/components/block-tools/index.js b/packages/block-editor/src/components/block-tools/index.js index 8e3b240838fd0..b804ad6dd013f 100644 --- a/packages/block-editor/src/components/block-tools/index.js +++ b/packages/block-editor/src/components/block-tools/index.js @@ -71,6 +71,7 @@ export default function BlockTools( { const clientIds = getSelectedBlockClientIds(); if ( clientIds.length ) { event.preventDefault(); + event.stopPropagation(); const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); moveBlocksUp( clientIds, rootClientId ); } @@ -78,6 +79,7 @@ export default function BlockTools( { const clientIds = getSelectedBlockClientIds(); if ( clientIds.length ) { event.preventDefault(); + event.stopPropagation(); const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); moveBlocksDown( clientIds, rootClientId ); } @@ -85,30 +87,35 @@ export default function BlockTools( { const clientIds = getSelectedBlockClientIds(); if ( clientIds.length ) { event.preventDefault(); + event.stopPropagation(); duplicateBlocks( clientIds ); } } else if ( isMatch( 'core/block-editor/remove', event ) ) { const clientIds = getSelectedBlockClientIds(); if ( clientIds.length ) { event.preventDefault(); + event.stopPropagation(); removeBlocks( clientIds ); } } else if ( isMatch( 'core/block-editor/insert-after', event ) ) { const clientIds = getSelectedBlockClientIds(); if ( clientIds.length ) { event.preventDefault(); + event.stopPropagation(); insertAfterBlock( clientIds[ clientIds.length - 1 ] ); } } else if ( isMatch( 'core/block-editor/insert-before', event ) ) { const clientIds = getSelectedBlockClientIds(); if ( clientIds.length ) { event.preventDefault(); + event.stopPropagation(); insertBeforeBlock( clientIds[ 0 ] ); } } else if ( isMatch( 'core/block-editor/unselect', event ) ) { const clientIds = getSelectedBlockClientIds(); if ( clientIds.length ) { event.preventDefault(); + event.stopPropagation(); // If there is more than one block selected, select the first // block so that focus is directed back to the beginning of the selection. From bd15218d547d0326549a504bec1b1a8452ba15f8 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 30 Aug 2023 17:43:02 +0100 Subject: [PATCH 11/11] Try scoped useShortcut --- .../block-settings-dropdown.js | 385 +++++++++--------- .../src/components/block-tools/index.js | 132 +++--- .../list-view/block-select-button.js | 91 +++-- .../components/writing-flow/use-select-all.js | 28 +- .../components/src/dropdown-menu/index.tsx | 7 +- .../components/src/dropdown-menu/types.ts | 6 + packages/keyboard-shortcuts/README.md | 1 + packages/keyboard-shortcuts/src/context.js | 18 +- .../src/hooks/use-shortcut.js | 8 +- 9 files changed, 354 insertions(+), 322 deletions(-) diff --git a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js index ce059d7720a9f..5f844b2a03668 100644 --- a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js +++ b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js @@ -18,7 +18,7 @@ import { import { __, sprintf } from '@wordpress/i18n'; import { store as keyboardShortcutsStore, - __unstableUseShortcutEventMatch, + useShortcut, } from '@wordpress/keyboard-shortcuts'; import { pipe, useCopyToClipboard } from '@wordpress/compose'; @@ -47,12 +47,51 @@ function CopyMenuItem( { blocks, onCopy, label } ) { } export function BlockSettingsDropdown( { + clientIds, + __experimentalSelectBlock, + ...props +} ) { + return ( + + { ( blocksActionsProps ) => ( + + ) } + + ); +} + +function BlockSettingsDropdownMenu( { clientIds, __experimentalSelectBlock, children, __unstableDisplayLocation, + + // The following props are injected by BlockActions + canCopyStyles, + canDuplicate, + canInsertDefaultBlock, + canMove, + canRemove, + onDuplicate, + onInsertAfter, + onInsertBefore, + onRemove, + onCopy, + onPasteStyles, + onMoveTo, + blocks, + ...props } ) { + const scope = useRef(); const blockClientIds = Array.isArray( clientIds ) ? clientIds : [ clientIds ]; @@ -117,7 +156,6 @@ export function BlockSettingsDropdown( { ), }; }, [] ); - const isMatch = __unstableUseShortcutEventMatch(); const { selectBlock } = useDispatch( blockEditorStore ); const hasSelectedBlocks = selectedBlockClientIds.length > 0; @@ -158,6 +196,50 @@ export function BlockSettingsDropdown( { getSelectedBlockClientIds, ] ); + useShortcut( + 'core/block-editor/remove', + ( event ) => { + if ( event.defaultPrevented || ! canRemove ) return; + + event.preventDefault(); + updateSelectionAfterRemove( onRemove() ); + }, + { scope } + ); + + useShortcut( + 'core/block-editor/duplicate', + ( event ) => { + if ( event.defaultPrevented || ! canDuplicate ) return; + + event.preventDefault(); + updateSelectionAfterDuplicate( onDuplicate() ); + }, + { scope } + ); + + useShortcut( + 'core/block-editor/insert-after', + ( event ) => { + if ( event.defaultPrevented || ! canInsertDefaultBlock ) return; + + event.preventDefault(); + onInsertAfter(); + }, + { scope } + ); + + useShortcut( + 'core/block-editor/insert-after', + ( event ) => { + if ( event.defaultPrevented || ! canInsertDefaultBlock ) return; + + event.preventDefault(); + onInsertBefore(); + }, + { scope } + ); + const removeBlockLabel = count === 1 ? __( 'Delete' ) : __( 'Delete blocks' ); @@ -175,203 +257,120 @@ export function BlockSettingsDropdown( { selectedBlockClientIds?.includes( firstParentClientId ); return ( - - { ( { - canCopyStyles, - canDuplicate, - canInsertDefaultBlock, - canMove, - canRemove, - onDuplicate, - onInsertAfter, - onInsertBefore, - onRemove, - onCopy, - onPasteStyles, - onMoveTo, - blocks, - } ) => ( - - { ( { onClose } ) => ( - <> - - <__unstableBlockSettingsMenuFirstItem.Slot - fillProps={ { onClose } } - /> - { ! parentBlockIsSelected && - !! firstParentClientId && ( - - } - onClick={ () => - selectBlock( - firstParentClientId - ) - } - > - { sprintf( - /* translators: %s: Name of the block's parent. */ - __( - 'Select parent block (%s)' - ), - parentBlockType.title - ) } - - ) } - { count === 1 && ( - - ) } - - { canDuplicate && ( - - { __( 'Duplicate' ) } - - ) } - { canInsertDefaultBlock && ( - <> - - { __( 'Add before' ) } - - - { __( 'Add after' ) } - - + { ( { onClose } ) => ( + <> + + <__unstableBlockSettingsMenuFirstItem.Slot + fillProps={ { onClose } } + /> + { ! parentBlockIsSelected && !! firstParentClientId && ( + + } + onClick={ () => + selectBlock( firstParentClientId ) + } + > + { sprintf( + /* translators: %s: Name of the block's parent. */ + __( 'Select parent block (%s)' ), + parentBlockType.title ) } - - { canCopyStyles && ( - - - - { __( 'Paste styles' ) } - - - ) } - + ) } + { count === 1 && ( + + ) } + + { canDuplicate && ( + + { __( 'Duplicate' ) } + + ) } + { canInsertDefaultBlock && ( + <> + + { __( 'Add before' ) } + + + { __( 'Add after' ) } + + + ) } + + { canCopyStyles && ( + + - { typeof children === 'function' - ? children( { onClose } ) - : Children.map( ( child ) => - cloneElement( child, { onClose } ) - ) } - { canRemove && ( - - - { removeBlockLabel } - - - ) } - + + { __( 'Paste styles' ) } + + + ) } + + { typeof children === 'function' + ? children( { onClose } ) + : Children.map( ( child ) => + cloneElement( child, { onClose } ) + ) } + { canRemove && ( + + + { removeBlockLabel } + + ) } - + ) } - + ); } diff --git a/packages/block-editor/src/components/block-tools/index.js b/packages/block-editor/src/components/block-tools/index.js index b804ad6dd013f..8b3105ac85f6a 100644 --- a/packages/block-editor/src/components/block-tools/index.js +++ b/packages/block-editor/src/components/block-tools/index.js @@ -4,7 +4,7 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { useViewportMatch } from '@wordpress/compose'; import { Popover } from '@wordpress/components'; -import { __unstableUseShortcutEventMatch as useShortcutEventMatch } from '@wordpress/keyboard-shortcuts'; +import { useShortcut } from '@wordpress/keyboard-shortcuts'; import { useRef } from '@wordpress/element'; /** @@ -45,12 +45,12 @@ export default function BlockTools( { __unstableContentRef, ...props } ) { + const scope = useRef( null ); const isLargeViewport = useViewportMatch( 'medium' ); const { hasFixedToolbar, isZoomOutMode, isTyping } = useSelect( selector, [] ); - const isMatch = useShortcutEventMatch(); const { getSelectedBlockClientIds, getBlockRootClientId } = useSelect( blockEditorStore ); const { @@ -64,81 +64,89 @@ export default function BlockTools( { moveBlocksDown, } = useDispatch( blockEditorStore ); - function onKeyDown( event ) { - if ( event.defaultPrevented ) return; - - if ( isMatch( 'core/block-editor/move-up', event ) ) { + useShortcut( + 'core/block-editor/move-up', + ( event ) => { + if ( event.defaultPrevented ) return; const clientIds = getSelectedBlockClientIds(); if ( clientIds.length ) { event.preventDefault(); - event.stopPropagation(); const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); moveBlocksUp( clientIds, rootClientId ); } - } else if ( isMatch( 'core/block-editor/move-down', event ) ) { - const clientIds = getSelectedBlockClientIds(); - if ( clientIds.length ) { - event.preventDefault(); - event.stopPropagation(); - const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); - moveBlocksDown( clientIds, rootClientId ); - } - } else if ( isMatch( 'core/block-editor/duplicate', event ) ) { - const clientIds = getSelectedBlockClientIds(); - if ( clientIds.length ) { - event.preventDefault(); - event.stopPropagation(); - duplicateBlocks( clientIds ); - } - } else if ( isMatch( 'core/block-editor/remove', event ) ) { - const clientIds = getSelectedBlockClientIds(); - if ( clientIds.length ) { - event.preventDefault(); - event.stopPropagation(); - removeBlocks( clientIds ); - } - } else if ( isMatch( 'core/block-editor/insert-after', event ) ) { - const clientIds = getSelectedBlockClientIds(); - if ( clientIds.length ) { - event.preventDefault(); - event.stopPropagation(); - insertAfterBlock( clientIds[ clientIds.length - 1 ] ); - } - } else if ( isMatch( 'core/block-editor/insert-before', event ) ) { - const clientIds = getSelectedBlockClientIds(); - if ( clientIds.length ) { - event.preventDefault(); - event.stopPropagation(); - insertBeforeBlock( clientIds[ 0 ] ); - } - } else if ( isMatch( 'core/block-editor/unselect', event ) ) { - const clientIds = getSelectedBlockClientIds(); - if ( clientIds.length ) { - event.preventDefault(); - event.stopPropagation(); + }, + { scope } + ); + + useShortcut( 'core/block-editor/move-down', ( event ) => { + if ( event.defaultPrevented ) return; + const clientIds = getSelectedBlockClientIds(); + if ( clientIds.length ) { + event.preventDefault(); + const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); + moveBlocksDown( clientIds, rootClientId ); + } + } ); + + useShortcut( 'core/block-editor/duplicate', ( event ) => { + if ( event.defaultPrevented ) return; + const clientIds = getSelectedBlockClientIds(); + if ( clientIds.length ) { + event.preventDefault(); + duplicateBlocks( clientIds ); + } + } ); + + useShortcut( 'core/block-editor/remove', ( event ) => { + const clientIds = getSelectedBlockClientIds(); + if ( clientIds.length ) { + event.preventDefault(); + removeBlocks( clientIds ); + } + } ); + + useShortcut( 'core/block-editor/insert-after', ( event ) => { + const clientIds = getSelectedBlockClientIds(); + if ( clientIds.length ) { + event.preventDefault(); + insertAfterBlock( clientIds[ clientIds.length - 1 ] ); + } + } ); + + useShortcut( 'core/block-editor/insert-before', ( event ) => { + const clientIds = getSelectedBlockClientIds(); + if ( clientIds.length ) { + event.preventDefault(); + insertBeforeBlock( clientIds[ 0 ] ); + } + } ); + + useShortcut( 'core/block-editor/unselect', ( event ) => { + const clientIds = getSelectedBlockClientIds(); + if ( clientIds.length ) { + event.preventDefault(); - // If there is more than one block selected, select the first - // block so that focus is directed back to the beginning of the selection. - // In effect, to the user this feels like deselecting the multi-selection. - if ( clientIds.length > 1 ) { - selectBlock( clientIds[ 0 ] ); - } else { - clearSelectedBlock(); - } - event.target.ownerDocument.defaultView - .getSelection() - .removeAllRanges(); - __unstableContentRef?.current.focus(); + // If there is more than one block selected, select the first + // block so that focus is directed back to the beginning of the selection. + // In effect, to the user this feels like deselecting the multi-selection. + if ( clientIds.length > 1 ) { + selectBlock( clientIds[ 0 ] ); + } else { + clearSelectedBlock(); } + event.target.ownerDocument.defaultView + .getSelection() + .removeAllRanges(); + __unstableContentRef?.current.focus(); } - } + } ); const blockToolbarRef = usePopoverScroll( __unstableContentRef ); const blockToolbarAfterRef = usePopoverScroll( __unstableContentRef ); return ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions -
+
{ ! isTyping && ( 0 && - getSelectedBlockClientIds().length === 0; + // Update the selection if the original selection has been removed. + const shouldUpdateSelection = + selectedBlockClientIds.length > 0 && + getSelectedBlockClientIds().length === 0; - // If there's no previous block nor parent block, focus the first block. - if ( ! blockToFocus ) { - blockToFocus = getBlockOrder()[ 0 ]; - } + // If there's no previous block nor parent block, focus the first block. + if ( ! blockToFocus ) { + blockToFocus = getBlockOrder()[ 0 ]; + } + + updateFocusAndSelection( blockToFocus, shouldUpdateSelection ); + } - updateFocusAndSelection( blockToFocus, shouldUpdateSelection ); - } else if ( isMatch( 'core/block-editor/duplicate', event ) ) { + useShortcut( 'core/block-editor/remove', onRemoveBlock, { scope } ); + useShortcut( + 'core/block-editor/duplicate', + async ( event ) => { if ( event.defaultPrevented ) { return; } @@ -183,9 +179,22 @@ function ListViewBlockSelectButton( updateFocusAndSelection( updatedBlocks[ 0 ], false ); } } + }, + { scope } + ); + + async function onKeyDownHandler( event ) { + if ( event.keyCode === ENTER || event.keyCode === SPACE ) { + onClick( event ); + } else if ( event.keyCode === BACKSPACE || event.keyCode === DELETE ) { + onRemoveBlock(); } } + const scopeRef = useRefEffect( ( node ) => { + scope.current = node; + }, [] ); + return ( <>