UNPKG

@progress/kendo-charts

Version:

Kendo UI platform-independent Charts library

408 lines (332 loc) 10.2 kB
import { Observable, elementOffset, eventMap } from '../../common'; import { proxy } from '../utils'; const extend = Object.assign; const CHANGE = 'change'; export class TapCapture extends Observable { constructor(element, options) { super(); let that = this, domElement = element[0] || element; that.capture = false; if (domElement.addEventListener) { eventMap.down.split(' ').forEach(function(event) { domElement.addEventListener(event, proxy(that._press, that), true); }); eventMap.up.split(' ').forEach(function(event) { domElement.addEventListener(event, proxy(that._release, that), true); }); } else { eventMap.down.split(' ').forEach(function(event) { domElement.attachEvent(event, proxy(that._press, that)); }); eventMap.up.split(' ').forEach(function(event) { domElement.attachEvent(event, proxy(that._release, that)); }); } that.bind([ 'press', 'release' ], options || {}); } captureNext() { this.capture = true; } cancelCapture() { this.capture = false; } _press(e) { let that = this; that.trigger('press'); if (that.capture) { e.preventDefault(); } } _release(e) { let that = this; that.trigger('release'); if (that.capture) { e.preventDefault(); that.cancelCapture(); } } } export class PaneDimension extends Observable { constructor(options) { super(); let that = this; that.forcedEnabled = false; extend(that, options); that.scale = 1; if (that.horizontal) { that.measure = 'offsetWidth'; that.scrollSize = 'scrollWidth'; that.axis = 'x'; } else { that.measure = 'offsetHeight'; that.scrollSize = 'scrollHeight'; that.axis = 'y'; } } makeVirtual() { extend(this, { virtual: true, forcedEnabled: true, _virtualMin: 0, _virtualMax: 0 }); } virtualSize(min, max) { if (this._virtualMin !== min || this._virtualMax !== max) { this._virtualMin = min; this._virtualMax = max; this.update(); } } outOfBounds(offset) { return offset > this.max || offset < this.min; } forceEnabled() { this.forcedEnabled = true; } getSize() { return this.container[this.measure]; } getTotal() { return this.element[this.scrollSize]; } rescale(scale) { this.scale = scale; } update(silent) { let that = this, total = that.virtual ? that._virtualMax : that.getTotal(), scaledTotal = total * that.scale, size = that.getSize(); if (total === 0 && !that.forcedEnabled) { return; } that.max = that.virtual ? -that._virtualMin : 0; that.size = size; that.total = scaledTotal; that.min = Math.min(that.max, size - scaledTotal); that.minScale = size / total; that.centerOffset = (scaledTotal - size) / 2; that.enabled = that.forcedEnabled || scaledTotal > size; if (!silent) { that.trigger(CHANGE, that); } } } export class PaneDimensions extends Observable { constructor(options) { super(); let that = this; that.x = new PaneDimension(extend({ horizontal: true }, options)); that.y = new PaneDimension(extend({ horizontal: false }, options)); that.container = options.container; that.forcedMinScale = options.minScale; that.maxScale = options.maxScale || 100; that.bind(CHANGE, options); } rescale(newScale) { this.x.rescale(newScale); this.y.rescale(newScale); this.refresh(); } centerCoordinates() { return { x: Math.min(0, -this.x.centerOffset), y: Math.min(0, -this.y.centerOffset) }; } refresh() { let that = this; that.x.update(); that.y.update(); that.enabled = that.x.enabled || that.y.enabled; that.minScale = that.forcedMinScale || Math.min(that.x.minScale, that.y.minScale); that.fitScale = Math.max(that.x.minScale, that.y.minScale); that.trigger(CHANGE); } } export class PaneAxis extends Observable { constructor(options) { super(); extend(this, options); } outOfBounds() { return this.dimension.outOfBounds(this.movable[this.axis]); } dragMove(delta) { let that = this, dimension = that.dimension, axis = that.axis, movable = that.movable, position = movable[axis] + delta; if (!dimension.enabled) { return; } let dragDelta = delta; if (position < dimension.min && delta < 0 || position > dimension.max && delta > 0) { dragDelta *= that.resistance; } movable.translateAxis(axis, dragDelta); that.trigger(CHANGE, that); } } export class Pane { constructor(options) { let that = this, x, y, resistance, movable; extend(that, { elastic: true }, options); resistance = that.elastic ? 0.5 : 0; movable = that.movable; that.x = x = new PaneAxis({ axis: 'x', dimension: that.dimensions.x, resistance: resistance, movable: movable }); that.y = y = new PaneAxis({ axis: 'y', dimension: that.dimensions.y, resistance: resistance, movable: movable }); that.userEvents.bind([ 'press', 'move', 'end', 'gesturestart', 'gesturechange' ], { gesturestart(e) { that.gesture = e; that.offset = elementOffset(that.dimensions.container); }, press(e) { const closestAnchor = e.event.target.closest('a'); if (closestAnchor && closestAnchor.matches('[data-navigate-on-press=true]')) { e.sender.cancel(); } }, gesturechange(e) { let previousGesture = that.gesture, previousCenter = previousGesture.center, center = e.center, scaleDelta = e.distance / previousGesture.distance, minScale = that.dimensions.minScale, maxScale = that.dimensions.maxScale, coordinates; if (movable.scale <= minScale && scaleDelta < 1) { scaleDelta += (1 - scaleDelta) * 0.8; } if (movable.scale * scaleDelta >= maxScale) { scaleDelta = maxScale / movable.scale; } let offsetX = movable.x + that.offset.left, offsetY = movable.y + that.offset.top; coordinates = { x: (offsetX - previousCenter.x) * scaleDelta + center.x - offsetX, y: (offsetY - previousCenter.y) * scaleDelta + center.y - offsetY }; movable.scaleWith(scaleDelta); x.dragMove(coordinates.x); y.dragMove(coordinates.y); that.dimensions.rescale(movable.scale); that.gesture = e; e.preventDefault(); }, move(e) { if (e.event.target.tagName.match(/textarea|input/i)) { return; } if (x.dimension.enabled || y.dimension.enabled) { x.dragMove(e.x.delta); y.dragMove(e.y.delta); e.preventDefault(); } else { e.touch.skip(); } }, end(e) { e.preventDefault(); } }); } } let translate = function(x, y, scale) { return 'translate3d(' + x + 'px,' + y + 'px,0) scale(' + scale + ')'; }; export class Movable extends Observable { constructor(element) { super(); let that = this; that.element = element; that.element.style.transformOrigin = 'left top'; that.x = 0; that.y = 0; that.scale = 1; const coordinates = translate(that.x, that.y, that.scale); that.element.style.transform = coordinates; that._saveCoordinates(coordinates); } translateAxis(axis, by) { this[axis] += by; this.refresh(); } scaleTo(scale) { this.scale = scale; this.refresh(); } scaleWith(scaleDelta) { this.scale *= scaleDelta; this.refresh(); } translate(coordinates) { this.x += coordinates.x; this.y += coordinates.y; this.refresh(); } moveAxis(axis, value) { this[axis] = value; this.refresh(); } moveTo(coordinates) { extend(this, coordinates); this.refresh(); } refresh() { let that = this, x = that.x, y = that.y, newCoordinates; if (that.round) { x = Math.round(x); y = Math.round(y); } newCoordinates = translate(x, y, that.scale); if (newCoordinates !== that.coordinates) { that.element.style.transform = newCoordinates; that._saveCoordinates(newCoordinates); that.trigger(CHANGE); } } _saveCoordinates(coordinates) { this.coordinates = coordinates; } }