Skip to content
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

Rich text: move autocomplete key handler to ref callback #31770

Merged
merged 5 commits into from
May 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 17 additions & 67 deletions packages/block-editor/src/components/rich-text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,19 @@ import { omit } from 'lodash';
*/
import { RawHTML, useRef, useCallback, forwardRef } from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import {
children as childrenSource,
getBlockTransforms,
findTransform,
} from '@wordpress/blocks';
import { children as childrenSource } from '@wordpress/blocks';
import { useInstanceId, useMergeRefs } from '@wordpress/compose';
import {
__unstableUseRichText as useRichText,
__unstableCreateElement,
isEmpty,
__unstableIsEmptyLine as isEmptyLine,
insert,
__unstableInsertLineSeparator as insertLineSeparator,
split,
toHTMLString,
isCollapsed,
removeFormat,
} from '@wordpress/rich-text';
import deprecated from '@wordpress/deprecated';
import { BACKSPACE, DELETE, ENTER } from '@wordpress/keycodes';
import { BACKSPACE, DELETE } from '@wordpress/keycodes';

/**
* Internal dependencies
Expand All @@ -43,6 +36,7 @@ import { useCaretInFormat } from './use-caret-in-format';
import { useMarkPersistent } from './use-mark-persistent';
import { usePasteHandler } from './use-paste-handler';
import { useInputRules } from './use-input-rules';
import { useEnter } from './use-enter';
import { useFormatTypes } from './use-format-types';
import FormatEdit from './format-edit';
import { getMultilineTag, getAllowedFormats } from './utils';
Expand Down Expand Up @@ -119,9 +113,7 @@ function RichTextWrapper(
const { selectionStart, selectionEnd, isSelected, disabled } = useSelect(
selector
);
const { selectionChange, __unstableMarkAutomaticChange } = useDispatch(
blockEditorStore
);
const { selectionChange } = useDispatch( blockEditorStore );
const multilineTag = getMultilineTag( multiline );
const adjustedAllowedFormats = getAllowedFormats( {
allowedFormats,
Expand Down Expand Up @@ -319,57 +311,7 @@ function RichTextWrapper(
return;
}

if ( event.keyCode === ENTER ) {
event.preventDefault();

const _value = { ...value };
_value.formats = removeEditorOnlyFormats( value );
const canSplit = onReplace && onSplit;

if ( onReplace ) {
const transforms = getBlockTransforms( 'from' ).filter(
( { type } ) => type === 'enter'
);
const transformation = findTransform( transforms, ( item ) => {
return item.regExp.test( _value.text );
} );

if ( transformation ) {
onReplace( [
transformation.transform( {
content: _value.text,
} ),
] );
__unstableMarkAutomaticChange();
}
}

if ( multiline ) {
if ( event.shiftKey ) {
if ( ! disableLineBreaks ) {
onChange( insert( _value, '\n' ) );
}
} else if ( canSplit && isEmptyLine( _value ) ) {
splitValue( _value );
} else {
onChange( insertLineSeparator( _value ) );
}
} else {
const { text, start, end } = _value;
const canSplitAtEnd =
onSplitAtEnd && start === end && end === text.length;

if ( event.shiftKey || ( ! canSplit && ! canSplitAtEnd ) ) {
if ( ! disableLineBreaks ) {
onChange( insert( _value, '\n' ) );
}
} else if ( ! canSplit && canSplitAtEnd ) {
onSplitAtEnd();
} else if ( canSplit ) {
splitValue( _value );
}
}
} else if ( keyCode === DELETE || keyCode === BACKSPACE ) {
if ( keyCode === DELETE || keyCode === BACKSPACE ) {
const { start, end, text } = value;
const isReverse = keyCode === BACKSPACE;

Expand Down Expand Up @@ -454,6 +396,17 @@ function RichTextWrapper(
preserveWhiteSpace,
pastePlainText,
} ),
useEnter( {
removeEditorOnlyFormats,
value,
onReplace,
onSplit,
multiline,
onChange,
disableLineBreaks,
splitValue,
onSplitAtEnd,
} ),
anchorRef,
forwardedRef,
] ) }
Expand All @@ -466,10 +419,7 @@ function RichTextWrapper(
'rich-text'
) }
onFocus={ unstableOnFocus }
onKeyDown={ ( event ) => {
autocompleteProps.onKeyDown( event );
onKeyDown( event );
} }
onKeyDown={ onKeyDown }
/>
</>
);
Expand Down
105 changes: 105 additions & 0 deletions packages/block-editor/src/components/rich-text/use-enter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* WordPress dependencies
*/
/**
* WordPress dependencies
*/
import { useRef } from '@wordpress/element';
import { useRefEffect } from '@wordpress/compose';
import { ENTER } from '@wordpress/keycodes';
import {
insert,
__unstableIsEmptyLine as isEmptyLine,
__unstableInsertLineSeparator as insertLineSeparator,
} from '@wordpress/rich-text';
import { getBlockTransforms, findTransform } from '@wordpress/blocks';
import { useDispatch } from '@wordpress/data';

/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';

export function useEnter( props ) {
const { __unstableMarkAutomaticChange } = useDispatch( blockEditorStore );
const propsRef = useRef( props );
propsRef.current = props;
return useRefEffect( ( element ) => {
function onKeyDown( event ) {
if ( event.defaultPrevented ) {
return;
}

const {
removeEditorOnlyFormats,
value,
onReplace,
onSplit,
multiline,
onChange,
disableLineBreaks,
splitValue,
onSplitAtEnd,
} = propsRef.current;

if ( event.keyCode !== ENTER ) {
return;
}

event.preventDefault();

const _value = { ...value };
_value.formats = removeEditorOnlyFormats( value );
const canSplit = onReplace && onSplit;

if ( onReplace ) {
const transforms = getBlockTransforms( 'from' ).filter(
( { type } ) => type === 'enter'
);
const transformation = findTransform( transforms, ( item ) => {
return item.regExp.test( _value.text );
} );

if ( transformation ) {
onReplace( [
transformation.transform( {
content: _value.text,
} ),
] );
__unstableMarkAutomaticChange();
}
}

if ( multiline ) {
if ( event.shiftKey ) {
if ( ! disableLineBreaks ) {
onChange( insert( _value, '\n' ) );
}
} else if ( canSplit && isEmptyLine( _value ) ) {
splitValue( _value );
} else {
onChange( insertLineSeparator( _value ) );
}
} else {
const { text, start, end } = _value;
const canSplitAtEnd =
onSplitAtEnd && start === end && end === text.length;

if ( event.shiftKey || ( ! canSplit && ! canSplitAtEnd ) ) {
if ( ! disableLineBreaks ) {
onChange( insert( _value, '\n' ) );
}
} else if ( ! canSplit && canSplitAtEnd ) {
onSplitAtEnd();
} else if ( canSplit ) {
splitValue( _value );
}
}
}

element.addEventListener( 'keydown', onKeyDown );
return () => {
element.removeEventListener( 'keydown', onKeyDown );
};
}, [] );
}
24 changes: 20 additions & 4 deletions packages/components/src/autocomplete/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ import {
BACKSPACE,
} from '@wordpress/keycodes';
import { __, _n, sprintf } from '@wordpress/i18n';
import { useInstanceId, useDebounce } from '@wordpress/compose';
import {
useInstanceId,
useDebounce,
useMergeRefs,
useRefEffect,
} from '@wordpress/compose';
import {
create,
slice,
Expand Down Expand Up @@ -565,15 +570,26 @@ function useAutocomplete( {

export function useAutocompleteProps( options ) {
const ref = useRef();
const onKeyDownRef = useRef();
const { popover, listBoxId, activeId, onKeyDown } = useAutocomplete( {
...options,
contentRef: ref,
} );

onKeyDownRef.current = onKeyDown;
return {
ref,
ref: useMergeRefs( [
ref,
useRefEffect( ( element ) => {
function _onKeyDown( event ) {
onKeyDownRef.current( event );
}
element.addEventListener( 'keydown', _onKeyDown );
return () => {
element.removeEventListener( 'keydown', _onKeyDown );
};
}, [] ),
] ),
children: popover,
onKeyDown,
'aria-autocomplete': listBoxId ? 'list' : undefined,
'aria-owns': listBoxId,
'aria-activedescendant': activeId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ async function getSelectedFlatIndices() {
* Tests if the native selection matches the block selection.
*/
async function testNativeSelection() {
// Wait for the selection to update.
await page.evaluate( () => new Promise( window.requestAnimationFrame ) );
// Wait for the selection to update and async mode to update classes of
// deselected blocks.
await page.evaluate( () => new Promise( window.requestIdleCallback ) );
await page.evaluate( () => {
const selection = window.getSelection();
const elements = Array.from(
Expand Down
31 changes: 16 additions & 15 deletions packages/rich-text/src/component/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,20 @@ export function useRichText( {
record.current.end = selectionEnd;
}

const hadSelectionUpdate = useRef( false );

if ( ! record.current ) {
setRecordFromProps();
} else if (
selectionStart !== record.current.start ||
selectionEnd !== record.current.end
) {
hadSelectionUpdate.current = isSelected;
record.current = {
...record.current,
start: selectionStart,
end: selectionEnd,
};
}

/**
Expand Down Expand Up @@ -155,24 +167,13 @@ export function useRichText( {

// Value updates must happen synchonously to avoid overwriting newer values.
useLayoutEffect( () => {
if ( ! didMount.current ) {
if ( ! hadSelectionUpdate.current ) {
return;
}

if (
isSelected &&
( selectionStart !== record.current.start ||
selectionEnd !== record.current.end )
) {
applyFromProps();
} else {
record.current = {
...record.current,
start: selectionStart,
end: selectionEnd,
};
}
}, [ selectionStart, selectionEnd, isSelected ] );
applyFromProps();
hadSelectionUpdate.current = false;
}, [ hadSelectionUpdate.current ] );

function focus() {
ref.current.focus();
Expand Down