Merge pull request #16809 from calixteman/editor_focus_after_moving_in_dom
[Editor] Move an the editor div in the DOM once a translation with the keyboard is done
This commit is contained in:
commit
77392dfce4
@ -320,19 +320,15 @@ class AnnotationEditorLayer {
|
|||||||
|
|
||||||
this.detach(editor);
|
this.detach(editor);
|
||||||
this.#uiManager.removeEditor(editor);
|
this.#uiManager.removeEditor(editor);
|
||||||
editor.div.style.display = "none";
|
if (editor.div.contains(document.activeElement)) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// When the div is removed from DOM the focus can move on the
|
// When the div is removed from DOM the focus can move on the
|
||||||
// document.body, so we just slightly postpone the removal in
|
// document.body, so we need to move it back to the main container.
|
||||||
// order to let an element potentially grab the focus before
|
|
||||||
// the body.
|
|
||||||
editor.div.style.display = "";
|
|
||||||
editor.div.remove();
|
|
||||||
editor.isAttachedToDOM = false;
|
|
||||||
if (document.activeElement === document.body) {
|
|
||||||
this.#uiManager.focusMainContainer();
|
this.#uiManager.focusMainContainer();
|
||||||
}
|
}, 0);
|
||||||
}, 0);
|
}
|
||||||
|
editor.div.remove();
|
||||||
|
editor.isAttachedToDOM = false;
|
||||||
|
|
||||||
if (!this.#isCleaningUp) {
|
if (!this.#isCleaningUp) {
|
||||||
this.addInkEditorIfNeeded(/* isCommitting = */ false);
|
this.addInkEditorIfNeeded(/* isCommitting = */ false);
|
||||||
@ -385,6 +381,25 @@ class AnnotationEditorLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
moveEditorInDOM(editor) {
|
moveEditorInDOM(editor) {
|
||||||
|
const { activeElement } = document;
|
||||||
|
if (editor.div.contains(activeElement)) {
|
||||||
|
// When the div is moved in the DOM the focus can move somewhere else,
|
||||||
|
// so we want to be sure that the focus will stay on the editor but we
|
||||||
|
// don't want to call any focus callbacks, hence we disable them and only
|
||||||
|
// re-enable them when the editor has the focus.
|
||||||
|
editor._focusEventsAllowed = false;
|
||||||
|
setTimeout(() => {
|
||||||
|
editor.div.addEventListener(
|
||||||
|
"focusin",
|
||||||
|
() => {
|
||||||
|
editor._focusEventsAllowed = true;
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
activeElement.focus();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
this.#accessibilityManager?.moveElementInDOM(
|
this.#accessibilityManager?.moveElementInDOM(
|
||||||
this.div,
|
this.div,
|
||||||
editor.div,
|
editor.div,
|
||||||
|
@ -50,6 +50,8 @@ class AnnotationEditor {
|
|||||||
|
|
||||||
_uiManager = null;
|
_uiManager = null;
|
||||||
|
|
||||||
|
_focusEventsAllowed = true;
|
||||||
|
|
||||||
#isDraggable = false;
|
#isDraggable = false;
|
||||||
|
|
||||||
#zIndex = AnnotationEditor._zIndex++;
|
#zIndex = AnnotationEditor._zIndex++;
|
||||||
@ -190,6 +192,9 @@ class AnnotationEditor {
|
|||||||
* onfocus callback.
|
* onfocus callback.
|
||||||
*/
|
*/
|
||||||
focusin(event) {
|
focusin(event) {
|
||||||
|
if (!this._focusEventsAllowed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!this.#hasBeenSelected) {
|
if (!this.#hasBeenSelected) {
|
||||||
this.parent.setSelected(this);
|
this.parent.setSelected(this);
|
||||||
} else {
|
} else {
|
||||||
@ -202,6 +207,10 @@ class AnnotationEditor {
|
|||||||
* @param {FocusEvent} event
|
* @param {FocusEvent} event
|
||||||
*/
|
*/
|
||||||
focusout(event) {
|
focusout(event) {
|
||||||
|
if (!this._focusEventsAllowed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.isAttachedToDOM) {
|
if (!this.isAttachedToDOM) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -284,6 +293,7 @@ class AnnotationEditor {
|
|||||||
*/
|
*/
|
||||||
translateInPage(x, y) {
|
translateInPage(x, y) {
|
||||||
this.#translate(this.pageDimensions, x, y);
|
this.#translate(this.pageDimensions, x, y);
|
||||||
|
this.parent.moveEditorInDOM(this);
|
||||||
this.div.scrollIntoView({ block: "nearest" });
|
this.div.scrollIntoView({ block: "nearest" });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -790,7 +800,6 @@ class AnnotationEditor {
|
|||||||
|
|
||||||
this.fixAndSetPosition();
|
this.fixAndSetPosition();
|
||||||
this.parent.moveEditorInDOM(this);
|
this.parent.moveEditorInDOM(this);
|
||||||
this.div.focus();
|
|
||||||
};
|
};
|
||||||
window.addEventListener("pointerup", pointerUpCallback);
|
window.addEventListener("pointerup", pointerUpCallback);
|
||||||
// If the user is using alt+tab during the dragging session, the pointerup
|
// If the user is using alt+tab during the dragging session, the pointerup
|
||||||
|
@ -342,6 +342,9 @@ class FreeTextEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
focusin(event) {
|
focusin(event) {
|
||||||
|
if (!this._focusEventsAllowed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
super.focusin(event);
|
super.focusin(event);
|
||||||
if (event.target !== this.editorDiv) {
|
if (event.target !== this.editorDiv) {
|
||||||
this.editorDiv.focus();
|
this.editorDiv.focus();
|
||||||
|
@ -634,6 +634,9 @@ class InkEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
focusin(event) {
|
focusin(event) {
|
||||||
|
if (!this._focusEventsAllowed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
super.focusin(event);
|
super.focusin(event);
|
||||||
this.enableEditMode();
|
this.enableEditMode();
|
||||||
}
|
}
|
||||||
|
@ -390,6 +390,7 @@ describe("FreeText Editor", () => {
|
|||||||
await clearAll(page);
|
await clearAll(page);
|
||||||
|
|
||||||
await page.mouse.click(rect.x + 200, rect.y + 100);
|
await page.mouse.click(rect.x + 200, rect.y + 100);
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
await page.type(`${getEditorSelector(9)} .internal`, "A");
|
await page.type(`${getEditorSelector(9)} .internal`, "A");
|
||||||
@ -2111,4 +2112,85 @@ describe("FreeText Editor", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Freetext must stay focused after having been moved", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must keep the focus", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await page.click("#editorFreeText");
|
||||||
|
|
||||||
|
const rect = await page.$eval(".annotationEditorLayer", el => {
|
||||||
|
const { x, y } = el.getBoundingClientRect();
|
||||||
|
return { x, y };
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.mouse.click(rect.x + 100, rect.y + 100);
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
await page.type(`${getEditorSelector(0)} .internal`, "A");
|
||||||
|
|
||||||
|
// Commit.
|
||||||
|
await page.keyboard.press("Escape");
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
|
||||||
|
await page.mouse.click(rect.x + 110, rect.y + 150);
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
await page.type(`${getEditorSelector(0)} .internal`, "B");
|
||||||
|
|
||||||
|
// Commit.
|
||||||
|
await page.keyboard.press("Escape");
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
|
||||||
|
await page.mouse.click(rect.x + 115, rect.y + 155);
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
|
||||||
|
const pos = n =>
|
||||||
|
page.evaluate(sel => {
|
||||||
|
const editor = document.querySelector(sel);
|
||||||
|
return Array.prototype.indexOf.call(
|
||||||
|
editor.parentNode.childNodes,
|
||||||
|
editor
|
||||||
|
);
|
||||||
|
}, getEditorSelector(n));
|
||||||
|
|
||||||
|
expect(await pos(0))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(0);
|
||||||
|
expect(await pos(1))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(1);
|
||||||
|
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
await page.keyboard.down("Control");
|
||||||
|
await page.keyboard.press("ArrowUp");
|
||||||
|
await page.keyboard.up("Control");
|
||||||
|
await page.waitForTimeout(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.waitForTimeout(100);
|
||||||
|
const focused = await page.evaluate(sel => {
|
||||||
|
const editor = document.querySelector(sel);
|
||||||
|
return editor === document.activeElement;
|
||||||
|
}, getEditorSelector(1));
|
||||||
|
expect(focused).withContext(`In ${browserName}`).toEqual(true);
|
||||||
|
|
||||||
|
expect(await pos(0))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(1);
|
||||||
|
expect(await pos(1))
|
||||||
|
.withContext(`In ${browserName}`)
|
||||||
|
.toEqual(0);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -50,23 +50,28 @@ describe("Ink Editor", () => {
|
|||||||
await page.mouse.down();
|
await page.mouse.down();
|
||||||
await page.mouse.move(x + 50, y + 50);
|
await page.mouse.move(x + 50, y + 50);
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
|
||||||
await page.keyboard.press("Escape");
|
await page.keyboard.press("Escape");
|
||||||
|
await page.waitForTimeout(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.keyboard.down("Control");
|
await page.keyboard.down("Control");
|
||||||
await page.keyboard.press("a");
|
await page.keyboard.press("a");
|
||||||
await page.keyboard.up("Control");
|
await page.keyboard.up("Control");
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
|
||||||
expect(await getSelectedEditors(page))
|
expect(await getSelectedEditors(page))
|
||||||
.withContext(`In ${browserName}`)
|
.withContext(`In ${browserName}`)
|
||||||
.toEqual([0, 1, 2]);
|
.toEqual([0, 1, 2]);
|
||||||
|
|
||||||
await page.keyboard.press("Backspace");
|
await page.keyboard.press("Backspace");
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
|
||||||
await page.keyboard.down("Control");
|
await page.keyboard.down("Control");
|
||||||
await page.keyboard.press("z");
|
await page.keyboard.press("z");
|
||||||
await page.keyboard.up("Control");
|
await page.keyboard.up("Control");
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
|
||||||
expect(await getSelectedEditors(page))
|
expect(await getSelectedEditors(page))
|
||||||
.withContext(`In ${browserName}`)
|
.withContext(`In ${browserName}`)
|
||||||
@ -81,8 +86,10 @@ describe("Ink Editor", () => {
|
|||||||
await page.keyboard.down("Control");
|
await page.keyboard.down("Control");
|
||||||
await page.keyboard.press("a");
|
await page.keyboard.press("a");
|
||||||
await page.keyboard.up("Control");
|
await page.keyboard.up("Control");
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
|
||||||
await page.keyboard.press("Backspace");
|
await page.keyboard.press("Backspace");
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
|
||||||
const rect = await page.$eval(".annotationEditorLayer", el => {
|
const rect = await page.$eval(".annotationEditorLayer", el => {
|
||||||
// With Chrome something is wrong when serializing a DomRect,
|
// With Chrome something is wrong when serializing a DomRect,
|
||||||
@ -97,8 +104,10 @@ describe("Ink Editor", () => {
|
|||||||
await page.mouse.down();
|
await page.mouse.down();
|
||||||
await page.mouse.move(xStart + 50, yStart + 50);
|
await page.mouse.move(xStart + 50, yStart + 50);
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
|
||||||
await page.keyboard.press("Escape");
|
await page.keyboard.press("Escape");
|
||||||
|
await page.waitForTimeout(10);
|
||||||
|
|
||||||
const rectBefore = await page.$eval(".inkEditor canvas", el => {
|
const rectBefore = await page.$eval(".inkEditor canvas", el => {
|
||||||
const { x, y } = el.getBoundingClientRect();
|
const { x, y } = el.getBoundingClientRect();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user