@@ -15,34 +15,26 @@ import { I18nMixin } from '@vaadin/component-base/src/i18n-mixin.js';
15
15
16
16
const Quill = window . Quill ;
17
17
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
39
34
}
40
35
}
41
36
42
- CustomColor . blotName = 'customColor' ;
43
- CustomColor . tagName = 'FONT' ;
44
-
45
- Quill . register ( CustomColor , true ) ;
37
+ Quill . register ( 'formats/code-block-container' , CodeBlockContainer , true ) ;
46
38
47
39
const HANDLERS = [
48
40
'bold' ,
@@ -69,8 +61,6 @@ const STATE = {
69
61
CLICKED : 2 ,
70
62
} ;
71
63
72
- const TAB_KEY = 9 ;
73
-
74
64
const DEFAULT_I18N = {
75
65
undo : 'undo' ,
76
66
redo : 'redo' ,
@@ -374,23 +364,21 @@ export const RichTextEditorMixin = (superClass) =>
374
364
}
375
365
} ) ;
376
366
377
- const TAB_KEY = 9 ;
378
-
379
367
editorContent . addEventListener ( 'keydown' , ( e ) => {
380
368
if ( e . key === 'Escape' ) {
381
369
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 ;
384
372
}
385
373
} else if ( this . __tabBindings ) {
386
- this . _editor . keyboard . bindings [ TAB_KEY ] = this . __tabBindings ;
374
+ this . _editor . keyboard . bindings . Tab = this . __tabBindings ;
387
375
this . __tabBindings = null ;
388
376
}
389
377
} ) ;
390
378
391
379
editorContent . addEventListener ( 'blur' , ( ) => {
392
380
if ( this . __tabBindings ) {
393
- this . _editor . keyboard . bindings [ TAB_KEY ] = this . __tabBindings ;
381
+ this . _editor . keyboard . bindings . Tab = this . __tabBindings ;
394
382
this . __tabBindings = null ;
395
383
}
396
384
} ) ;
@@ -496,7 +484,7 @@ export const RichTextEditorMixin = (superClass) =>
496
484
buttons [ index ] . focus ( ) ;
497
485
}
498
486
// 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 ) ) {
500
488
e . preventDefault ( ) ;
501
489
this . _editor . focus ( ) ;
502
490
}
@@ -552,19 +540,19 @@ export const RichTextEditorMixin = (superClass) =>
552
540
this . _toolbar . querySelector ( 'button:not([tabindex])' ) . focus ( ) ;
553
541
} ;
554
542
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 ;
557
545
558
546
// Exclude Quill shift-tab bindings, except for code block,
559
547
// as some of those are breaking when on a newline in the list
560
548
// https://github.com/vaadin/vaadin-rich-text-editor/issues/67
561
549
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 } ;
563
551
564
- keyboard . bindings [ TAB_KEY ] = [ ...originalBindings , moveFocusBinding ] ;
552
+ keyboard . bindings . Tab = [ ...originalBindings , moveFocusBinding ] ;
565
553
566
554
// Alt-f10 focuses a toolbar button
567
- keyboard . addBinding ( { key : 121 , altKey : true , handler : focusToolbar } ) ;
555
+ keyboard . addBinding ( { key : 'F10' , altKey : true , handler : focusToolbar } ) ;
568
556
}
569
557
570
558
/** @private */
@@ -603,6 +591,7 @@ export const RichTextEditorMixin = (superClass) =>
603
591
_applyLink ( link ) {
604
592
if ( link ) {
605
593
this . _markToolbarClicked ( ) ;
594
+ this . _editor . focus ( ) ;
606
595
this . _editor . format ( 'link' , link , SOURCE . USER ) ;
607
596
this . _editor . getModule ( 'toolbar' ) . update ( this . _editor . selection . savedRange ) ;
608
597
}
@@ -686,6 +675,7 @@ export const RichTextEditorMixin = (superClass) =>
686
675
const color = event . detail . color ;
687
676
this . _colorValue = color === '#000000' ? null : color ;
688
677
this . _markToolbarClicked ( ) ;
678
+ this . _editor . focus ( ) ;
689
679
this . _editor . format ( 'color' , this . _colorValue , SOURCE . USER ) ;
690
680
this . _toolbar . style . setProperty ( '--_color-value' , this . _colorValue ) ;
691
681
this . _colorEditing = false ;
@@ -701,36 +691,23 @@ export const RichTextEditorMixin = (superClass) =>
701
691
const color = event . detail . color ;
702
692
this . _backgroundValue = color === '#ffffff' ? null : color ;
703
693
this . _markToolbarClicked ( ) ;
694
+ this . _editor . focus ( ) ;
704
695
this . _editor . format ( 'background' , this . _backgroundValue , SOURCE . USER ) ;
705
696
this . _toolbar . style . setProperty ( '--_background-value' , this . _backgroundValue ) ;
706
697
this . _backgroundEditing = false ;
707
698
}
708
699
709
700
/** @private */
710
701
__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 ( ) ;
724
704
// Replace Quill align classes with inline styles
725
705
[ this . __dir === 'rtl' ? 'left' : 'right' , 'center' , 'justify' ] . forEach ( ( align ) => {
726
706
content = content . replace (
727
707
new RegExp ( ` class=[\\\\]?"\\s?ql-align-${ align } [\\\\]?"` , 'gu' ) ,
728
708
` style="text-align: ${ align } "` ,
729
709
) ;
730
710
} ) ;
731
-
732
- content = content . replace ( / c l a s s = " " / gu, '' ) ;
733
-
734
711
this . _setHtmlValue ( content ) ;
735
712
}
736
713
@@ -778,7 +755,7 @@ export const RichTextEditorMixin = (superClass) =>
778
755
htmlValue = htmlValue . replaceAll ( / > [ ^ < ] * < / gu, ( match ) => match . replaceAll ( character , replacement ) ) ; // NOSONAR
779
756
} ) ;
780
757
781
- const deltaFromHtml = this . _editor . clipboard . convert ( htmlValue ) ;
758
+ const deltaFromHtml = this . _editor . clipboard . convert ( { html : htmlValue } ) ;
782
759
783
760
// Restore whitespace characters after the conversion
784
761
Object . entries ( whitespaceCharacters ) . forEach ( ( [ character , replacement ] ) => {
0 commit comments