fix: stop preventing canvas pointerdown/tapend events (#3207)
This commit is contained in:
parent
edc62c550a
commit
e90e56452f
@ -112,8 +112,7 @@
|
|||||||
Roboto, Helvetica, Arial, sans-serif;
|
Roboto, Helvetica, Arial, sans-serif;
|
||||||
font-family: var(--ui-font);
|
font-family: var(--ui-font);
|
||||||
-webkit-text-size-adjust: 100%;
|
-webkit-text-size-adjust: 100%;
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
@ -1105,7 +1105,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private onTapEnd = (event: TouchEvent) => {
|
private onTapEnd = (event: TouchEvent) => {
|
||||||
event.preventDefault();
|
|
||||||
if (event.touches.length > 0) {
|
if (event.touches.length > 0) {
|
||||||
this.setState({
|
this.setState({
|
||||||
previousSelectedElementIds: {},
|
previousSelectedElementIds: {},
|
||||||
@ -1631,10 +1630,12 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
updateBoundElements(element);
|
updateBoundElements(element);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
onSubmit: withBatchedUpdates((text) => {
|
onSubmit: withBatchedUpdates(({ text, viaKeyboard }) => {
|
||||||
const isDeleted = !text.trim();
|
const isDeleted = !text.trim();
|
||||||
updateElement(text, isDeleted);
|
updateElement(text, isDeleted);
|
||||||
if (!isDeleted) {
|
// select the created text element only if submitting via keyboard
|
||||||
|
// (when submitting via click it should act as signal to deselect)
|
||||||
|
if (!isDeleted && viaKeyboard) {
|
||||||
this.setState((prevState) => ({
|
this.setState((prevState) => ({
|
||||||
selectedElementIds: {
|
selectedElementIds: {
|
||||||
...prevState.selectedElementIds,
|
...prevState.selectedElementIds,
|
||||||
@ -2151,15 +2152,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
|
|
||||||
this.updateGestureOnPointerDown(event);
|
this.updateGestureOnPointerDown(event);
|
||||||
|
|
||||||
// fixes pointermove causing selection of UI texts #32
|
|
||||||
event.preventDefault();
|
|
||||||
// Preventing the event above disables default behavior
|
|
||||||
// of defocusing potentially focused element, which is what we
|
|
||||||
// want when clicking inside the canvas.
|
|
||||||
if (document.activeElement instanceof HTMLElement) {
|
|
||||||
document.activeElement.blur();
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't select while panning
|
// don't select while panning
|
||||||
if (gesture.pointers.size > 1) {
|
if (gesture.pointers.size > 1) {
|
||||||
return;
|
return;
|
||||||
|
@ -17,6 +17,13 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|
||||||
|
// serves 2 purposes:
|
||||||
|
// 1. prevent selecting text outside the component when double-clicking or
|
||||||
|
// dragging inside it (e.g. on canvas)
|
||||||
|
// 2. prevent selecting UI, both from the inside, and from outside the
|
||||||
|
// component (e.g. if you select text in a sidebar)
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@ -29,7 +36,6 @@
|
|||||||
|
|
||||||
canvas {
|
canvas {
|
||||||
touch-action: none;
|
touch-action: none;
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
// following props improve blurriness at certain devicePixelRatios.
|
// following props improve blurriness at certain devicePixelRatios.
|
||||||
// AFAIK it doesn't affect export (in fact, export seems sharp either way).
|
// AFAIK it doesn't affect export (in fact, export seems sharp either way).
|
||||||
|
@ -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();
|
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user