From 0592f0245dc589b0b7216f20bc27420e62df2c33 Mon Sep 17 00:00:00 2001 From: Zihua Li Date: Mon, 20 Nov 2023 20:18:30 +0800 Subject: [PATCH 1/2] Handle multiple navigation shortcuts --- modules/uiNode.ts | 7 ++++-- test/unit/modules/uiNode.spec.ts | 41 ++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 test/unit/modules/uiNode.spec.ts diff --git a/modules/uiNode.ts b/modules/uiNode.ts index 488eb2cb2e..4b88b1a24c 100644 --- a/modules/uiNode.ts +++ b/modules/uiNode.ts @@ -4,6 +4,9 @@ import Quill from '../core/quill'; const isMac = /Mac/i.test(navigator.platform); +// Export for testing +export const TTL_FOR_VALID_SELECTION_CHANGE = 100; + // A loose check to determine if the shortcut can move the caret before a UI node: // [CARET]
[CONTENT]
const canMoveCaretBeforeUINode = (event: KeyboardEvent) => { @@ -78,10 +81,10 @@ class UINode extends Module { * the selection within the handler of a `selectionchange` event. */ private ensureListeningToSelectionChange() { - if (this.isListening) return; + this.selectionChangeDeadline = Date.now() + TTL_FOR_VALID_SELECTION_CHANGE; + if (this.isListening) return; this.isListening = true; - this.selectionChangeDeadline = Date.now() + 100; const listener = () => { this.isListening = false; diff --git a/test/unit/modules/uiNode.spec.ts b/test/unit/modules/uiNode.spec.ts new file mode 100644 index 0000000000..3f271b565c --- /dev/null +++ b/test/unit/modules/uiNode.spec.ts @@ -0,0 +1,41 @@ +import '../../../quill'; +import { describe, expect, test } from 'vitest'; +import UINode, { + TTL_FOR_VALID_SELECTION_CHANGE, +} from '../../../modules/uiNode'; +import Quill, { Delta } from '../../../core'; + +// Fake timer is not supported in browser mode yet. +const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +describe('uiNode', () => { + test('extends deadline when multiple possible shortcuts are pressed', async () => { + const quill = new Quill(document.createElement('div')); + document.body.appendChild(quill.container); + quill.setContents( + new Delta().insert('item 1').insert('\n', { list: 'bullet' }), + ); + new UINode(quill, {}); + + for (let i = 0; i < 2; i += 1) { + quill.root.dispatchEvent( + new KeyboardEvent('keydown', { key: 'ArrowRight', metaKey: true }), + ); + await delay(TTL_FOR_VALID_SELECTION_CHANGE / 2); + } + + quill.root.dispatchEvent( + new KeyboardEvent('keydown', { key: 'ArrowLeft', metaKey: true }), + ); + const range = document.createRange(); + range.setStart(quill.root.querySelector('li')!, 0); + range.setEnd(quill.root.querySelector('li')!, 0); + + const selection = document.getSelection(); + selection?.removeAllRanges(); + selection?.addRange(range); + + await delay(TTL_FOR_VALID_SELECTION_CHANGE / 2); + expect(selection?.getRangeAt(0).startOffset).toEqual(1); + }); +}); From be0306ee177ac7fbdfb90e8da6d69bdbf42136d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=8B=E9=93=84=E8=BF=90=20=28Alan=20Song=29?= Date: Mon, 20 Nov 2023 06:16:26 -0800 Subject: [PATCH 2/2] Switch to lodash-es for small bundle size (#3910) --- core/editor.ts | 4 +-- core/quill.ts | 3 +- core/selection.ts | 3 +- modules/keyboard.ts | 3 +- package-lock.json | 72 ++++++++++++++------------------------------- package.json | 8 ++--- themes/base.ts | 2 +- themes/bubble.ts | 2 +- themes/snow.ts | 2 +- 9 files changed, 31 insertions(+), 68 deletions(-) diff --git a/core/editor.ts b/core/editor.ts index 20ffb9f3d2..edf35ba58b 100644 --- a/core/editor.ts +++ b/core/editor.ts @@ -1,6 +1,4 @@ -import cloneDeep from 'lodash.clonedeep'; -import isEqual from 'lodash.isequal'; -import merge from 'lodash.merge'; +import { cloneDeep, isEqual, merge } from 'lodash-es'; import { LeafBlot, EmbedBlot, Scope, ParentBlot } from 'parchment'; import type { Blot } from 'parchment'; import Delta, { AttributeMap, Op } from 'quill-delta'; diff --git a/core/quill.ts b/core/quill.ts index ae53eec3c5..f005594e98 100644 --- a/core/quill.ts +++ b/core/quill.ts @@ -1,5 +1,4 @@ -import cloneDeep from 'lodash.clonedeep'; -import merge from 'lodash.merge'; +import { cloneDeep, merge } from 'lodash-es'; import * as Parchment from 'parchment'; import type { Op } from 'quill-delta'; import Delta from 'quill-delta'; diff --git a/core/selection.ts b/core/selection.ts index 9d37ee9425..fe52e266d3 100644 --- a/core/selection.ts +++ b/core/selection.ts @@ -1,6 +1,5 @@ import { LeafBlot, Scope } from 'parchment'; -import cloneDeep from 'lodash.clonedeep'; -import isEqual from 'lodash.isequal'; +import { cloneDeep, isEqual } from 'lodash-es'; import Emitter from './emitter'; import type { EmitterSource } from './emitter'; import logger from './logger'; diff --git a/modules/keyboard.ts b/modules/keyboard.ts index d238e49b06..77e95b890b 100644 --- a/modules/keyboard.ts +++ b/modules/keyboard.ts @@ -1,5 +1,4 @@ -import cloneDeep from 'lodash.clonedeep'; -import isEqual from 'lodash.isequal'; +import { cloneDeep, isEqual } from 'lodash-es'; import Delta, { AttributeMap } from 'quill-delta'; import { EmbedBlot, Scope, TextBlot } from 'parchment'; import type { Blot, BlockBlot } from 'parchment'; diff --git a/package-lock.json b/package-lock.json index 41dd6295a2..2def434b37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,9 +13,6 @@ ], "dependencies": { "eventemitter3": "^4.0.7", - "lodash.clonedeep": "^4.5.0", - "lodash.isequal": "^4.5.0", - "lodash.merge": "^4.5.0", "parchment": "^3.0.0-alpha.1", "quill-delta": "^5.1.0" }, @@ -24,9 +21,7 @@ "@babel/preset-env": "^7.22.5", "@babel/preset-typescript": "^7.22.5", "@playwright/test": "^1.39.0", - "@types/lodash.clonedeep": "^4.5.7", - "@types/lodash.isequal": "^4.5.6", - "@types/lodash.merge": "^4.6.7", + "@types/lodash-es": "^4.17.11", "@typescript-eslint/eslint-plugin": "^6.9.1", "@typescript-eslint/parser": "^6.9.1", "@vitest/browser": "^0.34.6", @@ -44,6 +39,7 @@ "http-proxy": "^1.18.0", "jsdom": "^22.1.0", "lodash": "^4.17.15", + "lodash-es": "^4.17.21", "mini-css-extract-plugin": "^2.7.6", "npm-run-all": "^4.1.5", "prettier": "^3.0.3", @@ -4800,28 +4796,10 @@ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.186.tgz", "integrity": "sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==" }, - "node_modules/@types/lodash.clonedeep": { - "version": "4.5.7", - "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.7.tgz", - "integrity": "sha512-ccNqkPptFIXrpVqUECi60/DFxjNKsfoQxSQsgcBJCX/fuX1wgyQieojkcWH/KpE3xzLoWN/2k+ZeGqIN3paSvw==", - "dev": true, - "dependencies": { - "@types/lodash": "*" - } - }, - "node_modules/@types/lodash.isequal": { - "version": "4.5.6", - "resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.6.tgz", - "integrity": "sha512-Ww4UGSe3DmtvLLJm2F16hDwEQSv7U0Rr8SujLUA2wHI2D2dm8kPu6Et+/y303LfjTIwSBKXB/YTUcAKpem/XEg==", - "dev": true, - "dependencies": { - "@types/lodash": "*" - } - }, - "node_modules/@types/lodash.merge": { - "version": "4.6.7", - "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.7.tgz", - "integrity": "sha512-OwxUJ9E50gw3LnAefSHJPHaBLGEKmQBQ7CZe/xflHkyy/wH2zVyEIAKReHvVrrn7zKdF58p16We9kMfh7v0RRQ==", + "node_modules/@types/lodash-es": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.11.tgz", + "integrity": "sha512-eCw8FYAWHt2DDl77s+AMLLzPn310LKohruumpucZI4oOFJkIgnlaJcy23OKMJxx4r9PeTF13Gv6w+jqjWQaYUg==", "dev": true, "dependencies": { "@types/lodash": "*" @@ -15237,6 +15215,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -27223,28 +27207,10 @@ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.186.tgz", "integrity": "sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==" }, - "@types/lodash.clonedeep": { - "version": "4.5.7", - "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.7.tgz", - "integrity": "sha512-ccNqkPptFIXrpVqUECi60/DFxjNKsfoQxSQsgcBJCX/fuX1wgyQieojkcWH/KpE3xzLoWN/2k+ZeGqIN3paSvw==", - "dev": true, - "requires": { - "@types/lodash": "*" - } - }, - "@types/lodash.isequal": { - "version": "4.5.6", - "resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.6.tgz", - "integrity": "sha512-Ww4UGSe3DmtvLLJm2F16hDwEQSv7U0Rr8SujLUA2wHI2D2dm8kPu6Et+/y303LfjTIwSBKXB/YTUcAKpem/XEg==", - "dev": true, - "requires": { - "@types/lodash": "*" - } - }, - "@types/lodash.merge": { - "version": "4.6.7", - "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.7.tgz", - "integrity": "sha512-OwxUJ9E50gw3LnAefSHJPHaBLGEKmQBQ7CZe/xflHkyy/wH2zVyEIAKReHvVrrn7zKdF58p16We9kMfh7v0RRQ==", + "@types/lodash-es": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.11.tgz", + "integrity": "sha512-eCw8FYAWHt2DDl77s+AMLLzPn310LKohruumpucZI4oOFJkIgnlaJcy23OKMJxx4r9PeTF13Gv6w+jqjWQaYUg==", "dev": true, "requires": { "@types/lodash": "*" @@ -34860,6 +34826,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true + }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", diff --git a/package.json b/package.json index 43045faaac..3358ea691a 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,6 @@ ], "dependencies": { "eventemitter3": "^4.0.7", - "lodash.clonedeep": "^4.5.0", - "lodash.isequal": "^4.5.0", - "lodash.merge": "^4.5.0", "parchment": "^3.0.0-alpha.1", "quill-delta": "^5.1.0" }, @@ -28,9 +25,7 @@ "@babel/preset-env": "^7.22.5", "@babel/preset-typescript": "^7.22.5", "@playwright/test": "^1.39.0", - "@types/lodash.clonedeep": "^4.5.7", - "@types/lodash.isequal": "^4.5.6", - "@types/lodash.merge": "^4.6.7", + "@types/lodash-es": "^4.17.11", "@typescript-eslint/eslint-plugin": "^6.9.1", "@typescript-eslint/parser": "^6.9.1", "@vitest/browser": "^0.34.6", @@ -48,6 +43,7 @@ "http-proxy": "^1.18.0", "jsdom": "^22.1.0", "lodash": "^4.17.15", + "lodash-es": "^4.17.21", "mini-css-extract-plugin": "^2.7.6", "npm-run-all": "^4.1.5", "prettier": "^3.0.3", diff --git a/themes/base.ts b/themes/base.ts index d7609a5990..bf1621a86b 100644 --- a/themes/base.ts +++ b/themes/base.ts @@ -1,4 +1,4 @@ -import merge from 'lodash.merge'; +import { merge } from 'lodash-es'; import type Quill from '../core/quill'; import Emitter from '../core/emitter'; import Theme from '../core/theme'; diff --git a/themes/bubble.ts b/themes/bubble.ts index 6c0fcd5525..9f06992b81 100644 --- a/themes/bubble.ts +++ b/themes/bubble.ts @@ -1,4 +1,4 @@ -import merge from 'lodash.merge'; +import { merge } from 'lodash-es'; import Emitter from '../core/emitter'; import BaseTheme, { BaseTooltip } from './base'; import { Range } from '../core/selection'; diff --git a/themes/snow.ts b/themes/snow.ts index f80eedb63f..820ca25640 100644 --- a/themes/snow.ts +++ b/themes/snow.ts @@ -1,4 +1,4 @@ -import merge from 'lodash.merge'; +import { merge } from 'lodash-es'; import Emitter from '../core/emitter'; import BaseTheme, { BaseTooltip } from './base'; import LinkBlot from '../formats/link';