UNPKG

tldraw

Version:

A tiny little drawing editor.

535 lines (534 loc) • 19.2 kB
"use strict"; 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 Idle_exports = {}; __export(Idle_exports, { GRID_INCREMENT: () => GRID_INCREMENT, Idle: () => Idle, MAJOR_NUDGE_FACTOR: () => MAJOR_NUDGE_FACTOR, MINOR_NUDGE_FACTOR: () => MINOR_NUDGE_FACTOR }); module.exports = __toCommonJS(Idle_exports); var import_editor = require("@tldraw/editor"); var import_arrowLabel = require("../../../shapes/arrow/arrowLabel"); var import_getHitShapeOnCanvasPointerDown = require("../../selection-logic/getHitShapeOnCanvasPointerDown"); var import_selectOnCanvasPointerUp = require("../../selection-logic/selectOnCanvasPointerUp"); var import_updateHoveredShapeId = require("../../selection-logic/updateHoveredShapeId"); var import_selectHelpers = require("../selectHelpers"); const SKIPPED_KEYS_FOR_AUTO_EDITING = [ "Delete", "Backspace", "[", "]", "Enter", " ", "Shift", "Tab" ]; class Idle extends import_editor.StateNode { static id = "idle"; selectedShapesOnKeyDown = []; onEnter() { this.parent.setCurrentToolIdMask(void 0); (0, import_updateHoveredShapeId.updateHoveredShapeId)(this.editor); this.selectedShapesOnKeyDown = []; this.editor.setCursor({ type: "default", rotation: 0 }); } onExit() { import_updateHoveredShapeId.updateHoveredShapeId.cancel(); } onPointerMove() { (0, import_updateHoveredShapeId.updateHoveredShapeId)(this.editor); } onPointerDown(info) { switch (info.target) { case "canvas": { const hitShape = (0, import_getHitShapeOnCanvasPointerDown.getHitShapeOnCanvasPointerDown)(this.editor); if (hitShape && !hitShape.isLocked) { this.onPointerDown({ ...info, shape: hitShape, target: "shape" }); return; } const selectedShapeIds = this.editor.getSelectedShapeIds(); const onlySelectedShape = this.editor.getOnlySelectedShape(); const currentPagePoint = this.editor.inputs.getCurrentPagePoint(); if (selectedShapeIds.length > 1 || onlySelectedShape && !this.editor.getShapeUtil(onlySelectedShape).hideSelectionBoundsBg(onlySelectedShape)) { if (isPointInRotatedSelectionBounds(this.editor, currentPagePoint)) { this.onPointerDown({ ...info, target: "selection" }); return; } } this.parent.transition("pointing_canvas", info); break; } case "shape": { const { shape } = info; if (this.editor.isShapeOrAncestorLocked(shape)) { this.parent.transition("pointing_canvas", info); break; } this.parent.transition("pointing_shape", info); break; } case "handle": { if (this.editor.getIsReadonly()) break; if (this.editor.inputs.getAltKey()) { this.parent.transition("pointing_shape", info); } else { this.parent.transition("pointing_handle", info); } break; } case "selection": { switch (info.handle) { case "mobile_rotate": case "top_left_rotate": case "top_right_rotate": case "bottom_left_rotate": case "bottom_right_rotate": { if (info.accelKey) { this.parent.transition("brushing", info); break; } this.parent.transition("pointing_rotate_handle", info); break; } case "top": case "right": case "bottom": case "left": case "top_left": case "top_right": case "bottom_left": case "bottom_right": { const onlySelectedShape = this.editor.getOnlySelectedShape(); if (info.ctrlKey && this.editor.canCropShape(onlySelectedShape)) { this.parent.transition("crop.pointing_crop_handle", info); } else { if (info.accelKey) { this.parent.transition("brushing", info); break; } this.parent.transition("pointing_resize_handle", info); } break; } default: { const hoveredShape = this.editor.getHoveredShape(); if (hoveredShape && !this.editor.getSelectedShapeIds().includes(hoveredShape.id) && !hoveredShape.isLocked) { this.onPointerDown({ ...info, shape: hoveredShape, target: "shape" }); return; } this.parent.transition("pointing_selection", info); } } break; } } } onDoubleClick(info) { if (this.editor.inputs.getShiftKey() || info.phase !== "up") return; if (info.ctrlKey || info.shiftKey) return; switch (info.target) { case "canvas": { const hoveredShape = this.editor.getHoveredShape(); const currentPagePoint = this.editor.inputs.getCurrentPagePoint(); const hitShape = hoveredShape && !this.editor.isShapeOfType(hoveredShape, "group") ? hoveredShape : this.editor.getSelectedShapeAtPoint(currentPagePoint) ?? this.editor.getShapeAtPoint(currentPagePoint, { margin: this.editor.options.hitTestMargin / this.editor.getZoomLevel(), hitInside: false }); const focusedGroupId = this.editor.getFocusedGroupId(); if (hitShape) { if (this.editor.isShapeOfType(hitShape, "group")) { (0, import_selectOnCanvasPointerUp.selectOnCanvasPointerUp)(this.editor, info); return; } else { const parent = this.editor.getShape(hitShape.parentId); if (parent && this.editor.isShapeOfType(parent, "group")) { if (focusedGroupId && parent.id === focusedGroupId) { } else { (0, import_selectOnCanvasPointerUp.selectOnCanvasPointerUp)(this.editor, info); return; } } } this.onDoubleClick({ ...info, shape: hitShape, target: "shape" }); return; } if (!this.editor.inputs.getShiftKey()) { this.handleDoubleClickOnCanvas(info); } break; } case "selection": { const onlySelectedShape = this.editor.getOnlySelectedShape(); if (onlySelectedShape) { const util = this.editor.getShapeUtil(onlySelectedShape); const isEdge = info.handle === "right" || info.handle === "left" || info.handle === "top" || info.handle === "bottom"; const isCorner = info.handle === "top_left" || info.handle === "top_right" || info.handle === "bottom_right" || info.handle === "bottom_left"; if (this.editor.getIsReadonly()) { if (this.editor.canEditShape(onlySelectedShape, { type: isCorner ? "double-click-corner" : isEdge ? "double-click-edge" : "double-click" })) { this.startEditingShape( onlySelectedShape, info, true /* select all */ ); } break; } if (isEdge) { const change = util.onDoubleClickEdge?.(onlySelectedShape, info); if (change) { this.editor.markHistoryStoppingPoint("double click edge"); this.editor.updateShapes([change]); (0, import_editor.kickoutOccludedShapes)(this.editor, [onlySelectedShape.id]); return; } } if (isCorner) { const change = util.onDoubleClickCorner?.(onlySelectedShape, info); if (change) { this.editor.markHistoryStoppingPoint("double click corner"); this.editor.updateShapes([change]); (0, import_editor.kickoutOccludedShapes)(this.editor, [onlySelectedShape.id]); return; } } if (this.editor.canCropShape(onlySelectedShape)) { this.parent.transition("crop", info); return; } if (this.editor.canEditShape(onlySelectedShape)) { this.startEditingShape( onlySelectedShape, info, true /* select all */ ); } } break; } case "shape": { const { shape } = info; const util = this.editor.getShapeUtil(shape); if (shape.type !== "video" && shape.type !== "embed" && this.editor.getIsReadonly()) break; if (util.onDoubleClick) { const change = util.onDoubleClick?.(shape); if (change) { this.editor.updateShapes([change]); return; } } if (util.canCrop(shape) && !this.editor.isShapeOrAncestorLocked(shape)) { this.editor.markHistoryStoppingPoint("select and crop"); this.editor.select(info.shape?.id); this.parent.transition("crop", info); return; } if (this.editor.canEditShape(shape)) { this.startEditingShape( shape, info, true /* select all */ ); } else { this.handleDoubleClickOnCanvas(info); } break; } case "handle": { if (this.editor.getIsReadonly()) break; const { shape, handle } = info; const util = this.editor.getShapeUtil(shape); const changes = util.onDoubleClickHandle?.(shape, handle); if (changes) { this.editor.updateShapes([changes]); } else { if (this.editor.canEditShape(shape)) { this.startEditingShape( shape, info, true /* select all */ ); } } } } } onRightClick(info) { switch (info.target) { case "canvas": { const hoveredShape = this.editor.getHoveredShape(); const hitShape = hoveredShape && !this.editor.isShapeOfType(hoveredShape, "group") ? hoveredShape : this.editor.getShapeAtPoint(this.editor.inputs.getCurrentPagePoint(), { margin: this.editor.options.hitTestMargin / this.editor.getZoomLevel(), hitInside: false, hitLabels: true, hitLocked: true, hitFrameInside: true, renderingOnly: true }); if (hitShape) { this.onRightClick({ ...info, shape: hitShape, target: "shape" }); return; } const selectedShapeIds = this.editor.getSelectedShapeIds(); const onlySelectedShape = this.editor.getOnlySelectedShape(); const currentPagePoint = this.editor.inputs.getCurrentPagePoint(); if (selectedShapeIds.length > 1 || onlySelectedShape && !this.editor.getShapeUtil(onlySelectedShape).hideSelectionBoundsBg(onlySelectedShape)) { if (isPointInRotatedSelectionBounds(this.editor, currentPagePoint)) { this.onRightClick({ ...info, target: "selection" }); return; } } this.editor.selectNone(); break; } case "shape": { const { selectedShapeIds } = this.editor.getCurrentPageState(); const { shape } = info; const targetShape = this.editor.getOutermostSelectableShape( shape, (parent) => !selectedShapeIds.includes(parent.id) ); if (!selectedShapeIds.includes(targetShape.id) && !this.editor.findShapeAncestor( targetShape, (shape2) => selectedShapeIds.includes(shape2.id) )) { this.editor.markHistoryStoppingPoint("selecting shape"); this.editor.setSelectedShapes([targetShape.id]); } break; } } } onCancel() { if (this.editor.getFocusedGroupId() !== this.editor.getCurrentPageId() && this.editor.getSelectedShapeIds().length > 0) { this.editor.popFocusedGroupId(); } else { this.editor.markHistoryStoppingPoint("clearing selection"); this.editor.selectNone(); } } onKeyDown(info) { this.selectedShapesOnKeyDown = this.editor.getSelectedShapes(); switch (info.code) { case "ArrowLeft": case "ArrowRight": case "ArrowUp": case "ArrowDown": { if (info.accelKey) { if (info.shiftKey) { if (info.code === "ArrowDown") { this.editor.selectFirstChildShape(); } else if (info.code === "ArrowUp") { this.editor.selectParentShape(); } } else { this.editor.selectAdjacentShape( info.code.replace("Arrow", "").toLowerCase() ); } return; } this.nudgeSelectedShapes(false); return; } } if (import_editor.debugFlags["editOnType"].get()) { if (!SKIPPED_KEYS_FOR_AUTO_EDITING.includes(info.key) && !info.altKey && !info.ctrlKey) { const onlySelectedShape = this.editor.getOnlySelectedShape(); if (onlySelectedShape && // If it's a note shape, then edit on type this.editor.isShapeOfType(onlySelectedShape, "note") && // If it's not locked or anything this.editor.canEditShape(onlySelectedShape)) { this.startEditingShape( onlySelectedShape, { ...info, target: "shape", shape: onlySelectedShape }, true /* select all */ ); return; } } } } onKeyRepeat(info) { switch (info.code) { case "ArrowLeft": case "ArrowRight": case "ArrowUp": case "ArrowDown": { if (info.accelKey) { this.editor.selectAdjacentShape( info.code.replace("Arrow", "").toLowerCase() ); return; } this.nudgeSelectedShapes(true); break; } case "Tab": { const selectedShapes = this.editor.getSelectedShapes(); if (selectedShapes.length && !info.altKey) { this.editor.selectAdjacentShape(info.shiftKey ? "prev" : "next"); } break; } } } onKeyUp(info) { switch (info.key) { case "Enter": { if (!this.selectedShapesOnKeyDown.length) return; const selectedShapes = this.editor.getSelectedShapes(); if (selectedShapes.every((shape) => this.editor.isShapeOfType(shape, "group"))) { this.editor.setSelectedShapes( selectedShapes.flatMap((shape) => this.editor.getSortedChildIdsForParent(shape.id)) ); return; } const onlySelectedShape = this.editor.getOnlySelectedShape(); if (onlySelectedShape && this.editor.canEditShape(onlySelectedShape, { type: "press_enter" })) { this.startEditingShape( onlySelectedShape, { ...info, target: "shape", shape: onlySelectedShape }, true /* select all */ ); return; } if (this.editor.canCropShape(onlySelectedShape)) { this.parent.transition("crop", info); } break; } case "Tab": { const selectedShapes = this.editor.getSelectedShapes(); if (selectedShapes.length && !info.altKey) { this.editor.selectAdjacentShape(info.shiftKey ? "prev" : "next"); } break; } } } startEditingShape(shape, info, shouldSelectAll) { const { editor } = this; this.editor.markHistoryStoppingPoint("editing shape"); if ((0, import_selectHelpers.hasRichText)(shape)) { (0, import_selectHelpers.startEditingShapeWithRichText)(editor, shape, { selectAll: shouldSelectAll }); } else { editor.setEditingShape(shape); } this.parent.transition("editing_shape", info); } isOverArrowLabelTest(shape) { if (!shape) return false; return (0, import_arrowLabel.isOverArrowLabel)(this.editor, shape); } handleDoubleClickOnCanvas(info) { if (this.editor.getIsReadonly()) return; if (!this.editor.options.createTextOnCanvasDoubleClick) return; this.editor.markHistoryStoppingPoint("creating text shape"); const id = (0, import_editor.createShapeId)(); const { x, y } = this.editor.inputs.getCurrentPagePoint(); this.editor.createShapes([ { id, type: "text", x, y, props: { richText: (0, import_editor.toRichText)(""), autoSize: true } } ]); const shape = this.editor.getShape(id); if (!shape) return; if (!this.editor.canEditShape(shape)) return; (0, import_selectHelpers.startEditingShapeWithRichText)(this.editor, id, { info }); } nudgeSelectedShapes(ephemeral = false) { const { editor: { inputs: { keys } } } = this; const shiftKey = keys.has("ShiftLeft"); const delta = new import_editor.Vec(0, 0); if (keys.has("ArrowLeft")) delta.x -= 1; if (keys.has("ArrowRight")) delta.x += 1; if (keys.has("ArrowUp")) delta.y -= 1; if (keys.has("ArrowDown")) delta.y += 1; if (delta.equals(new import_editor.Vec(0, 0))) return; if (!ephemeral) this.editor.markHistoryStoppingPoint("nudge shapes"); const { gridSize } = this.editor.getDocumentSettings(); const step = this.editor.getInstanceState().isGridMode ? shiftKey ? gridSize * GRID_INCREMENT : gridSize : shiftKey ? MAJOR_NUDGE_FACTOR : MINOR_NUDGE_FACTOR; const selectedShapeIds = this.editor.getSelectedShapeIds(); this.editor.nudgeShapes(selectedShapeIds, delta.mul(step)); (0, import_editor.kickoutOccludedShapes)(this.editor, selectedShapeIds); } } const MAJOR_NUDGE_FACTOR = 10; const MINOR_NUDGE_FACTOR = 1; const GRID_INCREMENT = 5; function isPointInRotatedSelectionBounds(editor, point) { const selectionBounds = editor.getSelectionRotatedPageBounds(); if (!selectionBounds) return false; const selectionRotation = editor.getSelectionRotation(); if (!selectionRotation) return selectionBounds.containsPoint(point); return (0, import_editor.pointInPolygon)( point, selectionBounds.corners.map((c) => import_editor.Vec.RotWith(c, selectionBounds.point, selectionRotation)) ); } //# sourceMappingURL=Idle.js.map