Skip to content

Commit

Permalink
Classic block: use hooks (#25737)
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix authored Sep 30, 2020
1 parent 48cfd5f commit 72f6885
Showing 1 changed file with 165 additions and 171 deletions.
336 changes: 165 additions & 171 deletions packages/block-library/src/classic/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { debounce } from 'lodash';
*/
import { BlockControls } from '@wordpress/block-editor';
import { ToolbarGroup } from '@wordpress/components';
import { Component } from '@wordpress/element';
import { useEffect, useRef } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { BACKSPACE, DELETE, F10, isKeyboardEvent } from '@wordpress/keycodes';

Expand All @@ -35,211 +35,205 @@ function isTmceEmpty( editor ) {
return /^\n?$/.test( body.innerText || body.textContent );
}

export default class ClassicEdit extends Component {
constructor( props ) {
super( props );
this.initialize = this.initialize.bind( this );
this.onSetup = this.onSetup.bind( this );
this.focus = this.focus.bind( this );
}
export default function ClassicEdit( {
clientId,
attributes: { content },
setAttributes,
onReplace,
} ) {
const didMount = useRef( false );

useEffect( () => {
if ( ! didMount.current ) {
return;
}

const editor = window.tinymce.get( `editor-${ clientId }` );
const currentContent = editor?.getContent();

if ( currentContent !== content ) {
editor.setContent( content || '' );
}
}, [ content ] );

componentDidMount() {
useEffect( () => {
const { baseURL, suffix } = window.wpEditorL10n.tinymce;

didMount.current = true;

window.tinymce.EditorManager.overrideDefaults( {
base_url: baseURL,
suffix,
} );

if ( document.readyState === 'complete' ) {
this.initialize();
} else {
document.addEventListener( 'readystatechange', () => {
if ( document.readyState === 'complete' ) {
this.initialize();
}
} );
}
}

componentWillUnmount() {
window.addEventListener( 'DOMContentLoaded', this.initialize );
wp.oldEditor.remove( `editor-${ this.props.clientId }` );
}
function onSetup( editor ) {
let bookmark;

componentDidUpdate( prevProps ) {
const {
clientId,
attributes: { content },
} = this.props;
if ( content ) {
editor.on( 'loadContent', () => editor.setContent( content ) );
}

const editor = window.tinymce.get( `editor-${ clientId }` );
const currentContent = editor?.getContent();
editor.on( 'blur', () => {
bookmark = editor.selection.getBookmark( 2, true );
// There is an issue with Chrome and the editor.focus call in core at https://core.trac.wordpress.org/browser/trunk/src/js/_enqueues/lib/link.js#L451.
// This causes a scroll to the top of editor content on return from some content updating dialogs so tracking
// scroll position until this is fixed in core.
const scrollContainer = document.querySelector(
'.interface-interface-skeleton__content'
);
const scrollPosition = scrollContainer.scrollTop;

if (
prevProps.attributes.content !== content &&
currentContent !== content
) {
editor.setContent( content || '' );
}
}
setAttributes( {
content: editor.getContent(),
} );

initialize() {
const { clientId } = this.props;
const { settings } = window.wpEditorL10n.tinymce;
wp.oldEditor.initialize( `editor-${ clientId }`, {
tinymce: {
...settings,
inline: true,
content_css: false,
fixed_toolbar_container: `#toolbar-${ clientId }`,
setup: this.onSetup,
},
} );
}
editor.once( 'focus', () => {
if ( bookmark ) {
editor.selection.moveToBookmark( bookmark );
if ( scrollContainer.scrollTop !== scrollPosition ) {
scrollContainer.scrollTop = scrollPosition;
}
}
} );

onSetup( editor ) {
const {
attributes: { content },
setAttributes,
} = this.props;
let bookmark;
return false;
} );

this.editor = editor;
editor.on( 'mousedown touchstart', () => {
bookmark = null;
} );

if ( content ) {
editor.on( 'loadContent', () => editor.setContent( content ) );
}
const debouncedOnChange = debounce( () => {
const value = editor.getContent();

editor.on( 'blur', () => {
bookmark = editor.selection.getBookmark( 2, true );
// There is an issue with Chrome and the editor.focus call in core at https://core.trac.wordpress.org/browser/trunk/src/js/_enqueues/lib/link.js#L451.
// This causes a scroll to the top of editor content on return from some content updating dialogs so tracking
// scroll position until this is fixed in core.
const scrollContainer = document.querySelector(
'.interface-interface-skeleton__content'
);
const scrollPosition = scrollContainer.scrollTop;
if ( value !== editor._lastChange ) {
editor._lastChange = value;
setAttributes( {
content: value,
} );
}
}, 250 );
editor.on( 'Paste Change input Undo Redo', debouncedOnChange );

// We need to cancel the debounce call because when we remove
// the editor (onUnmount) this callback is executed in
// another tick. This results in setting the content to empty.
editor.on( 'remove', debouncedOnChange.cancel );

editor.on( 'keydown', ( event ) => {
if ( isKeyboardEvent.primary( event, 'z' ) ) {
// Prevent the gutenberg undo kicking in so TinyMCE undo stack works as expected
event.stopPropagation();
}

setAttributes( {
content: editor.getContent(),
} );
if (
( event.keyCode === BACKSPACE ||
event.keyCode === DELETE ) &&
isTmceEmpty( editor )
) {
// delete the block
onReplace( [] );
event.preventDefault();
event.stopImmediatePropagation();
}

editor.once( 'focus', () => {
if ( bookmark ) {
editor.selection.moveToBookmark( bookmark );
if ( scrollContainer.scrollTop !== scrollPosition ) {
scrollContainer.scrollTop = scrollPosition;
}
const { altKey } = event;
/*
* Prevent Mousetrap from kicking in: TinyMCE already uses its own
* `alt+f10` shortcut to focus its toolbar.
*/
if ( altKey && event.keyCode === F10 ) {
event.stopPropagation();
}
} );

return false;
} );

editor.on( 'mousedown touchstart', () => {
bookmark = null;
} );

const debouncedOnChange = debounce( () => {
const value = editor.getContent();
editor.on( 'init', () => {
const rootNode = editor.getBody();

if ( value !== editor._lastChange ) {
editor._lastChange = value;
setAttributes( {
content: value,
} );
}
}, 250 );
editor.on( 'Paste Change input Undo Redo', debouncedOnChange );

// We need to cancel the debounce call because when we remove
// the editor (onUnmount) this callback is executed in
// another tick. This results in setting the content to empty.
editor.on( 'remove', debouncedOnChange.cancel );

editor.on( 'keydown', ( event ) => {
if ( isKeyboardEvent.primary( event, 'z' ) ) {
// Prevent the gutenberg undo kicking in so TinyMCE undo stack works as expected
event.stopPropagation();
}
// Create the toolbar by refocussing the editor.
if ( rootNode.ownerDocument.activeElement === rootNode ) {
rootNode.blur();
editor.focus();
}
} );
}

if (
( event.keyCode === BACKSPACE || event.keyCode === DELETE ) &&
isTmceEmpty( editor )
) {
// delete the block
this.props.onReplace( [] );
event.preventDefault();
event.stopImmediatePropagation();
}
function initialize() {
const { settings } = window.wpEditorL10n.tinymce;
wp.oldEditor.initialize( `editor-${ clientId }`, {
tinymce: {
...settings,
inline: true,
content_css: false,
fixed_toolbar_container: `#toolbar-${ clientId }`,
setup: onSetup,
},
} );
}

const { altKey } = event;
/*
* Prevent Mousetrap from kicking in: TinyMCE already uses its own
* `alt+f10` shortcut to focus its toolbar.
*/
if ( altKey && event.keyCode === F10 ) {
event.stopPropagation();
function onReadyStateChange() {
if ( document.readyState === 'complete' ) {
initialize();
}
} );
}

editor.on( 'init', () => {
const rootNode = this.editor.getBody();
if ( document.readyState === 'complete' ) {
initialize();
} else {
document.addEventListener( 'readystatechange', onReadyStateChange );
}

// Create the toolbar by refocussing the editor.
if ( rootNode.ownerDocument.activeElement === rootNode ) {
rootNode.blur();
this.editor.focus();
}
} );
}
return () => {
document.removeEventListener(
'readystatechange',
onReadyStateChange
);
wp.oldEditor.remove( `editor-${ clientId }` );
};
}, [] );

focus() {
if ( this.editor ) {
this.editor.focus();
function focus() {
const editor = window.tinymce.get( `editor-${ clientId }` );
if ( editor ) {
editor.focus();
}
}

onToolbarKeyDown( event ) {
function onToolbarKeyDown( event ) {
// Prevent WritingFlow from kicking in and allow arrows navigation on the toolbar.
event.stopPropagation();
// Prevent Mousetrap from moving focus to the top toolbar when pressing `alt+f10` on this block toolbar.
event.nativeEvent.stopImmediatePropagation();
}

render() {
const { clientId } = this.props;

// Disable reasons:
//
// jsx-a11y/no-static-element-interactions
// - the toolbar itself is non-interactive, but must capture events
// from the KeyboardShortcuts component to stop their propagation.

/* eslint-disable jsx-a11y/no-static-element-interactions */
return (
<>
<BlockControls>
<ToolbarGroup>
<ConvertToBlocksButton clientId={ clientId } />
</ToolbarGroup>
</BlockControls>
<div
key="toolbar"
id={ `toolbar-${ clientId }` }
className="block-library-classic__toolbar"
onClick={ this.focus }
data-placeholder={ __( 'Classic' ) }
onKeyDown={ this.onToolbarKeyDown }
/>
<div
key="editor"
id={ `editor-${ clientId }` }
className="wp-block-freeform block-library-rich-text__tinymce"
/>
</>
);
/* eslint-enable jsx-a11y/no-static-element-interactions */
}
// Disable reasons:
//
// jsx-a11y/no-static-element-interactions
// - the toolbar itself is non-interactive, but must capture events
// from the KeyboardShortcuts component to stop their propagation.

/* eslint-disable jsx-a11y/no-static-element-interactions */
return (
<>
<BlockControls>
<ToolbarGroup>
<ConvertToBlocksButton clientId={ clientId } />
</ToolbarGroup>
</BlockControls>
<div
key="toolbar"
id={ `toolbar-${ clientId }` }
className="block-library-classic__toolbar"
onClick={ focus }
data-placeholder={ __( 'Classic' ) }
onKeyDown={ onToolbarKeyDown }
/>
<div
key="editor"
id={ `editor-${ clientId }` }
className="wp-block-freeform block-library-rich-text__tinymce"
/>
</>
);
/* eslint-enable jsx-a11y/no-static-element-interactions */
}

0 comments on commit 72f6885

Please sign in to comment.