diff --git a/blocks/api/raw-handling/image-corrector.js b/blocks/api/raw-handling/image-corrector.js index 142cfdcdd89947..1533c6cf225848 100644 --- a/blocks/api/raw-handling/image-corrector.js +++ b/blocks/api/raw-handling/image-corrector.js @@ -57,4 +57,9 @@ export default function( node ) { if ( node.height === 1 || node.width === 1 ) { node.parentNode.removeChild( node ); } + + // Restore emoji as images. + if ( node.className === 'emoji' ) { + node.parentNode.replaceChild( document.createTextNode( node.alt ), node ); + } } diff --git a/blocks/api/raw-handling/test/image-corrector.js b/blocks/api/raw-handling/test/image-corrector.js index 93f11687e7c486..452ae38048d3b3 100644 --- a/blocks/api/raw-handling/test/image-corrector.js +++ b/blocks/api/raw-handling/test/image-corrector.js @@ -21,4 +21,10 @@ describe( 'imageCorrector', () => { const output = ''; equal( deepFilterHTML( input, [ imageCorrector ] ), output ); } ); + + it( 'should restore emoji', () => { + const input = '🍒'; + const output = '🍒'; + equal( deepFilterHTML( input, [ imageCorrector ] ), output ); + } ); } ); diff --git a/blocks/editable/emoji.js b/blocks/editable/emoji.js new file mode 100644 index 00000000000000..930a840e2c45f7 --- /dev/null +++ b/blocks/editable/emoji.js @@ -0,0 +1,56 @@ +/** + * Browser dependencies + */ +const { wp } = window; + +export default function( editor ) { + const mceEmojiAttrs = { + 'data-mce-resize': 'false', + 'data-mce-placeholder': '1', + 'data-wp-emoji': '1', + }; + + let isTyping = false; + + editor.on( 'keydown keyup', ( { type } ) => { + isTyping = ( type === 'keydown' ); + } ); + + function parse( { withBookmark } ) { + if ( ! wp.emoji ) { + return; + } + + const node = editor.selection.getNode(); + + if ( ! wp.emoji.test( node.textContent ) ) { + return; + } + + const bookmark = withBookmark ? editor.selection.getBookmark() : null; + + wp.emoji.parse( node, { imgAttr: mceEmojiAttrs } ); + + if ( bookmark ) { + editor.selection.moveToBookmark( bookmark ); + } + } + + // Most browsers trigger input, but no typing events, on inserting emoji. + editor.on( 'input', () => { + if ( ! isTyping ) { + parse( { withBookmark: true } ); + } + } ); + + // Windows 8+ triggers normal keyboard events with keyCode 231. + editor.on( 'keyup', ( { keyCode } ) => { + if ( keyCode === 231 ) { + parse( { withBookmark: true } ); + } + } ); + + editor.on( 'setcontent', ( { selection } ) => { + parse( { withBookmark: selection } ); + } ); +} diff --git a/blocks/editable/index.js b/blocks/editable/index.js index f8059dbcd8bb91..61576aba8d8770 100644 --- a/blocks/editable/index.js +++ b/blocks/editable/index.js @@ -32,6 +32,7 @@ import { rawHandler } from '../api'; import FormatToolbar from './format-toolbar'; import TinyMCE from './tinymce'; import { pickAriaProps } from './aria'; +import emoji from './emoji'; import patterns from './patterns'; import { EVENTS } from './constants'; @@ -46,6 +47,10 @@ function createTinyMCEElement( type, props, ...children ) { return children; } + if ( props.hasOwnProperty( 'data-wp-emoji' ) ) { + return props.alt; + } + return createElement( type, omitBy( props, ( value, key ) => key.indexOf( 'data-mce-' ) === 0 ), @@ -136,6 +141,7 @@ export default class Editable extends Component { editor.on( 'PastePreProcess', this.onPastePreProcess, true /* Add before core handlers */ ); editor.on( 'paste', this.onPaste, true /* Add before core handlers */ ); + emoji.apply( this, [ editor ] ); patterns.apply( this, [ editor ] ); if ( this.props.onSetup ) {