|
@@ -47,7 +47,7 @@ export const textWysiwyg = ({
|
|
|
id: ExcalidrawElement["id"];
|
|
|
appState: AppState;
|
|
|
onChange?: (text: string) => void;
|
|
|
- onSubmit: (text: string) => void;
|
|
|
+ onSubmit: (data: { text: string; viaKeyboard: boolean }) => void;
|
|
|
getViewportCoords: (x: number, y: number) => [number, number];
|
|
|
element: ExcalidrawElement;
|
|
|
canvas: HTMLCanvasElement | null;
|
|
@@ -136,12 +136,14 @@ export const textWysiwyg = ({
|
|
|
editable.onkeydown = (event) => {
|
|
|
if (event.key === KEYS.ESCAPE) {
|
|
|
event.preventDefault();
|
|
|
+ submittedViaKeyboard = true;
|
|
|
handleSubmit();
|
|
|
} else if (event.key === KEYS.ENTER && event[KEYS.CTRL_OR_CMD]) {
|
|
|
event.preventDefault();
|
|
|
if (event.isComposing || event.keyCode === 229) {
|
|
|
return;
|
|
|
}
|
|
|
+ submittedViaKeyboard = true;
|
|
|
handleSubmit();
|
|
|
} else if (event.key === KEYS.ENTER && !event.altKey) {
|
|
|
event.stopPropagation();
|
|
@@ -153,8 +155,14 @@ export const textWysiwyg = ({
|
|
|
event.stopPropagation();
|
|
|
};
|
|
|
|
|
|
+ // using a state variable instead of passing it to the handleSubmit callback
|
|
|
+ // so that we don't need to create separate a callback for event handlers
|
|
|
+ let submittedViaKeyboard = false;
|
|
|
const handleSubmit = () => {
|
|
|
- onSubmit(normalizeText(editable.value));
|
|
|
+ onSubmit({
|
|
|
+ text: normalizeText(editable.value),
|
|
|
+ viaKeyboard: submittedViaKeyboard,
|
|
|
+ });
|
|
|
cleanup();
|
|
|
};
|
|
|
|
|
@@ -175,7 +183,7 @@ export const textWysiwyg = ({
|
|
|
window.removeEventListener("resize", updateWysiwygStyle);
|
|
|
window.removeEventListener("wheel", stopEvent, true);
|
|
|
window.removeEventListener("pointerdown", onPointerDown);
|
|
|
- window.removeEventListener("pointerup", rebindBlur);
|
|
|
+ window.removeEventListener("pointerup", bindBlurEvent);
|
|
|
window.removeEventListener("blur", handleSubmit);
|
|
|
|
|
|
unbindUpdate();
|
|
@@ -183,10 +191,12 @@ export const textWysiwyg = ({
|
|
|
editable.remove();
|
|
|
};
|
|
|
|
|
|
- const rebindBlur = () => {
|
|
|
- window.removeEventListener("pointerup", rebindBlur);
|
|
|
- // deferred to guard against focus traps on various UIs that steal focus
|
|
|
- // upon pointerUp
|
|
|
+ const bindBlurEvent = () => {
|
|
|
+ window.removeEventListener("pointerup", bindBlurEvent);
|
|
|
+ // Deferred so that the pointerdown that initiates the wysiwyg doesn't
|
|
|
+ // trigger the blur on ensuing pointerup.
|
|
|
+ // Also to handle cases such as picking a color which would trigger a blur
|
|
|
+ // in that same tick.
|
|
|
setTimeout(() => {
|
|
|
editable.onblur = handleSubmit;
|
|
|
// case: clicking on the same property → no change → no update → no focus
|
|
@@ -202,7 +212,7 @@ export const textWysiwyg = ({
|
|
|
!isWritableElement(event.target)
|
|
|
) {
|
|
|
editable.onblur = null;
|
|
|
- window.addEventListener("pointerup", rebindBlur);
|
|
|
+ window.addEventListener("pointerup", bindBlurEvent);
|
|
|
// handle edge-case where pointerup doesn't fire e.g. due to user
|
|
|
// alt-tabbing away
|
|
|
window.addEventListener("blur", handleSubmit);
|
|
@@ -215,9 +225,14 @@ export const textWysiwyg = ({
|
|
|
editable.focus();
|
|
|
});
|
|
|
|
|
|
+ // ---------------------------------------------------------------------------
|
|
|
+
|
|
|
let isDestroyed = false;
|
|
|
|
|
|
- editable.onblur = handleSubmit;
|
|
|
+ // select on init (focusing is done separately inside the bindBlurEvent()
|
|
|
+ // because we need it to happen *after* the blur event from `pointerdown`)
|
|
|
+ editable.select();
|
|
|
+ bindBlurEvent();
|
|
|
|
|
|
// reposition wysiwyg in case of canvas is resized. Using ResizeObserver
|
|
|
// is preferred so we catch changes from host, where window may not resize.
|
|
@@ -239,6 +254,4 @@ export const textWysiwyg = ({
|
|
|
document
|
|
|
.querySelector(".excalidraw-textEditorContainer")!
|
|
|
.appendChild(editable);
|
|
|
- editable.focus();
|
|
|
- editable.select();
|
|
|
};
|