@@ -15,34 +15,26 @@ import { I18nMixin } from '@vaadin/component-base/src/i18n-mixin.js';
1515
1616const Quill = window . Quill ;
1717
18- // Workaround for text disappearing when accepting spellcheck suggestion
19- // See https://github.com/quilljs/quill/issues/2096#issuecomment-399576957
20- const Inline = Quill . import ( 'blots/inline' ) ;
21-
22- class CustomColor extends Inline {
23- constructor ( domNode , value ) {
24- super ( domNode , value ) ;
25-
26- // Map <font> properties
27- domNode . style . color = domNode . color ;
28-
29- const span = this . replaceWith ( new Inline ( Inline . create ( ) ) ) ;
30-
31- span . children . forEach ( ( child ) => {
32- if ( child . attributes ) child . attributes . copy ( span ) ;
33- if ( child . unwrap ) child . unwrap ( ) ;
34- } ) ;
35-
36- this . remove ( ) ;
37-
38- return span ; // eslint-disable-line no-constructor-return
18+ // There are some issues e.g. `spellcheck="false"` not preserved
19+ // See https://github.com/slab/quill/issues/4289
20+ // Fix to add `spellcheck="false"` on the `<pre>` tag removed by Quill
21+ const QuillCodeBlockContainer = Quill . import ( 'formats/code-block-container' ) ;
22+
23+ class CodeBlockContainer extends QuillCodeBlockContainer {
24+ html ( index , length ) {
25+ const markup = super . html ( index , length ) ;
26+ const tempDiv = document . createElement ( 'div' ) ;
27+ tempDiv . innerHTML = markup ;
28+ const preTag = tempDiv . querySelector ( 'pre' ) ;
29+ if ( preTag ) {
30+ preTag . setAttribute ( 'spellcheck' , 'false' ) ;
31+ return preTag . outerHTML ;
32+ }
33+ return markup ; // fallback
3934 }
4035}
4136
42- CustomColor . blotName = 'customColor' ;
43- CustomColor . tagName = 'FONT' ;
44-
45- Quill . register ( CustomColor , true ) ;
37+ Quill . register ( 'formats/code-block-container' , CodeBlockContainer , true ) ;
4638
4739const HANDLERS = [
4840 'bold' ,
@@ -69,8 +61,6 @@ const STATE = {
6961 CLICKED : 2 ,
7062} ;
7163
72- const TAB_KEY = 9 ;
73-
7464const DEFAULT_I18N = {
7565 undo : 'undo' ,
7666 redo : 'redo' ,
@@ -374,23 +364,21 @@ export const RichTextEditorMixin = (superClass) =>
374364 }
375365 } ) ;
376366
377- const TAB_KEY = 9 ;
378-
379367 editorContent . addEventListener ( 'keydown' , ( e ) => {
380368 if ( e . key === 'Escape' ) {
381369 if ( ! this . __tabBindings ) {
382- this . __tabBindings = this . _editor . keyboard . bindings [ TAB_KEY ] ;
383- this . _editor . keyboard . bindings [ TAB_KEY ] = null ;
370+ this . __tabBindings = this . _editor . keyboard . bindings . Tab ;
371+ this . _editor . keyboard . bindings . Tab = null ;
384372 }
385373 } else if ( this . __tabBindings ) {
386- this . _editor . keyboard . bindings [ TAB_KEY ] = this . __tabBindings ;
374+ this . _editor . keyboard . bindings . Tab = this . __tabBindings ;
387375 this . __tabBindings = null ;
388376 }
389377 } ) ;
390378
391379 editorContent . addEventListener ( 'blur' , ( ) => {
392380 if ( this . __tabBindings ) {
393- this . _editor . keyboard . bindings [ TAB_KEY ] = this . __tabBindings ;
381+ this . _editor . keyboard . bindings . Tab = this . __tabBindings ;
394382 this . __tabBindings = null ;
395383 }
396384 } ) ;
@@ -496,7 +484,7 @@ export const RichTextEditorMixin = (superClass) =>
496484 buttons [ index ] . focus ( ) ;
497485 }
498486 // Esc and Tab focuses the content
499- if ( e . keyCode === 27 || ( e . keyCode === TAB_KEY && ! e . shiftKey ) ) {
487+ if ( e . keyCode === 27 || ( e . key === 'Tab' && ! e . shiftKey ) ) {
500488 e . preventDefault ( ) ;
501489 this . _editor . focus ( ) ;
502490 }
@@ -552,19 +540,19 @@ export const RichTextEditorMixin = (superClass) =>
552540 this . _toolbar . querySelector ( 'button:not([tabindex])' ) . focus ( ) ;
553541 } ;
554542
555- const keyboard = this . _editor . getModule ( ' keyboard' ) ;
556- const bindings = keyboard . bindings [ TAB_KEY ] ;
543+ const keyboard = this . _editor . keyboard ;
544+ const bindings = keyboard . bindings . Tab ;
557545
558546 // Exclude Quill shift-tab bindings, except for code block,
559547 // as some of those are breaking when on a newline in the list
560548 // https://github.com/vaadin/vaadin-rich-text-editor/issues/67
561549 const originalBindings = bindings . filter ( ( b ) => ! b . shiftKey || ( b . format && b . format [ 'code-block' ] ) ) ;
562- const moveFocusBinding = { key : TAB_KEY , shiftKey : true , handler : focusToolbar } ;
550+ const moveFocusBinding = { key : 'Tab' , shiftKey : true , handler : focusToolbar } ;
563551
564- keyboard . bindings [ TAB_KEY ] = [ ...originalBindings , moveFocusBinding ] ;
552+ keyboard . bindings . Tab = [ ...originalBindings , moveFocusBinding ] ;
565553
566554 // Alt-f10 focuses a toolbar button
567- keyboard . addBinding ( { key : 121 , altKey : true , handler : focusToolbar } ) ;
555+ keyboard . addBinding ( { key : 'F10' , altKey : true , handler : focusToolbar } ) ;
568556 }
569557
570558 /** @private */
@@ -603,6 +591,7 @@ export const RichTextEditorMixin = (superClass) =>
603591 _applyLink ( link ) {
604592 if ( link ) {
605593 this . _markToolbarClicked ( ) ;
594+ this . _editor . focus ( ) ;
606595 this . _editor . format ( 'link' , link , SOURCE . USER ) ;
607596 this . _editor . getModule ( 'toolbar' ) . update ( this . _editor . selection . savedRange ) ;
608597 }
@@ -686,6 +675,7 @@ export const RichTextEditorMixin = (superClass) =>
686675 const color = event . detail . color ;
687676 this . _colorValue = color === '#000000' ? null : color ;
688677 this . _markToolbarClicked ( ) ;
678+ this . _editor . focus ( ) ;
689679 this . _editor . format ( 'color' , this . _colorValue , SOURCE . USER ) ;
690680 this . _toolbar . style . setProperty ( '--_color-value' , this . _colorValue ) ;
691681 this . _colorEditing = false ;
@@ -701,36 +691,23 @@ export const RichTextEditorMixin = (superClass) =>
701691 const color = event . detail . color ;
702692 this . _backgroundValue = color === '#ffffff' ? null : color ;
703693 this . _markToolbarClicked ( ) ;
694+ this . _editor . focus ( ) ;
704695 this . _editor . format ( 'background' , this . _backgroundValue , SOURCE . USER ) ;
705696 this . _toolbar . style . setProperty ( '--_background-value' , this . _backgroundValue ) ;
706697 this . _backgroundEditing = false ;
707698 }
708699
709700 /** @private */
710701 __updateHtmlValue ( ) {
711- const editor = this . shadowRoot . querySelector ( '.ql-editor' ) ;
712- let content = editor . innerHTML ;
713-
714- // Remove Quill classes, e.g. ql-syntax, except for align
715- content = content . replace ( / c l a s s = " ( [ ^ " ] * ) " / gu, ( _match , group1 ) => {
716- const classes = group1 . split ( ' ' ) . filter ( ( className ) => {
717- return ! className . startsWith ( 'ql-' ) || className . startsWith ( 'ql-align' ) ;
718- } ) ;
719- return `class="${ classes . join ( ' ' ) } "` ;
720- } ) ;
721- // Remove meta spans, e.g. cursor which are empty after Quill classes removed
722- content = content . replace ( / < s p a n [ ^ > ] * > < \/ s p a n > / gu, '' ) ;
723-
702+ // We have to use this instead of `innerHTML` to get correct tags like `<pre>` etc.
703+ let content = this . _editor . getSemanticHTML ( ) ;
724704 // Replace Quill align classes with inline styles
725705 [ this . __dir === 'rtl' ? 'left' : 'right' , 'center' , 'justify' ] . forEach ( ( align ) => {
726706 content = content . replace (
727707 new RegExp ( ` class=[\\\\]?"\\s?ql-align-${ align } [\\\\]?"` , 'gu' ) ,
728708 ` style="text-align: ${ align } "` ,
729709 ) ;
730710 } ) ;
731-
732- content = content . replace ( / c l a s s = " " / gu, '' ) ;
733-
734711 this . _setHtmlValue ( content ) ;
735712 }
736713
@@ -778,7 +755,7 @@ export const RichTextEditorMixin = (superClass) =>
778755 htmlValue = htmlValue . replaceAll ( / > [ ^ < ] * < / gu, ( match ) => match . replaceAll ( character , replacement ) ) ; // NOSONAR
779756 } ) ;
780757
781- const deltaFromHtml = this . _editor . clipboard . convert ( htmlValue ) ;
758+ const deltaFromHtml = this . _editor . clipboard . convert ( { html : htmlValue } ) ;
782759
783760 // Restore whitespace characters after the conversion
784761 Object . entries ( whitespaceCharacters ) . forEach ( ( [ character , replacement ] ) => {
0 commit comments