Skip to content
This repository was archived by the owner on May 5, 2021. It is now read-only.

Commit 9e61869

Browse files
Gorashdmo-odoo
authored andcommitted
[FIX] DomLayout,KeyMap: remove flickering on delete, backspace, enter...
Adds a level in keymap in order to define default behavior which will be overridden by customization and plugins behavior.
1 parent 4537bd8 commit 9e61869

File tree

4 files changed

+804
-308
lines changed

4 files changed

+804
-308
lines changed

packages/plugin-dom-editable/src/EventNormalizer.ts

Lines changed: 132 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { caretPositionFromPoint, elementFromPoint } from '../../utils/src/polyfi
44
import { targetDeepest } from '../../utils/src/Dom';
55
import { nodeName, getDocument, nodeLength, isInstanceOf } from '../../utils/src/utils';
66
import { removeFormattingSpace } from '../../utils/src/formattingSpace';
7+
import { isBlock } from '../../utils/src/isBlock';
78

89
const navigationKey = new Set([
910
'ArrowUp',
@@ -287,6 +288,7 @@ interface CurrentStackObservations {
287288
keyboardSelectAll?: CustomEvent;
288289
};
289290
mutations: MutationRecord[];
291+
flickeringRemoved: Set<EventToProcess>;
290292
}
291293

292294
/**
@@ -777,7 +779,10 @@ export class EventNormalizer {
777779
// events occuring during one tick is not enough so we need to delay the
778780
// analysis after we observe events during two ticks instead.
779781
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+
);
781786
});
782787
if (needSecondTickObservation && !secondTickObservation) {
783788
return await new Promise((resolve): void => {
@@ -813,6 +818,7 @@ export class EventNormalizer {
813818
const inferredKeydownEvent: InferredKeydownEvent =
814819
keydownEvent &&
815820
keydownEvent.key === 'Unidentified' &&
821+
inputEvent &&
816822
this._inferKeydownEvent(inputEvent);
817823
//
818824
// First pass to get the informations
@@ -876,14 +882,18 @@ export class EventNormalizer {
876882
// multiples keys pushed.
877883
if (currentStackObservation._multiKeyStack.length > 1 && possibleMultiKeydown) {
878884
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+
}
887897
}
888898
});
889899
} else if (cutEvent) {
@@ -906,15 +916,19 @@ export class EventNormalizer {
906916
normalizedActions.length === 0 &&
907917
((!compositionEvent && key) || isCompositionKeyboard || isVirtualKeyboard)
908918
) {
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+
}
918932
}
919933

920934
if (compositionReplaceOneChar) {
@@ -978,6 +992,7 @@ export class EventNormalizer {
978992
_multiKeyStack: [],
979993
_eventsMap: {},
980994
mutations: undefined,
995+
flickeringRemoved: new Set(),
981996
};
982997
}
983998

@@ -1042,7 +1057,6 @@ export class EventNormalizer {
10421057
mutations: MutationRecord[],
10431058
key: string,
10441059
inputType: string,
1045-
hasMutatedElements: boolean,
10461060
keydownEvent?: KeyboardEvent,
10471061
):
10481062
| InsertLineBreakAction
@@ -1051,8 +1065,7 @@ export class EventNormalizer {
10511065
| DeleteWordAction
10521066
| DeleteHardLineAction
10531067
| DeleteContentAction {
1054-
const isInsertOrRemoveAction = hasMutatedElements && !inputTypeCommands.has(inputType);
1055-
if (isInsertOrRemoveAction) {
1068+
if (!inputTypeCommands.has(inputType)) {
10561069
if (key === 'Backspace' || key === 'Delete') {
10571070
return this._getRemoveAction(mutations, key, inputType, keydownEvent);
10581071
} else if (key === 'Enter') {
@@ -1720,6 +1733,104 @@ export class EventNormalizer {
17201733
}
17211734
const [offsetNode, offset] = targetDeepest(selection.anchorNode, selection.anchorOffset);
17221735
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;
17231834
}
17241835
/**
17251836
* Set internal properties of the pointer down event to retrieve them later

0 commit comments

Comments
 (0)