mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
rewrite wysiwyg property updating (#1387)
* rewrite wysiwyg property updating * reuse existing class * fix case of focus being stolen by other UIs * revert mistake csp removal * ensure we don't run cleanup twice * fix opacity updating * add shape actions menu class to constants
This commit is contained in:
parent
d79c859cd9
commit
6771b505ad
9 changed files with 73 additions and 80 deletions
|
@ -1,6 +1,8 @@
|
|||
import { KEYS } from "../keys";
|
||||
import { selectNode } from "../utils";
|
||||
import { WysiwigElement } from "./types";
|
||||
import { selectNode, isWritableElement } from "../utils";
|
||||
import { globalSceneState } from "../scene";
|
||||
import { isTextElement } from "./typeChecks";
|
||||
import { CLASSES } from "../constants";
|
||||
|
||||
function trimText(text: string) {
|
||||
// whitespace only → trim all because we'd end up inserting invisible element
|
||||
|
@ -14,6 +16,7 @@ function trimText(text: string) {
|
|||
}
|
||||
|
||||
type TextWysiwygParams = {
|
||||
id: string;
|
||||
initText: string;
|
||||
x: number;
|
||||
y: number;
|
||||
|
@ -29,6 +32,7 @@ type TextWysiwygParams = {
|
|||
};
|
||||
|
||||
export function textWysiwyg({
|
||||
id,
|
||||
initText,
|
||||
x,
|
||||
y,
|
||||
|
@ -41,7 +45,7 @@ export function textWysiwyg({
|
|||
textAlign,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}: TextWysiwygParams): WysiwigElement {
|
||||
}: TextWysiwygParams) {
|
||||
const editable = document.createElement("div");
|
||||
try {
|
||||
editable.contentEditable = "plaintext-only";
|
||||
|
@ -136,25 +140,74 @@ export function textWysiwyg({
|
|||
}
|
||||
|
||||
function cleanup() {
|
||||
if (isDestroyed) {
|
||||
return;
|
||||
}
|
||||
isDestroyed = true;
|
||||
// remove events to ensure they don't late-fire
|
||||
editable.onblur = null;
|
||||
editable.onpaste = null;
|
||||
editable.oninput = null;
|
||||
editable.onkeydown = null;
|
||||
|
||||
window.removeEventListener("wheel", stopEvent, true);
|
||||
window.removeEventListener("pointerdown", onPointerDown);
|
||||
window.removeEventListener("pointerup", rebindBlur);
|
||||
window.removeEventListener("blur", handleSubmit);
|
||||
|
||||
unbindUpdate();
|
||||
|
||||
document.body.removeChild(editable);
|
||||
}
|
||||
|
||||
const rebindBlur = () => {
|
||||
window.removeEventListener("pointerup", rebindBlur);
|
||||
// deferred to guard against focus traps on various UIs that steal focus
|
||||
// upon pointerUp
|
||||
setTimeout(() => {
|
||||
editable.onblur = handleSubmit;
|
||||
// case: clicking on the same property → no change → no update → no focus
|
||||
editable.focus();
|
||||
});
|
||||
};
|
||||
|
||||
// prevent blur when changing properties from the menu
|
||||
const onPointerDown = (event: MouseEvent) => {
|
||||
if (
|
||||
event.target instanceof HTMLElement &&
|
||||
event.target.closest(CLASSES.SHAPE_ACTIONS_MENU) &&
|
||||
!isWritableElement(event.target)
|
||||
) {
|
||||
editable.onblur = null;
|
||||
window.addEventListener("pointerup", rebindBlur);
|
||||
// handle edge-case where pointerup doesn't fire e.g. due to user
|
||||
// alt-tabbing away
|
||||
window.addEventListener("blur", handleSubmit);
|
||||
}
|
||||
};
|
||||
|
||||
// handle updates of textElement properties of editing element
|
||||
const unbindUpdate = globalSceneState.addCallback(() => {
|
||||
const editingElement = globalSceneState
|
||||
.getElementsIncludingDeleted()
|
||||
.find((element) => element.id === id);
|
||||
if (editingElement && isTextElement(editingElement)) {
|
||||
Object.assign(editable.style, {
|
||||
font: editingElement.font,
|
||||
textAlign: editingElement.textAlign,
|
||||
color: editingElement.strokeColor,
|
||||
opacity: editingElement.opacity / 100,
|
||||
});
|
||||
}
|
||||
editable.focus();
|
||||
});
|
||||
|
||||
let isDestroyed = false;
|
||||
|
||||
editable.onblur = handleSubmit;
|
||||
window.addEventListener("pointerdown", onPointerDown);
|
||||
window.addEventListener("wheel", stopEvent, true);
|
||||
document.body.appendChild(editable);
|
||||
editable.focus();
|
||||
selectNode(editable);
|
||||
|
||||
return {
|
||||
submit: handleSubmit,
|
||||
changeStyle: (style: any) => {
|
||||
Object.assign(editable.style, style);
|
||||
editable.focus();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue