mirror of
https://github.com/block/buzz.git
synced 2026-11-03 16:01:35 +00:00
fix(composer): preserve mounted geometry fallbacks
Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
@@ -28,3 +28,66 @@ test("selection geometry returns null instead of throwing after view teardown",
|
||||
test("link geometry returns null instead of throwing after view teardown", () => {
|
||||
assert.equal(getEditorLinkRect(unmountedEditor(), 1, 2), null);
|
||||
});
|
||||
|
||||
function mountedEditorWithCaretGeometry() {
|
||||
const coords = new Map([
|
||||
[1, { left: 10, right: 11, top: 20, bottom: 32 }],
|
||||
[2, { left: 30, right: 31, top: 20, bottom: 32 }],
|
||||
]);
|
||||
return {
|
||||
view: {
|
||||
domAtPos() {
|
||||
return { node: {}, offset: 99 };
|
||||
},
|
||||
coordsAtPos(pos) {
|
||||
return coords.get(pos);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function withThrowingRange(callback) {
|
||||
const previousDocument = globalThis.document;
|
||||
globalThis.document = {
|
||||
createRange() {
|
||||
throw new DOMException("stale DOM offset", "IndexSizeError");
|
||||
},
|
||||
};
|
||||
try {
|
||||
callback();
|
||||
} finally {
|
||||
globalThis.document = previousDocument;
|
||||
}
|
||||
}
|
||||
|
||||
test("selection geometry keeps the caret fallback when Range construction fails", () => {
|
||||
withThrowingRange(() => {
|
||||
assert.deepEqual(
|
||||
getEditorSelectionRect(mountedEditorWithCaretGeometry(), 1, 2),
|
||||
{
|
||||
left: 10,
|
||||
top: 20,
|
||||
width: 21,
|
||||
height: 12,
|
||||
right: 31,
|
||||
bottom: 32,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("link geometry keeps the caret fallback when Range construction fails", () => {
|
||||
withThrowingRange(() => {
|
||||
assert.deepEqual(
|
||||
getEditorLinkRect(mountedEditorWithCaretGeometry(), 1, 2),
|
||||
{
|
||||
left: 10,
|
||||
top: 20,
|
||||
width: 1,
|
||||
height: 12,
|
||||
right: 11,
|
||||
bottom: 32,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,18 +15,22 @@ export function getEditorSelectionRect(
|
||||
to: number,
|
||||
): EditorRect | null {
|
||||
try {
|
||||
const range = document.createRange();
|
||||
const start = editor.view.domAtPos(from);
|
||||
const end = editor.view.domAtPos(to);
|
||||
range.setStart(start.node, start.offset);
|
||||
range.setEnd(end.node, end.offset);
|
||||
try {
|
||||
const range = document.createRange();
|
||||
const start = editor.view.domAtPos(from);
|
||||
const end = editor.view.domAtPos(to);
|
||||
range.setStart(start.node, start.offset);
|
||||
range.setEnd(end.node, end.offset);
|
||||
|
||||
const clientRects = Array.from(range.getClientRects()).filter(
|
||||
(rect) => rect.width > 0 || rect.height > 0,
|
||||
);
|
||||
const rect = clientRects[0] ?? range.getBoundingClientRect();
|
||||
range.detach();
|
||||
if (rect.width > 0 || rect.height > 0) return rect;
|
||||
const clientRects = Array.from(range.getClientRects()).filter(
|
||||
(rect) => rect.width > 0 || rect.height > 0,
|
||||
);
|
||||
const rect = clientRects[0] ?? range.getBoundingClientRect();
|
||||
range.detach();
|
||||
if (rect.width > 0 || rect.height > 0) return rect;
|
||||
} catch {
|
||||
// A mounted view can still have stale Range offsets; use caret geometry.
|
||||
}
|
||||
|
||||
const startCoords = editor.view.coordsAtPos(from);
|
||||
const endCoords = editor.view.coordsAtPos(to);
|
||||
@@ -55,23 +59,27 @@ export function getEditorLinkRect(
|
||||
to: number,
|
||||
): EditorRect | null {
|
||||
try {
|
||||
const range = document.createRange();
|
||||
const start = editor.view.domAtPos(from);
|
||||
const end = editor.view.domAtPos(to);
|
||||
range.setStart(start.node, start.offset);
|
||||
range.setEnd(end.node, end.offset);
|
||||
try {
|
||||
const range = document.createRange();
|
||||
const start = editor.view.domAtPos(from);
|
||||
const end = editor.view.domAtPos(to);
|
||||
range.setStart(start.node, start.offset);
|
||||
range.setEnd(end.node, end.offset);
|
||||
|
||||
const rect = range.getBoundingClientRect();
|
||||
range.detach();
|
||||
if (rect.width > 0 || rect.height > 0) {
|
||||
return {
|
||||
left: rect.left,
|
||||
top: rect.top,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
};
|
||||
const rect = range.getBoundingClientRect();
|
||||
range.detach();
|
||||
if (rect.width > 0 || rect.height > 0) {
|
||||
return {
|
||||
left: rect.left,
|
||||
top: rect.top,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
// A mounted view can still have stale Range offsets; use caret geometry.
|
||||
}
|
||||
|
||||
const coords = editor.view.coordsAtPos(from);
|
||||
|
||||
Reference in New Issue
Block a user