mirror of
https://github.com/penpot/penpot.git
synced 2026-06-01 05:00:17 +00:00
🐛 Fix wrong typography font size in sidebar when selecting text in Firefox (editor v2)
This commit is contained in:
parent
6268a8aaf1
commit
334039668d
@ -291,6 +291,13 @@ export class TextNodeIterator {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// The loop exits when currentNode === endNode without yielding endNode.
|
||||||
|
// Callers (e.g. selection style merge) must visit every text/BR node in the
|
||||||
|
// range, including the last one, or the final span is omitted (e.g. empty
|
||||||
|
// paragraph with only <br>) and the sidebar shows "mixed" incorrectly.
|
||||||
|
if (this.#currentNode === endNode) {
|
||||||
|
yield this.#currentNode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -70,4 +70,29 @@ describe("TextNodeIterator", () => {
|
|||||||
textNodeIterator.nextNode();
|
textNodeIterator.nextNode();
|
||||||
expect(textNodeIterator.currentNode.nodeValue).toBe("Whatever");
|
expect(textNodeIterator.currentNode.nodeValue).toBe("Whatever");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("collectFrom includes the end node (iterateFrom must yield end inclusive)", () => {
|
||||||
|
const rootNode = createRoot([
|
||||||
|
createParagraph([createTextSpan(new Text("Hello"))]),
|
||||||
|
createParagraph([createTextSpan(createLineBreak())]),
|
||||||
|
]);
|
||||||
|
const firstText = rootNode.firstChild.firstChild.firstChild;
|
||||||
|
const br = rootNode.lastChild.firstChild.firstChild;
|
||||||
|
const textNodeIterator = new TextNodeIterator(rootNode);
|
||||||
|
const nodes = textNodeIterator.collectFrom(firstText, br);
|
||||||
|
expect(nodes.length).toBe(2);
|
||||||
|
expect(nodes[0]).toBe(firstText);
|
||||||
|
expect(nodes[1]).toBe(br);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("collectFrom with identical start and end returns one node", () => {
|
||||||
|
const rootNode = createRoot([
|
||||||
|
createParagraph([createTextSpan(new Text("Hi"))]),
|
||||||
|
]);
|
||||||
|
const text = rootNode.firstChild.firstChild.firstChild;
|
||||||
|
const textNodeIterator = new TextNodeIterator(rootNode);
|
||||||
|
const nodes = textNodeIterator.collectFrom(text, text);
|
||||||
|
expect(nodes.length).toBe(1);
|
||||||
|
expect(nodes[0]).toBe(text);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -403,7 +403,12 @@ export class SelectionController extends EventTarget {
|
|||||||
this.#updateCurrentStyle(textSpan);
|
this.#updateCurrentStyle(textSpan);
|
||||||
} else {
|
} else {
|
||||||
// SELECTION.
|
// SELECTION.
|
||||||
this.#updateCurrentStyleFrom(this.#anchorNode, this.#focusNode);
|
// Use range boundaries normalized to text nodes, not anchor/focus.
|
||||||
|
// Firefox may set anchorNode on the paragraph element and focusNode on a
|
||||||
|
// text node for word selection; passing those to #updateCurrentStyleFrom
|
||||||
|
// breaks TextNodeIterator and yields wrong styles (e.g. default 14px).
|
||||||
|
const { startNode, endNode } = this.getRanges();
|
||||||
|
this.#updateCurrentStyleFrom(startNode, endNode);
|
||||||
}
|
}
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("stylechange", {
|
new CustomEvent("stylechange", {
|
||||||
|
|||||||
@ -1666,6 +1666,30 @@ describe("SelectionController", () => {
|
|||||||
expect(selectionController.focusAtEnd).toBeTruthy();
|
expect(selectionController.focusAtEnd).toBeTruthy();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("`currentStyle` uses text span font-size when anchor is paragraph (Firefox-style word selection)", () => {
|
||||||
|
const textEditorMock = TextEditorMock.createTextEditorMockWithParagraphs([
|
||||||
|
createParagraph([
|
||||||
|
createTextSpan(new Text("Hello World"), { "font-size": "36" }),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
const root = textEditorMock.root;
|
||||||
|
const paragraph = root.firstChild;
|
||||||
|
const textNode = paragraph.firstChild.firstChild;
|
||||||
|
const selection = document.getSelection();
|
||||||
|
const selectionController = new SelectionController(
|
||||||
|
textEditorMock,
|
||||||
|
selection,
|
||||||
|
);
|
||||||
|
textEditorMock.element.focus();
|
||||||
|
// Anchor on the paragraph (child offset 0) and focus in the text node — matches
|
||||||
|
// Firefox when double-click selects a word; anchor/focus are not both text nodes.
|
||||||
|
selection.setBaseAndExtent(paragraph, 0, textNode, 5);
|
||||||
|
document.dispatchEvent(new Event("selectionchange"));
|
||||||
|
expect(selectionController.currentStyle.getPropertyValue("font-size")).toBe(
|
||||||
|
"36px",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("`dispose` should release every held reference", () => {
|
test("`dispose` should release every held reference", () => {
|
||||||
const textEditorMock = TextEditorMock.createTextEditorMockWithParagraphs([
|
const textEditorMock = TextEditorMock.createTextEditorMockWithParagraphs([
|
||||||
createParagraphWith(["Hello, "], {
|
createParagraphWith(["Hello, "], {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user