import { Attrib, ShapeStyles, ShapeType } from "../type"; import { Entity } from "../packages/entity"; import { Shape } from "konva/lib/Shape"; import { Group } from "konva/lib/Group"; import { getAbsoluteTransform } from "./shape-helper"; import { KonvaEventObject } from "konva/lib/Node"; let mouseDisabled = false; const dragShapes: (Shape | Group)[] = []; export const disableMouse = () => { mouseDisabled = true; dragShapes.forEach((shape) => shape.draggable(false)); }; export const enableMouse = () => { mouseDisabled = false; dragShapes.forEach((shape) => shape.draggable(true)); }; export const openShapeMouseStyles = ( shape: T, styles: ShapeStyles, namespace = "mouse-style" ) => { shape.listening(true); const useEvents: string[] = []; const useOn = (name: string, cb: (ev: KonvaEventObject) => void) => { shape.on(name, cb); useEvents.push(name); }; let enter = false; let draging = false; let active = false; const mouseHandler = (ev: KonvaEventObject) => { const api = draging ? "draging" : active ? "active" : enter ? "hover" : "common"; if (!mouseDisabled) { styles[api] && styles[api](ev); } }; if (styles.hover) { useOn(`mouseenter.${namespace}`, (ev) => { enter = true; mouseHandler(ev); }); useOn(`mouseleave.${namespace}`, (ev) => { if (!draging) { enter = false; mouseHandler(ev); } }); } if (styles.active) { useOn(`click.${namespace} touchend.${namespace}`, (ev) => { if (!styles.active) return; if (draging) return; active = true; mouseHandler(ev); setTimeout(() => { const stage = shape.getStage(); if (!stage) return; stage.on( `click.${namespace}${shape.id()} touchend.${namespace}${shape.id()}`, (evt) => { if (evt.target !== shape) { active = false; mouseHandler(evt); } setTimeout(() => { if (!stage) return; stage.off( `click.${namespace}${shape.id()} touchend.${namespace}${shape.id()}` ); }); } ); }); }); } useOn(`dragstart.${namespace}`, (ev) => { draging = true; mouseHandler(ev); }); useOn(`dragend.${namespace}`, (ev) => { setTimeout(() => { draging = false; mouseHandler(ev); }, 16); }); return () => { shape.listening(false); shape.off(useEvents.join(" ")); }; }; export type DragHandlers = { readyHandler?: (ev: KonvaEventObject) => T; moveHandler: ( move: number[], readyData: T, ev: KonvaEventObject ) => void; endHandler?: (readyData: T, ev: KonvaEventObject) => void; }; export const openShapeDrag = ( shape: T, handler: DragHandlers, transform = true, tfIncludeSelf = false ) => { let readlyData = null as D; if (!mouseDisabled) { shape.draggable(true); } dragShapes.push(shape); shape.dragBoundFunc((pos, ev) => { if (!mouseDisabled) { let move = pos; if (transform) { const tf = getAbsoluteTransform(shape, tfIncludeSelf).invert(); move = tf.point(pos); } handler.moveHandler([move.x, move.y], readlyData, ev); } return shape.absolutePosition(); }); if (handler.readyHandler) { shape.on("dragstart.drag", (ev) => { if (!mouseDisabled) { readlyData = handler.readyHandler(ev); } }); } if (handler.endHandler) { shape.on("dragend.drag", (ev) => { if (!mouseDisabled) { handler.endHandler(readlyData, ev); } }); } return () => { const ndx = dragShapes.indexOf(shape); if (~ndx) { dragShapes.splice(ndx, 1); } shape.draggable(false); shape.off("dragstart.drag dragend.drag"); }; }; export type EntityDragHandlers = { readyHandler?: (attrib: R, ev: KonvaEventObject) => T; moveHandler: ( attrib: R, move: number[], readyData: T, ev: KonvaEventObject ) => void; endHandler?: (attrib: R, readyData: T, ev: KonvaEventObject) => void; }; export const openEntityDrag = ( entity: Entity, handler: EntityDragHandlers ) => { entity.enableDrag({ readyHandler(ev) { return handler.readyHandler && handler.readyHandler(entity.attrib, ev); }, moveHandler(move: number[], readyData: R, ev) { return handler.moveHandler(entity.attrib, move, readyData, ev); }, endHandler(readlyData: R, ev) { return ( handler.endHandler && handler.endHandler(entity.attrib, readlyData, ev) ); }, }); }; export const shapeTreeEq = ( parent: ShapeType, eq: (shape: ShapeType) => boolean ) => { if (eq(parent)) { return parent; } if ("children" in parent) { for (const child of parent.children) { const e = shapeTreeEq(child, eq); if (e) { return child; } } } return null; }; export const shapeParentsEq = ( target: ShapeType, eq: (shape: ShapeType) => boolean ) => { while (target) { if (eq(target)) { return target; } target = target.parent as any; } return null; }; export const shapeTreeContain = (parent: ShapeType, target: ShapeType) => { const eqShape = shapeTreeEq(parent, (shape) => shape === target); return !!eqShape; };