tldraw
Version:
A tiny little drawing editor.
128 lines (127 loc) • 3.96 kB
JavaScript
import {
Mat,
StateNode,
Vec,
createShapeId,
getIndexAbove,
last,
maybeSnapToGrid,
sortByIndex,
structuredClone
} from "@tldraw/editor";
const MINIMUM_DISTANCE_BETWEEN_SHIFT_CLICKED_HANDLES = 2;
class Pointing extends StateNode {
static id = "pointing";
shape = {};
markId;
onEnter(info) {
const { inputs } = this.editor;
const { currentPagePoint } = inputs;
this.markId = void 0;
const shape = info.shapeId && this.editor.getShape(info.shapeId);
if (shape && inputs.shiftKey) {
this.markId = this.editor.markHistoryStoppingPoint(`creating_line:${shape.id}`);
this.shape = shape;
const handles = this.editor.getShapeHandles(this.shape);
if (!handles) return;
const vertexHandles = handles.filter((h) => h.type === "vertex").sort(sortByIndex);
const endHandle = vertexHandles[vertexHandles.length - 1];
const prevEndHandle = vertexHandles[vertexHandles.length - 2];
const shapePagePoint = Mat.applyToPoint(
this.editor.getShapeParentTransform(this.shape),
new Vec(this.shape.x, this.shape.y)
);
const nudgedPoint = Vec.Sub(currentPagePoint, shapePagePoint).addXY(0.1, 0.1);
const nextPoint = maybeSnapToGrid(nudgedPoint, this.editor);
const points = structuredClone(this.shape.props.points);
if (Vec.DistMin(endHandle, prevEndHandle, MINIMUM_DISTANCE_BETWEEN_SHIFT_CLICKED_HANDLES) || Vec.DistMin(nextPoint, endHandle, MINIMUM_DISTANCE_BETWEEN_SHIFT_CLICKED_HANDLES)) {
points[endHandle.id] = {
id: endHandle.id,
index: endHandle.index,
x: nextPoint.x,
y: nextPoint.y
};
} else {
const nextIndex = getIndexAbove(endHandle.index);
points[nextIndex] = {
id: nextIndex,
index: nextIndex,
x: nextPoint.x,
y: nextPoint.y
};
}
this.editor.updateShapes([
{
id: this.shape.id,
type: this.shape.type,
props: {
points
}
}
]);
} else {
const id = createShapeId();
this.markId = this.editor.markHistoryStoppingPoint(`creating_line:${id}`);
const newPoint = maybeSnapToGrid(currentPagePoint, this.editor);
this.editor.createShapes([
{
id,
type: "line",
x: newPoint.x,
y: newPoint.y,
props: {
scale: this.editor.user.getIsDynamicResizeMode() ? 1 / this.editor.getZoomLevel() : 1
}
}
]);
this.editor.select(id);
this.shape = this.editor.getShape(id);
}
}
onPointerMove() {
if (!this.shape) return;
if (this.editor.inputs.isDragging) {
const handles = this.editor.getShapeHandles(this.shape);
if (!handles) {
if (this.markId) this.editor.bailToMark(this.markId);
throw Error("No handles found");
}
const lastHandle = last(handles);
this.editor.setCurrentTool("select.dragging_handle", {
shape: this.shape,
isCreating: true,
creatingMarkId: this.markId,
// remove the offset that we added to the handle when we created it
handle: { ...lastHandle, x: lastHandle.x - 0.1, y: lastHandle.y - 0.1 },
onInteractionEnd: "line"
});
}
}
onPointerUp() {
this.complete();
}
onCancel() {
this.cancel();
}
onComplete() {
this.complete();
}
onInterrupt() {
this.parent.transition("idle");
if (this.markId) this.editor.bailToMark(this.markId);
this.editor.snaps.clearIndicators();
}
complete() {
this.parent.transition("idle", { shapeId: this.shape.id });
this.editor.snaps.clearIndicators();
}
cancel() {
if (this.markId) this.editor.bailToMark(this.markId);
this.parent.transition("idle", { shapeId: this.shape.id });
this.editor.snaps.clearIndicators();
}
}
export {
Pointing
};
//# sourceMappingURL=Pointing.mjs.map