tldraw
Version:
A tiny little drawing editor.
479 lines (478 loc) • 18.6 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
var __typeError = (msg) => {
throw TypeError(msg);
};
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
var __runInitializers = (array, flags, self, value) => {
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
return value;
};
var __decorateElement = (array, flags, name, decorators, target, extra) => {
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
return __privateGet(this, extra);
}, set [name](x) {
return __privateSet(this, extra, x);
} }, name));
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
for (var i = decorators.length - 1; i >= 0; i--) {
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
if (k) {
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
}
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
else if (typeof it !== "object" || it === null) __typeError("Object expected");
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
}
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
};
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
var Translating_exports = {};
__export(Translating_exports, {
Translating: () => Translating,
moveShapesToPoint: () => moveShapesToPoint
});
module.exports = __toCommonJS(Translating_exports);
var import_editor = require("@tldraw/editor");
var import_noteHelpers = require("../../../shapes/note/noteHelpers");
var import_DragAndDropManager = require("../DragAndDropManager");
var _updateParentTransforms_dec, _a, _init;
class Translating extends (_a = import_editor.StateNode, _updateParentTransforms_dec = [import_editor.bind], _a) {
constructor() {
super(...arguments);
__runInitializers(_init, 5, this);
__publicField(this, "info", {});
__publicField(this, "selectionSnapshot", {});
__publicField(this, "snapshot", {});
__publicField(this, "markId", "");
__publicField(this, "isCloning", false);
__publicField(this, "isCreating", false);
__publicField(this, "dragAndDropManager", new import_DragAndDropManager.DragAndDropManager(this.editor));
}
onCreate(_shape) {
return;
}
onEnter(info) {
const { isCreating = false, creatingMarkId, onCreate = () => void 0 } = info;
if (!this.editor.getSelectedShapeIds()?.length) {
this.parent.transition("idle");
return;
}
this.info = info;
if (typeof info.onInteractionEnd === "string") {
this.parent.setCurrentToolIdMask(info.onInteractionEnd);
}
this.isCreating = isCreating;
this.markId = "";
if (isCreating) {
if (creatingMarkId) {
this.markId = creatingMarkId;
} else {
const markId = this.editor.getMarkIdMatching(
`creating:${this.editor.getOnlySelectedShapeId()}`
);
if (markId) {
this.markId = markId;
}
}
} else {
this.markId = this.editor.markHistoryStoppingPoint("translating");
}
this.onCreate = onCreate;
this.isCloning = false;
this.info = info;
this.editor.setCursor({ type: "move", rotation: 0 });
this.selectionSnapshot = getTranslatingSnapshot(this.editor);
if (!this.isCreating) {
if (this.editor.inputs.getAltKey()) {
this.startCloning();
if (this.isCloning) return;
}
}
this.snapshot = this.selectionSnapshot;
this.handleStart();
this.updateShapes();
}
onExit() {
this.parent.setCurrentToolIdMask(void 0);
this.selectionSnapshot = {};
this.snapshot = {};
this.editor.snaps.clearIndicators();
this.editor.setCursor({ type: "default", rotation: 0 });
this.dragAndDropManager.clear();
}
onTick({ elapsed }) {
const { editor } = this;
if (!editor.inputs.getIsDragging() || editor.inputs.getIsPanning()) return;
editor.edgeScrollManager.updateEdgeScrolling(elapsed);
}
onPointerMove() {
this.updateShapes();
}
onKeyDown() {
if (this.editor.inputs.getAltKey() && !this.isCloning) {
this.startCloning();
if (this.isCloning) return;
}
this.updateShapes();
}
onKeyUp() {
if (!this.editor.inputs.getAltKey() && this.isCloning) {
this.stopCloning();
return;
}
this.updateShapes();
}
onPointerUp() {
this.complete();
}
onComplete() {
this.complete();
}
onCancel() {
this.cancel();
}
startCloning() {
if (this.isCreating) return;
const shapeIds = Array.from(this.editor.getSelectedShapeIds());
if (!this.editor.canCreateShapes(shapeIds)) return;
this.isCloning = true;
this.reset();
this.markId = this.editor.markHistoryStoppingPoint("translate cloning");
this.editor.duplicateShapes(Array.from(this.editor.getSelectedShapeIds()));
this.snapshot = getTranslatingSnapshot(this.editor);
this.handleStart();
this.updateShapes();
}
stopCloning() {
this.isCloning = false;
this.snapshot = this.selectionSnapshot;
this.reset();
this.markId = this.editor.markHistoryStoppingPoint("translate");
this.updateShapes();
}
reset() {
this.editor.bailToMark(this.markId);
}
complete() {
this.updateShapes();
this.dragAndDropManager.dropShapes(this.snapshot.movingShapes);
this.handleEnd();
(0, import_editor.kickoutOccludedShapes)(
this.editor,
this.snapshot.movingShapes.map((s) => s.id)
);
const { onInteractionEnd } = this.info;
if (onInteractionEnd) {
if (typeof onInteractionEnd === "string") {
if (this.editor.getInstanceState().isToolLocked) {
this.editor.setCurrentTool(onInteractionEnd);
return;
}
} else {
onInteractionEnd();
return;
}
}
if (this.isCreating) {
this.onCreate?.(this.editor.getOnlySelectedShape());
} else {
this.parent.transition("idle");
}
}
cancel() {
const { movingShapes } = this.snapshot;
movingShapes.forEach((shape) => {
const current = this.editor.getShape(shape.id);
if (current) {
const util = this.editor.getShapeUtil(shape);
util.onTranslateCancel?.(shape, current);
}
});
this.reset();
const { onInteractionEnd } = this.info;
if (onInteractionEnd) {
if (typeof onInteractionEnd === "string") {
this.editor.setCurrentTool(onInteractionEnd);
} else {
onInteractionEnd();
}
return;
}
this.parent.transition("idle", this.info);
}
handleStart() {
const { movingShapes } = this.snapshot;
const changes = [];
movingShapes.forEach((shape) => {
const util = this.editor.getShapeUtil(shape);
const change = util.onTranslateStart?.(shape);
if (change) {
changes.push(change);
}
});
if (changes.length > 0) {
this.editor.updateShapes(changes);
}
this.dragAndDropManager.startDraggingShapes(
// Get fresh shapes from the snapshot, in case onTranslateStart mutates the shape
(0, import_editor.compact)(this.snapshot.movingShapes.map((s) => this.editor.getShape(s.id))),
// Start from the place where the user started dragging
this.editor.inputs.getOriginPagePoint(),
this.updateParentTransforms
);
this.editor.setHoveredShape(null);
}
handleEnd() {
const { movingShapes } = this.snapshot;
if (this.isCloning && movingShapes.length > 0) {
const currentAveragePagePoint = import_editor.Vec.Average(
movingShapes.map((s) => this.editor.getShapePageTransform(s.id).point())
);
const offset = import_editor.Vec.Sub(currentAveragePagePoint, this.selectionSnapshot.averagePagePoint);
if (!import_editor.Vec.IsNaN(offset)) {
this.editor.updateInstanceState({
duplicateProps: {
shapeIds: movingShapes.map((s) => s.id),
offset: { x: offset.x, y: offset.y }
}
});
}
}
const changes = [];
movingShapes.forEach((shape) => {
const current = this.editor.getShape(shape.id);
const util = this.editor.getShapeUtil(shape);
const change = util.onTranslateEnd?.(shape, current);
if (change) {
changes.push(change);
}
});
if (changes.length > 0) {
this.editor.updateShapes(changes);
}
}
updateShapes() {
const { snapshot } = this;
this.dragAndDropManager.startDraggingShapes(
snapshot.movingShapes,
this.editor.inputs.getOriginPagePoint(),
this.updateParentTransforms
);
moveShapesToPoint({
editor: this.editor,
snapshot
});
const { movingShapes } = snapshot;
const changes = [];
movingShapes.forEach((shape) => {
const current = this.editor.getShape(shape.id);
const util = this.editor.getShapeUtil(shape);
const change = util.onTranslate?.(shape, current);
if (change) {
changes.push(change);
}
});
if (changes.length > 0) {
this.editor.updateShapes(changes);
}
}
updateParentTransforms() {
const {
editor,
snapshot: { shapeSnapshots }
} = this;
const movingShapes = [];
shapeSnapshots.forEach((shapeSnapshot) => {
const shape = editor.getShape(shapeSnapshot.shape.id);
if (!shape) return;
movingShapes.push(shape);
const parentTransform = (0, import_editor.isPageId)(shape.parentId) ? null : import_editor.Mat.Inverse(editor.getShapePageTransform(shape.parentId));
shapeSnapshot.parentTransform = parentTransform;
});
}
}
_init = __decoratorStart(_a);
__decorateElement(_init, 1, "updateParentTransforms", _updateParentTransforms_dec, Translating);
__decoratorMetadata(_init, Translating);
__publicField(Translating, "id", "translating");
function getTranslatingSnapshot(editor) {
const movingShapes = [];
const pagePoints = [];
const selectedShapeIds = editor.getSelectedShapeIds();
const shapeSnapshots = (0, import_editor.compact)(
selectedShapeIds.map((id) => {
const shape = editor.getShape(id);
if (!shape) return null;
movingShapes.push(shape);
const pageTransform = editor.getShapePageTransform(id);
const pagePoint = pageTransform.point();
const pageRotation = pageTransform.rotation();
pagePoints.push(pagePoint);
const parentTransform = import_editor.PageRecordType.isId(shape.parentId) ? null : import_editor.Mat.Inverse(editor.getShapePageTransform(shape.parentId));
return {
shape,
pagePoint,
pageRotation,
parentTransform
};
})
);
const onlySelectedShape = editor.getOnlySelectedShape();
let initialSnapPoints = [];
if (onlySelectedShape) {
initialSnapPoints = editor.snaps.shapeBounds.getSnapPoints(onlySelectedShape.id);
} else {
const selectionPageBounds = editor.getSelectionPageBounds();
if (selectionPageBounds) {
initialSnapPoints = selectionPageBounds.cornersAndCenter.map((p, i) => ({
id: "selection:" + i,
x: p.x,
y: p.y
}));
}
}
let noteAdjacentPositions;
let noteSnapshot;
const originPagePoint = editor.inputs.getOriginPagePoint();
const allHoveredNotes = shapeSnapshots.filter(
(s) => editor.isShapeOfType(s.shape, "note") && editor.isPointInShape(s.shape, originPagePoint)
);
if (allHoveredNotes.length === 0) {
} else if (allHoveredNotes.length === 1) {
noteSnapshot = allHoveredNotes[0];
} else {
const allShapesSorted = editor.getCurrentPageShapesSorted();
noteSnapshot = allHoveredNotes.map((s) => ({
snapshot: s,
index: allShapesSorted.findIndex((shape) => shape.id === s.shape.id)
})).sort((a, b) => b.index - a.index)[0]?.snapshot;
}
if (noteSnapshot) {
noteAdjacentPositions = (0, import_noteHelpers.getAvailableNoteAdjacentPositions)(
editor,
noteSnapshot.pageRotation,
noteSnapshot.shape.props.scale,
noteSnapshot.shape.props.growY ?? 0
);
}
return {
averagePagePoint: import_editor.Vec.Average(pagePoints),
movingShapes,
shapeSnapshots,
initialPageBounds: editor.getSelectionPageBounds(),
initialSnapPoints,
noteAdjacentPositions,
noteSnapshot
};
}
function moveShapesToPoint({
editor,
snapshot
}) {
const { inputs } = editor;
const {
noteSnapshot,
noteAdjacentPositions,
initialPageBounds,
initialSnapPoints,
shapeSnapshots,
averagePagePoint
} = snapshot;
const shiftKey = editor.inputs.getShiftKey();
const accelKey = editor.inputs.getAccelKey();
const isGridMode = editor.getInstanceState().isGridMode;
const gridSize = editor.getDocumentSettings().gridSize;
const delta = import_editor.Vec.Sub(inputs.getCurrentPagePoint(), inputs.getOriginPagePoint());
const flatten = shiftKey ? Math.abs(delta.x) < Math.abs(delta.y) ? "x" : "y" : null;
if (flatten === "x") {
delta.x = 0;
} else if (flatten === "y") {
delta.y = 0;
}
editor.snaps.clearIndicators();
const isSnapping = editor.user.getIsSnapMode() ? !accelKey : accelKey;
let snappedToPit = false;
if (isSnapping && editor.inputs.getPointerVelocity().len() < 0.5) {
const { nudge } = editor.snaps.shapeBounds.snapTranslateShapes({
dragDelta: delta,
initialSelectionPageBounds: initialPageBounds,
lockedAxis: flatten,
initialSelectionSnapPoints: initialSnapPoints
});
delta.add(nudge);
} else {
if (noteSnapshot && noteAdjacentPositions) {
const { scale } = noteSnapshot.shape.props;
const pageCenter = noteSnapshot.pagePoint.clone().add(delta).add(import_noteHelpers.NOTE_CENTER_OFFSET.clone().mul(scale).rot(noteSnapshot.pageRotation));
let min = import_noteHelpers.NOTE_ADJACENT_POSITION_SNAP_RADIUS / editor.getZoomLevel();
let offset = new import_editor.Vec(0, 0);
for (const pit of noteAdjacentPositions) {
const deltaToPit = import_editor.Vec.Sub(pageCenter, pit);
const dist = deltaToPit.len();
if (dist < min) {
snappedToPit = true;
min = dist;
offset = deltaToPit;
}
}
delta.sub(offset);
}
}
const averageSnappedPoint = import_editor.Vec.Add(averagePagePoint, delta);
const snapIndicators = editor.snaps.getIndicators();
if (isGridMode && !accelKey && !snappedToPit && snapIndicators.length === 0) {
averageSnappedPoint.snapToGrid(gridSize);
}
const averageSnap = import_editor.Vec.Sub(averageSnappedPoint, averagePagePoint);
editor.updateShapes(
(0, import_editor.compact)(
shapeSnapshots.map(({ shape, pagePoint, parentTransform }) => {
const newPagePoint = import_editor.Vec.Add(pagePoint, averageSnap);
const newLocalPoint = parentTransform ? import_editor.Mat.applyToPoint(parentTransform, newPagePoint) : newPagePoint;
return {
id: shape.id,
type: shape.type,
x: newLocalPoint.x,
y: newLocalPoint.y
};
})
)
);
}
//# sourceMappingURL=Translating.js.map