@leafer-ui/core
Version:
1,362 lines (1,328 loc) • 49.3 kB
JavaScript
'use strict';
var draw = require('@leafer-ui/draw');
var core = require('@leafer/core');
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
function __decorate(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
exports.App = class App extends draw.Leafer {
get __tag() { return 'App'; }
get isApp() { return true; }
constructor(userConfig, data) {
super(userConfig, data);
}
init(userConfig, parentApp) {
super.init(userConfig, parentApp);
if (userConfig) {
const { ground, tree, sky, editor } = userConfig;
if (ground)
this.ground = this.addLeafer(ground);
if (tree || editor)
this.tree = this.addLeafer(tree || { type: userConfig.type || 'design' });
if (sky || editor)
this.sky = this.addLeafer(sky);
if (editor)
core.Creator.editor(editor, this);
}
}
__setApp() {
const { canvas } = this;
const { realCanvas, view } = this.config;
if (realCanvas || view === this.canvas.view || !canvas.parentView)
this.realCanvas = true;
else
canvas.unrealCanvas();
this.leafer = this;
this.watcher.disable();
this.layouter.disable();
}
__updateLocalBounds() {
this.forEach(leafer => leafer.updateLayout());
super.__updateLocalBounds();
}
start() {
super.start();
this.forEach(leafer => leafer.start());
}
stop() {
this.forEach(leafer => leafer.stop());
super.stop();
}
unlockLayout() {
super.unlockLayout();
this.forEach(leafer => leafer.unlockLayout());
}
lockLayout() {
super.lockLayout();
this.forEach(leafer => leafer.lockLayout());
}
forceRender(bounds, sync) {
this.forEach(leafer => leafer.forceRender(bounds, sync));
}
addLeafer(merge) {
const leafer = new draw.Leafer(merge);
this.add(leafer);
return leafer;
}
add(leafer, index) {
if (!leafer.view) {
if (this.realCanvas && !this.canvas.bounds) {
setTimeout(() => this.add(leafer, index), 10);
return;
}
leafer.init(this.__getChildConfig(leafer.userConfig), this);
}
super.add(leafer, index);
if (index !== undefined)
leafer.canvas.childIndex = index;
this.__listenChildEvents(leafer);
}
forEach(fn) {
this.children.forEach(fn);
}
__onCreated() {
this.created = this.children.every(child => child.created);
}
__onReady() {
if (this.children.every(child => child.ready))
super.__onReady();
}
__onViewReady() {
if (this.children.every(child => child.viewReady))
super.__onViewReady();
}
__onChildRenderEnd(e) {
this.renderer.addBlock(e.renderBounds);
if (this.viewReady)
this.renderer.update();
}
__render(canvas, options) {
if (canvas.context)
this.forEach(leafer => options.matrix ? leafer.__render(canvas, options) : canvas.copyWorld(leafer.canvas, options && options.bounds));
}
__onResize(event) {
this.forEach(leafer => leafer.resize(event));
super.__onResize(event);
}
updateLayout() {
this.forEach(leafer => leafer.updateLayout());
}
__getChildConfig(userConfig) {
const config = Object.assign({}, this.config);
config.hittable = config.realCanvas = undefined;
if (userConfig)
core.DataHelper.assign(config, userConfig);
if (this.autoLayout)
core.DataHelper.copyAttrs(config, this, core.canvasSizeAttrs);
config.view = this.realCanvas ? undefined : this.view;
config.fill = undefined;
return config;
}
__listenChildEvents(leafer) {
leafer.once([
[core.LayoutEvent.END, this.__onReady, this],
[core.RenderEvent.START, this.__onCreated, this],
[core.RenderEvent.END, this.__onViewReady, this]
]);
if (this.realCanvas)
this.__eventIds.push(leafer.on_(core.RenderEvent.END, this.__onChildRenderEnd, this));
}
};
exports.App = __decorate([
core.registerUI()
], exports.App);
const downKeyMap = {};
const Keyboard = {
isHoldSpaceKey() {
return Keyboard.isHold('Space');
},
isHold(code) {
return downKeyMap[code];
},
setDownCode(code) {
if (!downKeyMap[code])
downKeyMap[code] = true;
},
setUpCode(code) {
downKeyMap[code] = false;
}
};
const PointerButton = {
LEFT: 1,
RIGHT: 2,
MIDDLE: 4,
defaultLeft(event) { if (!event.buttons)
event.buttons = 1; },
left(event) { return event.buttons === 1; },
right(event) { return event.buttons === 2; },
middle(event) { return event.buttons === 4; }
};
class UIEvent extends core.Event {
get spaceKey() { return Keyboard.isHoldSpaceKey(); }
get left() { return PointerButton.left(this); }
get right() { return PointerButton.right(this); }
get middle() { return PointerButton.middle(this); }
constructor(params) {
super(params.type);
this.bubbles = true;
Object.assign(this, params);
}
getBoxPoint(relative) {
return (relative || this.current).getBoxPoint(this);
}
getInnerPoint(relative) {
return (relative || this.current).getInnerPoint(this);
}
getLocalPoint(relative) {
return (relative || this.current).getLocalPoint(this);
}
getPagePoint() {
return this.current.getPagePoint(this);
}
getInner(relative) { return this.getInnerPoint(relative); }
getLocal(relative) { return this.getLocalPoint(relative); }
getPage() { return this.getPagePoint(); }
static changeName(oldName, newName) {
core.EventCreator.changeName(oldName, newName);
}
}
exports.PointerEvent = class PointerEvent extends UIEvent {
};
exports.PointerEvent.POINTER = 'pointer';
exports.PointerEvent.BEFORE_DOWN = 'pointer.before_down';
exports.PointerEvent.BEFORE_MOVE = 'pointer.before_move';
exports.PointerEvent.BEFORE_UP = 'pointer.before_up';
exports.PointerEvent.DOWN = 'pointer.down';
exports.PointerEvent.MOVE = 'pointer.move';
exports.PointerEvent.UP = 'pointer.up';
exports.PointerEvent.OVER = 'pointer.over';
exports.PointerEvent.OUT = 'pointer.out';
exports.PointerEvent.ENTER = 'pointer.enter';
exports.PointerEvent.LEAVE = 'pointer.leave';
exports.PointerEvent.TAP = 'tap';
exports.PointerEvent.DOUBLE_TAP = 'double_tap';
exports.PointerEvent.CLICK = 'click';
exports.PointerEvent.DOUBLE_CLICK = 'double_click';
exports.PointerEvent.LONG_PRESS = 'long_press';
exports.PointerEvent.LONG_TAP = 'long_tap';
exports.PointerEvent.MENU = 'pointer.menu';
exports.PointerEvent.MENU_TAP = 'pointer.menu_tap';
exports.PointerEvent = __decorate([
core.registerUIEvent()
], exports.PointerEvent);
const MyPointerEvent = exports.PointerEvent;
const tempMove = {};
exports.DragEvent = class DragEvent extends exports.PointerEvent {
static setList(data) {
this.list = data instanceof core.LeafList ? data : new core.LeafList(data);
}
static setData(data) {
this.data = data;
}
static getValidMove(leaf, start, total) {
const { draggable, dragBounds } = leaf, move = leaf.getLocalPoint(total, null, true);
core.PointHelper.move(move, start.x - leaf.x, start.y - leaf.y);
if (dragBounds)
this.getMoveInDragBounds(leaf.__localBoxBounds, dragBounds === 'parent' ? leaf.parent.boxBounds : dragBounds, move, true);
if (draggable === 'x')
move.y = 0;
if (draggable === 'y')
move.x = 0;
return move;
}
static getMoveInDragBounds(childBox, dragBounds, move, change) {
const x = childBox.x + move.x, y = childBox.y + move.y, right = x + childBox.width, bottom = y + childBox.height;
const boundsRight = dragBounds.x + dragBounds.width, boundsBottom = dragBounds.y + dragBounds.height;
if (!change)
move = Object.assign({}, move);
if (core.BoundsHelper.includes(childBox, dragBounds)) {
if (x > dragBounds.x)
move.x += dragBounds.x - x;
else if (right < boundsRight)
move.x += boundsRight - right;
if (y > dragBounds.y)
move.y += dragBounds.y - y;
else if (bottom < boundsBottom)
move.y += boundsBottom - bottom;
}
else {
if (x < dragBounds.x)
move.x += dragBounds.x - x;
else if (right > boundsRight)
move.x += boundsRight - right;
if (y < dragBounds.y)
move.y += dragBounds.y - y;
else if (bottom > boundsBottom)
move.y += boundsBottom - bottom;
}
return move;
}
getPageMove(total) {
this.assignMove(total);
return this.current.getPagePoint(tempMove, null, true);
}
getInnerMove(relative, total) {
if (!relative)
relative = this.current;
this.assignMove(total);
return relative.getInnerPoint(tempMove, null, true);
}
getLocalMove(relative, total) {
if (!relative)
relative = this.current;
this.assignMove(total);
return relative.getLocalPoint(tempMove, null, true);
}
getPageTotal() {
return this.getPageMove(true);
}
getInnerTotal(relative) {
return this.getInnerMove(relative, true);
}
getLocalTotal(relative) {
return this.getLocalMove(relative, true);
}
getPageBounds() {
const total = this.getPageTotal(), start = this.getPagePoint(), bounds = {};
core.BoundsHelper.set(bounds, start.x - total.x, start.y - total.y, total.x, total.y);
core.BoundsHelper.unsign(bounds);
return bounds;
}
assignMove(total) {
tempMove.x = total ? this.totalX : this.moveX;
tempMove.y = total ? this.totalY : this.moveY;
}
};
exports.DragEvent.BEFORE_DRAG = 'drag.before_drag';
exports.DragEvent.START = 'drag.start';
exports.DragEvent.DRAG = 'drag';
exports.DragEvent.END = 'drag.end';
exports.DragEvent.OVER = 'drag.over';
exports.DragEvent.OUT = 'drag.out';
exports.DragEvent.ENTER = 'drag.enter';
exports.DragEvent.LEAVE = 'drag.leave';
exports.DragEvent = __decorate([
core.registerUIEvent()
], exports.DragEvent);
const MyDragEvent = exports.DragEvent;
exports.DropEvent = class DropEvent extends exports.PointerEvent {
static setList(data) {
exports.DragEvent.setList(data);
}
static setData(data) {
exports.DragEvent.setData(data);
}
};
exports.DropEvent.DROP = 'drop';
exports.DropEvent = __decorate([
core.registerUIEvent()
], exports.DropEvent);
exports.MoveEvent = class MoveEvent extends exports.DragEvent {
};
exports.MoveEvent.BEFORE_MOVE = 'move.before_move';
exports.MoveEvent.START = 'move.start';
exports.MoveEvent.MOVE = 'move';
exports.MoveEvent.END = 'move.end';
exports.MoveEvent = __decorate([
core.registerUIEvent()
], exports.MoveEvent);
exports.RotateEvent = class RotateEvent extends exports.PointerEvent {
};
exports.RotateEvent.BEFORE_ROTATE = 'rotate.before_rotate';
exports.RotateEvent.START = 'rotate.start';
exports.RotateEvent.ROTATE = 'rotate';
exports.RotateEvent.END = 'rotate.end';
exports.RotateEvent = __decorate([
core.registerUIEvent()
], exports.RotateEvent);
exports.SwipeEvent = class SwipeEvent extends exports.DragEvent {
};
exports.SwipeEvent.SWIPE = 'swipe';
exports.SwipeEvent.LEFT = 'swipe.left';
exports.SwipeEvent.RIGHT = 'swipe.right';
exports.SwipeEvent.UP = 'swipe.up';
exports.SwipeEvent.DOWN = 'swipe.down';
exports.SwipeEvent = __decorate([
core.registerUIEvent()
], exports.SwipeEvent);
exports.ZoomEvent = class ZoomEvent extends exports.PointerEvent {
};
exports.ZoomEvent.BEFORE_ZOOM = 'zoom.before_zoom';
exports.ZoomEvent.START = 'zoom.start';
exports.ZoomEvent.ZOOM = 'zoom';
exports.ZoomEvent.END = 'zoom.end';
exports.ZoomEvent = __decorate([
core.registerUIEvent()
], exports.ZoomEvent);
exports.KeyEvent = class KeyEvent extends UIEvent {
};
exports.KeyEvent.DOWN = 'key.down';
exports.KeyEvent.HOLD = 'key.hold';
exports.KeyEvent.UP = 'key.up';
exports.KeyEvent = __decorate([
core.registerUIEvent()
], exports.KeyEvent);
const InteractionHelper = {
getDragEventData(startPoint, lastPoint, event) {
return Object.assign(Object.assign({}, event), { x: event.x, y: event.y, moveX: event.x - lastPoint.x, moveY: event.y - lastPoint.y, totalX: event.x - startPoint.x, totalY: event.y - startPoint.y });
},
getDropEventData(event, list, data) {
return Object.assign(Object.assign({}, event), { list,
data });
},
getSwipeDirection(angle) {
if (angle < -45 && angle > -135)
return exports.SwipeEvent.UP;
else if (angle > 45 && angle < 135)
return exports.SwipeEvent.DOWN;
else if (angle <= 45 && angle >= -45)
return exports.SwipeEvent.RIGHT;
else
return exports.SwipeEvent.LEFT;
},
getSwipeEventData(startPoint, lastDragData, event) {
return Object.assign(Object.assign({}, event), { moveX: lastDragData.moveX, moveY: lastDragData.moveY, totalX: event.x - startPoint.x, totalY: event.y - startPoint.y, type: I.getSwipeDirection(core.PointHelper.getAngle(startPoint, event)) });
},
getBase(e) {
const pointerUpButtons = e.button === 1 ? 4 : e.button;
return {
altKey: e.altKey,
ctrlKey: e.ctrlKey,
shiftKey: e.shiftKey,
metaKey: e.metaKey,
buttons: e.buttons === undefined ? 1 : (e.buttons === 0 ? pointerUpButtons : e.buttons),
origin: e
};
},
pathHasEventType(path, type) {
const { list } = path;
for (let i = 0, len = list.length; i < len; i++) {
if (list[i].hasEvent(type))
return true;
}
return false;
},
filterPathByEventType(path, type) {
const find = new core.LeafList();
const { list } = path;
for (let i = 0, len = list.length; i < len; i++) {
if (list[i].hasEvent(type))
find.add(list[i]);
}
return find;
},
pathCanDrag(path) {
return path && path.list.some(item => item.draggable || item.editable || (!item.isLeafer && item.hasEvent(exports.DragEvent.DRAG)));
},
pathHasOutside(path) {
return path && path.list.some(item => item.isOutside);
},
};
const I = InteractionHelper;
const emptyList = new core.LeafList();
const { getDragEventData, getDropEventData, getSwipeEventData } = InteractionHelper;
class Dragger {
constructor(interaction) {
this.interaction = interaction;
}
setDragData(data) {
if (this.animateWait)
this.dragEndReal();
this.downData = this.interaction.downData;
this.dragData = getDragEventData(data, data, data);
this.canAnimate = this.canDragOut = true;
}
getList(realDraggable, hover) {
const { proxy } = this.interaction.selector;
const hasProxyList = proxy && proxy.list.length, dragList = exports.DragEvent.list || this.draggableList || emptyList;
return this.dragging && (hasProxyList ? (realDraggable ? emptyList : new core.LeafList(hover ? [...proxy.list, ...proxy.dragHoverExclude] : proxy.list)) : dragList);
}
checkDrag(data, canDrag) {
const { interaction } = this;
if (this.moving && data.buttons < 1) {
this.canAnimate = false;
interaction.pointerCancel();
return;
}
if (!this.moving && canDrag) {
if (this.moving = interaction.canMove(this.downData) || interaction.isHoldRightKey || interaction.isMobileDragEmpty) {
this.dragData.moveType = 'drag';
interaction.emit(exports.MoveEvent.START, this.dragData);
}
}
if (!this.moving)
this.dragStart(data, canDrag);
this.drag(data);
}
dragStart(data, canDrag) {
if (!this.dragging) {
this.dragging = canDrag && PointerButton.left(data);
if (this.dragging) {
this.interaction.emit(exports.DragEvent.START, this.dragData);
this.getDraggableList(this.dragData.path);
this.setDragStartPoints(this.realDraggableList = this.getList(true));
}
}
}
setDragStartPoints(list) {
this.dragStartPoints = {};
list.forEach(leaf => this.dragStartPoints[leaf.innerId] = { x: leaf.x, y: leaf.y });
}
getDraggableList(path) {
let leaf;
for (let i = 0, len = path.length; i < len; i++) {
leaf = path.list[i];
if ((leaf.draggable || leaf.editable) && leaf.hitSelf && !leaf.locked) {
this.draggableList = new core.LeafList(leaf);
break;
}
}
}
drag(data) {
const { interaction, dragData, downData } = this;
const { path, throughPath } = downData;
this.dragData = getDragEventData(downData, dragData, data);
if (throughPath)
this.dragData.throughPath = throughPath;
this.dragData.path = path;
if (this.moving) {
this.dragData.moveType = 'drag';
interaction.emit(exports.MoveEvent.BEFORE_MOVE, this.dragData);
interaction.emit(exports.MoveEvent.MOVE, this.dragData);
}
else if (this.dragging) {
this.dragReal();
interaction.emit(exports.DragEvent.BEFORE_DRAG, this.dragData);
interaction.emit(exports.DragEvent.DRAG, this.dragData);
}
}
dragReal() {
const { running } = this.interaction;
const list = this.realDraggableList;
if (list.length && running) {
const { totalX, totalY } = this.dragData;
list.forEach(leaf => leaf.draggable && leaf.move(exports.DragEvent.getValidMove(leaf, this.dragStartPoints[leaf.innerId], { x: totalX, y: totalY })));
}
}
dragOverOrOut(data) {
const { interaction } = this;
const { dragOverPath } = this;
const { path } = data;
this.dragOverPath = path;
if (dragOverPath) {
if (path.indexAt(0) !== dragOverPath.indexAt(0)) {
interaction.emit(exports.DragEvent.OUT, data, dragOverPath);
interaction.emit(exports.DragEvent.OVER, data, path);
}
}
else
interaction.emit(exports.DragEvent.OVER, data, path);
}
dragEnterOrLeave(data) {
const { interaction } = this;
const { dragEnterPath } = this;
const { path } = data;
interaction.emit(exports.DragEvent.LEAVE, data, dragEnterPath, path);
interaction.emit(exports.DragEvent.ENTER, data, path, dragEnterPath);
this.dragEnterPath = path;
}
dragEnd(data, speed) {
if (!this.dragging && !this.moving)
return;
if (this.checkDragEndAnimate(data, speed))
return;
this.dragEndReal(data);
}
dragEndReal(data) {
const { interaction, downData, dragData } = this;
if (!data)
data = dragData;
const { path, throughPath } = downData;
const endDragData = getDragEventData(downData, data, data);
if (throughPath)
endDragData.throughPath = throughPath;
endDragData.path = path;
if (this.moving) {
this.moving = false;
endDragData.moveType = 'drag';
interaction.emit(exports.MoveEvent.END, endDragData);
}
if (this.dragging) {
const dropList = this.getList();
this.dragging = false;
interaction.emit(exports.DragEvent.END, endDragData);
this.swipe(data, downData, dragData, endDragData);
this.drop(data, dropList, this.dragEnterPath);
}
this.autoMoveCancel();
this.dragReset();
this.animate(null, 'off');
}
swipe(data, downData, dragData, endDragData) {
const { interaction } = this;
if (core.PointHelper.getDistance(downData, data) > interaction.config.pointer.swipeDistance) {
const swipeData = getSwipeEventData(downData, dragData, endDragData);
this.interaction.emit(swipeData.type, swipeData);
}
}
drop(data, dropList, dragEnterPath) {
const dropData = getDropEventData(data, dropList, exports.DragEvent.data);
dropData.path = dragEnterPath;
this.interaction.emit(exports.DropEvent.DROP, dropData);
this.interaction.emit(exports.DragEvent.LEAVE, data, dragEnterPath);
}
dragReset() {
exports.DragEvent.list = exports.DragEvent.data = this.draggableList = this.dragData = this.downData = this.dragOverPath = this.dragEnterPath = null;
}
checkDragEndAnimate(_data, _speed) { return false; }
animate(_func, _off) { }
checkDragOut(_data) { }
autoMoveOnDragOut(_data) { }
autoMoveCancel() { }
destroy() {
this.dragReset();
}
}
const debug = core.Debug.get('emit');
function emit(type, data, path, excludePath) {
if (!path && !data.path)
return;
let leaf;
data.type = type;
if (path) {
data = Object.assign(Object.assign({}, data), { path });
}
else {
path = data.path;
}
data.target = path.indexAt(0);
try {
for (let i = path.length - 1; i > -1; i--) {
leaf = path.list[i];
if (emitEvent(leaf, type, data, true, excludePath))
return;
if (leaf.isApp)
emitAppChildren(leaf, type, data, true, excludePath);
}
for (let i = 0, len = path.length; i < len; i++) {
leaf = path.list[i];
if (leaf.isApp)
emitAppChildren(leaf, type, data, false, excludePath);
if (emitEvent(leaf, type, data, false, excludePath))
return;
}
}
catch (e) {
debug.error(e);
}
}
const allowTypes = ['move', 'zoom', 'rotate', 'key'];
function emitAppChildren(leaf, type, data, capture, excludePath) {
if (allowTypes.some(name => type.startsWith(name)) && leaf.__.hitChildren && !exclude(leaf, excludePath)) {
let child;
for (let i = 0, len = leaf.children.length; i < len; i++) {
child = leaf.children[i];
if (!data.path.has(child) && child.__.hittable)
emitEvent(child, type, data, capture, excludePath);
}
}
}
function emitEvent(leaf, type, data, capture, excludePath) {
if (leaf.destroyed)
return false;
if (leaf.__.hitSelf && !exclude(leaf, excludePath)) {
if (draw.State.updateEventStyle && !capture)
draw.State.updateEventStyle(leaf, type);
if (leaf.hasEvent(type, capture)) {
data.phase = capture ? 1 : ((leaf === data.target) ? 2 : 3);
const event = core.EventCreator.get(type, data);
leaf.emitEvent(event, capture);
if (event.isStop)
return true;
}
}
return false;
}
function exclude(leaf, excludePath) {
return excludePath && excludePath.has(leaf);
}
const config = {
wheel: {
zoomSpeed: 0.5,
moveSpeed: 0.5,
rotateSpeed: 0.5,
delta: { x: 80 / 4, y: 8.0 },
},
pointer: {
type: 'pointer',
snap: true,
hitRadius: 5,
tapTime: 120,
longPressTime: 800,
transformTime: 500,
hover: true,
dragHover: true,
dragDistance: 2,
swipeDistance: 20,
},
touch: {
preventDefault: 'auto'
},
multiTouch: {},
move: { autoDistance: 2 },
zoom: {},
cursor: true,
keyEvent: true
};
const { pathHasEventType, pathCanDrag, pathHasOutside } = InteractionHelper;
class InteractionBase {
get dragging() { return this.dragger.dragging; }
get transforming() { return this.transformer.transforming; }
get moveMode() { return this.m.drag === true || this.isHoldSpaceKey || this.isHoldMiddleKey || (this.isHoldRightKey && this.dragger.moving) || this.isDragEmpty; }
get canHover() { return this.p.hover && !this.config.mobile; }
get isDragEmpty() { return this.m.dragEmpty && this.isRootPath(this.hoverData) && (!this.downData || this.isRootPath(this.downData)); }
get isMobileDragEmpty() { return this.m.dragEmpty && !this.canHover && this.downData && this.isTreePath(this.downData); }
get isHoldMiddleKey() { return this.m.holdMiddleKey && this.downData && PointerButton.middle(this.downData); }
get isHoldRightKey() { return this.m.holdRightKey && this.downData && PointerButton.right(this.downData); }
get isHoldSpaceKey() { return this.m.holdSpaceKey && Keyboard.isHoldSpaceKey(); }
get m() { return this.config.move; }
get p() { return this.config.pointer; }
get hitRadius() { return this.p.hitRadius; }
constructor(target, canvas, selector, userConfig) {
this.config = core.DataHelper.clone(config);
this.tapCount = 0;
this.downKeyMap = {};
this.target = target;
this.canvas = canvas;
this.selector = selector;
this.defaultPath = new core.LeafList(target);
this.createTransformer();
this.dragger = new Dragger(this);
if (userConfig)
this.config = core.DataHelper.default(userConfig, this.config);
this.__listenEvents();
}
start() {
this.running = true;
}
stop() {
this.running = false;
}
receive(_event) { }
pointerDown(data, useDefaultPath) {
if (!data)
data = this.hoverData;
if (!data)
return;
PointerButton.defaultLeft(data);
this.updateDownData(data);
this.checkPath(data, useDefaultPath);
this.downTime = Date.now();
this.emit(exports.PointerEvent.BEFORE_DOWN, data);
this.emit(exports.PointerEvent.DOWN, data);
if (PointerButton.left(data)) {
this.tapWait();
this.longPressWait(data);
}
this.waitRightTap = PointerButton.right(data);
this.dragger.setDragData(data);
if (!this.isHoldRightKey)
this.updateCursor(data);
}
pointerMove(data) {
if (!data)
data = this.hoverData;
if (!data)
return;
const { downData } = this;
if (downData)
PointerButton.defaultLeft(data);
const hit = this.canvas.bounds.hitPoint(data);
if (hit || downData) {
this.pointerMoveReal(data);
if (downData)
this.dragger.checkDragOut(data);
}
}
pointerMoveReal(data) {
this.emit(exports.PointerEvent.BEFORE_MOVE, data, this.defaultPath);
if (this.downData) {
const canDrag = core.PointHelper.getDistance(this.downData, data) > this.p.dragDistance;
if (canDrag) {
this.pointerWaitCancel();
this.waitRightTap = false;
}
this.dragger.checkDrag(data, canDrag);
}
if (!this.dragger.moving) {
this.updateHoverData(data);
this.checkPath(data);
this.emit(exports.PointerEvent.MOVE, data);
this.pointerHover(data);
if (this.dragging) {
this.dragger.dragOverOrOut(data);
this.dragger.dragEnterOrLeave(data);
}
}
this.updateCursor(this.downData || data);
}
pointerUp(data) {
const { downData } = this;
if (!data)
data = downData;
if (!downData)
return;
PointerButton.defaultLeft(data);
data.multiTouch = downData.multiTouch;
this.findPath(data);
const upData = Object.assign(Object.assign({}, data), { path: data.path.clone() });
data.path.addList(downData.path.list);
this.checkPath(data);
this.downData = null;
this.emit(exports.PointerEvent.BEFORE_UP, data);
this.emit(exports.PointerEvent.UP, data);
this.touchLeave(data);
if (!data.isCancel) {
this.tap(data);
this.menuTap(data);
}
this.dragger.dragEnd(data);
this.updateCursor(upData);
}
pointerCancel() {
const data = Object.assign({}, this.dragger.dragData);
data.isCancel = true;
this.pointerUp(data);
}
menu(data) {
this.findPath(data);
this.emit(exports.PointerEvent.MENU, data);
this.waitMenuTap = true;
if (!this.downData && this.waitRightTap)
this.menuTap(data);
}
menuTap(data) {
if (this.waitRightTap && this.waitMenuTap) {
this.emit(exports.PointerEvent.MENU_TAP, data);
this.waitRightTap = this.waitMenuTap = false;
}
}
createTransformer() { }
move(_data) { }
zoom(_data) { }
rotate(_data) { }
transformEnd() { }
wheel(_data) { }
multiTouch(_data, _list) { }
keyDown(data) {
if (!this.config.keyEvent)
return;
const { code } = data;
if (!this.downKeyMap[code]) {
this.downKeyMap[code] = true;
Keyboard.setDownCode(code);
this.emit(exports.KeyEvent.HOLD, data, this.defaultPath);
if (this.moveMode) {
this.cancelHover();
this.updateCursor();
}
}
this.emit(exports.KeyEvent.DOWN, data, this.defaultPath);
}
keyUp(data) {
if (!this.config.keyEvent)
return;
const { code } = data;
this.downKeyMap[code] = false;
Keyboard.setUpCode(code);
this.emit(exports.KeyEvent.UP, data, this.defaultPath);
if (this.cursor === 'grab')
this.updateCursor();
}
pointerHover(data) {
if (this.canHover && !(this.dragging && !this.p.dragHover)) {
data.path || (data.path = new core.LeafList());
this.pointerOverOrOut(data);
this.pointerEnterOrLeave(data);
}
}
pointerOverOrOut(data) {
const { path } = data;
const { overPath } = this;
this.overPath = path;
if (overPath) {
if (path.indexAt(0) !== overPath.indexAt(0)) {
this.emit(exports.PointerEvent.OUT, data, overPath);
this.emit(exports.PointerEvent.OVER, data, path);
}
}
else {
this.emit(exports.PointerEvent.OVER, data, path);
}
}
pointerEnterOrLeave(data) {
let { path } = data;
if (this.downData && !this.moveMode) {
path = path.clone();
this.downData.path.forEach(leaf => path.add(leaf));
}
const { enterPath } = this;
this.enterPath = path;
this.emit(exports.PointerEvent.LEAVE, data, enterPath, path);
this.emit(exports.PointerEvent.ENTER, data, path, enterPath);
}
touchLeave(data) {
if (data.pointerType === 'touch') {
if (this.enterPath) {
this.emit(exports.PointerEvent.LEAVE, data);
if (this.dragger.dragging)
this.emit(exports.DropEvent.LEAVE, data);
}
}
}
tap(data) {
const { pointer } = this.config;
const hasLong = this.longTap(data);
if (!pointer.tapMore && hasLong)
return;
if (!this.waitTap)
return;
if (pointer.tapMore)
this.emitTap(data);
const useTime = Date.now() - this.downTime;
const hasDouble = [exports.PointerEvent.DOUBLE_TAP, exports.PointerEvent.DOUBLE_CLICK].some(type => pathHasEventType(data.path, type));
if (useTime < pointer.tapTime + 50 && hasDouble) {
this.tapCount++;
if (this.tapCount === 2) {
this.tapWaitCancel();
this.emitDoubleTap(data);
}
else {
clearTimeout(this.tapTimer);
this.tapTimer = setTimeout(() => {
if (!pointer.tapMore) {
this.tapWaitCancel();
this.emitTap(data);
}
}, pointer.tapTime);
}
}
else {
if (!pointer.tapMore) {
this.tapWaitCancel();
this.emitTap(data);
}
}
}
findPath(data, options) {
const { hitRadius, through } = this.p;
const { bottomList, target } = this;
if (!core.Platform.backgrounder && !data.origin)
target && target.updateLayout();
const find = this.selector.getByPoint(data, hitRadius, Object.assign({ bottomList, name: data.type }, (options || { through })));
if (find.throughPath)
data.throughPath = find.throughPath;
data.path = find.path;
return find.path;
}
isRootPath(data) {
return data && data.path.list[0].isLeafer;
}
isTreePath(data) {
const app = this.target.app;
if (!app || !app.isApp)
return false;
return app.editor && (!data.path.has(app.editor) && data.path.has(app.tree) && !data.target.syncEventer);
}
checkPath(data, useDefaultPath) {
if (useDefaultPath || (this.moveMode && !pathHasOutside(data.path)))
data.path = this.defaultPath;
}
canMove(data) {
return data && (this.moveMode || (this.m.drag === 'auto' && !pathCanDrag(data.path))) && !pathHasOutside(data.path);
}
isDrag(leaf) {
return this.dragger.getList().has(leaf);
}
isPress(leaf) {
return this.downData && this.downData.path.has(leaf);
}
isHover(leaf) {
return this.enterPath && this.enterPath.has(leaf);
}
isFocus(leaf) {
return this.focusData === leaf;
}
cancelHover() {
const { hoverData } = this;
if (hoverData) {
hoverData.path = this.defaultPath;
this.pointerHover(hoverData);
}
}
updateDownData(data, options, merge) {
const { downData } = this;
if (!data && downData)
data = downData;
if (!data)
return;
this.findPath(data, options);
if (merge && downData)
data.path.addList(downData.path.list);
this.downData = data;
}
updateHoverData(data) {
if (!data)
data = this.hoverData;
if (!data)
return;
this.findPath(data, { exclude: this.dragger.getList(false, true), name: exports.PointerEvent.MOVE });
this.hoverData = data;
}
updateCursor(data) {
if (!this.config.cursor || !this.canHover)
return;
if (!data) {
this.updateHoverData();
data = this.downData || this.hoverData;
}
if (this.dragger.moving) {
return this.setCursor('grabbing');
}
else if (this.canMove(data)) {
return this.setCursor(this.downData ? 'grabbing' : 'grab');
}
else if (!data)
return;
let leaf, cursor;
const { path } = data;
for (let i = 0, len = path.length; i < len; i++) {
leaf = path.list[i];
cursor = (leaf.syncEventer && leaf.syncEventer.cursor) || leaf.cursor;
if (cursor)
break;
}
this.setCursor(cursor);
}
setCursor(cursor) {
this.cursor = cursor;
}
getLocal(clientPoint, updateClient) {
const clientBounds = this.canvas.getClientBounds(updateClient);
const point = { x: clientPoint.clientX - clientBounds.x, y: clientPoint.clientY - clientBounds.y };
if (this.p.snap)
core.PointHelper.round(point);
return point;
}
emitTap(data) {
this.emit(exports.PointerEvent.TAP, data);
this.emit(exports.PointerEvent.CLICK, data);
}
emitDoubleTap(data) {
this.emit(exports.PointerEvent.DOUBLE_TAP, data);
this.emit(exports.PointerEvent.DOUBLE_CLICK, data);
}
pointerWaitCancel() {
this.tapWaitCancel();
this.longPressWaitCancel();
}
tapWait() {
clearTimeout(this.tapTimer);
this.waitTap = true;
}
tapWaitCancel() {
if (this.waitTap) {
clearTimeout(this.tapTimer);
this.waitTap = false;
this.tapCount = 0;
}
}
longPressWait(data) {
clearTimeout(this.longPressTimer);
this.longPressTimer = setTimeout(() => {
this.longPressed = true;
this.emit(exports.PointerEvent.LONG_PRESS, data);
}, this.p.longPressTime);
}
longTap(data) {
let hasLong;
if (this.longPressed) {
this.emit(exports.PointerEvent.LONG_TAP, data);
if (pathHasEventType(data.path, exports.PointerEvent.LONG_TAP) || pathHasEventType(data.path, exports.PointerEvent.LONG_PRESS))
hasLong = true;
}
this.longPressWaitCancel();
return hasLong;
}
longPressWaitCancel() {
if (this.longPressTimer) {
clearTimeout(this.longPressTimer);
this.longPressed = false;
}
}
__onResize() {
const { dragOut } = this.m;
this.shrinkCanvasBounds = new core.Bounds(this.canvas.bounds);
this.shrinkCanvasBounds.spread(-(typeof dragOut === 'number' ? dragOut : 2));
}
__listenEvents() {
const { target } = this;
this.__eventIds = [target.on_(core.ResizeEvent.RESIZE, this.__onResize, this)];
target.once(core.LeaferEvent.READY, () => this.__onResize());
}
__removeListenEvents() {
this.target.off_(this.__eventIds);
this.__eventIds.length = 0;
}
emit(type, data, path, excludePath) {
if (this.running)
emit(type, data, path, excludePath);
}
destroy() {
if (this.__eventIds.length) {
this.stop();
this.__removeListenEvents();
this.dragger.destroy();
if (this.transformer)
this.transformer.destroy();
this.downData = this.overPath = this.enterPath = null;
}
}
}
class Cursor {
static set(name, value) {
this.custom[name] = value;
}
static get(name) {
return this.custom[name];
}
}
Cursor.custom = {};
class HitCanvasManager extends core.CanvasManager {
constructor() {
super(...arguments);
this.maxTotal = 1000;
this.pathList = new core.LeafList();
this.pixelList = new core.LeafList();
}
getPixelType(leaf, config) {
this.__autoClear();
this.pixelList.add(leaf);
return core.Creator.hitCanvas(config);
}
getPathType(leaf) {
this.__autoClear();
this.pathList.add(leaf);
return core.Creator.hitCanvas();
}
clearImageType() {
this.__clearLeafList(this.pixelList);
}
clearPathType() {
this.__clearLeafList(this.pathList);
}
__clearLeafList(leafList) {
if (leafList.length) {
leafList.forEach(leaf => {
if (leaf.__hitCanvas) {
leaf.__hitCanvas.destroy();
leaf.__hitCanvas = null;
}
});
leafList.reset();
}
}
__autoClear() {
if (this.pathList.length + this.pixelList.length > this.maxTotal)
this.clear();
}
clear() {
this.clearPathType();
this.clearImageType();
}
}
const { toInnerRadiusPointOf, copy, setRadius } = core.PointHelper;
const inner = {};
const leaf = core.Leaf.prototype;
leaf.__hitWorld = function (point) {
const data = this.__;
if (!data.hitSelf)
return false;
const world = this.__world, layout = this.__layout;
const isSmall = world.width < 10 && world.height < 10;
if (data.hitRadius) {
copy(inner, point), point = inner;
setRadius(point, data.hitRadius);
}
toInnerRadiusPointOf(point, world, inner);
if (data.hitBox || isSmall) {
if (core.BoundsHelper.hitRadiusPoint(layout.boxBounds, inner))
return true;
if (isSmall)
return false;
}
if (layout.hitCanvasChanged || !this.__hitCanvas) {
this.__updateHitCanvas();
if (!layout.boundsChanged)
layout.hitCanvasChanged = false;
}
return this.__hit(inner);
};
leaf.__hitFill = function (inner) { const h = this.__hitCanvas; return h && h.hitFill(inner, this.__.windingRule); };
leaf.__hitStroke = function (inner, strokeWidth) { const h = this.__hitCanvas; return h && h.hitStroke(inner, strokeWidth); };
leaf.__hitPixel = function (inner) { const h = this.__hitCanvas; return h && h.hitPixel(inner, this.__layout.renderBounds, h.hitScale); };
leaf.__drawHitPath = function (canvas) { canvas && this.__drawRenderPath(canvas); };
const matrix = new core.Matrix();
const ui$1 = draw.UI.prototype;
ui$1.__updateHitCanvas = function () {
if (this.__box)
this.__box.__updateHitCanvas();
const leafer = this.leafer || (this.parent && this.parent.leafer);
if (!leafer)
return;
const data = this.__, { hitCanvasManager } = leafer;
const isHitPixelFill = (data.__isAlphaPixelFill || data.__isCanvas) && data.hitFill === 'pixel';
const isHitPixelStroke = data.__isAlphaPixelStroke && data.hitStroke === 'pixel';
const isHitPixel = isHitPixelFill || isHitPixelStroke;
if (!this.__hitCanvas)
this.__hitCanvas = isHitPixel ? hitCanvasManager.getPixelType(this, { contextSettings: { willReadFrequently: true } }) : hitCanvasManager.getPathType(this);
const h = this.__hitCanvas;
if (isHitPixel) {
const { renderBounds } = this.__layout;
const size = core.Platform.image.hitCanvasSize;
const scale = h.hitScale = core.tempBounds.set(0, 0, size, size).getFitMatrix(renderBounds).a;
const { x, y, width, height } = core.tempBounds.set(renderBounds).scale(scale);
h.resize({ width, height, pixelRatio: 1 });
h.clear();
core.ImageManager.patternLocked = true;
this.__renderShape(h, { matrix: matrix.setWith(this.__world).scaleWith(1 / scale).invertWith().translate(-x, -y), ignoreFill: !isHitPixelFill, ignoreStroke: !isHitPixelStroke });
core.ImageManager.patternLocked = false;
h.resetTransform();
data.__isHitPixel = true;
}
else {
data.__isHitPixel && (data.__isHitPixel = false);
}
this.__drawHitPath(h);
h.setStrokeOptions(data);
};
ui$1.__hit = function (inner) {
if (this.__box && this.__box.__hit(inner))
return true;
const data = this.__;
if (data.__isHitPixel && this.__hitPixel(inner))
return true;
const { hitFill } = data;
const needHitFillPath = ((data.fill || data.__isCanvas) && (hitFill === 'path' || (hitFill === 'pixel' && !(data.__isAlphaPixelFill || data.__isCanvas)))) || hitFill === 'all';
if (needHitFillPath && this.__hitFill(inner))
return true;
const { hitStroke, __maxStrokeWidth: strokeWidth } = data;
const needHitStrokePath = (data.stroke && (hitStroke === 'path' || (hitStroke === 'pixel' && !data.__isAlphaPixelStroke))) || hitStroke === 'all';
if (!needHitFillPath && !needHitStrokePath)
return false;
const radiusWidth = inner.radiusX * 2;
let hitWidth = radiusWidth;
if (needHitStrokePath) {
switch (data.strokeAlign) {
case 'inside':
hitWidth += strokeWidth * 2;
if (!needHitFillPath && this.__hitFill(inner) && this.__hitStroke(inner, hitWidth))
return true;
hitWidth = radiusWidth;
break;
case 'center':
hitWidth += strokeWidth;
break;
case 'outside':
hitWidth += strokeWidth * 2;
if (!needHitFillPath) {
if (!this.__hitFill(inner) && this.__hitStroke(inner, hitWidth))
return true;
hitWidth = radiusWidth;
}
break;
}
}
return hitWidth ? this.__hitStroke(inner, hitWidth) : false;
};
const ui = draw.UI.prototype, rect = draw.Rect.prototype, box = draw.Box.prototype;
rect.__updateHitCanvas = box.__updateHitCanvas = function () {
if (this.stroke || this.cornerRadius || ((this.fill || this.__.__isCanvas) && this.hitFill === 'pixel') || this.hitStroke === 'all')
ui.__updateHitCanvas.call(this);
else if (this.__hitCanvas)
this.__hitCanvas = null;
};
rect.__hitFill = box.__hitFill = function (inner) {
return this.__hitCanvas ? ui.__hitFill.call(this, inner) : core.BoundsHelper.hitRadiusPoint(this.__layout.boxBounds, inner);
};
draw.Text.prototype.__drawHitPath = function (canvas) {
const { __lineHeight, fontSize, __baseLine, __letterSpacing, __textDrawData: data } = this.__;
canvas.beginPath();
if (__letterSpacing < 0)
this.__drawPathByBox(canvas);
else
data.rows.forEach(row => canvas.rect(row.x, row.y - __baseLine, row.width, __lineHeight < fontSize ? fontSize : __lineHeight));
};
function getSelector(ui) {
return ui.leafer ? ui.leafer.selector : (core.Platform.selector || (core.Platform.selector = core.Creator.selector()));
}
draw.Group.prototype.pick = function (hitPoint, options) {
options || (options = draw.emptyData);
this.updateLayout();
return getSelector(this).getByPoint(hitPoint, options.hitRadius || 0, Object.assign(Object.assign({}, options), { target: this }));
};
const canvas = core.LeaferCanvasBase.prototype;
canvas.hitFill = function (point, fillRule) {
return fillRule ? this.context.isPointInPath(point.x, point.y, fillRule) : this.context.isPointInPath(point.x, point.y);
};
canvas.hitStroke = function (point, strokeWidth) {
this.strokeWidth = strokeWidth;
return this.context.isPointInStroke(point.x, point.y);
};
canvas.hitPixel = function (radiusPoint, offset, scale = 1) {
let { x, y, radiusX, radiusY } = radiusPoint;
if (offset)
x -= offset.x, y -= offset.y;
core.tempBounds.set(x - radiusX, y - radiusY, radiusX * 2, radiusY * 2).scale(scale).ceil();
const { data } = this.context.getImageData(core.tempBounds.x, core.tempBounds.y, core.tempBounds.width || 1, core.tempBounds.height || 1);
for (let i = 0, len = data.length; i < len; i += 4) {
if (data[i + 3] > 0)
return true;
}
return data[3] > 0;
};
exports.Cursor = Cursor;
exports.Dragger = Dragger;
exports.HitCanvasManager = HitCanvasManager;
exports.InteractionBase = InteractionBase;
exports.InteractionHelper = InteractionHelper;
exports.Keyboard = Keyboard;
exports.MyDragEvent = MyDragEvent;
exports.MyPointerEvent = MyPointerEvent;
exports.PointerButton = PointerButton;
exports.UIEvent = UIEvent;
Object.keys(draw).forEach(function (k) {
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
enumerable: true,
get: function () { return draw[k]; }
});
});