Pull onPointerDown, onDoubleClick, onPointerMove into instance methods (#876)
* Pull onPointerDown, onDoubleClick, onPointerMove into instance methods * Use bound instance methods
This commit is contained in:
parent
c89584832d
commit
92ba401da8
@ -571,7 +571,255 @@ export class App extends React.Component<any, AppState> {
|
||||
left: event.clientX,
|
||||
});
|
||||
}}
|
||||
onPointerDown={event => {
|
||||
onPointerDown={this.handleCanvasPointerDown}
|
||||
onDoubleClick={this.handleCanvasDoubleClick}
|
||||
onPointerMove={this.handleCanvasPointerMove}
|
||||
onPointerUp={this.removePointer}
|
||||
onPointerCancel={this.removePointer}
|
||||
onDrop={event => {
|
||||
const file = event.dataTransfer.files[0];
|
||||
if (
|
||||
file?.type === "application/json" ||
|
||||
file?.name.endsWith(".excalidraw")
|
||||
) {
|
||||
loadFromBlob(file)
|
||||
.then(({ elements, appState }) =>
|
||||
this.syncActionResult({ elements, appState }),
|
||||
)
|
||||
.catch(error => console.error(error));
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t("labels.drawingCanvas")}
|
||||
</canvas>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private handleCanvasDoubleClick = (
|
||||
event: React.MouseEvent<HTMLCanvasElement>,
|
||||
) => {
|
||||
resetCursor();
|
||||
|
||||
const { x, y } = viewportCoordsToSceneCoords(
|
||||
event,
|
||||
this.state,
|
||||
this.canvas,
|
||||
);
|
||||
|
||||
const elementAtPosition = getElementAtPosition(
|
||||
elements,
|
||||
this.state,
|
||||
x,
|
||||
y,
|
||||
this.state.zoom,
|
||||
);
|
||||
|
||||
const element =
|
||||
elementAtPosition && isTextElement(elementAtPosition)
|
||||
? elementAtPosition
|
||||
: newTextElement(
|
||||
newElement(
|
||||
"text",
|
||||
x,
|
||||
y,
|
||||
this.state.currentItemStrokeColor,
|
||||
this.state.currentItemBackgroundColor,
|
||||
this.state.currentItemFillStyle,
|
||||
this.state.currentItemStrokeWidth,
|
||||
this.state.currentItemRoughness,
|
||||
this.state.currentItemOpacity,
|
||||
),
|
||||
"", // default text
|
||||
this.state.currentItemFont, // default font
|
||||
);
|
||||
|
||||
this.setState({ editingElement: element });
|
||||
|
||||
let textX = event.clientX;
|
||||
let textY = event.clientY;
|
||||
|
||||
if (elementAtPosition && isTextElement(elementAtPosition)) {
|
||||
elements = elements.filter(
|
||||
element => element.id !== elementAtPosition.id,
|
||||
);
|
||||
this.setState({});
|
||||
|
||||
const centerElementX = elementAtPosition.x + elementAtPosition.width / 2;
|
||||
const centerElementY = elementAtPosition.y + elementAtPosition.height / 2;
|
||||
|
||||
const {
|
||||
x: centerElementXInViewport,
|
||||
y: centerElementYInViewport,
|
||||
} = sceneCoordsToViewportCoords(
|
||||
{ sceneX: centerElementX, sceneY: centerElementY },
|
||||
this.state,
|
||||
this.canvas,
|
||||
);
|
||||
|
||||
textX = centerElementXInViewport;
|
||||
textY = centerElementYInViewport;
|
||||
|
||||
// x and y will change after calling newTextElement function
|
||||
element.x = centerElementX;
|
||||
element.y = centerElementY;
|
||||
} else if (!event.altKey) {
|
||||
const snappedToCenterPosition = this.getTextWysiwygSnappedToCenterPosition(
|
||||
x,
|
||||
y,
|
||||
);
|
||||
|
||||
if (snappedToCenterPosition) {
|
||||
element.x = snappedToCenterPosition.elementCenterX;
|
||||
element.y = snappedToCenterPosition.elementCenterY;
|
||||
textX = snappedToCenterPosition.wysiwygX;
|
||||
textY = snappedToCenterPosition.wysiwygY;
|
||||
}
|
||||
}
|
||||
|
||||
const resetSelection = () => {
|
||||
this.setState({
|
||||
draggingElement: null,
|
||||
editingElement: null,
|
||||
});
|
||||
};
|
||||
|
||||
textWysiwyg({
|
||||
initText: element.text,
|
||||
x: textX,
|
||||
y: textY,
|
||||
strokeColor: element.strokeColor,
|
||||
font: element.font,
|
||||
opacity: this.state.currentItemOpacity,
|
||||
zoom: this.state.zoom,
|
||||
onSubmit: text => {
|
||||
if (text) {
|
||||
elements = [
|
||||
...elements,
|
||||
{
|
||||
// we need to recreate the element to update dimensions &
|
||||
// position
|
||||
...newTextElement(element, text, element.font),
|
||||
},
|
||||
];
|
||||
}
|
||||
this.setState(prevState => ({
|
||||
selectedElementIds: {
|
||||
...prevState.selectedElementIds,
|
||||
[element.id]: true,
|
||||
},
|
||||
}));
|
||||
history.resumeRecording();
|
||||
resetSelection();
|
||||
},
|
||||
onCancel: () => {
|
||||
resetSelection();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
private handleCanvasPointerMove = (
|
||||
event: React.PointerEvent<HTMLCanvasElement>,
|
||||
) => {
|
||||
gesture.pointers = gesture.pointers.map(pointer =>
|
||||
pointer.id === event.pointerId
|
||||
? {
|
||||
id: event.pointerId,
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
}
|
||||
: pointer,
|
||||
);
|
||||
|
||||
if (gesture.pointers.length === 2) {
|
||||
const center = getCenter(gesture.pointers);
|
||||
const deltaX = center.x - gesture.lastCenter!.x;
|
||||
const deltaY = center.y - gesture.lastCenter!.y;
|
||||
gesture.lastCenter = center;
|
||||
|
||||
const distance = getDistance(gesture.pointers);
|
||||
const scaleFactor = distance / gesture.initialDistance!;
|
||||
|
||||
this.setState({
|
||||
scrollX: normalizeScroll(this.state.scrollX + deltaX / this.state.zoom),
|
||||
scrollY: normalizeScroll(this.state.scrollY + deltaY / this.state.zoom),
|
||||
zoom: getNormalizedZoom(gesture.initialScale! * scaleFactor),
|
||||
});
|
||||
} else {
|
||||
gesture.lastCenter = gesture.initialDistance = gesture.initialScale = null;
|
||||
}
|
||||
|
||||
if (isHoldingSpace || isPanning || isDraggingScrollBar) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
isOverHorizontalScrollBar,
|
||||
isOverVerticalScrollBar,
|
||||
} = isOverScrollBars(currentScrollBars, event.clientX, event.clientY);
|
||||
const isOverScrollBar =
|
||||
isOverVerticalScrollBar || isOverHorizontalScrollBar;
|
||||
if (!this.state.draggingElement && !this.state.multiElement) {
|
||||
if (isOverScrollBar) {
|
||||
resetCursor();
|
||||
} else {
|
||||
setCursorForShape(this.state.elementType);
|
||||
}
|
||||
}
|
||||
|
||||
const { x, y } = viewportCoordsToSceneCoords(
|
||||
event,
|
||||
this.state,
|
||||
this.canvas,
|
||||
);
|
||||
if (this.state.multiElement) {
|
||||
const { multiElement } = this.state;
|
||||
const originX = multiElement.x;
|
||||
const originY = multiElement.y;
|
||||
const points = multiElement.points;
|
||||
const pnt = points[points.length - 1];
|
||||
pnt[0] = x - originX;
|
||||
pnt[1] = y - originY;
|
||||
invalidateShapeForElement(multiElement);
|
||||
this.setState({});
|
||||
return;
|
||||
}
|
||||
|
||||
const hasDeselectedButton = Boolean(event.buttons);
|
||||
if (hasDeselectedButton || this.state.elementType !== "selection") {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedElements = getSelectedElements(elements, this.state);
|
||||
if (selectedElements.length === 1 && !isOverScrollBar) {
|
||||
const resizeElement = getElementWithResizeHandler(
|
||||
elements,
|
||||
this.state,
|
||||
{ x, y },
|
||||
this.state.zoom,
|
||||
event.pointerType,
|
||||
);
|
||||
if (resizeElement && resizeElement.resizeHandle) {
|
||||
document.documentElement.style.cursor = getCursorForResizingElement(
|
||||
resizeElement,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const hitElement = getElementAtPosition(
|
||||
elements,
|
||||
this.state,
|
||||
x,
|
||||
y,
|
||||
this.state.zoom,
|
||||
);
|
||||
document.documentElement.style.cursor =
|
||||
hitElement && !isOverScrollBar ? "move" : "";
|
||||
};
|
||||
|
||||
private handleCanvasPointerDown = (
|
||||
event: React.PointerEvent<HTMLCanvasElement>,
|
||||
) => {
|
||||
if (lastPointerUp !== null) {
|
||||
// Unfortunately, sometimes we don't get a pointerup after a pointerdown,
|
||||
// this can happen when a contextual menu or alert is triggered. In order to avoid
|
||||
@ -664,11 +912,7 @@ export class App extends React.Component<any, AppState> {
|
||||
const {
|
||||
isOverHorizontalScrollBar,
|
||||
isOverVerticalScrollBar,
|
||||
} = isOverScrollBars(
|
||||
currentScrollBars,
|
||||
event.clientX,
|
||||
event.clientY,
|
||||
);
|
||||
} = isOverScrollBars(currentScrollBars, event.clientX, event.clientY);
|
||||
|
||||
const { x, y } = viewportCoordsToSceneCoords(
|
||||
event,
|
||||
@ -695,9 +939,7 @@ export class App extends React.Component<any, AppState> {
|
||||
const x = event.clientX;
|
||||
const dx = x - lastX;
|
||||
this.setState({
|
||||
scrollX: normalizeScroll(
|
||||
this.state.scrollX - dx / this.state.zoom,
|
||||
),
|
||||
scrollX: normalizeScroll(this.state.scrollX - dx / this.state.zoom),
|
||||
});
|
||||
lastX = x;
|
||||
return;
|
||||
@ -707,9 +949,7 @@ export class App extends React.Component<any, AppState> {
|
||||
const y = event.clientY;
|
||||
const dy = y - lastY;
|
||||
this.setState({
|
||||
scrollY: normalizeScroll(
|
||||
this.state.scrollY - dy / this.state.zoom,
|
||||
),
|
||||
scrollY: normalizeScroll(this.state.scrollY - dy / this.state.zoom),
|
||||
});
|
||||
lastY = y;
|
||||
}
|
||||
@ -746,11 +986,7 @@ export class App extends React.Component<any, AppState> {
|
||||
);
|
||||
|
||||
if (isTextElement(element)) {
|
||||
element = newTextElement(
|
||||
element,
|
||||
"",
|
||||
this.state.currentItemFont,
|
||||
);
|
||||
element = newTextElement(element, "", this.state.currentItemFont);
|
||||
}
|
||||
|
||||
type ResizeTestType = ReturnType<typeof resizeTest>;
|
||||
@ -768,15 +1004,10 @@ export class App extends React.Component<any, AppState> {
|
||||
event.pointerType,
|
||||
);
|
||||
|
||||
const selectedElements = getSelectedElements(
|
||||
elements,
|
||||
this.state,
|
||||
);
|
||||
const selectedElements = getSelectedElements(elements, this.state);
|
||||
if (selectedElements.length === 1 && resizeElement) {
|
||||
this.setState({
|
||||
resizingElement: resizeElement
|
||||
? resizeElement.element
|
||||
: null,
|
||||
resizingElement: resizeElement ? resizeElement.element : null,
|
||||
});
|
||||
|
||||
resizeHandle = resizeElement.resizeHandle;
|
||||
@ -794,9 +1025,7 @@ export class App extends React.Component<any, AppState> {
|
||||
);
|
||||
// clear selection if shift is not clicked
|
||||
if (
|
||||
!(
|
||||
hitElement && this.state.selectedElementIds[hitElement.id]
|
||||
) &&
|
||||
!(hitElement && this.state.selectedElementIds[hitElement.id]) &&
|
||||
!event.shiftKey
|
||||
) {
|
||||
this.setState({ selectedElementIds: {} });
|
||||
@ -886,11 +1115,7 @@ export class App extends React.Component<any, AppState> {
|
||||
elements = [
|
||||
...elements,
|
||||
{
|
||||
...newTextElement(
|
||||
element,
|
||||
text,
|
||||
this.state.currentItemFont,
|
||||
),
|
||||
...newTextElement(element, text, this.state.currentItemFont),
|
||||
},
|
||||
];
|
||||
}
|
||||
@ -1039,9 +1264,7 @@ export class App extends React.Component<any, AppState> {
|
||||
const x = event.clientX;
|
||||
const dx = x - lastX;
|
||||
this.setState({
|
||||
scrollX: normalizeScroll(
|
||||
this.state.scrollX - dx / this.state.zoom,
|
||||
),
|
||||
scrollX: normalizeScroll(this.state.scrollX - dx / this.state.zoom),
|
||||
});
|
||||
lastX = x;
|
||||
return;
|
||||
@ -1051,9 +1274,7 @@ export class App extends React.Component<any, AppState> {
|
||||
const y = event.clientY;
|
||||
const dy = y - lastY;
|
||||
this.setState({
|
||||
scrollY: normalizeScroll(
|
||||
this.state.scrollY - dy / this.state.zoom,
|
||||
),
|
||||
scrollY: normalizeScroll(this.state.scrollY - dy / this.state.zoom),
|
||||
});
|
||||
lastY = y;
|
||||
return;
|
||||
@ -1081,10 +1302,7 @@ export class App extends React.Component<any, AppState> {
|
||||
if (isResizingElements && this.state.resizingElement) {
|
||||
this.setState({ isResizing: true });
|
||||
const el = this.state.resizingElement;
|
||||
const selectedElements = getSelectedElements(
|
||||
elements,
|
||||
this.state,
|
||||
);
|
||||
const selectedElements = getSelectedElements(elements, this.state);
|
||||
if (selectedElements.length === 1) {
|
||||
const { x, y } = viewportCoordsToSceneCoords(
|
||||
event,
|
||||
@ -1094,8 +1312,7 @@ export class App extends React.Component<any, AppState> {
|
||||
const deltaX = x - lastX;
|
||||
const deltaY = y - lastY;
|
||||
const element = selectedElements[0];
|
||||
const isLinear =
|
||||
element.type === "line" || element.type === "arrow";
|
||||
const isLinear = element.type === "line" || element.type === "arrow";
|
||||
switch (resizeHandle) {
|
||||
case "nw":
|
||||
if (isLinear && element.points.length === 2) {
|
||||
@ -1225,9 +1442,7 @@ export class App extends React.Component<any, AppState> {
|
||||
if (element.points.length > 0) {
|
||||
const len = element.points.length;
|
||||
|
||||
const points = [...element.points].sort(
|
||||
(a, b) => a[1] - b[1],
|
||||
);
|
||||
const points = [...element.points].sort((a, b) => a[1] - b[1]);
|
||||
|
||||
for (let i = 1; i < points.length; ++i) {
|
||||
const pnt = points[i];
|
||||
@ -1242,9 +1457,7 @@ export class App extends React.Component<any, AppState> {
|
||||
|
||||
if (element.points.length > 0) {
|
||||
const len = element.points.length;
|
||||
const points = [...element.points].sort(
|
||||
(a, b) => a[0] - b[0],
|
||||
);
|
||||
const points = [...element.points].sort((a, b) => a[0] - b[0]);
|
||||
|
||||
for (let i = 0; i < points.length; ++i) {
|
||||
const pnt = points[i];
|
||||
@ -1257,9 +1470,7 @@ export class App extends React.Component<any, AppState> {
|
||||
element.height += deltaY;
|
||||
if (element.points.length > 0) {
|
||||
const len = element.points.length;
|
||||
const points = [...element.points].sort(
|
||||
(a, b) => a[1] - b[1],
|
||||
);
|
||||
const points = [...element.points].sort((a, b) => a[1] - b[1]);
|
||||
|
||||
for (let i = 1; i < points.length; ++i) {
|
||||
const pnt = points[i];
|
||||
@ -1272,9 +1483,7 @@ export class App extends React.Component<any, AppState> {
|
||||
element.width += deltaX;
|
||||
if (element.points.length > 0) {
|
||||
const len = element.points.length;
|
||||
const points = [...element.points].sort(
|
||||
(a, b) => a[0] - b[0],
|
||||
);
|
||||
const points = [...element.points].sort((a, b) => a[0] - b[0]);
|
||||
|
||||
for (let i = 1; i < points.length; ++i) {
|
||||
const pnt = points[i];
|
||||
@ -1286,16 +1495,14 @@ export class App extends React.Component<any, AppState> {
|
||||
}
|
||||
|
||||
if (resizeHandle) {
|
||||
resizeHandle = normalizeResizeHandle(
|
||||
element,
|
||||
resizeHandle,
|
||||
);
|
||||
resizeHandle = normalizeResizeHandle(element, resizeHandle);
|
||||
}
|
||||
normalizeDimensions(element);
|
||||
|
||||
document.documentElement.style.cursor = getCursorForResizingElement(
|
||||
{ element, resizeHandle },
|
||||
);
|
||||
document.documentElement.style.cursor = getCursorForResizingElement({
|
||||
element,
|
||||
resizeHandle,
|
||||
});
|
||||
el.x = element.x;
|
||||
el.y = element.y;
|
||||
invalidateShapeForElement(el);
|
||||
@ -1307,17 +1514,11 @@ export class App extends React.Component<any, AppState> {
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
hitElement &&
|
||||
this.state.selectedElementIds[hitElement.id]
|
||||
) {
|
||||
if (hitElement && this.state.selectedElementIds[hitElement.id]) {
|
||||
// Marking that click was used for dragging to check
|
||||
// if elements should be deselected on pointerup
|
||||
draggingOccurred = true;
|
||||
const selectedElements = getSelectedElements(
|
||||
elements,
|
||||
this.state,
|
||||
);
|
||||
const selectedElements = getSelectedElements(elements, this.state);
|
||||
if (selectedElements.length > 0) {
|
||||
const { x, y } = viewportCoordsToSceneCoords(
|
||||
event,
|
||||
@ -1353,8 +1554,7 @@ export class App extends React.Component<any, AppState> {
|
||||
let height = distance(originY, y);
|
||||
|
||||
const isLinear =
|
||||
this.state.elementType === "line" ||
|
||||
this.state.elementType === "arrow";
|
||||
this.state.elementType === "line" || this.state.elementType === "arrow";
|
||||
|
||||
if (isLinear) {
|
||||
draggingOccurred = true;
|
||||
@ -1400,10 +1600,7 @@ export class App extends React.Component<any, AppState> {
|
||||
invalidateShapeForElement(draggingElement);
|
||||
|
||||
if (this.state.elementType === "selection") {
|
||||
if (
|
||||
!event.shiftKey &&
|
||||
isSomeElementSelected(elements, this.state)
|
||||
) {
|
||||
if (!event.shiftKey && isSomeElementSelected(elements, this.state)) {
|
||||
this.setState({ selectedElementIds: {} });
|
||||
}
|
||||
const elementsWithinSelection = getElementsWithinSelection(
|
||||
@ -1414,10 +1611,7 @@ export class App extends React.Component<any, AppState> {
|
||||
selectedElementIds: {
|
||||
...prevState.selectedElementIds,
|
||||
...Object.fromEntries(
|
||||
elementsWithinSelection.map(element => [
|
||||
element.id,
|
||||
true,
|
||||
]),
|
||||
elementsWithinSelection.map(element => [element.id, true]),
|
||||
),
|
||||
},
|
||||
}));
|
||||
@ -1508,13 +1702,8 @@ export class App extends React.Component<any, AppState> {
|
||||
this.setState({});
|
||||
}
|
||||
|
||||
if (
|
||||
resizingElement &&
|
||||
isInvisiblySmallElement(resizingElement)
|
||||
) {
|
||||
elements = elements.filter(
|
||||
el => el.id !== resizingElement.id,
|
||||
);
|
||||
if (resizingElement && isInvisiblySmallElement(resizingElement)) {
|
||||
elements = elements.filter(el => el.id !== resizingElement.id);
|
||||
}
|
||||
|
||||
// If click occurred on already selected element
|
||||
@ -1525,11 +1714,7 @@ export class App extends React.Component<any, AppState> {
|
||||
// If click occurred and elements were dragged or some element
|
||||
// was added to selection (on pointerdown phase) we need to keep
|
||||
// selection unchanged
|
||||
if (
|
||||
hitElement &&
|
||||
!draggingOccurred &&
|
||||
!elementIsAddedToSelection
|
||||
) {
|
||||
if (hitElement && !draggingOccurred && !elementIsAddedToSelection) {
|
||||
if (event.shiftKey) {
|
||||
this.setState(prevState => ({
|
||||
selectedElementIds: {
|
||||
@ -1583,260 +1768,8 @@ export class App extends React.Component<any, AppState> {
|
||||
|
||||
window.addEventListener("pointermove", onPointerMove);
|
||||
window.addEventListener("pointerup", onPointerUp);
|
||||
}}
|
||||
onDoubleClick={event => {
|
||||
resetCursor();
|
||||
|
||||
const { x, y } = viewportCoordsToSceneCoords(
|
||||
event,
|
||||
this.state,
|
||||
this.canvas,
|
||||
);
|
||||
|
||||
const elementAtPosition = getElementAtPosition(
|
||||
elements,
|
||||
this.state,
|
||||
x,
|
||||
y,
|
||||
this.state.zoom,
|
||||
);
|
||||
|
||||
const element =
|
||||
elementAtPosition && isTextElement(elementAtPosition)
|
||||
? elementAtPosition
|
||||
: newTextElement(
|
||||
newElement(
|
||||
"text",
|
||||
x,
|
||||
y,
|
||||
this.state.currentItemStrokeColor,
|
||||
this.state.currentItemBackgroundColor,
|
||||
this.state.currentItemFillStyle,
|
||||
this.state.currentItemStrokeWidth,
|
||||
this.state.currentItemRoughness,
|
||||
this.state.currentItemOpacity,
|
||||
),
|
||||
"", // default text
|
||||
this.state.currentItemFont, // default font
|
||||
);
|
||||
|
||||
this.setState({ editingElement: element });
|
||||
|
||||
let textX = event.clientX;
|
||||
let textY = event.clientY;
|
||||
|
||||
if (elementAtPosition && isTextElement(elementAtPosition)) {
|
||||
elements = elements.filter(
|
||||
element => element.id !== elementAtPosition.id,
|
||||
);
|
||||
this.setState({});
|
||||
|
||||
const centerElementX =
|
||||
elementAtPosition.x + elementAtPosition.width / 2;
|
||||
const centerElementY =
|
||||
elementAtPosition.y + elementAtPosition.height / 2;
|
||||
|
||||
const {
|
||||
x: centerElementXInViewport,
|
||||
y: centerElementYInViewport,
|
||||
} = sceneCoordsToViewportCoords(
|
||||
{ sceneX: centerElementX, sceneY: centerElementY },
|
||||
this.state,
|
||||
this.canvas,
|
||||
);
|
||||
|
||||
textX = centerElementXInViewport;
|
||||
textY = centerElementYInViewport;
|
||||
|
||||
// x and y will change after calling newTextElement function
|
||||
element.x = centerElementX;
|
||||
element.y = centerElementY;
|
||||
} else if (!event.altKey) {
|
||||
const snappedToCenterPosition = this.getTextWysiwygSnappedToCenterPosition(
|
||||
x,
|
||||
y,
|
||||
);
|
||||
|
||||
if (snappedToCenterPosition) {
|
||||
element.x = snappedToCenterPosition.elementCenterX;
|
||||
element.y = snappedToCenterPosition.elementCenterY;
|
||||
textX = snappedToCenterPosition.wysiwygX;
|
||||
textY = snappedToCenterPosition.wysiwygY;
|
||||
}
|
||||
}
|
||||
|
||||
const resetSelection = () => {
|
||||
this.setState({
|
||||
draggingElement: null,
|
||||
editingElement: null,
|
||||
});
|
||||
};
|
||||
|
||||
textWysiwyg({
|
||||
initText: element.text,
|
||||
x: textX,
|
||||
y: textY,
|
||||
strokeColor: element.strokeColor,
|
||||
font: element.font,
|
||||
opacity: this.state.currentItemOpacity,
|
||||
zoom: this.state.zoom,
|
||||
onSubmit: text => {
|
||||
if (text) {
|
||||
elements = [
|
||||
...elements,
|
||||
{
|
||||
// we need to recreate the element to update dimensions &
|
||||
// position
|
||||
...newTextElement(element, text, element.font),
|
||||
},
|
||||
];
|
||||
}
|
||||
this.setState(prevState => ({
|
||||
selectedElementIds: {
|
||||
...prevState.selectedElementIds,
|
||||
[element.id]: true,
|
||||
},
|
||||
}));
|
||||
history.resumeRecording();
|
||||
resetSelection();
|
||||
},
|
||||
onCancel: () => {
|
||||
resetSelection();
|
||||
},
|
||||
});
|
||||
}}
|
||||
onPointerMove={event => {
|
||||
gesture.pointers = gesture.pointers.map(pointer =>
|
||||
pointer.id === event.pointerId
|
||||
? {
|
||||
id: event.pointerId,
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
}
|
||||
: pointer,
|
||||
);
|
||||
|
||||
if (gesture.pointers.length === 2) {
|
||||
const center = getCenter(gesture.pointers);
|
||||
const deltaX = center.x - gesture.lastCenter!.x;
|
||||
const deltaY = center.y - gesture.lastCenter!.y;
|
||||
gesture.lastCenter = center;
|
||||
|
||||
const distance = getDistance(gesture.pointers);
|
||||
const scaleFactor = distance / gesture.initialDistance!;
|
||||
|
||||
this.setState({
|
||||
scrollX: normalizeScroll(
|
||||
this.state.scrollX + deltaX / this.state.zoom,
|
||||
),
|
||||
scrollY: normalizeScroll(
|
||||
this.state.scrollY + deltaY / this.state.zoom,
|
||||
),
|
||||
zoom: getNormalizedZoom(gesture.initialScale! * scaleFactor),
|
||||
});
|
||||
} else {
|
||||
gesture.lastCenter = gesture.initialDistance = gesture.initialScale = null;
|
||||
}
|
||||
|
||||
if (isHoldingSpace || isPanning || isDraggingScrollBar) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
isOverHorizontalScrollBar,
|
||||
isOverVerticalScrollBar,
|
||||
} = isOverScrollBars(
|
||||
currentScrollBars,
|
||||
event.clientX,
|
||||
event.clientY,
|
||||
);
|
||||
const isOverScrollBar =
|
||||
isOverVerticalScrollBar || isOverHorizontalScrollBar;
|
||||
if (!this.state.draggingElement && !this.state.multiElement) {
|
||||
if (isOverScrollBar) {
|
||||
resetCursor();
|
||||
} else {
|
||||
setCursorForShape(this.state.elementType);
|
||||
}
|
||||
}
|
||||
|
||||
const { x, y } = viewportCoordsToSceneCoords(
|
||||
event,
|
||||
this.state,
|
||||
this.canvas,
|
||||
);
|
||||
if (this.state.multiElement) {
|
||||
const { multiElement } = this.state;
|
||||
const originX = multiElement.x;
|
||||
const originY = multiElement.y;
|
||||
const points = multiElement.points;
|
||||
const pnt = points[points.length - 1];
|
||||
pnt[0] = x - originX;
|
||||
pnt[1] = y - originY;
|
||||
invalidateShapeForElement(multiElement);
|
||||
this.setState({});
|
||||
return;
|
||||
}
|
||||
|
||||
const hasDeselectedButton = Boolean(event.buttons);
|
||||
if (
|
||||
hasDeselectedButton ||
|
||||
this.state.elementType !== "selection"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedElements = getSelectedElements(
|
||||
elements,
|
||||
this.state,
|
||||
);
|
||||
if (selectedElements.length === 1 && !isOverScrollBar) {
|
||||
const resizeElement = getElementWithResizeHandler(
|
||||
elements,
|
||||
this.state,
|
||||
{ x, y },
|
||||
this.state.zoom,
|
||||
event.pointerType,
|
||||
);
|
||||
if (resizeElement && resizeElement.resizeHandle) {
|
||||
document.documentElement.style.cursor = getCursorForResizingElement(
|
||||
resizeElement,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const hitElement = getElementAtPosition(
|
||||
elements,
|
||||
this.state,
|
||||
x,
|
||||
y,
|
||||
this.state.zoom,
|
||||
);
|
||||
document.documentElement.style.cursor =
|
||||
hitElement && !isOverScrollBar ? "move" : "";
|
||||
}}
|
||||
onPointerUp={this.removePointer}
|
||||
onPointerCancel={this.removePointer}
|
||||
onDrop={event => {
|
||||
const file = event.dataTransfer.files[0];
|
||||
if (
|
||||
file?.type === "application/json" ||
|
||||
file?.name.endsWith(".excalidraw")
|
||||
) {
|
||||
loadFromBlob(file)
|
||||
.then(({ elements, appState }) =>
|
||||
this.syncActionResult({ elements, appState }),
|
||||
)
|
||||
.catch(error => console.error(error));
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t("labels.drawingCanvas")}
|
||||
</canvas>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private handleWheel = (event: WheelEvent) => {
|
||||
event.preventDefault();
|
||||
const { deltaX, deltaY } = event;
|
||||
|
Loading…
x
Reference in New Issue
Block a user