diff --git a/packages/block-editor/src/components/block-tools/block-popover.js b/packages/block-editor/src/components/block-tools/block-popover.js
index 3797f68edadffc..cfc0fa0b02251f 100644
--- a/packages/block-editor/src/components/block-tools/block-popover.js
+++ b/packages/block-editor/src/components/block-tools/block-popover.js
@@ -7,7 +7,7 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
-import { useState, useCallback, useRef, useEffect } from '@wordpress/element';
+import { useState, useRef, useEffect } from '@wordpress/element';
import { isUnmodifiedDefaultBlock } from '@wordpress/blocks';
import { Popover } from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
@@ -107,13 +107,11 @@ function BlockPopover( {
useShortcut(
'core/block-editor/focus-toolbar',
- useCallback( () => {
+ () => {
setIsToolbarForced( true );
stopTyping( true );
- }, [] ),
+ },
{
- bindGlobal: true,
- eventName: 'keydown',
isDisabled: ! canFocusHiddenToolbar,
}
);
diff --git a/packages/block-editor/src/components/iframe/index.js b/packages/block-editor/src/components/iframe/index.js
index e5dbb6907094bc..e1de1591e6a0d5 100644
--- a/packages/block-editor/src/components/iframe/index.js
+++ b/packages/block-editor/src/components/iframe/index.js
@@ -134,7 +134,7 @@ function bubbleEvents( doc ) {
}
}
- const eventTypes = [ 'keydown', 'keypress', 'dragover' ];
+ const eventTypes = [ 'dragover' ];
for ( const name of eventTypes ) {
doc.addEventListener( name, bubbleEvent );
diff --git a/packages/block-editor/src/components/navigable-toolbar/index.js b/packages/block-editor/src/components/navigable-toolbar/index.js
index d010153fb26622..a988fa2c7575fb 100644
--- a/packages/block-editor/src/components/navigable-toolbar/index.js
+++ b/packages/block-editor/src/components/navigable-toolbar/index.js
@@ -100,10 +100,7 @@ function useToolbarFocus(
}, [] );
// Focus on toolbar when pressing alt+F10 when the toolbar is visible
- useShortcut( 'core/block-editor/focus-toolbar', focusToolbar, {
- bindGlobal: true,
- eventName: 'keydown',
- } );
+ useShortcut( 'core/block-editor/focus-toolbar', focusToolbar );
useEffect( () => {
if ( initialFocusOnMount ) {
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 3b3b3cf607d45b..7f61046809d71a 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
@@ -27,9 +27,11 @@ export default function useTabNav() {
const focusCaptureBeforeRef = useRef();
const focusCaptureAfterRef = useRef();
const lastFocus = useRef();
- const { hasMultiSelection, getSelectedBlockClientId } = useSelect(
- blockEditorStore
- );
+ const {
+ hasMultiSelection,
+ getSelectedBlockClientId,
+ getBlockCount,
+ } = useSelect( blockEditorStore );
const { setNavigationMode } = useDispatch( blockEditorStore );
const isNavigationMode = useSelect(
( select ) => select( blockEditorStore ).isNavigationMode(),
@@ -143,6 +145,18 @@ export default function useTabNav() {
function onFocusOut( event ) {
lastFocus.current = event.target;
+
+ const { ownerDocument } = node;
+
+ // If focus disappears due to there being no blocks, move focus to
+ // the writing flow wrapper.
+ if (
+ ! event.relatedTarget &&
+ ownerDocument.activeElement === ownerDocument.body &&
+ getBlockCount() === 0
+ ) {
+ node.focus();
+ }
}
// When tabbing back to an element in block list, this event handler prevents scrolling if the
diff --git a/packages/customize-widgets/src/components/customize-widgets/index.js b/packages/customize-widgets/src/components/customize-widgets/index.js
index d206108398283a..3306e438f80e94 100644
--- a/packages/customize-widgets/src/components/customize-widgets/index.js
+++ b/packages/customize-widgets/src/components/customize-widgets/index.js
@@ -3,6 +3,7 @@
*/
import { useState, useEffect, useRef, createPortal } from '@wordpress/element';
import { SlotFillProvider, Popover } from '@wordpress/components';
+import { ShortcutProvider } from '@wordpress/keyboard-shortcuts';
/**
* Internal dependencies
@@ -67,16 +68,21 @@ export default function CustomizeWidgets( {
);
return (
-
-
-
- { activeSidebar }
- { popover }
-
-
-
+
+
+
+
+ { activeSidebar }
+ { popover }
+
+
+
+
);
}
diff --git a/packages/customize-widgets/src/components/keyboard-shortcut-help-modal/index.js b/packages/customize-widgets/src/components/keyboard-shortcut-help-modal/index.js
index e600474a933cb8..77911be2610015 100644
--- a/packages/customize-widgets/src/components/keyboard-shortcut-help-modal/index.js
+++ b/packages/customize-widgets/src/components/keyboard-shortcut-help-modal/index.js
@@ -101,9 +101,7 @@ export default function KeyboardShortcutHelpModal( {
},
} );
- useShortcut( 'core/customize-widgets/keyboard-shortcuts', toggleModal, {
- bindGlobal: true,
- } );
+ useShortcut( 'core/customize-widgets/keyboard-shortcuts', toggleModal );
if ( ! isModalActive ) {
return null;
diff --git a/packages/customize-widgets/src/components/keyboard-shortcuts/index.js b/packages/customize-widgets/src/components/keyboard-shortcuts/index.js
index fe7349fdae04a8..56888f1cfe3ae5 100644
--- a/packages/customize-widgets/src/components/keyboard-shortcuts/index.js
+++ b/packages/customize-widgets/src/components/keyboard-shortcuts/index.js
@@ -10,32 +10,20 @@ import { useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
function KeyboardShortcuts( { undo, redo, save } ) {
- useShortcut(
- 'core/customize-widgets/undo',
- ( event ) => {
- undo();
- event.preventDefault();
- },
- { bindGlobal: true }
- );
+ useShortcut( 'core/customize-widgets/undo', ( event ) => {
+ undo();
+ event.preventDefault();
+ } );
- useShortcut(
- 'core/customize-widgets/redo',
- ( event ) => {
- redo();
- event.preventDefault();
- },
- { bindGlobal: true }
- );
+ useShortcut( 'core/customize-widgets/redo', ( event ) => {
+ redo();
+ event.preventDefault();
+ } );
- useShortcut(
- 'core/customize-widgets/save',
- ( event ) => {
- event.preventDefault();
- save();
- },
- { bindGlobal: true }
- );
+ useShortcut( 'core/customize-widgets/save', ( event ) => {
+ event.preventDefault();
+ save();
+ } );
return null;
}
diff --git a/packages/customize-widgets/src/components/more-menu/index.js b/packages/customize-widgets/src/components/more-menu/index.js
index 45c186f467c6e6..c87487ade1bce6 100644
--- a/packages/customize-widgets/src/components/more-menu/index.js
+++ b/packages/customize-widgets/src/components/more-menu/index.js
@@ -33,10 +33,7 @@ export default function MoreMenu() {
useShortcut(
'core/customize-widgets/keyboard-shortcuts',
- toggleKeyboardShortcutsModal,
- {
- bindGlobal: true,
- }
+ toggleKeyboardShortcutsModal
);
return (
diff --git a/packages/e2e-tests/specs/performance/site-editor.test.js b/packages/e2e-tests/specs/performance/site-editor.test.js
index 70f8922f5edbb9..9bb0f846efff4b 100644
--- a/packages/e2e-tests/specs/performance/site-editor.test.js
+++ b/packages/e2e-tests/specs/performance/site-editor.test.js
@@ -114,24 +114,9 @@ describe( 'Site Editor Performance', () => {
keyUpEvents,
] = getTypingEventDurations( traceResults );
- // Both keydown and keypress events are bubbled from the iframe to the
- // main frame, which must be ignored. These will be the odd values in
- // the array.
- const _keyDownEvents = keyDownEvents.filter(
- ( v, ii ) => ii % 2 === 0
- );
- const _keyPressEvents = keyPressEvents.filter(
- ( v, ii ) => ii % 2 === 0
- );
-
- expect(
- _keyDownEvents.length === _keyPressEvents.length &&
- _keyPressEvents.length === keyUpEvents.length
- ).toBe( true );
-
- for ( let j = 0; j < _keyDownEvents.length; j++ ) {
+ for ( let j = 0; j < keyDownEvents.length; j++ ) {
results.type.push(
- _keyDownEvents[ j ] + _keyPressEvents[ j ] + keyUpEvents[ j ]
+ keyDownEvents[ j ] + keyPressEvents[ j ] + keyUpEvents[ j ]
);
}
diff --git a/packages/e2e-tests/specs/widgets/editing-widgets.test.js b/packages/e2e-tests/specs/widgets/editing-widgets.test.js
index a67c1575e75a48..a883887b4c8615 100644
--- a/packages/e2e-tests/specs/widgets/editing-widgets.test.js
+++ b/packages/e2e-tests/specs/widgets/editing-widgets.test.js
@@ -822,6 +822,8 @@ describe( 'Widgets screen', () => {
// Delete the last block and save again.
await pressKeyWithModifier( 'access', 'z' );
await saveWidgets();
+ // To do: clicking on the Snackbar causes focus loss.
+ await page.focus( '.block-editor-writing-flow' );
// Undo block deletion and save again
await pressKeyWithModifier( 'primary', 'z' );
diff --git a/packages/edit-navigation/src/components/layout/index.js b/packages/edit-navigation/src/components/layout/index.js
index 92dd19040ea0c0..6b03909cf2be3b 100644
--- a/packages/edit-navigation/src/components/layout/index.js
+++ b/packages/edit-navigation/src/components/layout/index.js
@@ -16,6 +16,7 @@ import {
store as interfaceStore,
} from '@wordpress/interface';
import { __ } from '@wordpress/i18n';
+import { ShortcutProvider } from '@wordpress/keyboard-shortcuts';
/**
* Internal dependencies
@@ -96,96 +97,100 @@ export default function Layout( { blockEditorSettings } ) {
return (
-
-
-
-
-
-
-
- [
- isMenuNameControlFocused,
- setIsMenuNameControlFocused,
- ],
- [ isMenuNameControlFocused ]
- ) }
+
+
+
+
+
+
+
+
-
- }
- content={
- <>
- { ! hasFinishedInitialLoad && }
-
- { ! isMenuSelected &&
- hasFinishedInitialLoad && (
-
+ [
+ isMenuNameControlFocused,
+ setIsMenuNameControlFocused,
+ ],
+ [ isMenuNameControlFocused ]
+ ) }
+ >
+
+ }
+ content={
+ <>
+ { ! hasFinishedInitialLoad && (
+
) }
- { isBlockEditorReady && (
-
-
-
-
-
- ) }
- >
- }
- sidebar={
- hasSidebarEnabled && (
-
- )
- }
- />
- { isMenuSelected && (
-
+
+
+
+
+ ) }
+ >
+ }
+ sidebar={
+ hasSidebarEnabled && (
+
+ )
+ }
/>
- ) }
-
-
-
-
-
+ { isMenuSelected && (
+
+ ) }
+
+
+
+
+
+
);
}
diff --git a/packages/edit-navigation/src/components/layout/shortcuts.js b/packages/edit-navigation/src/components/layout/shortcuts.js
index ab03b25e7cd3a7..1ced3ba2183fee 100644
--- a/packages/edit-navigation/src/components/layout/shortcuts.js
+++ b/packages/edit-navigation/src/components/layout/shortcuts.js
@@ -1,7 +1,7 @@
/**
* WordPress dependencies
*/
-import { useEffect, useCallback } from '@wordpress/element';
+import { useEffect } from '@wordpress/element';
import { useDispatch } from '@wordpress/data';
import {
useShortcut,
@@ -11,35 +11,21 @@ import { __ } from '@wordpress/i18n';
import { store as coreStore } from '@wordpress/core-data';
function NavigationEditorShortcuts( { saveBlocks } ) {
- useShortcut(
- 'core/edit-navigation/save-menu',
- useCallback( ( event ) => {
- event.preventDefault();
- saveBlocks();
- } ),
- {
- bindGlobal: true,
- }
- );
+ useShortcut( 'core/edit-navigation/save-menu', ( event ) => {
+ event.preventDefault();
+ saveBlocks();
+ } );
const { redo, undo } = useDispatch( coreStore );
- useShortcut(
- 'core/edit-navigation/undo',
- ( event ) => {
- undo();
- event.preventDefault();
- },
- { bindGlobal: true }
- );
+ useShortcut( 'core/edit-navigation/undo', ( event ) => {
+ undo();
+ event.preventDefault();
+ } );
- useShortcut(
- 'core/edit-navigation/redo',
- ( event ) => {
- redo();
- event.preventDefault();
- },
- { bindGlobal: true }
- );
+ useShortcut( 'core/edit-navigation/redo', ( event ) => {
+ redo();
+ event.preventDefault();
+ } );
return null;
}
diff --git a/packages/edit-post/src/components/keyboard-shortcut-help-modal/index.js b/packages/edit-post/src/components/keyboard-shortcut-help-modal/index.js
index a05e3f20ba4a3d..e5177ef2d03f37 100644
--- a/packages/edit-post/src/components/keyboard-shortcut-help-modal/index.js
+++ b/packages/edit-post/src/components/keyboard-shortcut-help-modal/index.js
@@ -91,9 +91,7 @@ const ShortcutCategorySection = ( {
};
export function KeyboardShortcutHelpModal( { isModalActive, toggleModal } ) {
- useShortcut( 'core/edit-post/keyboard-shortcuts', toggleModal, {
- bindGlobal: true,
- } );
+ useShortcut( 'core/edit-post/keyboard-shortcuts', toggleModal );
if ( ! isModalActive ) {
return null;
diff --git a/packages/edit-post/src/components/keyboard-shortcuts/index.js b/packages/edit-post/src/components/keyboard-shortcuts/index.js
index 357a5607f6e3c5..33b66961006c18 100644
--- a/packages/edit-post/src/components/keyboard-shortcuts/index.js
+++ b/packages/edit-post/src/components/keyboard-shortcuts/index.js
@@ -131,44 +131,31 @@ function KeyboardShortcuts() {
);
},
{
- bindGlobal: true,
isDisabled: isModeToggleDisabled,
}
);
- useShortcut(
- 'core/edit-post/toggle-fullscreen',
- () => {
- toggleFeature( 'fullscreenMode' );
- },
- {
- bindGlobal: true,
+ useShortcut( 'core/edit-post/toggle-fullscreen', () => {
+ toggleFeature( 'fullscreenMode' );
+ } );
+
+ useShortcut( 'core/edit-post/toggle-sidebar', ( event ) => {
+ // This shortcut has no known clashes, but use preventDefault to prevent any
+ // obscure shortcuts from triggering.
+ event.preventDefault();
+
+ if ( isEditorSidebarOpened() ) {
+ closeGeneralSidebar();
+ } else {
+ const sidebarToOpen = getBlockSelectionStart()
+ ? 'edit-post/block'
+ : 'edit-post/document';
+ openGeneralSidebar( sidebarToOpen );
}
- );
+ } );
- useShortcut(
- 'core/edit-post/toggle-sidebar',
- ( event ) => {
- // This shortcut has no known clashes, but use preventDefault to prevent any
- // obscure shortcuts from triggering.
- event.preventDefault();
-
- if ( isEditorSidebarOpened() ) {
- closeGeneralSidebar();
- } else {
- const sidebarToOpen = getBlockSelectionStart()
- ? 'edit-post/block'
- : 'edit-post/document';
- openGeneralSidebar( sidebarToOpen );
- }
- },
- { bindGlobal: true }
- );
-
- useShortcut(
- 'core/edit-post/toggle-list-view',
- () => setIsListViewOpened( ! isListViewOpened() ),
- { bindGlobal: true }
+ useShortcut( 'core/edit-post/toggle-list-view', () =>
+ setIsListViewOpened( ! isListViewOpened() )
);
return null;
diff --git a/packages/edit-post/src/editor.js b/packages/edit-post/src/editor.js
index 3f34c2a5f32bd2..2f0c0266d883af 100644
--- a/packages/edit-post/src/editor.js
+++ b/packages/edit-post/src/editor.js
@@ -17,6 +17,7 @@ import {
import { StrictMode, useMemo } from '@wordpress/element';
import { KeyboardShortcuts, SlotFillProvider } from '@wordpress/components';
import { store as coreStore } from '@wordpress/core-data';
+import { ShortcutProvider } from '@wordpress/keyboard-shortcuts';
/**
* Internal dependencies
@@ -167,29 +168,31 @@ function Editor( {
return (
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js
index c9225340907ba3..ef7377d00e50bf 100644
--- a/packages/edit-site/src/components/editor/index.js
+++ b/packages/edit-site/src/components/editor/index.js
@@ -26,6 +26,7 @@ import {
} from '@wordpress/editor';
import { __ } from '@wordpress/i18n';
import { PluginArea } from '@wordpress/plugins';
+import { ShortcutProvider } from '@wordpress/keyboard-shortcuts';
/**
* Internal dependencies
@@ -178,7 +179,7 @@ function Editor( { initialSettings, onError } ) {
};
return (
- <>
+
@@ -289,7 +290,7 @@ function Editor( { initialSettings, onError } ) {
- >
+
);
}
export default Editor;
diff --git a/packages/edit-site/src/components/keyboard-shortcuts/index.js b/packages/edit-site/src/components/keyboard-shortcuts/index.js
index a248b0b3337ef3..0b531dbd97d5ae 100644
--- a/packages/edit-site/src/components/keyboard-shortcuts/index.js
+++ b/packages/edit-site/src/components/keyboard-shortcuts/index.js
@@ -1,7 +1,7 @@
/**
* WordPress dependencies
*/
-import { useCallback, useEffect } from '@wordpress/element';
+import { useEffect } from '@wordpress/element';
import {
useShortcut,
store as keyboardShortcutsStore,
@@ -35,47 +35,31 @@ function KeyboardShortcuts() {
interfaceStore
);
- useShortcut(
- 'core/edit-site/undo',
- ( event ) => {
- undo();
- event.preventDefault();
- },
- { bindGlobal: true }
- );
+ useShortcut( 'core/edit-site/undo', ( event ) => {
+ undo();
+ event.preventDefault();
+ } );
- useShortcut(
- 'core/edit-site/redo',
- ( event ) => {
- redo();
- event.preventDefault();
- },
- { bindGlobal: true }
- );
+ useShortcut( 'core/edit-site/redo', ( event ) => {
+ redo();
+ event.preventDefault();
+ } );
- useShortcut(
- 'core/edit-site/toggle-list-view',
- useCallback( () => {
- setIsListViewOpened( ! isListViewOpen );
- }, [ isListViewOpen, setIsListViewOpened ] ),
- { bindGlobal: true }
- );
+ useShortcut( 'core/edit-site/toggle-list-view', () => {
+ setIsListViewOpened( ! isListViewOpen );
+ } );
- useShortcut(
- 'core/edit-site/toggle-block-settings-sidebar',
- ( event ) => {
- // This shortcut has no known clashes, but use preventDefault to prevent any
- // obscure shortcuts from triggering.
- event.preventDefault();
+ useShortcut( 'core/edit-site/toggle-block-settings-sidebar', ( event ) => {
+ // This shortcut has no known clashes, but use preventDefault to prevent any
+ // obscure shortcuts from triggering.
+ event.preventDefault();
- if ( isBlockInspectorOpen ) {
- disableComplementaryArea( STORE_NAME );
- } else {
- enableComplementaryArea( STORE_NAME, SIDEBAR_BLOCK );
- }
- },
- { bindGlobal: true }
- );
+ if ( isBlockInspectorOpen ) {
+ disableComplementaryArea( STORE_NAME );
+ } else {
+ enableComplementaryArea( STORE_NAME, SIDEBAR_BLOCK );
+ }
+ } );
return null;
}
diff --git a/packages/edit-widgets/src/components/keyboard-shortcuts/index.js b/packages/edit-widgets/src/components/keyboard-shortcuts/index.js
index 2ec1321e37c240..dbb5afc9ea4dc0 100644
--- a/packages/edit-widgets/src/components/keyboard-shortcuts/index.js
+++ b/packages/edit-widgets/src/components/keyboard-shortcuts/index.js
@@ -19,32 +19,20 @@ function KeyboardShortcuts() {
const { redo, undo } = useDispatch( coreStore );
const { saveEditedWidgetAreas } = useDispatch( editWidgetsStore );
- useShortcut(
- 'core/edit-widgets/undo',
- ( event ) => {
- undo();
- event.preventDefault();
- },
- { bindGlobal: true }
- );
+ useShortcut( 'core/edit-widgets/undo', ( event ) => {
+ undo();
+ event.preventDefault();
+ } );
- useShortcut(
- 'core/edit-widgets/redo',
- ( event ) => {
- redo();
- event.preventDefault();
- },
- { bindGlobal: true }
- );
+ useShortcut( 'core/edit-widgets/redo', ( event ) => {
+ redo();
+ event.preventDefault();
+ } );
- useShortcut(
- 'core/edit-widgets/save',
- ( event ) => {
- event.preventDefault();
- saveEditedWidgetAreas();
- },
- { bindGlobal: true }
- );
+ useShortcut( 'core/edit-widgets/save', ( event ) => {
+ event.preventDefault();
+ saveEditedWidgetAreas();
+ } );
return null;
}
diff --git a/packages/edit-widgets/src/components/more-menu/index.js b/packages/edit-widgets/src/components/more-menu/index.js
index 59d6973d57dd8f..82dd9983160190 100644
--- a/packages/edit-widgets/src/components/more-menu/index.js
+++ b/packages/edit-widgets/src/components/more-menu/index.js
@@ -25,10 +25,7 @@ export default function MoreMenu() {
useShortcut(
'core/edit-widgets/keyboard-shortcuts',
- toggleKeyboardShortcutsModal,
- {
- bindGlobal: true,
- }
+ toggleKeyboardShortcutsModal
);
const isLargeViewport = useViewportMatch( 'medium' );
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 786823b11c2dc8..fa33e73b01c65e 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
@@ -17,6 +17,7 @@ import {
} from '@wordpress/block-editor';
import { ReusableBlocksMenuItems } from '@wordpress/reusable-blocks';
import { store as interfaceStore } from '@wordpress/interface';
+import { ShortcutProvider } from '@wordpress/keyboard-shortcuts';
/**
* Internal dependencies
@@ -100,7 +101,7 @@ export default function WidgetAreasBlockEditorProvider( {
);
return (
- <>
+
@@ -116,6 +117,6 @@ export default function WidgetAreasBlockEditorProvider( {
- >
+
);
}
diff --git a/packages/editor/src/components/global-keyboard-shortcuts/save-shortcut.js b/packages/editor/src/components/global-keyboard-shortcuts/save-shortcut.js
index 8f18ad168e789b..95069e7ea59efb 100644
--- a/packages/editor/src/components/global-keyboard-shortcuts/save-shortcut.js
+++ b/packages/editor/src/components/global-keyboard-shortcuts/save-shortcut.js
@@ -24,39 +24,32 @@ function SaveShortcut( { resetBlocksOnSave } ) {
};
}, [] );
- useShortcut(
- 'core/editor/save',
- ( event ) => {
- event.preventDefault();
-
- // TODO: This should be handled in the `savePost` effect in
- // considering `isSaveable`. See note on `isEditedPostSaveable`
- // selector about dirtiness and meta-boxes.
- //
- // See: `isEditedPostSaveable`
- if ( ! isEditedPostDirty() ) {
- return;
- }
-
- // The text editor requires that editor blocks are updated for a
- // save to work correctly. Usually this happens when the textarea
- // for the code editors blurs, but the shortcut can be used without
- // blurring the textarea.
- if ( resetBlocksOnSave ) {
- const postEdits = getPostEdits();
- if (
- postEdits.content &&
- typeof postEdits.content === 'string'
- ) {
- const blocks = parse( postEdits.content );
- resetEditorBlocks( blocks );
- }
+ useShortcut( 'core/editor/save', ( event ) => {
+ event.preventDefault();
+
+ // TODO: This should be handled in the `savePost` effect in
+ // considering `isSaveable`. See note on `isEditedPostSaveable`
+ // selector about dirtiness and meta-boxes.
+ //
+ // See: `isEditedPostSaveable`
+ if ( ! isEditedPostDirty() ) {
+ return;
+ }
+
+ // The text editor requires that editor blocks are updated for a
+ // save to work correctly. Usually this happens when the textarea
+ // for the code editors blurs, but the shortcut can be used without
+ // blurring the textarea.
+ if ( resetBlocksOnSave ) {
+ const postEdits = getPostEdits();
+ if ( postEdits.content && typeof postEdits.content === 'string' ) {
+ const blocks = parse( postEdits.content );
+ resetEditorBlocks( blocks );
}
+ }
- savePost();
- },
- { bindGlobal: true }
- );
+ savePost();
+ } );
return null;
}
diff --git a/packages/editor/src/components/global-keyboard-shortcuts/visual-editor-shortcuts.js b/packages/editor/src/components/global-keyboard-shortcuts/visual-editor-shortcuts.js
index 1d406b54f47022..5e5875f275845f 100644
--- a/packages/editor/src/components/global-keyboard-shortcuts/visual-editor-shortcuts.js
+++ b/packages/editor/src/components/global-keyboard-shortcuts/visual-editor-shortcuts.js
@@ -13,23 +13,15 @@ import { store as editorStore } from '../../store';
function VisualEditorGlobalKeyboardShortcuts() {
const { redo, undo } = useDispatch( editorStore );
- useShortcut(
- 'core/editor/undo',
- ( event ) => {
- undo();
- event.preventDefault();
- },
- { bindGlobal: true }
- );
+ useShortcut( 'core/editor/undo', ( event ) => {
+ undo();
+ event.preventDefault();
+ } );
- useShortcut(
- 'core/editor/redo',
- ( event ) => {
- redo();
- event.preventDefault();
- },
- { bindGlobal: true }
- );
+ useShortcut( 'core/editor/redo', ( event ) => {
+ redo();
+ event.preventDefault();
+ } );
return ;
}
diff --git a/packages/editor/src/components/post-saved-state/index.js b/packages/editor/src/components/post-saved-state/index.js
index a6e2bca9e5bbbe..3d4fd3e60a978a 100644
--- a/packages/editor/src/components/post-saved-state/index.js
+++ b/packages/editor/src/components/post-saved-state/index.js
@@ -100,79 +100,65 @@ export default function PostSavedState( {
return () => clearTimeout( timeoutId );
}, [ isSaving ] );
- if ( isSaving ) {
- // TODO: Classes generation should be common across all return
- // paths of this function, including proper naming convention for
- // the "Save Draft" button.
- const classes = classnames(
- 'editor-post-saved-state',
- 'is-saving',
- getAnimateClassName( { type: 'loading' } ),
- {
- 'is-autosaving': isAutosaving,
- }
- );
-
- return (
-
-
- { isAutosaving ? __( 'Autosaving' ) : __( 'Saving' ) }
-
- );
- }
-
- if ( isPublished || isScheduled ) {
- return ;
- }
-
- if ( ! isSaveable ) {
- return null;
- }
-
- if ( forceSavedMessage || ( ! isNew && ! isDirty ) ) {
- return (
-
-
- { __( 'Saved' ) }
-
- );
- }
-
// Once the post has been submitted for review this button
// is not needed for the contributor role.
-
if ( ! hasPublishAction && isPending ) {
return null;
}
+ if ( isPublished || isScheduled ) {
+ return ;
+ }
+
/* translators: button label text should, if possible, be under 16 characters. */
const label = isPending ? __( 'Save as pending' ) : __( 'Save draft' );
/* translators: button label text should, if possible, be under 16 characters. */
const shortLabel = __( 'Save' );
- if ( ! isLargeViewport ) {
- return (
-
- );
+ const isSaved = forceSavedMessage || ( ! isNew && ! isDirty );
+ const isSavedState = isSaving || isSaved;
+ const isDisabled = isSaving || isSaved || ! isSaveable;
+
+ let text;
+
+ if ( isSaving ) {
+ text = isAutosaving ? __( 'Autosaving' ) : __( 'Saving' );
+ } else if ( isSaved ) {
+ text = __( 'Saved' );
+ } else if ( isLargeViewport ) {
+ text = label;
+ } else if ( showIconLabels ) {
+ text = shortLabel;
}
+ // Use common Button instance for all saved states so that focus is not
+ // lost.
return (
);
}
diff --git a/packages/editor/src/components/post-saved-state/test/__snapshots__/index.js.snap b/packages/editor/src/components/post-saved-state/test/__snapshots__/index.js.snap
index 575ba88aa66114..92d745bcca8453 100644
--- a/packages/editor/src/components/post-saved-state/test/__snapshots__/index.js.snap
+++ b/packages/editor/src/components/post-saved-state/test/__snapshots__/index.js.snap
@@ -1,9 +1,28 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`PostSavedState returns a disabled button if the post is not saveable 1`] = `
+
+
+
+ }
+ label="Save draft"
+ shortcut="Ctrl+S"
+/>
+`;
+
exports[`PostSavedState returns a switch to draft link if the post is published 1`] = ``;
exports[`PostSavedState should return Save button if edits to be saved 1`] = `
{
expect( wrapper.text() ).toContain( 'Saving' );
} );
- it( 'returns null if the post is not saveable', () => {
+ it( 'returns a disabled button if the post is not saveable', () => {
useSelect.mockImplementation( () => ( {
isDirty: false,
isNew: true,
@@ -58,7 +58,7 @@ describe( 'PostSavedState', () => {
const wrapper = shallow( );
- expect( wrapper.type() ).toBeNull();
+ expect( wrapper ).toMatchSnapshot();
} );
it( 'returns a switch to draft link if the post is published', () => {
diff --git a/packages/keyboard-shortcuts/README.md b/packages/keyboard-shortcuts/README.md
index 9d62456b0f0627..89d20406992336 100644
--- a/packages/keyboard-shortcuts/README.md
+++ b/packages/keyboard-shortcuts/README.md
@@ -16,6 +16,18 @@ _This package assumes that your code will run in an **ES2015+** environment. If
+### ShortcutProvider
+
+Handles callbacks added to context by `useShortcut`.
+
+_Parameters_
+
+- _props_ `Object`: Props to pass to `div`.
+
+_Returns_
+
+- `import('@wordpress/element').WPElement`: Component.
+
### store
Store definition for the keyboard shortcuts namespace.
@@ -37,6 +49,7 @@ _Parameters_
- _name_ `string`: Shortcut name.
- _callback_ `Function`: Shortcut callback.
- _options_ `Object`: Shortcut options.
+- _options.isDisabled_ `boolean`: Whether to disable to shortut.
diff --git a/packages/keyboard-shortcuts/src/components/shortcut-provider.js b/packages/keyboard-shortcuts/src/components/shortcut-provider.js
new file mode 100644
index 00000000000000..bebad586268a72
--- /dev/null
+++ b/packages/keyboard-shortcuts/src/components/shortcut-provider.js
@@ -0,0 +1,38 @@
+/**
+ * WordPress dependencies
+ */
+import { useRef } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import { context } from '../context';
+
+const { Provider } = context;
+
+/**
+ * Handles callbacks added to context by `useShortcut`.
+ *
+ * @param {Object} props Props to pass to `div`.
+ *
+ * @return {import('@wordpress/element').WPElement} Component.
+ */
+export function ShortcutProvider( props ) {
+ const keyboardShortcuts = useRef( new Set() );
+
+ function onKeyDown( event ) {
+ if ( props.onKeyDown ) props.onKeyDown( event );
+
+ for ( const keyboardShortcut of keyboardShortcuts.current ) {
+ keyboardShortcut( event );
+ }
+ }
+
+ /* eslint-disable jsx-a11y/no-static-element-interactions */
+ return (
+
+
+
+ );
+ /* eslint-enable jsx-a11y/no-static-element-interactions */
+}
diff --git a/packages/keyboard-shortcuts/src/context.js b/packages/keyboard-shortcuts/src/context.js
new file mode 100644
index 00000000000000..bb885af5c721f3
--- /dev/null
+++ b/packages/keyboard-shortcuts/src/context.js
@@ -0,0 +1,6 @@
+/**
+ * WordPress dependencies
+ */
+import { createContext } from '@wordpress/element';
+
+export const context = createContext();
diff --git a/packages/keyboard-shortcuts/src/hooks/use-shortcut.js b/packages/keyboard-shortcuts/src/hooks/use-shortcut.js
index df8ef91cd6769a..c60f84cc58da74 100644
--- a/packages/keyboard-shortcuts/src/hooks/use-shortcut.js
+++ b/packages/keyboard-shortcuts/src/hooks/use-shortcut.js
@@ -1,32 +1,42 @@
/**
* WordPress dependencies
*/
-import { useSelect } from '@wordpress/data';
-import { useKeyboardShortcut } from '@wordpress/compose';
+import { useContext, useEffect, useRef } from '@wordpress/element';
/**
* Internal dependencies
*/
-import { store as keyboardShortcutsStore } from '../store';
+import useShortcutEventMatch from './use-shortcut-event-match';
+import { context } from '../context';
/**
* Attach a keyboard shortcut handler.
*
- * @param {string} name Shortcut name.
- * @param {Function} callback Shortcut callback.
- * @param {Object} options Shortcut options.
+ * @param {string} name Shortcut name.
+ * @param {Function} callback Shortcut callback.
+ * @param {Object} options Shortcut options.
+ * @param {boolean} options.isDisabled Whether to disable to shortut.
*/
-function useShortcut( name, callback, options ) {
- const shortcuts = useSelect(
- ( select ) => {
- return select(
- keyboardShortcutsStore
- ).getAllShortcutRawKeyCombinations( name );
- },
- [ name ]
- );
+export default function useShortcut( name, callback, { isDisabled } = {} ) {
+ const shortcuts = useContext( context );
+ const isMatch = useShortcutEventMatch();
+ const callbackRef = useRef();
+ callbackRef.current = callback;
- useKeyboardShortcut( shortcuts, callback, options );
-}
+ useEffect( () => {
+ if ( isDisabled ) {
+ return;
+ }
+
+ function _callback( event ) {
+ if ( isMatch( name, event ) ) {
+ callbackRef.current( event );
+ }
+ }
-export default useShortcut;
+ shortcuts.current.add( _callback );
+ return () => {
+ shortcuts.current.delete( _callback );
+ };
+ }, [ name, isDisabled ] );
+}
diff --git a/packages/keyboard-shortcuts/src/index.js b/packages/keyboard-shortcuts/src/index.js
index 26f2b004b6e04b..e8a0a747ca25f7 100644
--- a/packages/keyboard-shortcuts/src/index.js
+++ b/packages/keyboard-shortcuts/src/index.js
@@ -1,3 +1,4 @@
export { store } from './store';
export { default as useShortcut } from './hooks/use-shortcut';
+export { ShortcutProvider } from './components/shortcut-provider';
export { default as __unstableUseShortcutEventMatch } from './hooks/use-shortcut-event-match';
diff --git a/packages/keycodes/src/index.js b/packages/keycodes/src/index.js
index 6390a01f5e4dbc..c63c925d698912 100644
--- a/packages/keycodes/src/index.js
+++ b/packages/keycodes/src/index.js
@@ -332,7 +332,7 @@ export const isKeyboardEvent = mapValues( modifiers, ( getModifiers ) => {
return includes( mods, key );
}
- if ( event.altKey ) {
+ if ( event.altKey && character.length === 1 ) {
key = String.fromCharCode( event.keyCode ).toLowerCase();
}
@@ -341,6 +341,6 @@ export const isKeyboardEvent = mapValues( modifiers, ( getModifiers ) => {
character = 'delete';
}
- return key === character;
+ return key === character.toLowerCase();
};
} );