Skip to content

Commit f5c5b04

Browse files
aferciayouknowriadellatrix
authored
Abstract keyboard shorcuts for heading to paragraph transform and vice-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]>
1 parent 857cc26 commit f5c5b04

File tree

17 files changed

+162
-300
lines changed

17 files changed

+162
-300
lines changed

package-lock.json

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/block-library/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"@wordpress/icons": "file:../icons",
5353
"@wordpress/interactivity": "file:../interactivity",
5454
"@wordpress/interactivity-router": "file:../interactivity-router",
55+
"@wordpress/keyboard-shortcuts": "file:../keyboard-shortcuts",
5556
"@wordpress/keycodes": "file:../keycodes",
5657
"@wordpress/notices": "file:../notices",
5758
"@wordpress/patterns": "file:../patterns",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* WordPress dependencies
3+
*/
4+
import { useEffect } from '@wordpress/element';
5+
import { useSelect, useDispatch } from '@wordpress/data';
6+
import {
7+
useShortcut,
8+
store as keyboardShortcutsStore,
9+
} from '@wordpress/keyboard-shortcuts';
10+
import { __ } from '@wordpress/i18n';
11+
import { createBlock } from '@wordpress/blocks';
12+
import { store as blockEditorStore } from '@wordpress/block-editor';
13+
14+
function BlockKeyboardShortcuts() {
15+
const { registerShortcut } = useDispatch( keyboardShortcutsStore );
16+
const { replaceBlocks } = useDispatch( blockEditorStore );
17+
const { getBlockName, getSelectedBlockClientId, getBlockAttributes } =
18+
useSelect( blockEditorStore );
19+
20+
const handleTransformHeadingAndParagraph = ( event, level ) => {
21+
event.preventDefault();
22+
const destinationBlockName =
23+
level === 0 ? 'core/paragraph' : 'core/heading';
24+
const currentClientId = getSelectedBlockClientId();
25+
if ( currentClientId === null ) {
26+
return;
27+
}
28+
const blockName = getBlockName( currentClientId );
29+
if ( blockName !== 'core/paragraph' && blockName !== 'core/heading' ) {
30+
return;
31+
}
32+
const attributes = getBlockAttributes( currentClientId );
33+
const textAlign =
34+
blockName === 'core/paragraph' ? 'align' : 'textAlign';
35+
const destinationTextAlign =
36+
destinationBlockName === 'core/paragraph' ? 'align' : 'textAlign';
37+
38+
replaceBlocks(
39+
currentClientId,
40+
createBlock( destinationBlockName, {
41+
level,
42+
content: attributes.content,
43+
...{ [ destinationTextAlign ]: attributes[ textAlign ] },
44+
} )
45+
);
46+
};
47+
48+
useEffect( () => {
49+
registerShortcut( {
50+
name: 'core/block-editor/transform-heading-to-paragraph',
51+
category: 'block-library',
52+
description: __( 'Transform heading to paragraph.' ),
53+
keyCombination: {
54+
modifier: 'access',
55+
character: '0',
56+
},
57+
aliases: [
58+
{
59+
modifier: 'access',
60+
character: '7',
61+
},
62+
],
63+
} );
64+
65+
[ 1, 2, 3, 4, 5, 6 ].forEach( ( level ) => {
66+
registerShortcut( {
67+
name: `core/block-editor/transform-paragraph-to-heading-${ level }`,
68+
category: 'block-library',
69+
description: __( 'Transform paragraph to heading.' ),
70+
keyCombination: {
71+
modifier: 'access',
72+
character: `${ level }`,
73+
},
74+
} );
75+
} );
76+
}, [] );
77+
78+
useShortcut(
79+
'core/block-editor/transform-heading-to-paragraph',
80+
( event ) => handleTransformHeadingAndParagraph( event, 0 )
81+
);
82+
83+
[ 1, 2, 3, 4, 5, 6 ].forEach( ( level ) => {
84+
//the loop is based off on a constant therefore
85+
//the hook will execute the same way every time
86+
//eslint-disable-next-line react-hooks/rules-of-hooks
87+
useShortcut(
88+
`core/block-editor/transform-paragraph-to-heading-${ level }`,
89+
( event ) => handleTransformHeadingAndParagraph( event, level )
90+
);
91+
} );
92+
93+
return null;
94+
}
95+
96+
export default BlockKeyboardShortcuts;

packages/block-library/src/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,5 @@ export const __experimentalRegisterExperimentalCoreBlocks = process.env
330330
.forEach( ( { init } ) => init() );
331331
}
332332
: undefined;
333+
334+
export { privateApis } from './private-apis';
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* Internal dependencies
3+
*/
4+
import { default as BlockKeyboardShortcuts } from './block-keyboard-shortcuts';
5+
import { lock } from './lock-unlock';
6+
7+
/**
8+
* @private
9+
*/
10+
export const privateApis = {};
11+
lock( privateApis, {
12+
BlockKeyboardShortcuts,
13+
} );

packages/customize-widgets/src/components/keyboard-shortcut-help-modal/config.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,16 @@ export const textFormattingShortcuts = [
3737
description: __( 'Make the selected text inline code.' ),
3838
},
3939
{
40-
keyCombination: { modifier: 'access', character: '0' },
40+
keyCombination: {
41+
modifier: 'access',
42+
character: '0',
43+
},
44+
aliases: [
45+
{
46+
modifier: 'access',
47+
character: '7',
48+
},
49+
],
4150
description: __( 'Convert the current heading to a paragraph.' ),
4251
},
4352
{

packages/customize-widgets/src/components/keyboard-shortcuts/index.js

+1-72
Original file line numberDiff line numberDiff line change
@@ -7,44 +7,10 @@ import {
77
store as keyboardShortcutsStore,
88
} from '@wordpress/keyboard-shortcuts';
99
import { isAppleOS } from '@wordpress/keycodes';
10-
import { useDispatch, useSelect } from '@wordpress/data';
10+
import { useDispatch } from '@wordpress/data';
1111
import { __ } from '@wordpress/i18n';
12-
import { store as blockEditorStore } from '@wordpress/block-editor';
13-
import { createBlock } from '@wordpress/blocks';
1412

1513
function KeyboardShortcuts( { undo, redo, save } ) {
16-
const { replaceBlocks } = useDispatch( blockEditorStore );
17-
const { getBlockName, getSelectedBlockClientId, getBlockAttributes } =
18-
useSelect( blockEditorStore );
19-
20-
const handleTextLevelShortcut = ( event, level ) => {
21-
event.preventDefault();
22-
const destinationBlockName =
23-
level === 0 ? 'core/paragraph' : 'core/heading';
24-
const currentClientId = getSelectedBlockClientId();
25-
if ( currentClientId === null ) {
26-
return;
27-
}
28-
const blockName = getBlockName( currentClientId );
29-
if ( blockName !== 'core/paragraph' && blockName !== 'core/heading' ) {
30-
return;
31-
}
32-
const attributes = getBlockAttributes( currentClientId );
33-
const textAlign =
34-
blockName === 'core/paragraph' ? 'align' : 'textAlign';
35-
const destinationTextAlign =
36-
destinationBlockName === 'core/paragraph' ? 'align' : 'textAlign';
37-
38-
replaceBlocks(
39-
currentClientId,
40-
createBlock( destinationBlockName, {
41-
level,
42-
content: attributes.content,
43-
...{ [ destinationTextAlign ]: attributes[ textAlign ] },
44-
} )
45-
);
46-
};
47-
4814
useShortcut( 'core/customize-widgets/undo', ( event ) => {
4915
undo();
5016
event.preventDefault();
@@ -60,21 +26,6 @@ function KeyboardShortcuts( { undo, redo, save } ) {
6026
save();
6127
} );
6228

63-
useShortcut(
64-
'core/customize-widgets/transform-heading-to-paragraph',
65-
( event ) => handleTextLevelShortcut( event, 0 )
66-
);
67-
68-
[ 1, 2, 3, 4, 5, 6 ].forEach( ( level ) => {
69-
//the loop is based off on a constant therefore
70-
//the hook will execute the same way every time
71-
//eslint-disable-next-line react-hooks/rules-of-hooks
72-
useShortcut(
73-
`core/customize-widgets/transform-paragraph-to-heading-${ level }`,
74-
( event ) => handleTextLevelShortcut( event, level )
75-
);
76-
} );
77-
7829
return null;
7930
}
8031

@@ -126,28 +77,6 @@ function KeyboardShortcutsRegister() {
12677
},
12778
} );
12879

129-
registerShortcut( {
130-
name: 'core/customize-widgets/transform-heading-to-paragraph',
131-
category: 'block-library',
132-
description: __( 'Transform heading to paragraph.' ),
133-
keyCombination: {
134-
modifier: 'access',
135-
character: `0`,
136-
},
137-
} );
138-
139-
[ 1, 2, 3, 4, 5, 6 ].forEach( ( level ) => {
140-
registerShortcut( {
141-
name: `core/customize-widgets/transform-paragraph-to-heading-${ level }`,
142-
category: 'block-library',
143-
description: __( 'Transform paragraph to heading.' ),
144-
keyCombination: {
145-
modifier: 'access',
146-
character: `${ level }`,
147-
},
148-
} );
149-
} );
150-
15180
return () => {
15281
unregisterShortcut( 'core/customize-widgets/undo' );
15382
unregisterShortcut( 'core/customize-widgets/redo' );

packages/customize-widgets/src/components/sidebar-block-editor/index.js

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
} from '@wordpress/block-editor';
1515
import { uploadMedia } from '@wordpress/media-utils';
1616
import { store as preferencesStore } from '@wordpress/preferences';
17+
import { privateApis as blockLibraryPrivateApis } from '@wordpress/block-library';
1718

1819
/**
1920
* Internal dependencies
@@ -31,6 +32,8 @@ const { ExperimentalBlockCanvas: BlockCanvas } = unlock(
3132
blockEditorPrivateApis
3233
);
3334

35+
const { BlockKeyboardShortcuts } = unlock( blockLibraryPrivateApis );
36+
3437
export default function SidebarBlockEditor( {
3538
blockEditorSettings,
3639
sidebar,
@@ -99,6 +102,7 @@ export default function SidebarBlockEditor( {
99102
return (
100103
<>
101104
<KeyboardShortcuts.Register />
105+
<BlockKeyboardShortcuts />
102106

103107
<SidebarEditorProvider sidebar={ sidebar } settings={ settings }>
104108
<KeyboardShortcuts

packages/edit-post/src/components/keyboard-shortcuts/index.js

+1-70
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@
22
* WordPress dependencies
33
*/
44
import { useEffect } from '@wordpress/element';
5-
import { useSelect, useDispatch } from '@wordpress/data';
5+
import { useDispatch } from '@wordpress/data';
66
import {
77
useShortcut,
88
store as keyboardShortcutsStore,
99
} from '@wordpress/keyboard-shortcuts';
1010
import { __ } from '@wordpress/i18n';
11-
import { store as blockEditorStore } from '@wordpress/block-editor';
12-
import { createBlock } from '@wordpress/blocks';
1311

1412
/**
1513
* Internal dependencies
@@ -19,37 +17,6 @@ import { store as editPostStore } from '../../store';
1917
function KeyboardShortcuts() {
2018
const { toggleFeature } = useDispatch( editPostStore );
2119
const { registerShortcut } = useDispatch( keyboardShortcutsStore );
22-
const { replaceBlocks } = useDispatch( blockEditorStore );
23-
const { getBlockName, getSelectedBlockClientId, getBlockAttributes } =
24-
useSelect( blockEditorStore );
25-
26-
const handleTextLevelShortcut = ( event, level ) => {
27-
event.preventDefault();
28-
const destinationBlockName =
29-
level === 0 ? 'core/paragraph' : 'core/heading';
30-
const currentClientId = getSelectedBlockClientId();
31-
if ( currentClientId === null ) {
32-
return;
33-
}
34-
const blockName = getBlockName( currentClientId );
35-
if ( blockName !== 'core/paragraph' && blockName !== 'core/heading' ) {
36-
return;
37-
}
38-
const attributes = getBlockAttributes( currentClientId );
39-
const textAlign =
40-
blockName === 'core/paragraph' ? 'align' : 'textAlign';
41-
const destinationTextAlign =
42-
destinationBlockName === 'core/paragraph' ? 'align' : 'textAlign';
43-
44-
replaceBlocks(
45-
currentClientId,
46-
createBlock( destinationBlockName, {
47-
level,
48-
content: attributes.content,
49-
...{ [ destinationTextAlign ]: attributes[ textAlign ] },
50-
} )
51-
);
52-
};
5320

5421
useEffect( () => {
5522
registerShortcut( {
@@ -97,48 +64,12 @@ function KeyboardShortcuts() {
9764
},
9865
],
9966
} );
100-
101-
registerShortcut( {
102-
name: 'core/edit-post/transform-heading-to-paragraph',
103-
category: 'block-library',
104-
description: __( 'Transform heading to paragraph.' ),
105-
keyCombination: {
106-
modifier: 'access',
107-
character: `0`,
108-
},
109-
} );
110-
111-
[ 1, 2, 3, 4, 5, 6 ].forEach( ( level ) => {
112-
registerShortcut( {
113-
name: `core/edit-post/transform-paragraph-to-heading-${ level }`,
114-
category: 'block-library',
115-
description: __( 'Transform paragraph to heading.' ),
116-
keyCombination: {
117-
modifier: 'access',
118-
character: `${ level }`,
119-
},
120-
} );
121-
} );
12267
}, [] );
12368

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

128-
useShortcut( 'core/edit-post/transform-heading-to-paragraph', ( event ) =>
129-
handleTextLevelShortcut( event, 0 )
130-
);
131-
132-
[ 1, 2, 3, 4, 5, 6 ].forEach( ( level ) => {
133-
//the loop is based off on a constant therefore
134-
//the hook will execute the same way every time
135-
//eslint-disable-next-line react-hooks/rules-of-hooks
136-
useShortcut(
137-
`core/edit-post/transform-paragraph-to-heading-${ level }`,
138-
( event ) => handleTextLevelShortcut( event, level )
139-
);
140-
} );
141-
14273
return null;
14374
}
14475

0 commit comments

Comments
 (0)