tldraw
Version:
A tiny little drawing editor.
150 lines (149 loc) • 5.53 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
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 Brushing_exports = {};
__export(Brushing_exports, {
Brushing: () => Brushing
});
module.exports = __toCommonJS(Brushing_exports);
var import_editor = require("@tldraw/editor");
class Brushing extends import_editor.StateNode {
static id = "brushing";
info = {};
initialSelectedShapeIds = [];
excludedShapeIds = /* @__PURE__ */ new Set();
isWrapMode = false;
// The shape that the brush started on
initialStartShape = null;
onEnter(info) {
const { altKey, currentPagePoint } = this.editor.inputs;
this.isWrapMode = this.editor.user.getIsWrapMode();
if (altKey) {
this.parent.transition("scribble_brushing", info);
return;
}
this.excludedShapeIds = new Set(
this.editor.getCurrentPageShapes().filter(
(shape) => this.editor.isShapeOfType(shape, "group") || this.editor.isShapeOrAncestorLocked(shape)
).map((shape) => shape.id)
);
this.info = info;
this.initialSelectedShapeIds = this.editor.getSelectedShapeIds().slice();
this.initialStartShape = this.editor.getShapesAtPoint(currentPagePoint)[0];
this.hitTestShapes();
}
onExit() {
this.initialSelectedShapeIds = [];
this.editor.updateInstanceState({ brush: null });
}
onTick({ elapsed }) {
const { editor } = this;
editor.edgeScrollManager.updateEdgeScrolling(elapsed);
}
onPointerMove() {
this.hitTestShapes();
}
onPointerUp() {
this.complete();
}
onComplete() {
this.complete();
}
onCancel(info) {
this.editor.setSelectedShapes(this.initialSelectedShapeIds);
this.parent.transition("idle", info);
}
onKeyDown(info) {
if (this.editor.inputs.altKey) {
this.parent.transition("scribble_brushing", info);
} else {
this.hitTestShapes();
}
}
onKeyUp() {
this.hitTestShapes();
}
complete() {
this.hitTestShapes();
this.parent.transition("idle");
}
hitTestShapes() {
const { editor, excludedShapeIds, isWrapMode } = this;
const {
inputs: { originPagePoint, currentPagePoint, shiftKey, ctrlKey }
} = editor;
const results = new Set(shiftKey ? this.initialSelectedShapeIds : []);
const isWrapping = isWrapMode ? !ctrlKey : ctrlKey;
const brush = import_editor.Box.FromPoints([originPagePoint, currentPagePoint]);
const { corners } = brush;
let A, B, shape, pageBounds, pageTransform, localCorners;
const currentPageShapes = editor.getCurrentPageRenderingShapesSorted();
const currentPageId = editor.getCurrentPageId();
testAllShapes: for (let i = 0, n = currentPageShapes.length; i < n; i++) {
shape = currentPageShapes[i];
if (excludedShapeIds.has(shape.id) || results.has(shape.id)) continue testAllShapes;
pageBounds = editor.getShapePageBounds(shape);
if (!pageBounds) continue testAllShapes;
if (brush.contains(pageBounds)) {
this.handleHit(shape, currentPagePoint, currentPageId, results, corners);
continue testAllShapes;
}
if (isWrapping || editor.isShapeOfType(shape, "frame")) {
continue testAllShapes;
}
if (brush.collides(pageBounds)) {
pageTransform = editor.getShapePageTransform(shape);
if (!pageTransform) continue testAllShapes;
localCorners = pageTransform.clone().invert().applyToPoints(corners);
const geometry = editor.getShapeGeometry(shape);
hitTestBrushEdges: for (let i2 = 0; i2 < 4; i2++) {
A = localCorners[i2];
B = localCorners[(i2 + 1) % 4];
if (geometry.hitTestLineSegment(A, B, 0)) {
this.handleHit(shape, currentPagePoint, currentPageId, results, corners);
break hitTestBrushEdges;
}
}
}
}
const currentBrush = editor.getInstanceState().brush;
if (!currentBrush || !brush.equals(currentBrush)) {
editor.updateInstanceState({ brush: { ...brush.toJson() } });
}
const current = editor.getSelectedShapeIds();
if (current.length !== results.size || current.some((id) => !results.has(id))) {
editor.setSelectedShapes(Array.from(results));
}
}
onInterrupt() {
this.editor.updateInstanceState({ brush: null });
}
handleHit(shape, currentPagePoint, currentPageId, results, corners) {
if (shape.parentId === currentPageId) {
results.add(shape.id);
return;
}
const selectedShape = this.editor.getOutermostSelectableShape(shape);
const pageMask = this.editor.getShapeMask(selectedShape.id);
if (pageMask && !(0, import_editor.polygonsIntersect)(pageMask, corners) && !(0, import_editor.pointInPolygon)(currentPagePoint, pageMask)) {
return;
}
results.add(selectedShape.id);
}
}
//# sourceMappingURL=Brushing.js.map