@@ -4,6 +4,7 @@ import { caretPositionFromPoint, elementFromPoint } from '../../utils/src/polyfi
4
4
import { targetDeepest } from '../../utils/src/Dom' ;
5
5
import { nodeName , getDocument , nodeLength , isInstanceOf } from '../../utils/src/utils' ;
6
6
import { removeFormattingSpace } from '../../utils/src/formattingSpace' ;
7
+ import { isBlock } from '../../utils/src/isBlock' ;
7
8
8
9
const navigationKey = new Set ( [
9
10
'ArrowUp' ,
@@ -287,6 +288,7 @@ interface CurrentStackObservations {
287
288
keyboardSelectAll ?: CustomEvent ;
288
289
} ;
289
290
mutations : MutationRecord [ ] ;
291
+ flickeringRemoved : Set < EventToProcess > ;
290
292
}
291
293
292
294
/**
@@ -777,7 +779,10 @@ export class EventNormalizer {
777
779
// events occuring during one tick is not enough so we need to delay the
778
780
// analysis after we observe events during two ticks instead.
779
781
const needSecondTickObservation = currentStackObservation . _events . every ( ev => {
780
- return ! MultiStackEventTypes . includes ( ev . type ) ;
782
+ return (
783
+ ! MultiStackEventTypes . includes ( ev . type ) &&
784
+ ! currentStackObservation . flickeringRemoved . has ( ev )
785
+ ) ;
781
786
} ) ;
782
787
if ( needSecondTickObservation && ! secondTickObservation ) {
783
788
return await new Promise ( ( resolve ) : void => {
@@ -813,6 +818,7 @@ export class EventNormalizer {
813
818
const inferredKeydownEvent : InferredKeydownEvent =
814
819
keydownEvent &&
815
820
keydownEvent . key === 'Unidentified' &&
821
+ inputEvent &&
816
822
this . _inferKeydownEvent ( inputEvent ) ;
817
823
//
818
824
// First pass to get the informations
@@ -876,14 +882,18 @@ export class EventNormalizer {
876
882
// multiples keys pushed.
877
883
if ( currentStackObservation . _multiKeyStack . length > 1 && possibleMultiKeydown ) {
878
884
currentStackObservation . _multiKeyStack . map ( keydownMap => {
879
- const keyboardAction = this . _getKeyboardAction (
880
- currentStackObservation . mutations ,
881
- keydownMap . keydown . key ,
882
- ( keydownMap . input && keydownMap . input . inputType ) || '' ,
883
- ! ! mutatedElements . size ,
884
- ) ;
885
- if ( keyboardAction ) {
886
- normalizedActions . push ( keyboardAction ) ;
885
+ if (
886
+ mutatedElements . size ||
887
+ currentStackObservation . flickeringRemoved . has ( keydownMap . keydown )
888
+ ) {
889
+ const keyboardAction = this . _getKeyboardAction (
890
+ currentStackObservation . mutations ,
891
+ keydownMap . keydown . key ,
892
+ ( keydownMap . input && keydownMap . input . inputType ) || '' ,
893
+ ) ;
894
+ if ( keyboardAction ) {
895
+ normalizedActions . push ( keyboardAction ) ;
896
+ }
887
897
}
888
898
} ) ;
889
899
} else if ( cutEvent ) {
@@ -906,15 +916,19 @@ export class EventNormalizer {
906
916
normalizedActions . length === 0 &&
907
917
( ( ! compositionEvent && key ) || isCompositionKeyboard || isVirtualKeyboard )
908
918
) {
909
- const keyboardAction = this . _getKeyboardAction (
910
- currentStackObservation . mutations ,
911
- key ,
912
- inputType ,
913
- ! ! mutatedElements . size ,
914
- keydownEvent ,
915
- ) ;
916
- if ( keyboardAction ) {
917
- normalizedActions . push ( keyboardAction ) ;
919
+ if (
920
+ mutatedElements . size ||
921
+ currentStackObservation . flickeringRemoved . has ( keydownEvent )
922
+ ) {
923
+ const keyboardAction = this . _getKeyboardAction (
924
+ currentStackObservation . mutations ,
925
+ key ,
926
+ inputType ,
927
+ keydownEvent ,
928
+ ) ;
929
+ if ( keyboardAction ) {
930
+ normalizedActions . push ( keyboardAction ) ;
931
+ }
918
932
}
919
933
920
934
if ( compositionReplaceOneChar ) {
@@ -978,6 +992,7 @@ export class EventNormalizer {
978
992
_multiKeyStack : [ ] ,
979
993
_eventsMap : { } ,
980
994
mutations : undefined ,
995
+ flickeringRemoved : new Set ( ) ,
981
996
} ;
982
997
}
983
998
@@ -1042,7 +1057,6 @@ export class EventNormalizer {
1042
1057
mutations : MutationRecord [ ] ,
1043
1058
key : string ,
1044
1059
inputType : string ,
1045
- hasMutatedElements : boolean ,
1046
1060
keydownEvent ?: KeyboardEvent ,
1047
1061
) :
1048
1062
| InsertLineBreakAction
@@ -1051,8 +1065,7 @@ export class EventNormalizer {
1051
1065
| DeleteWordAction
1052
1066
| DeleteHardLineAction
1053
1067
| DeleteContentAction {
1054
- const isInsertOrRemoveAction = hasMutatedElements && ! inputTypeCommands . has ( inputType ) ;
1055
- if ( isInsertOrRemoveAction ) {
1068
+ if ( ! inputTypeCommands . has ( inputType ) ) {
1056
1069
if ( key === 'Backspace' || key === 'Delete' ) {
1057
1070
return this . _getRemoveAction ( mutations , key , inputType , keydownEvent ) ;
1058
1071
} else if ( key === 'Enter' ) {
@@ -1720,6 +1733,104 @@ export class EventNormalizer {
1720
1733
}
1721
1734
const [ offsetNode , offset ] = targetDeepest ( selection . anchorNode , selection . anchorOffset ) ;
1722
1735
this . _initialCaretPosition = { offsetNode, offset } ;
1736
+
1737
+ Promise . resolve ( ) . then ( ( ) => {
1738
+ // Wait the next micro task to allow the external features to
1739
+ // prevent the default and cancel the normalized keypress.
1740
+ if ( ! ev . defaultPrevented && this . _isSafeToPreventKeyboardEvent ( ev , selection ) ) {
1741
+ ev . preventDefault ( ) ;
1742
+ this . currentStackObservation . flickeringRemoved . add ( ev ) ;
1743
+ }
1744
+ } ) ;
1745
+ }
1746
+ /**
1747
+ * Check the selection to preventDefault events (Enter, Backspace, Delete)
1748
+ * and remove flickering without break the browser auto completion and
1749
+ * spellcheckers.
1750
+ *
1751
+ * @param {KeyboardEvent } ev
1752
+ */
1753
+ _isSafeToPreventKeyboardEvent ( ev : KeyboardEvent , selection : DomSelectionDescription ) : boolean {
1754
+ const code = ev . code || ev . key ;
1755
+ if (
1756
+ this . _modifierKeys . ctrlKey ||
1757
+ this . _modifierKeys . altKey ||
1758
+ this . _modifierKeys . metaKey ||
1759
+ this . _modifierKeys . shiftKey ||
1760
+ ( code !== 'Backspace' && code !== 'Delete' && code !== 'Enter' )
1761
+ ) {
1762
+ // If a control key is applied, you must let the browser apply its
1763
+ // own behavior because you do not know the command in advance.
1764
+ return false ;
1765
+ }
1766
+
1767
+ if (
1768
+ isBlock ( selection . anchorNode ) ||
1769
+ ( selection . anchorNode !== selection . focusNode && isBlock ( selection . focusNode ) )
1770
+ ) {
1771
+ // If the range is on a block, there is no risk of altering the
1772
+ // behavior of the spellcheckers.
1773
+ return true ;
1774
+ }
1775
+
1776
+ const previous = this . _geSiblingInlineText (
1777
+ selection . anchorNode ,
1778
+ selection . anchorOffset ,
1779
+ true ,
1780
+ ) ;
1781
+ const next = this . _geSiblingInlineText ( selection . focusNode , selection . focusOffset , false ) ;
1782
+ const regExpChar = / [ ^ \s \t \r \n \u00A0 \u200b ] / ;
1783
+
1784
+ if (
1785
+ code === 'Backspace' &&
1786
+ ! regExpChar . test ( previous . slice ( - 2 ) ) &&
1787
+ ! regExpChar . test ( next [ 0 ] || '' )
1788
+ ) {
1789
+ return true ;
1790
+ }
1791
+ if (
1792
+ code === 'Delete' &&
1793
+ ! regExpChar . test ( previous [ previous . length - 1 ] || '' ) &&
1794
+ ! regExpChar . test ( next . slice ( 0 , 2 ) )
1795
+ ) {
1796
+ return true ;
1797
+ }
1798
+ if ( code === 'Enter' && ! regExpChar . test ( previous . slice ( - 2 ) ) ) {
1799
+ return true ;
1800
+ }
1801
+ }
1802
+ /**
1803
+ * Return the sibling text passing through inlines formatting.
1804
+ *
1805
+ * @param {KeyboardEvent } ev
1806
+ */
1807
+ _geSiblingInlineText ( node : Node , offset : number , before = true ) : string {
1808
+ let text = '' ;
1809
+ while ( node ) {
1810
+ if ( node . nodeType === Node . TEXT_NODE ) {
1811
+ if ( before ) {
1812
+ text += node . textContent . slice ( 0 , offset ) ;
1813
+ } else {
1814
+ text += node . textContent . slice ( offset ) ;
1815
+ }
1816
+ } else if ( isBlock ( node ) ) {
1817
+ return text ;
1818
+ } else {
1819
+ text += node . textContent ;
1820
+ }
1821
+ let sibling = node [ before ? 'previousSibling' : 'nextSibling' ] ;
1822
+ while ( node && ! sibling && node . parentNode ) {
1823
+ if ( isBlock ( node . parentNode ) ) {
1824
+ return text ;
1825
+ } else {
1826
+ node = node . parentNode ;
1827
+ sibling = node [ before ? 'previousSibling' : 'nextSibling' ] ;
1828
+ }
1829
+ }
1830
+ node = sibling ;
1831
+ offset = before ? nodeLength ( sibling ) : 0 ;
1832
+ }
1833
+ return text ;
1723
1834
}
1724
1835
/**
1725
1836
* Set internal properties of the pointer down event to retrieve them later
0 commit comments