From b9357415df24159df9ea72cdf276ddec919416ca Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Tue, 21 Feb 2023 12:54:47 +0200 Subject: [PATCH] Rich text: extract delete handler to hook (#48273) --- .../src/components/rich-text/index.js | 49 ++------------- .../src/components/rich-text/use-delete.js | 59 +++++++++++++++++++ 2 files changed, 65 insertions(+), 43 deletions(-) create mode 100644 packages/block-editor/src/components/rich-text/use-delete.js diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js index 62acbbd4bbbcf..030df831886f5 100644 --- a/packages/block-editor/src/components/rich-text/index.js +++ b/packages/block-editor/src/components/rich-text/index.js @@ -19,12 +19,9 @@ import { useInstanceId, useMergeRefs } from '@wordpress/compose'; import { __unstableUseRichText as useRichText, __unstableCreateElement, - isEmpty, - isCollapsed, removeFormat, } from '@wordpress/rich-text'; import deprecated from '@wordpress/deprecated'; -import { BACKSPACE, DELETE } from '@wordpress/keycodes'; import { Popover } from '@wordpress/components'; /** @@ -39,6 +36,7 @@ import { useMarkPersistent } from './use-mark-persistent'; import { usePasteHandler } from './use-paste-handler'; import { useBeforeInputRules } from './use-before-input-rules'; import { useInputRules } from './use-input-rules'; +import { useDelete } from './use-delete'; import { useEnter } from './use-enter'; import { useFormatTypes } from './use-format-types'; import { useRemoveBrowserShortcuts } from './use-remove-browser-shortcuts'; @@ -316,45 +314,6 @@ function RichTextWrapper( const keyboardShortcuts = useRef( new Set() ); const inputEvents = useRef( new Set() ); - function onKeyDown( event ) { - const { keyCode } = event; - - if ( event.defaultPrevented ) { - return; - } - - if ( keyCode === DELETE || keyCode === BACKSPACE ) { - const { start, end, text } = value; - const isReverse = keyCode === BACKSPACE; - const hasActiveFormats = - value.activeFormats && !! value.activeFormats.length; - - // Only process delete if the key press occurs at an uncollapsed edge. - if ( - ! isCollapsed( value ) || - hasActiveFormats || - ( isReverse && start !== 0 ) || - ( ! isReverse && end !== text.length ) - ) { - return; - } - - if ( onMerge ) { - onMerge( ! isReverse ); - } - - // Only handle remove on Backspace. This serves dual-purpose of being - // an intentional user interaction distinguishing between Backspace and - // Delete to remove the empty field, but also to avoid merge & remove - // causing destruction of two fields (merge, then removed merged). - if ( onRemove && isEmpty( value ) && isReverse ) { - onRemove( ! isReverse ); - } - - event.preventDefault(); - } - } - function onFocus() { anchorRef.current?.focus(); } @@ -428,6 +387,11 @@ function RichTextWrapper( preserveWhiteSpace, pastePlainText, } ), + useDelete( { + value, + onMerge, + onRemove, + } ), useEnter( { removeEditorOnlyFormats, value, @@ -449,7 +413,6 @@ function RichTextWrapper( props.className, 'rich-text' ) } - onKeyDown={ onKeyDown } /> ); diff --git a/packages/block-editor/src/components/rich-text/use-delete.js b/packages/block-editor/src/components/rich-text/use-delete.js new file mode 100644 index 0000000000000..f09a15265bd2b --- /dev/null +++ b/packages/block-editor/src/components/rich-text/use-delete.js @@ -0,0 +1,59 @@ +/** + * WordPress dependencies + */ +import { useRef } from '@wordpress/element'; +import { useRefEffect } from '@wordpress/compose'; +import { DELETE, BACKSPACE } from '@wordpress/keycodes'; +import { isCollapsed, isEmpty } from '@wordpress/rich-text'; + +export function useDelete( props ) { + const propsRef = useRef( props ); + propsRef.current = props; + return useRefEffect( ( element ) => { + function onKeyDown( event ) { + const { keyCode } = event; + + if ( event.defaultPrevented ) { + return; + } + + const { value, onMerge, onRemove } = propsRef.current; + + if ( keyCode === DELETE || keyCode === BACKSPACE ) { + const { start, end, text } = value; + const isReverse = keyCode === BACKSPACE; + const hasActiveFormats = + value.activeFormats && !! value.activeFormats.length; + + // Only process delete if the key press occurs at an uncollapsed edge. + if ( + ! isCollapsed( value ) || + hasActiveFormats || + ( isReverse && start !== 0 ) || + ( ! isReverse && end !== text.length ) + ) { + return; + } + + if ( onMerge ) { + onMerge( ! isReverse ); + } + + // Only handle remove on Backspace. This serves dual-purpose of being + // an intentional user interaction distinguishing between Backspace and + // Delete to remove the empty field, but also to avoid merge & remove + // causing destruction of two fields (merge, then removed merged). + if ( onRemove && isEmpty( value ) && isReverse ) { + onRemove( ! isReverse ); + } + + event.preventDefault(); + } + } + + element.addEventListener( 'keydown', onKeyDown ); + return () => { + element.removeEventListener( 'keydown', onKeyDown ); + }; + }, [] ); +}