2021-04-13 10:57:08 +02:00
|
|
|
// Copyright: Ankitects Pty Ltd and contributors
|
|
|
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
2021-02-09 04:38:04 +01:00
|
|
|
|
2021-02-28 14:12:48 +01:00
|
|
|
import { EditingArea } from "./editingArea";
|
2021-03-08 20:40:23 +01:00
|
|
|
import { caretToEnd, nodeIsElement } from "./helpers";
|
2021-02-08 19:45:42 +01:00
|
|
|
import { triggerChangeTimer } from "./changeTimer";
|
|
|
|
|
2021-04-19 16:04:20 +02:00
|
|
|
const getAnchorParent = <T extends Element>(
|
|
|
|
predicate: (element: Element) => element is T
|
|
|
|
) => (currentField: EditingArea): T | null => {
|
|
|
|
const anchor = currentField.getSelection()?.anchorNode;
|
|
|
|
|
|
|
|
if (!anchor) {
|
|
|
|
return null;
|
2021-02-08 19:45:42 +01:00
|
|
|
}
|
|
|
|
|
2021-04-19 16:04:20 +02:00
|
|
|
let anchorParent: T | null = null;
|
|
|
|
let element = nodeIsElement(anchor) ? anchor : anchor.parentElement;
|
|
|
|
|
|
|
|
while (element) {
|
|
|
|
anchorParent = anchorParent || (predicate(element) ? element : null);
|
|
|
|
element = element.parentElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
return anchorParent;
|
|
|
|
};
|
|
|
|
|
|
|
|
const getListItem = getAnchorParent(
|
|
|
|
(element: Element): element is HTMLLIElement =>
|
|
|
|
window.getComputedStyle(element).display === "list-item"
|
|
|
|
);
|
|
|
|
|
|
|
|
const getParagraph = getAnchorParent(
|
|
|
|
(element: Element): element is HTMLParamElement => element.tagName === "P"
|
|
|
|
);
|
2021-02-08 19:45:42 +01:00
|
|
|
|
|
|
|
export function onInput(event: Event): void {
|
|
|
|
// make sure IME changes get saved
|
|
|
|
triggerChangeTimer(event.currentTarget as EditingArea);
|
2021-03-30 06:14:00 +02:00
|
|
|
// @ts-ignore
|
|
|
|
editorToolbar.updateActiveButtons();
|
2021-02-08 19:45:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export function onKey(evt: KeyboardEvent): void {
|
|
|
|
const currentField = evt.currentTarget as EditingArea;
|
|
|
|
|
|
|
|
// esc clears focus, allowing dialog to close
|
|
|
|
if (evt.code === "Escape") {
|
|
|
|
currentField.blurEditable();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// prefer <br> instead of <div></div>
|
2021-04-19 19:56:01 +02:00
|
|
|
if (
|
|
|
|
evt.code === "Enter" &&
|
|
|
|
!getListItem(currentField) &&
|
|
|
|
!getParagraph(currentField)
|
|
|
|
) {
|
2021-02-08 19:45:42 +01:00
|
|
|
evt.preventDefault();
|
|
|
|
document.execCommand("insertLineBreak");
|
|
|
|
}
|
|
|
|
|
|
|
|
// // fix Ctrl+right/left handling in RTL fields
|
|
|
|
if (currentField.isRightToLeft()) {
|
|
|
|
const selection = currentField.getSelection();
|
|
|
|
const granularity = evt.ctrlKey ? "word" : "character";
|
|
|
|
const alter = evt.shiftKey ? "extend" : "move";
|
|
|
|
|
|
|
|
switch (evt.code) {
|
|
|
|
case "ArrowRight":
|
|
|
|
selection.modify(alter, "right", granularity);
|
|
|
|
evt.preventDefault();
|
|
|
|
return;
|
|
|
|
case "ArrowLeft":
|
|
|
|
selection.modify(alter, "left", granularity);
|
|
|
|
evt.preventDefault();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
triggerChangeTimer(currentField);
|
|
|
|
}
|
|
|
|
|
2021-03-08 20:40:23 +01:00
|
|
|
globalThis.addEventListener("keydown", (evt: KeyboardEvent) => {
|
|
|
|
if (evt.code === "Tab") {
|
2021-03-08 20:55:04 +01:00
|
|
|
globalThis.addEventListener(
|
|
|
|
"focusin",
|
|
|
|
(evt: FocusEvent) => {
|
|
|
|
const newFocusTarget = evt.target;
|
|
|
|
if (newFocusTarget instanceof EditingArea) {
|
|
|
|
caretToEnd(newFocusTarget);
|
2021-03-30 06:14:00 +02:00
|
|
|
// @ts-ignore
|
|
|
|
editorToolbar.updateActiveButtons();
|
2021-03-08 20:55:04 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
{ once: true }
|
|
|
|
);
|
2021-03-08 20:40:23 +01:00
|
|
|
}
|
2021-03-08 20:55:04 +01:00
|
|
|
});
|
2021-03-08 20:40:23 +01:00
|
|
|
|
2021-02-08 19:45:42 +01:00
|
|
|
export function onKeyUp(evt: KeyboardEvent): void {
|
|
|
|
const currentField = evt.currentTarget as EditingArea;
|
|
|
|
|
|
|
|
// Avoid div element on remove
|
|
|
|
if (evt.code === "Enter" || evt.code === "Backspace") {
|
|
|
|
const anchor = currentField.getSelection().anchorNode as Node;
|
|
|
|
|
|
|
|
if (
|
|
|
|
nodeIsElement(anchor) &&
|
|
|
|
anchor.tagName === "DIV" &&
|
|
|
|
!(anchor instanceof EditingArea) &&
|
|
|
|
anchor.childElementCount === 1 &&
|
|
|
|
anchor.children[0].tagName === "BR"
|
|
|
|
) {
|
|
|
|
anchor.replaceWith(anchor.children[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|