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