Skip to content

Commit

Permalink
Merge pull request #8 from miroapp/MDOCS-1393-3
Browse files Browse the repository at this point in the history
MDOCS-1393
  • Loading branch information
bmakuh authored Jan 27, 2025
2 parents d684abf + e2d6de2 commit 436e25e
Show file tree
Hide file tree
Showing 24 changed files with 217 additions and 96 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ on:
default: true
required: true

permissions:
contents: write

jobs:
test:
uses: ./.github/workflows/_test.yml
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "quill-monorepo",
"version": "2.0.2",
"version": "2.0.3",
"description": "Quill development environment",
"private": true,
"author": "Jason Chen <[email protected]>",
Expand Down
2 changes: 1 addition & 1 deletion packages/quill/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "quill",
"version": "2.0.2",
"version": "2.0.3",
"description": "Your powerful, rich text editor",
"author": "Jason Chen <[email protected]>",
"homepage": "https://quilljs.com",
Expand Down
3 changes: 3 additions & 0 deletions packages/quill/src/blots/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import Delta from 'quill-delta';
import Break from './break.js';
import Inline from './inline.js';
import TextBlot from './text.js';
import type Scroll from './scroll.js';

const NEWLINE_LENGTH = 1;

class Block extends BlockBlot {
scroll: Scroll;
cache: { delta?: Delta | null; length?: number } = {};

delta(): Delta {
Expand Down Expand Up @@ -129,6 +131,7 @@ Block.defaultChild = Break;
Block.allowedChildren = [Break, Inline, EmbedBlot, TextBlot];

class BlockEmbed extends EmbedBlot {
scroll: Scroll;
attributes: AttributorStore;
domNode: HTMLElement;

Expand Down
2 changes: 2 additions & 0 deletions packages/quill/src/blots/embed.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ScrollBlot } from 'parchment';
import { EmbedBlot } from 'parchment';
import TextBlot from './text.js';
import type Scroll from './scroll.js';

const GUARD_TEXT = '\uFEFF';

Expand All @@ -12,6 +13,7 @@ export interface EmbedContextRange {
}

class Embed extends EmbedBlot {
scroll: Scroll;
contentNode: HTMLSpanElement;
leftGuard: Text;
rightGuard: Text;
Expand Down
5 changes: 4 additions & 1 deletion packages/quill/src/blots/scroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,11 @@ class Scroll extends ScrollBlot {
if (!Array.isArray(mutations)) {
mutations = this.observer.takeRecords();
}
mutations = mutations.filter(({ target }) => {
mutations = mutations.filter(({ target, type }) => {
const blot = this.find(target, true);
if (type === 'attributes' && target === this.scroll.domNode) {
return false;
}
return blot && !isUpdatable(blot);
});
if (mutations.length > 0) {
Expand Down
25 changes: 12 additions & 13 deletions packages/quill/src/core/composition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@ class Composition {
}
});

this.scroll.domNode.addEventListener('compositionupdate', () => {
if (!this.scroll.batch) {
this.handleCompositionUpdate();
this.scroll.domNode.addEventListener('compositionupdate', (event) => {
if (this.isComposing) {
return;
}
this.handleCompositionUpdate(event);
});

this.scroll.domNode.addEventListener('compositionend', (event) => {
if (this.isComposing) {
// HACK: There is a bug in the safari browser in mobile devices and when we finish typing
// composition symbol MutationObserver dispatches part of events after firing compositionend event
// In normal behaviour MutationObserver dispatches all event before firing compositionend event
// https://bugs.webkit.org/show_bug.cgi?id=238013
// Webkit makes DOM changes after compositionend, so we use microtask to
// ensure the order.
// https://bugs.webkit.org/show_bug.cgi?id=31902
Expand All @@ -51,20 +56,14 @@ class Composition {
}
}

private handleCompositionUpdate() {
this.emitter.emit(Emitter.events.COMPOSITION_UPDATE);
private handleCompositionUpdate(event: CompositionEvent) {
this.emitter.emit(Emitter.events.COMPOSITION_UPDATE, event);
}

private handleCompositionEnd(event: CompositionEvent) {
this.emitter.emit(Emitter.events.COMPOSITION_BEFORE_END, event);
// HACK: There is a bug in the safari browser in mobile devices and when we finish typing
// composition symbol MutationObserver dispatches part of events after firing compositionend event
// In normal behaviour MutationObserver dispatches all event before firing compositionend event
// https://bugs.webkit.org/show_bug.cgi?id=238013
setTimeout(() => {
this.scroll.batchEnd();
this.emitter.emit(Emitter.events.COMPOSITION_END, event);
}, 0);
this.scroll.batchEnd();
this.emitter.emit(Emitter.events.COMPOSITION_END, event);
this.isComposing = false;
}
}
Expand Down
3 changes: 2 additions & 1 deletion packages/quill/src/core/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,8 @@ function convertHTML(
return blot.html(index, length) ?? '';
}
if (blot instanceof TextBlot) {
return escapeText(blot.value().slice(index, index + length));
const escapedText = escapeText(blot.value().slice(index, index + length));
return escapedText.replaceAll(' ', '&nbsp;');
}
if (blot instanceof ParentBlot) {
// TODO fix API
Expand Down
14 changes: 11 additions & 3 deletions packages/quill/src/core/quill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class Quill {
static import(name: `themes/${string}`): typeof Theme;
static import(name: 'parchment'): typeof Parchment;
static import(name: 'delta'): typeof Delta;
static import(name: string): unknown;
static import<T = unknown>(name: string): T | undefined;
static import(name: string) {
if (this.imports[name] == null) {
debug.error(`Cannot import ${name}. Are you sure it was registered?`);
Expand Down Expand Up @@ -237,6 +237,12 @@ class Quill {
this.root.classList.toggle('ql-blank', this.editor.isBlank());
}
});
this.emitter.on(Emitter.events.COMPOSITION_START, () => {
this.root.classList.toggle('ql-blank', this.editor.isBlank());
});
this.emitter.on(Emitter.events.COMPOSITION_END, () => {
this.root.classList.toggle('ql-blank', this.editor.isBlank());
});
this.emitter.on(Emitter.events.SCROLL_UPDATE, (source, mutations) => {
const oldRange = this.selection.lastRange;
const [newRange] = this.selection.getRange();
Expand Down Expand Up @@ -532,8 +538,10 @@ class Quill {
return this.scroll.lines(index, length);
}

getModule(name: string) {
return this.theme.modules[name];
getModule<T = unknown>(name: string): T | undefined {
if (this.theme.modules[name] !== undefined) {
return this.theme.modules[name] as T;
}
}

getSelection(focus: true): Range;
Expand Down
5 changes: 5 additions & 0 deletions packages/quill/src/core/selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type Cursor from '../blots/cursor.js';
import type Scroll from '../blots/scroll.js';

const debug = logger('quill:selection');
const COMPOSITION_CLASS_NAME = 'ql-composition';

type NativeRange = AbstractRange;

Expand Down Expand Up @@ -113,6 +114,7 @@ class Selection {
handleComposition() {
this.emitter.on(Emitter.events.COMPOSITION_BEFORE_START, () => {
this.composing = true;
this.root.classList.add(COMPOSITION_CLASS_NAME);
});
this.emitter.on(Emitter.events.COMPOSITION_UPDATE, () => {
if (this.cursor.parent) {
Expand All @@ -126,6 +128,9 @@ class Selection {
);
}
});
this.emitter.on(Emitter.events.COMPOSITION_BEFORE_END, () => {
this.root.classList.remove(COMPOSITION_CLASS_NAME);
});
this.emitter.on(Emitter.events.COMPOSITION_END, () => {
this.composing = false;
if (this.cursor.parent) {
Expand Down
25 changes: 13 additions & 12 deletions packages/quill/src/modules/clipboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const STYLE_ATTRIBUTORS = [
return memo;
}, {});

interface ClipboardOptions {
export interface ClipboardOptions {
matchers: [Selector, Matcher][];
}

Expand Down Expand Up @@ -173,7 +173,7 @@ class Clipboard extends Module<ClipboardOptions> {
e.preventDefault();
const [range] = this.quill.selection.getRange();
if (range == null) return;
const { html, text } = this.onCopy(range, isCut);
const { html, text } = this.onCopy(range);
e.clipboardData?.setData('text/plain', text);
e.clipboardData?.setData('text/html', html);
if (isCut) {
Expand Down Expand Up @@ -225,7 +225,6 @@ class Clipboard extends Module<ClipboardOptions> {
this.onPaste(range, { html, text });
}

onCopy(range: Range, isCut: boolean): { html: string; text: string };
onCopy(range: Range) {
const text = this.quill.getText(range);
const html = this.quill.getSemanticHTML(range);
Expand Down Expand Up @@ -626,7 +625,7 @@ function matchTable(

function matchText(node: HTMLElement, delta: Delta, scroll: ScrollBlot) {
// @ts-expect-error
let text = node.data;
let text = node.data as string;
// Word represents empty line with <o:p>&nbsp;</o:p>
if (node.parentElement?.tagName === 'O:P') {
return delta.insert(text.trim());
Expand All @@ -639,29 +638,31 @@ function matchText(node: HTMLElement, delta: Delta, scroll: ScrollBlot) {
) {
return delta;
}
const replacer = (collapse: unknown, match: string) => {
const replaced = match.replace(/[^\u00a0]/g, ''); // \u00a0 is nbsp;
return replaced.length < 1 && collapse ? ' ' : replaced;
};
text = text.replace(/\r\n/g, ' ').replace(/\n/g, ' ');
text = text.replace(/\s\s+/g, replacer.bind(replacer, true)); // collapse whitespace
// convert all non-nbsp whitespace into regular space
text = text.replace(/[^\S\u00a0]/g, ' ');
// collapse consecutive spaces into one
text = text.replace(/ {2,}/g, ' ');
if (
(node.previousSibling == null &&
node.parentElement != null &&
isLine(node.parentElement, scroll)) ||
(node.previousSibling instanceof Element &&
isLine(node.previousSibling, scroll))
) {
text = text.replace(/^\s+/, replacer.bind(replacer, false));
// block structure means we don't need leading space
text = text.replace(/^ /, '');
}
if (
(node.nextSibling == null &&
node.parentElement != null &&
isLine(node.parentElement, scroll)) ||
(node.nextSibling instanceof Element && isLine(node.nextSibling, scroll))
) {
text = text.replace(/\s+$/, replacer.bind(replacer, false));
// block structure means we don't need trailing space
text = text.replace(/ $/, '');
}
// done removing whitespace and can normalize all to regular space
text = text.replaceAll('\u00a0', ' ');
}
return delta.insert(text);
}
Expand Down
19 changes: 10 additions & 9 deletions packages/quill/src/modules/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,16 @@ class History extends Module<HistoryOptions> {
);
}

this.quill.root.addEventListener('beforeinput', (event) => {
if (event.inputType === 'historyUndo') {
this.undo();
event.preventDefault();
} else if (event.inputType === 'historyRedo') {
this.redo();
event.preventDefault();
}
});
// FIXME: this is causing a ton of issues with our keydown event bindings.
// this.quill.root.addEventListener('beforeinput', (event) => {
// if (event.inputType === 'historyUndo') {
// this.undo();
// event.preventDefault();
// } else if (event.inputType === 'historyRedo') {
// this.redo();
// event.preventDefault();
// }
// });
}

change(source: 'undo' | 'redo', dest: 'redo' | 'undo') {
Expand Down
14 changes: 13 additions & 1 deletion packages/quill/src/modules/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Delta from 'quill-delta';
import Module from '../core/module.js';
import Quill from '../core/quill.js';
import type { Range } from '../core/selection.js';
import { deleteRange } from './keyboard.js';
import { CHECK_KOREAN, deleteRange, ZERO_SPACE } from './keyboard.js';

const INSERT_TYPES = ['insertText', 'insertReplacementText'];

Expand Down Expand Up @@ -52,6 +52,18 @@ class Input extends Module {
event.defaultPrevented ||
!INSERT_TYPES.includes(event.inputType)
) {
const range = this.quill.getSelection();
const [, offset] = this.quill.getLine(
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
range?.index!,
);
const text = getPlainTextFromInputEvent(event);
// In the case that we are typing Korean at the beginning of a new line, sometimes
// jamo do not compose together as expected but placing a ZWSP immediately before fixes this problem
if (range && offset === 0 && text && CHECK_KOREAN.test(text)) {
this.quill.insertText(range.index, ZERO_SPACE, 'user');
}

return;
}

Expand Down
Loading

0 comments on commit 436e25e

Please sign in to comment.