Skip to content

Commit

Permalink
Rich text: replace global event handlers with local ones (#34492)
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix committed Sep 2, 2021
1 parent f005524 commit e2e3f6b
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 40 deletions.
40 changes: 29 additions & 11 deletions packages/block-editor/src/components/rich-text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ import { omit } from 'lodash';
/**
* WordPress dependencies
*/
import { RawHTML, useRef, useCallback, forwardRef } from '@wordpress/element';
import {
RawHTML,
useRef,
useCallback,
forwardRef,
createContext,
} from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import { children as childrenSource } from '@wordpress/blocks';
import { useInstanceId, useMergeRefs } from '@wordpress/compose';
Expand Down Expand Up @@ -36,9 +42,14 @@ import { useInputRules } from './use-input-rules';
import { useEnter } from './use-enter';
import { useFormatTypes } from './use-format-types';
import { useRemoveBrowserShortcuts } from './use-remove-browser-shortcuts';
import { useShortcuts } from './use-shortcuts';
import { useInputEvents } from './use-input-events';
import FormatEdit from './format-edit';
import { getMultilineTag, getAllowedFormats } from './utils';

export const keyboardShortcutContext = createContext();
export const inputEventContext = createContext();

/**
* Removes props used for the native version of RichText so that they are not
* passed to the DOM element and log warnings.
Expand Down Expand Up @@ -244,6 +255,9 @@ function RichTextWrapper(
useCaretInFormat( { value } );
useMarkPersistent( { html: adjustedValue, value } );

const keyboardShortcuts = useRef( new Set() );
const inputEvents = useRef( new Set() );

function onKeyDown( event ) {
const { keyCode } = event;

Expand Down Expand Up @@ -286,17 +300,19 @@ function RichTextWrapper(
const TagName = tagName;
const content = (
<>
{ isSelected &&
children &&
children( { value, onChange, onFocus } ) }
{ isSelected && (
<FormatEdit
value={ value }
onChange={ onChange }
onFocus={ onFocus }
formatTypes={ formatTypes }
forwardedRef={ anchorRef }
/>
<keyboardShortcutContext.Provider value={ keyboardShortcuts }>
<inputEventContext.Provider value={ inputEvents }>
{ children && children( { value, onChange, onFocus } ) }
<FormatEdit
value={ value }
onChange={ onChange }
onFocus={ onFocus }
formatTypes={ formatTypes }
forwardedRef={ anchorRef }
/>
</inputEventContext.Provider>
</keyboardShortcutContext.Provider>
) }
{ isSelected && hasFormats && (
<FormatToolbarContainer
Expand All @@ -323,6 +339,8 @@ function RichTextWrapper(
onReplace,
} ),
useRemoveBrowserShortcuts(),
useShortcuts( keyboardShortcuts ),
useInputEvents( inputEvents ),
useUndoAutomaticChange(),
usePasteHandler( {
isSelected,
Expand Down
41 changes: 21 additions & 20 deletions packages/block-editor/src/components/rich-text/input-event.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { useEffect, useContext, useRef } from '@wordpress/element';

export class __unstableRichTextInputEvent extends Component {
constructor() {
super( ...arguments );
/**
* Internal dependencies
*/
import { inputEventContext } from './';

this.onInput = this.onInput.bind( this );
}
export function __unstableRichTextInputEvent( { inputType, onInput } ) {
const callbacks = useContext( inputEventContext );
const onInputRef = useRef();
onInputRef.current = onInput;

onInput( event ) {
if ( event.inputType === this.props.inputType ) {
this.props.onInput();
useEffect( () => {
function callback( event ) {
if ( event.inputType === inputType ) {
onInputRef.current();
event.preventDefault();
}
}
}

componentDidMount() {
document.addEventListener( 'input', this.onInput, true );
}

componentWillUnmount() {
document.removeEventListener( 'input', this.onInput, true );
}
callbacks.current.add( callback );
return () => {
callbacks.current.delete( callback );
};
}, [ inputType ] );

render() {
return null;
}
return null;
}
33 changes: 24 additions & 9 deletions packages/block-editor/src/components/rich-text/shortcut.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
/**
* WordPress dependencies
*/
import { useKeyboardShortcut } from '@wordpress/compose';
import { rawShortcut } from '@wordpress/keycodes';
import { isKeyboardEvent } from '@wordpress/keycodes';
import { useEffect, useContext, useRef } from '@wordpress/element';

/**
* Internal dependencies
*/
import { keyboardShortcutContext } from './';

export function RichTextShortcut( { character, type, onUse } ) {
const callback = () => {
onUse();
return false;
};
useKeyboardShortcut( rawShortcut[ type ]( character ), callback, {
bindGlobal: true,
} );
const keyboardShortcuts = useContext( keyboardShortcutContext );
const onUseRef = useRef();
onUseRef.current = onUse;

useEffect( () => {
function callback( event ) {
if ( isKeyboardEvent[ type ]( event, character ) ) {
onUseRef.current();
event.preventDefault();
}
}

keyboardShortcuts.current.add( callback );
return () => {
keyboardShortcuts.current.delete( callback );
};
}, [ character, type ] );

return null;
}
19 changes: 19 additions & 0 deletions packages/block-editor/src/components/rich-text/use-input-events.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* WordPress dependencies
*/
import { useRefEffect } from '@wordpress/compose';

export function useInputEvents( inputEvents ) {
return useRefEffect( ( element ) => {
function onInput( event ) {
for ( const keyboardShortcut of inputEvents.current ) {
keyboardShortcut( event );
}
}

element.addEventListener( 'input', onInput );
return () => {
element.removeEventListener( 'input', onInput );
};
}, [] );
}
19 changes: 19 additions & 0 deletions packages/block-editor/src/components/rich-text/use-shortcuts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* WordPress dependencies
*/
import { useRefEffect } from '@wordpress/compose';

export function useShortcuts( keyboardShortcuts ) {
return useRefEffect( ( element ) => {
function onKeyDown( event ) {
for ( const keyboardShortcut of keyboardShortcuts.current ) {
keyboardShortcut( event );
}
}

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

0 comments on commit e2e3f6b

Please sign in to comment.