diff --git a/packages/block-editor/src/components/rich-text/index.native.js b/packages/block-editor/src/components/rich-text/index.native.js index 8c1afdd8903bd0..32bd1afd3d5404 100644 --- a/packages/block-editor/src/components/rich-text/index.native.js +++ b/packages/block-editor/src/components/rich-text/index.native.js @@ -21,6 +21,7 @@ import { __unstableCreateElement, isEmpty, insert, + remove, create, split, toHTMLString, @@ -70,6 +71,7 @@ function RichTextWrapper( onSplit, __unstableOnSplitAtEnd: onSplitAtEnd, __unstableOnSplitMiddle: onSplitMiddle, + __unstableOnSplitAtDoubleLineEnd: onSplitAtDoubleLineEnd, identifier, preserveWhiteSpace, __unstablePastePlainText: pastePlainText, @@ -339,14 +341,28 @@ function RichTextWrapper( splitStart === splitEnd && splitEnd === text.length; - if ( shiftKey || ( ! canSplit && ! canSplitAtEnd ) ) { + if ( shiftKey ) { if ( ! disableLineBreaks ) { onChange( insert( value, '\n' ) ); } - } else if ( ! canSplit && canSplitAtEnd ) { - onSplitAtEnd(); } else if ( canSplit ) { splitValue( value ); + } else if ( canSplitAtEnd ) { + onSplitAtEnd(); + } else if ( + // For some blocks it's desirable to split at the end of the + // block when there are two line breaks at the end of the + // block, so triple Enter exits the block. + onSplitAtDoubleLineEnd && + splitStart === splitEnd && + splitEnd === text.length && + text.slice( -2 ) === '\n\n' + ) { + value.start = value.end - 2; + onChange( remove( value ) ); + onSplitAtDoubleLineEnd(); + } else if ( ! disableLineBreaks ) { + onChange( insert( value, '\n' ) ); } }, // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/packages/block-library/src/code/edit.native.js b/packages/block-library/src/code/edit.native.js index 58188d0b2119f0..3353dbc3c25a01 100644 --- a/packages/block-library/src/code/edit.native.js +++ b/packages/block-library/src/code/edit.native.js @@ -9,6 +9,7 @@ import { View } from 'react-native'; import { PlainText } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; import { usePreferredColorSchemeStyle } from '@wordpress/compose'; +import { createBlock, getDefaultBlockName } from '@wordpress/blocks'; /** * Internal dependencies @@ -22,7 +23,15 @@ import styles from './theme.scss'; // Note: styling is applied directly to the (nested) PlainText component. Web-side components // apply it to the container 'div' but we don't have a proper proposal for cascading styling yet. export function CodeEdit( props ) { - const { attributes, setAttributes, onFocus, onBlur, style } = props; + const { + attributes, + setAttributes, + onFocus, + onBlur, + style, + insertBlocksAfter, + mergeBlocks, + } = props; const codeStyle = { ...usePreferredColorSchemeStyle( styles.blockCode, @@ -40,16 +49,21 @@ export function CodeEdit( props ) { setAttributes( { content } ) } + onMerge={ mergeBlocks } placeholder={ __( 'Write code…' ) } aria-label={ __( 'Code' ) } isSelected={ props.isSelected } onFocus={ onFocus } onBlur={ onBlur } placeholderTextColor={ placeholderStyle.color } + __unstableOnSplitAtDoubleLineEnd={ () => + insertBlocksAfter( createBlock( getDefaultBlockName() ) ) + } /> </View> ); diff --git a/packages/block-library/src/preformatted/test/edit.native.js b/packages/block-library/src/preformatted/test/edit.native.js index 1fdb4532dacab6..153b1b9e9b0ab5 100644 --- a/packages/block-library/src/preformatted/test/edit.native.js +++ b/packages/block-library/src/preformatted/test/edit.native.js @@ -70,4 +70,42 @@ describe( 'Preformatted', () => { <!-- /wp:preformatted -->" ` ); } ); + + it( 'should split on triple Enter', async () => { + // Arrange + const screen = await initializeEditor(); + + // Act + await addBlock( screen, 'Preformatted' ); + const preformattedTextInput = await screen.findByPlaceholderText( + 'Write preformatted text…' + ); + typeInRichText( preformattedTextInput, 'Hello' ); + fireEvent( preformattedTextInput, 'onKeyDown', { + nativeEvent: {}, + preventDefault() {}, + keyCode: ENTER, + } ); + fireEvent( preformattedTextInput, 'onKeyDown', { + nativeEvent: {}, + preventDefault() {}, + keyCode: ENTER, + } ); + fireEvent( preformattedTextInput, 'onKeyDown', { + nativeEvent: {}, + preventDefault() {}, + keyCode: ENTER, + } ); + + // Assert + expect( getEditorHtml() ).toMatchInlineSnapshot( ` + "<!-- wp:preformatted --> + <pre class="wp-block-preformatted">Hello</pre> + <!-- /wp:preformatted --> + + <!-- wp:paragraph --> + <p></p> + <!-- /wp:paragraph -->" + ` ); + } ); } ); diff --git a/packages/block-library/src/verse/test/edit.native.js b/packages/block-library/src/verse/test/edit.native.js index c91c261c89c358..8794419775d325 100644 --- a/packages/block-library/src/verse/test/edit.native.js +++ b/packages/block-library/src/verse/test/edit.native.js @@ -78,4 +78,41 @@ describe( 'Verse block', () => { <!-- /wp:verse -->" ` ); } ); + + it( 'should split on triple Enter', async () => { + // Arrange + const screen = await initializeEditor(); + await addBlock( screen, 'Verse' ); + + // Act + const verseTextInput = + await screen.findByPlaceholderText( 'Write verse…' ); + typeInRichText( verseTextInput, 'Hello' ); + fireEvent( verseTextInput, 'onKeyDown', { + nativeEvent: {}, + preventDefault() {}, + keyCode: ENTER, + } ); + fireEvent( verseTextInput, 'onKeyDown', { + nativeEvent: {}, + preventDefault() {}, + keyCode: ENTER, + } ); + fireEvent( verseTextInput, 'onKeyDown', { + nativeEvent: {}, + preventDefault() {}, + keyCode: ENTER, + } ); + + // Assert + expect( getEditorHtml() ).toMatchInlineSnapshot( ` + "<!-- wp:verse --> + <pre class="wp-block-verse">Hello</pre> + <!-- /wp:verse --> + + <!-- wp:paragraph --> + <p></p> + <!-- /wp:paragraph -->" + ` ); + } ); } ); diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 9316c4693cfaa6..600f855ba0ec11 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -10,6 +10,7 @@ For each user feature we should also add a importance categorization label to i --> ## Unreleased +- [*] Exit Preformatted and Verse blocks by triple pressing the Return key [#53354] ## 1.105.0 - [*] Limit inner blocks nesting depth to avoid call stack size exceeded crash [#54382]