-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Site Editor: Fix the tooltip and shortcut for the global save button #48282
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useShortcut } from '@wordpress/keyboard-shortcuts'; | ||
import { useDispatch, useSelect } from '@wordpress/data'; | ||
import { store as coreStore } from '@wordpress/core-data'; | ||
import { store as blockEditorStore } from '@wordpress/block-editor'; | ||
import { store as interfaceStore } from '@wordpress/interface'; | ||
import { createBlock } from '@wordpress/blocks'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { store as editSiteStore } from '../../store'; | ||
import { SIDEBAR_BLOCK } from '../sidebar-edit-mode/constants'; | ||
import { STORE_NAME } from '../../store/constants'; | ||
|
||
function KeyboardShortcutsEditMode() { | ||
const { getEditorMode } = useSelect( editSiteStore ); | ||
const isListViewOpen = useSelect( | ||
( select ) => select( editSiteStore ).isListViewOpened(), | ||
[] | ||
); | ||
const isBlockInspectorOpen = useSelect( | ||
( select ) => | ||
select( interfaceStore ).getActiveComplementaryArea( | ||
editSiteStore.name | ||
) === SIDEBAR_BLOCK, | ||
[] | ||
); | ||
const { redo, undo } = useDispatch( coreStore ); | ||
const { setIsListViewOpened, switchEditorMode } = | ||
useDispatch( editSiteStore ); | ||
const { enableComplementaryArea, disableComplementaryArea } = | ||
useDispatch( interfaceStore ); | ||
|
||
const { replaceBlocks } = useDispatch( blockEditorStore ); | ||
const { getBlockName, getSelectedBlockClientId, getBlockAttributes } = | ||
useSelect( blockEditorStore ); | ||
|
||
const handleTextLevelShortcut = ( event, level ) => { | ||
event.preventDefault(); | ||
const destinationBlockName = | ||
level === 0 ? 'core/paragraph' : 'core/heading'; | ||
const currentClientId = getSelectedBlockClientId(); | ||
if ( currentClientId === null ) { | ||
return; | ||
} | ||
const blockName = getBlockName( currentClientId ); | ||
if ( blockName !== 'core/paragraph' && blockName !== 'core/heading' ) { | ||
return; | ||
} | ||
const attributes = getBlockAttributes( currentClientId ); | ||
const textAlign = | ||
blockName === 'core/paragraph' ? 'align' : 'textAlign'; | ||
const destinationTextAlign = | ||
destinationBlockName === 'core/paragraph' ? 'align' : 'textAlign'; | ||
|
||
replaceBlocks( | ||
currentClientId, | ||
createBlock( destinationBlockName, { | ||
level, | ||
content: attributes.content, | ||
...{ [ destinationTextAlign ]: attributes[ textAlign ] }, | ||
} ) | ||
); | ||
}; | ||
|
||
useShortcut( 'core/edit-site/undo', ( event ) => { | ||
undo(); | ||
event.preventDefault(); | ||
} ); | ||
|
||
useShortcut( 'core/edit-site/redo', ( event ) => { | ||
redo(); | ||
event.preventDefault(); | ||
} ); | ||
|
||
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(); | ||
|
||
if ( isBlockInspectorOpen ) { | ||
disableComplementaryArea( STORE_NAME ); | ||
} else { | ||
enableComplementaryArea( STORE_NAME, SIDEBAR_BLOCK ); | ||
} | ||
} ); | ||
|
||
useShortcut( 'core/edit-site/toggle-mode', () => { | ||
switchEditorMode( getEditorMode() === 'visual' ? 'text' : 'visual' ); | ||
} ); | ||
|
||
useShortcut( 'core/edit-site/transform-heading-to-paragraph', ( event ) => | ||
handleTextLevelShortcut( event, 0 ) | ||
); | ||
|
||
[ 1, 2, 3, 4, 5, 6 ].forEach( ( level ) => { | ||
//the loop is based off on a constant therefore | ||
//the hook will execute the same way every time | ||
//eslint-disable-next-line react-hooks/rules-of-hooks | ||
useShortcut( | ||
`core/edit-site/transform-paragraph-to-heading-${ level }`, | ||
( event ) => handleTextLevelShortcut( event, level ) | ||
); | ||
} ); | ||
|
||
return null; | ||
} | ||
|
||
export default KeyboardShortcutsEditMode; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useShortcut } from '@wordpress/keyboard-shortcuts'; | ||
import { useDispatch, useSelect } from '@wordpress/data'; | ||
import { store as coreStore } from '@wordpress/core-data'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { store as editSiteStore } from '../../store'; | ||
|
||
function KeyboardShortcutsGlobal() { | ||
const { __experimentalGetDirtyEntityRecords, isSavingEntityRecord } = | ||
useSelect( coreStore ); | ||
const { setIsSaveViewOpened } = useDispatch( editSiteStore ); | ||
|
||
useShortcut( 'core/edit-site/save', ( event ) => { | ||
event.preventDefault(); | ||
|
||
const dirtyEntityRecords = __experimentalGetDirtyEntityRecords(); | ||
const isDirty = !! dirtyEntityRecords.length; | ||
const isSaving = dirtyEntityRecords.some( ( record ) => | ||
isSavingEntityRecord( record.kind, record.name, record.key ) | ||
); | ||
|
||
if ( ! isSaving && isDirty ) { | ||
setIsSaveViewOpened( true ); | ||
} | ||
} ); | ||
|
||
return null; | ||
} | ||
|
||
export default KeyboardShortcutsGlobal; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useEffect } from '@wordpress/element'; | ||
import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; | ||
import { isAppleOS } from '@wordpress/keycodes'; | ||
import { useDispatch } from '@wordpress/data'; | ||
import { __ } from '@wordpress/i18n'; | ||
|
||
function KeyboardShortcutsRegister() { | ||
// Registering the shortcuts. | ||
const { registerShortcut } = useDispatch( keyboardShortcutsStore ); | ||
useEffect( () => { | ||
registerShortcut( { | ||
name: 'core/edit-site/save', | ||
category: 'global', | ||
description: __( 'Save your changes.' ), | ||
keyCombination: { | ||
modifier: 'primary', | ||
character: 's', | ||
}, | ||
} ); | ||
|
||
registerShortcut( { | ||
name: 'core/edit-site/undo', | ||
category: 'global', | ||
description: __( 'Undo your last changes.' ), | ||
keyCombination: { | ||
modifier: 'primary', | ||
character: 'z', | ||
}, | ||
} ); | ||
|
||
registerShortcut( { | ||
name: 'core/edit-site/redo', | ||
category: 'global', | ||
description: __( 'Redo your last undo.' ), | ||
keyCombination: { | ||
modifier: 'primaryShift', | ||
character: 'z', | ||
}, | ||
// Disable on Apple OS because it conflicts with the browser's | ||
// history shortcut. It's a fine alias for both Windows and Linux. | ||
// Since there's no conflict for Ctrl+Shift+Z on both Windows and | ||
// Linux, we keep it as the default for consistency. | ||
aliases: isAppleOS() | ||
? [] | ||
: [ | ||
{ | ||
modifier: 'primary', | ||
character: 'y', | ||
}, | ||
], | ||
} ); | ||
|
||
registerShortcut( { | ||
name: 'core/edit-site/toggle-list-view', | ||
category: 'global', | ||
description: __( 'Open the block list view.' ), | ||
keyCombination: { | ||
modifier: 'access', | ||
character: 'o', | ||
}, | ||
} ); | ||
|
||
registerShortcut( { | ||
name: 'core/edit-site/toggle-block-settings-sidebar', | ||
category: 'global', | ||
description: __( 'Show or hide the block settings sidebar.' ), | ||
keyCombination: { | ||
modifier: 'primaryShift', | ||
character: ',', | ||
}, | ||
} ); | ||
|
||
registerShortcut( { | ||
name: 'core/edit-site/keyboard-shortcuts', | ||
category: 'main', | ||
description: __( 'Display these keyboard shortcuts.' ), | ||
keyCombination: { | ||
modifier: 'access', | ||
character: 'h', | ||
}, | ||
} ); | ||
|
||
registerShortcut( { | ||
name: 'core/edit-site/next-region', | ||
category: 'global', | ||
description: __( 'Navigate to the next part of the editor.' ), | ||
keyCombination: { | ||
modifier: 'ctrl', | ||
character: '`', | ||
}, | ||
aliases: [ | ||
{ | ||
modifier: 'access', | ||
character: 'n', | ||
}, | ||
], | ||
} ); | ||
|
||
registerShortcut( { | ||
name: 'core/edit-site/previous-region', | ||
category: 'global', | ||
description: __( 'Navigate to the previous part of the editor.' ), | ||
keyCombination: { | ||
modifier: 'ctrlShift', | ||
character: '`', | ||
}, | ||
aliases: [ | ||
{ | ||
modifier: 'access', | ||
character: 'p', | ||
}, | ||
{ | ||
modifier: 'ctrlShift', | ||
character: '~', | ||
}, | ||
], | ||
} ); | ||
registerShortcut( { | ||
name: 'core/edit-site/toggle-mode', | ||
category: 'global', | ||
description: __( 'Switch between visual editor and code editor.' ), | ||
keyCombination: { | ||
modifier: 'secondary', | ||
character: 'm', | ||
}, | ||
} ); | ||
|
||
registerShortcut( { | ||
name: `core/edit-site/transform-heading-to-paragraph`, | ||
category: 'block-library', | ||
description: __( 'Transform heading to paragraph.' ), | ||
keyCombination: { | ||
modifier: 'access', | ||
character: `0`, | ||
}, | ||
} ); | ||
|
||
[ 1, 2, 3, 4, 5, 6 ].forEach( ( level ) => { | ||
registerShortcut( { | ||
name: `core/edit-site/transform-paragraph-to-heading-${ level }`, | ||
category: 'block-library', | ||
description: __( 'Transform paragraph to heading.' ), | ||
keyCombination: { | ||
modifier: 'access', | ||
character: `${ level }`, | ||
}, | ||
} ); | ||
} ); | ||
}, [ registerShortcut ] ); | ||
|
||
return null; | ||
} | ||
|
||
export default KeyboardShortcutsRegister; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ import { displayShortcut } from '@wordpress/keycodes'; | |
*/ | ||
import { store as editSiteStore } from '../../store'; | ||
|
||
export default function SaveButton() { | ||
export default function SaveButton( { showTooltip = true } ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason for keeping the tooltip in the header There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, there is. In the post editor we want to force the tooltip to show regardless of whether there's a shortcut. |
||
const { isDirty, isSaving, isSaveViewOpen } = useSelect( ( select ) => { | ||
const { __experimentalGetDirtyEntityRecords, isSavingEntityRecord } = | ||
select( coreStore ); | ||
|
@@ -52,7 +52,7 @@ export default function SaveButton() { | |
* of the button that we want to avoid. By setting `showTooltip`, | ||
& the tooltip is always rendered even when there's no keyboard shortcut. | ||
*/ | ||
showTooltip | ||
showTooltip={ showTooltip } | ||
> | ||
{ label } | ||
</Button> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't make any change to these three files, I just split the previous "index.js" file into three components in order to be able to insert them in different contexts. That way we can always make sure that the "save shortcut" works in all modes.