tldraw
Version:
A tiny little drawing editor.
8 lines (7 loc) • 7.53 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../../../src/lib/tools/SelectTool/DragAndDropManager.ts"],
"sourcesContent": ["import { Editor, TLShape, TLShapeId, Vec, bind, compact } from '@tldraw/editor'\nimport { getOccludedChildren } from './selectHelpers'\n\nconst INITIAL_POINTER_LAG_DURATION = 20\nconst FAST_POINTER_LAG_DURATION = 100\n\n/** @public */\nexport class DragAndDropManager {\n\tconstructor(public editor: Editor) {\n\t\teditor.disposables.add(this.dispose)\n\t}\n\n\tprevDroppingShapeId: TLShapeId | null = null\n\n\tdroppingNodeTimer: number | null = null\n\n\tfirst = true\n\n\tupdateDroppingNode(movingShapes: TLShape[], cb: () => void) {\n\t\tif (this.first) {\n\t\t\tthis.editor.setHintingShapes(\n\t\t\t\tmovingShapes\n\t\t\t\t\t.map((s) => this.editor.findShapeAncestor(s, (v) => v.type !== 'group'))\n\t\t\t\t\t.filter((s) => s) as TLShape[]\n\t\t\t)\n\n\t\t\tthis.prevDroppingShapeId =\n\t\t\t\tthis.editor.getDroppingOverShape(this.editor.inputs.originPagePoint, movingShapes)?.id ??\n\t\t\t\tnull\n\t\t\tthis.first = false\n\t\t}\n\n\t\tif (this.droppingNodeTimer === null) {\n\t\t\tthis.setDragTimer(movingShapes, INITIAL_POINTER_LAG_DURATION, cb)\n\t\t} else if (this.editor.inputs.pointerVelocity.len() > 0.5) {\n\t\t\tclearTimeout(this.droppingNodeTimer)\n\t\t\tthis.setDragTimer(movingShapes, FAST_POINTER_LAG_DURATION, cb)\n\t\t}\n\t}\n\n\tprivate setDragTimer(movingShapes: TLShape[], duration: number, cb: () => void) {\n\t\tthis.droppingNodeTimer = this.editor.timers.setTimeout(() => {\n\t\t\tthis.editor.run(() => {\n\t\t\t\tthis.handleDrag(this.editor.inputs.currentPagePoint, movingShapes, cb)\n\t\t\t})\n\t\t\tthis.droppingNodeTimer = null\n\t\t}, duration)\n\t}\n\n\tprivate handleDrag(point: Vec, movingShapes: TLShape[], cb?: () => void) {\n\t\tmovingShapes = compact(movingShapes.map((shape) => this.editor.getShape(shape.id)))\n\n\t\tconst nextDroppingShapeId = this.editor.getDroppingOverShape(point, movingShapes)?.id ?? null\n\n\t\t// is the next dropping shape id different than the last one?\n\t\tif (nextDroppingShapeId === this.prevDroppingShapeId) {\n\t\t\tthis.hintParents(movingShapes)\n\t\t\treturn\n\t\t}\n\n\t\t// the old previous one\n\t\tconst { prevDroppingShapeId } = this\n\n\t\tconst prevDroppingShape = prevDroppingShapeId && this.editor.getShape(prevDroppingShapeId)\n\t\tconst nextDroppingShape = nextDroppingShapeId && this.editor.getShape(nextDroppingShapeId)\n\n\t\t// Even if we don't have a next dropping shape id (i.e. if we're dropping\n\t\t// onto the page) set the prev to the current, to avoid repeat calls to\n\t\t// the previous parent's onDragShapesOut\n\n\t\tif (prevDroppingShape) {\n\t\t\tthis.editor.getShapeUtil(prevDroppingShape).onDragShapesOut?.(prevDroppingShape, movingShapes)\n\t\t}\n\n\t\tif (nextDroppingShape) {\n\t\t\tthis.editor\n\t\t\t\t.getShapeUtil(nextDroppingShape)\n\t\t\t\t.onDragShapesOver?.(nextDroppingShape, movingShapes)\n\t\t}\n\n\t\tthis.hintParents(movingShapes)\n\t\tcb?.()\n\n\t\t// next -> curr\n\t\tthis.prevDroppingShapeId = nextDroppingShapeId\n\t}\n\n\thintParents(movingShapes: TLShape[]) {\n\t\t// Group moving shapes by their ancestor\n\t\tconst shapesGroupedByAncestor = new Map<TLShapeId, TLShapeId[]>()\n\t\tfor (const shape of movingShapes) {\n\t\t\tconst ancestor = this.editor.findShapeAncestor(shape, (v) => v.type !== 'group')\n\t\t\tif (!ancestor) continue\n\t\t\tif (!shapesGroupedByAncestor.has(ancestor.id)) {\n\t\t\t\tshapesGroupedByAncestor.set(ancestor.id, [])\n\t\t\t}\n\t\t\tshapesGroupedByAncestor.get(ancestor.id)!.push(shape.id)\n\t\t}\n\n\t\t// Only hint an ancestor if some shapes will drop into it on pointer up\n\t\tconst hintingShapes = []\n\t\tfor (const [ancestorId, shapeIds] of shapesGroupedByAncestor) {\n\t\t\tconst ancestor = this.editor.getShape(ancestorId)\n\t\t\tif (!ancestor) continue\n\t\t\t// If all of the ancestor's children would be occluded, then don't hint it\n\t\t\t// 1. get the number of fully occluded children\n\t\t\t// 2. if that number is less than the number of moving shapes, hint the ancestor\n\t\t\tif (getOccludedChildren(this.editor, ancestor).length < shapeIds.length) {\n\t\t\t\thintingShapes.push(ancestor.id)\n\t\t\t}\n\t\t}\n\n\t\tthis.editor.setHintingShapes(hintingShapes)\n\t}\n\n\tdropShapes(shapes: TLShape[]) {\n\t\tconst { prevDroppingShapeId } = this\n\n\t\tthis.handleDrag(this.editor.inputs.currentPagePoint, shapes)\n\n\t\tif (prevDroppingShapeId) {\n\t\t\tconst shape = this.editor.getShape(prevDroppingShapeId)\n\t\t\tif (!shape) return\n\t\t\tthis.editor.getShapeUtil(shape).onDropShapesOver?.(shape, shapes)\n\t\t}\n\t}\n\n\tclear() {\n\t\tthis.prevDroppingShapeId = null\n\n\t\tif (this.droppingNodeTimer !== null) {\n\t\t\tclearTimeout(this.droppingNodeTimer)\n\t\t}\n\n\t\tthis.droppingNodeTimer = null\n\t\tthis.editor.setHintingShapes([])\n\t\tthis.first = true\n\t}\n\n\t@bind\n\tdispose() {\n\t\tthis.clear()\n\t}\n}\n"],
"mappings": "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",
"names": []
}