Skip to content

Commit

Permalink
Rich text: only selectively handle keyup/pointerup (#48385)
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix authored Feb 24, 2023
1 parent 8164936 commit bf2efa5
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 25 deletions.
2 changes: 2 additions & 0 deletions packages/rich-text/src/component/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { useCopyHandler } from './use-copy-handler';
import { useFormatBoundaries } from './use-format-boundaries';
import { useSelectObject } from './use-select-object';
import { useInputAndSelection } from './use-input-and-selection';
import { useSelectionChangeCompat } from './use-selection-change-compat';
import { useDelete } from './use-delete';

export function useRichText( {
Expand Down Expand Up @@ -240,6 +241,7 @@ export function useRichText( {
isSelected,
onSelectionChange,
} ),
useSelectionChangeCompat(),
useRefEffect( () => {
applyFromProps();
didMount.current = true;
Expand Down
29 changes: 4 additions & 25 deletions packages/rich-text/src/component/use-input-and-selection.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,18 +113,11 @@ export function useInputAndSelection( props ) {

/**
* Syncs the selection to local state. A callback for the
* `selectionchange`, `keyup`, `mouseup` and `touchend` events.
*
* @param {Event} event
* `selectionchange` event.
*/
function handleSelectionChange( event ) {
const {
record,
applyRecord,
createRecord,
isSelected,
onSelectionChange,
} = propsRef.current;
function handleSelectionChange() {
const { record, applyRecord, createRecord, onSelectionChange } =
propsRef.current;

// Check if the implementor disabled editing. `contentEditable`
// does disable input, but not text selection, so we must ignore
Expand Down Expand Up @@ -178,10 +171,6 @@ export function useInputAndSelection( props ) {
return;
}

if ( event.type !== 'selectionchange' && ! isSelected ) {
return;
}

// In case of a keyboard event, ignore selection changes during
// composition.
if ( isComposing ) {
Expand Down Expand Up @@ -295,13 +284,6 @@ export function useInputAndSelection( props ) {
element.addEventListener( 'compositionstart', onCompositionStart );
element.addEventListener( 'compositionend', onCompositionEnd );
element.addEventListener( 'focus', onFocus );
// Selection updates must be done at these events as they
// happen before the `selectionchange` event. In some cases,
// the `selectionchange` event may not even fire, for
// example when the window receives focus again on click.
element.addEventListener( 'keyup', handleSelectionChange );
element.addEventListener( 'mouseup', handleSelectionChange );
element.addEventListener( 'touchend', handleSelectionChange );
ownerDocument.addEventListener(
'selectionchange',
handleSelectionChange
Expand All @@ -314,9 +296,6 @@ export function useInputAndSelection( props ) {
);
element.removeEventListener( 'compositionend', onCompositionEnd );
element.removeEventListener( 'focus', onFocus );
element.removeEventListener( 'keyup', handleSelectionChange );
element.removeEventListener( 'mouseup', handleSelectionChange );
element.removeEventListener( 'touchend', handleSelectionChange );
ownerDocument.removeEventListener(
'selectionchange',
handleSelectionChange
Expand Down
59 changes: 59 additions & 0 deletions packages/rich-text/src/component/use-selection-change-compat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* WordPress dependencies
*/
import { useRefEffect } from '@wordpress/compose';

/**
* Sometimes some browsers are not firing a `selectionchange` event when
* changing the selection by mouse or keyboard. This hook makes sure that, if we
* detect no `selectionchange` or `input` event between the up and down events,
* we fire a `selectionchange` event.
*
* @return {import('@wordpress/compose').RefEffect} A ref effect attaching the
* listeners.
*/
export function useSelectionChangeCompat() {
return useRefEffect( ( element ) => {
const { ownerDocument } = element;
const { defaultView } = ownerDocument;
const selection = defaultView.getSelection();

let range;

function getRange() {
return selection.rangeCount ? selection.getRangeAt( 0 ) : null;
}

function onDown( event ) {
const type = event.type === 'keydown' ? 'keyup' : 'pointerup';

function onCancel() {
ownerDocument.removeEventListener( type, onUp );
ownerDocument.removeEventListener(
'selectionchange',
onCancel
);
ownerDocument.removeEventListener( 'input', onCancel );
}

function onUp() {
onCancel();
if ( range === getRange() ) return;
ownerDocument.dispatchEvent( new Event( 'selectionchange' ) );
}

ownerDocument.addEventListener( type, onUp );
ownerDocument.addEventListener( 'selectionchange', onCancel );
ownerDocument.addEventListener( 'input', onCancel );

range = getRange();
}

element.addEventListener( 'pointerdown', onDown );
element.addEventListener( 'keydown', onDown );
return () => {
element.removeEventListener( 'pointerdown', onDown );
element.removeEventListener( 'keydown', onDown );
};
}, [] );
}

1 comment on commit bf2efa5

@github-actions