Skip to content

Commit

Permalink
Keep cursor unlinked to avoid elements being pushed down inside
Browse files Browse the repository at this point in the history
  • Loading branch information
luin committed Feb 16, 2022
1 parent 885f1a2 commit 71f0eb1
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 2 deletions.
28 changes: 27 additions & 1 deletion blots/cursor.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ class Cursor extends EmbedBlot {
restore() {
if (this.selection.composing || this.parent == null) return null;
const range = this.selection.getNativeRange();
// Link format will insert text outside of anchor tag
// Browser may push down styles/nodes inside the cursor blot.
// https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html#push-down-values
while (
this.domNode.lastChild != null &&
this.domNode.lastChild !== this.textNode
Expand Down Expand Up @@ -144,6 +145,31 @@ class Cursor extends EmbedBlot {
}
}

// Avoid .ql-cursor being a descendant of `<a/>`.
// The reason is Safari pushes down `<a/>` on text insertion.
// That will make DOM nodes not sync with the model.
//
// For example ({I} is the caret), given the markup:
// <a><span class="ql-cursor">\uFEFF{I}</span></a>
// When typing a char "x", `<a/>` will be pushed down inside the `<span>` first:
// <span class="ql-cursor"><a>\uFEFF{I}</a></span>
// And then "x" will be inserted after `<a/>`:
// <span class="ql-cursor"><a>\uFEFF</a>d{I}</span>
optimize(context) {
super.optimize(context);

let { parent } = this;
while (parent) {
if (parent.domNode.tagName === 'A') {
this.savedLength = Cursor.CONTENTS.length;
parent.isolate(this.offset(parent), this.length()).unwrap();
this.savedLength = 0;
break;
}
parent = parent.parent;
}
}

value() {
return '';
}
Expand Down
3 changes: 2 additions & 1 deletion modules/clipboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ class Clipboard extends Module {
if (!html && files.length > 0) {
this.quill.uploader.upload(range, files);
return;
} else if (html && files.length > 0) {
}
if (html && files.length > 0) {
const doc = new DOMParser().parseFromString(html, 'text/html');
if (
doc.body.childElementCount === 1 &&
Expand Down
34 changes: 34 additions & 0 deletions test/unit/core/selection.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,40 @@ describe('Selection', function() {
<p>01<em><span class="ql-cursor">${Cursor.CONTENTS}</span></em>23</p>
`);
});

describe('unlink cursor', function() {
const cursorHTML = `<span class="ql-cursor">${Cursor.CONTENTS}</span>`;

it('one level', function() {
this.setup(
'<p><strong><a href="https://example.com">link</a></strong></p><p><br></p>',
4,
);
this.selection.format('bold', false);
expect(this.container).toEqualHTML(`
<p><strong><a href="https://example.com">link</a></strong>${cursorHTML}</p><p><br></p>
`);
});

it('nested formats', function() {
this.setup(
'<p><strong><em><a href="https://example.com">bold</a></em></strong></p><p><br></p>',
4,
);
this.selection.format('italic', false);
expect(this.container).toEqualHTML(`
<p><strong><em><a href="https://example.com">bold</a></em>${cursorHTML}</strong></p><p><br></p>
`);
});

it('ignore link format', function() {
this.setup('<p><strong>bold</strong></p><p><br></p>', 4);
this.selection.format('link', 'https://example.com');
expect(this.container).toEqualHTML(`
<p><strong>bold${cursorHTML}</strong></p><p><br></p>
`);
});
});
});

describe('getBounds()', function() {
Expand Down

0 comments on commit 71f0eb1

Please sign in to comment.