Skip to content

Commit

Permalink
Abstract keyboard shorcuts for heading to paragraph transform and vic…
Browse files Browse the repository at this point in the history
…e-versa (#60606)

* Abstract keyboard shorcuts for heading to paragraph transform and vice-versa.

* Add alias to transform heading to paragraph for consistency with classic editor.

* Move code to block-library.

* Make BlockKeyboardShortcuts private.

* Adjust after rebase.

Co-authored-by: afercia <[email protected]>
Co-authored-by: youknowriad <[email protected]>
Co-authored-by: ellatrix <[email protected]>
  • Loading branch information
4 people authored Apr 22, 2024
1 parent 857cc26 commit f5c5b04
Show file tree
Hide file tree
Showing 17 changed files with 162 additions and 300 deletions.
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/block-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@wordpress/icons": "file:../icons",
"@wordpress/interactivity": "file:../interactivity",
"@wordpress/interactivity-router": "file:../interactivity-router",
"@wordpress/keyboard-shortcuts": "file:../keyboard-shortcuts",
"@wordpress/keycodes": "file:../keycodes",
"@wordpress/notices": "file:../notices",
"@wordpress/patterns": "file:../patterns",
Expand Down
96 changes: 96 additions & 0 deletions packages/block-library/src/block-keyboard-shortcuts/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* WordPress dependencies
*/
import { useEffect } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import {
useShortcut,
store as keyboardShortcutsStore,
} from '@wordpress/keyboard-shortcuts';
import { __ } from '@wordpress/i18n';
import { createBlock } from '@wordpress/blocks';
import { store as blockEditorStore } from '@wordpress/block-editor';

function BlockKeyboardShortcuts() {
const { registerShortcut } = useDispatch( keyboardShortcutsStore );
const { replaceBlocks } = useDispatch( blockEditorStore );
const { getBlockName, getSelectedBlockClientId, getBlockAttributes } =
useSelect( blockEditorStore );

const handleTransformHeadingAndParagraph = ( 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 ] },
} )
);
};

useEffect( () => {
registerShortcut( {
name: 'core/block-editor/transform-heading-to-paragraph',
category: 'block-library',
description: __( 'Transform heading to paragraph.' ),
keyCombination: {
modifier: 'access',
character: '0',
},
aliases: [
{
modifier: 'access',
character: '7',
},
],
} );

[ 1, 2, 3, 4, 5, 6 ].forEach( ( level ) => {
registerShortcut( {
name: `core/block-editor/transform-paragraph-to-heading-${ level }`,
category: 'block-library',
description: __( 'Transform paragraph to heading.' ),
keyCombination: {
modifier: 'access',
character: `${ level }`,
},
} );
} );
}, [] );

useShortcut(
'core/block-editor/transform-heading-to-paragraph',
( event ) => handleTransformHeadingAndParagraph( 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/block-editor/transform-paragraph-to-heading-${ level }`,
( event ) => handleTransformHeadingAndParagraph( event, level )
);
} );

return null;
}

export default BlockKeyboardShortcuts;
2 changes: 2 additions & 0 deletions packages/block-library/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,3 +330,5 @@ export const __experimentalRegisterExperimentalCoreBlocks = process.env
.forEach( ( { init } ) => init() );
}
: undefined;

export { privateApis } from './private-apis';
13 changes: 13 additions & 0 deletions packages/block-library/src/private-apis.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Internal dependencies
*/
import { default as BlockKeyboardShortcuts } from './block-keyboard-shortcuts';
import { lock } from './lock-unlock';

/**
* @private
*/
export const privateApis = {};
lock( privateApis, {
BlockKeyboardShortcuts,
} );
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,16 @@ export const textFormattingShortcuts = [
description: __( 'Make the selected text inline code.' ),
},
{
keyCombination: { modifier: 'access', character: '0' },
keyCombination: {
modifier: 'access',
character: '0',
},
aliases: [
{
modifier: 'access',
character: '7',
},
],
description: __( 'Convert the current heading to a paragraph.' ),
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,10 @@ import {
store as keyboardShortcutsStore,
} from '@wordpress/keyboard-shortcuts';
import { isAppleOS } from '@wordpress/keycodes';
import { useDispatch, useSelect } from '@wordpress/data';
import { useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { store as blockEditorStore } from '@wordpress/block-editor';
import { createBlock } from '@wordpress/blocks';

function KeyboardShortcuts( { undo, redo, save } ) {
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/customize-widgets/undo', ( event ) => {
undo();
event.preventDefault();
Expand All @@ -60,21 +26,6 @@ function KeyboardShortcuts( { undo, redo, save } ) {
save();
} );

useShortcut(
'core/customize-widgets/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/customize-widgets/transform-paragraph-to-heading-${ level }`,
( event ) => handleTextLevelShortcut( event, level )
);
} );

return null;
}

Expand Down Expand Up @@ -126,28 +77,6 @@ function KeyboardShortcutsRegister() {
},
} );

registerShortcut( {
name: 'core/customize-widgets/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/customize-widgets/transform-paragraph-to-heading-${ level }`,
category: 'block-library',
description: __( 'Transform paragraph to heading.' ),
keyCombination: {
modifier: 'access',
character: `${ level }`,
},
} );
} );

return () => {
unregisterShortcut( 'core/customize-widgets/undo' );
unregisterShortcut( 'core/customize-widgets/redo' );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from '@wordpress/block-editor';
import { uploadMedia } from '@wordpress/media-utils';
import { store as preferencesStore } from '@wordpress/preferences';
import { privateApis as blockLibraryPrivateApis } from '@wordpress/block-library';

/**
* Internal dependencies
Expand All @@ -31,6 +32,8 @@ const { ExperimentalBlockCanvas: BlockCanvas } = unlock(
blockEditorPrivateApis
);

const { BlockKeyboardShortcuts } = unlock( blockLibraryPrivateApis );

export default function SidebarBlockEditor( {
blockEditorSettings,
sidebar,
Expand Down Expand Up @@ -99,6 +102,7 @@ export default function SidebarBlockEditor( {
return (
<>
<KeyboardShortcuts.Register />
<BlockKeyboardShortcuts />

<SidebarEditorProvider sidebar={ sidebar } settings={ settings }>
<KeyboardShortcuts
Expand Down
71 changes: 1 addition & 70 deletions packages/edit-post/src/components/keyboard-shortcuts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
* WordPress dependencies
*/
import { useEffect } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { useDispatch } from '@wordpress/data';
import {
useShortcut,
store as keyboardShortcutsStore,
} from '@wordpress/keyboard-shortcuts';
import { __ } from '@wordpress/i18n';
import { store as blockEditorStore } from '@wordpress/block-editor';
import { createBlock } from '@wordpress/blocks';

/**
* Internal dependencies
Expand All @@ -19,37 +17,6 @@ import { store as editPostStore } from '../../store';
function KeyboardShortcuts() {
const { toggleFeature } = useDispatch( editPostStore );
const { registerShortcut } = useDispatch( keyboardShortcutsStore );
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 ] },
} )
);
};

useEffect( () => {
registerShortcut( {
Expand Down Expand Up @@ -97,48 +64,12 @@ function KeyboardShortcuts() {
},
],
} );

registerShortcut( {
name: 'core/edit-post/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-post/transform-paragraph-to-heading-${ level }`,
category: 'block-library',
description: __( 'Transform paragraph to heading.' ),
keyCombination: {
modifier: 'access',
character: `${ level }`,
},
} );
} );
}, [] );

useShortcut( 'core/edit-post/toggle-fullscreen', () => {
toggleFeature( 'fullscreenMode' );
} );

useShortcut( 'core/edit-post/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-post/transform-paragraph-to-heading-${ level }`,
( event ) => handleTextLevelShortcut( event, level )
);
} );

return null;
}

Expand Down
Loading

1 comment on commit f5c5b04

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in f5c5b04.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/8782688616
📝 Reported issues:

Please sign in to comment.