apexcharts
Version:
A JavaScript Chart Library
141 lines (119 loc) • 3.94 kB
JavaScript
// @ts-check
import { Environment } from '../utils/Environment.js'
// Install draggable behavior on SVGElement prototype
/**
* @param {any} ElementClass
*/
function installDraggable(ElementClass) {
/**
* @param {any} opts
*/
ElementClass.prototype.draggable = function (opts) {
if (opts === false) {
// Disable dragging
if (this._dragCleanup) {
this._dragCleanup()
this._dragCleanup = null
}
return this
}
const el = this
const constraints = opts || {}
/**
* @param {any} e
*/
const onPointerDown = (e) => {
// Ignore right-click
if (e.button && e.button !== 0) return
// Prevent parent handlers (e.g. ZoomPanSelection) from treating this as a new selection
e.stopPropagation()
const isTouch = e.type === 'touchstart'
const ev = isTouch ? e.touches[0] : e
const svgEl = el.node
const startAttrX = parseFloat(svgEl.getAttribute('x')) || 0
const startAttrY = parseFloat(svgEl.getAttribute('y')) || 0
const startClientX = ev.clientX
const startClientY = ev.clientY
// We need to convert client px to SVG coordinate space
const svgRoot = svgEl.ownerSVGElement
/** @type {any} */
let ctm = null
if (svgRoot) {
ctm = svgRoot.getScreenCTM()
}
/**
* @param {any} me
*/
const onMove = (me) => {
const mev = me.type === 'touchmove' ? me.touches[0] : me
let dx = mev.clientX - startClientX
let dy = mev.clientY - startClientY
// Convert pixel delta to SVG coordinate delta
if (ctm) {
dx = dx / ctm.a
dy = dy / ctm.d
}
let newX = startAttrX + dx
let newY = startAttrY + dy
const w = parseFloat(svgEl.getAttribute('width')) || 0
const h = parseFloat(svgEl.getAttribute('height')) || 0
// Apply constraints
if (constraints.minX !== undefined && newX < constraints.minX)
newX = constraints.minX
if (constraints.minY !== undefined && newY < constraints.minY)
newY = constraints.minY
if (constraints.maxX !== undefined && newX + w > constraints.maxX)
newX = constraints.maxX - w
if (constraints.maxY !== undefined && newY + h > constraints.maxY)
newY = constraints.maxY - h
const box = {
x: newX,
y: newY,
w: w,
h: h,
x2: newX + w,
y2: newY + h,
}
// Fire dragmove event matching SVG.js contract
const event = new CustomEvent('dragmove', {
detail: {
handler: {
/**
* @param {number} x
* @param {number} y
*/
move: function (x, y) {
svgEl.setAttribute('x', x)
svgEl.setAttribute('y', y)
},
},
box: box,
},
})
svgEl.dispatchEvent(event)
}
const onUp = () => {
if (Environment.isBrowser()) {
document.removeEventListener('mousemove', onMove)
document.removeEventListener('touchmove', onMove)
document.removeEventListener('mouseup', onUp)
document.removeEventListener('touchend', onUp)
}
}
if (Environment.isBrowser()) {
document.addEventListener('mousemove', onMove)
document.addEventListener('touchmove', onMove)
document.addEventListener('mouseup', onUp)
document.addEventListener('touchend', onUp)
}
}
el.node.addEventListener('mousedown', onPointerDown)
el.node.addEventListener('touchstart', onPointerDown)
el._dragCleanup = () => {
el.node.removeEventListener('mousedown', onPointerDown)
el.node.removeEventListener('touchstart', onPointerDown)
}
return el
}
}
export { installDraggable }