UNPKG

visbug-lib

Version:

<p align="center"> <img src="./assets/visbug.png" width="300" height="300" alt="visbug"> <br> <a href="https://www.npmjs.org/package/visbug"><img src="https://img.shields.io/npm/v/visbug.svg?style=flat" alt="npm latest version number"></a> <a href

235 lines (189 loc) 5.67 kB
import $ from 'blingblingjs' import hotkeys from 'hotkeys-js' import { metaKey, getStyle, getSide, showHideSelected } from '../utilities/' const key_events = 'up,down,left,right' .split(',') .reduce((events, event) => `${events},${event},alt+${event},shift+${event},shift+alt+${event}` , '') .substring(1) const command_events = `${metaKey}+up,${metaKey}+shift+up,${metaKey}+down,${metaKey}+shift+down` export function Position() { const state = { elements: [] } hotkeys(key_events, (e, handler) => { if (e.cancelBubble) return e.preventDefault() positionElement(state.elements, handler.key) }) const onNodesSelected = els => { state.elements.forEach(el => el.teardown()) state.elements = els.map(el => draggable({el})) } const disconnect = () => { state.elements.forEach(el => el.teardown()) hotkeys.unbind(key_events) hotkeys.unbind('up,down,left,right') } return { onNodesSelected, disconnect, } } export function draggable({el, surface = el, cursor = 'move'}) { const state = { target: el, surface, mouse: { down: false, x: 0, y: 0, }, element: { x: 0, y: 0, } } const setup = () => { el.style.transition = 'none' surface.style.cursor = cursor surface.addEventListener('mousedown', onMouseDown, true) surface.addEventListener('mouseup', onMouseUp, true) document.addEventListener('mousemove', onMouseMove, true) } const teardown = () => { el.style.transition = null surface.style.cursor = null surface.removeEventListener('mousedown', onMouseDown, true) surface.removeEventListener('mouseup', onMouseUp, true) document.removeEventListener('mousemove', onMouseMove, true) } const onMouseDown = e => { if(e.target !== state.surface) return e.preventDefault() if(getComputedStyle(el).position == 'static') el.style.position = 'relative' el.style.willChange = 'top,left' if (el instanceof SVGElement) { const translate = el.getAttribute('transform') const [ x, y ] = translate ? extractSVGTranslate(translate) : [0,0] state.element.x = x state.element.y = y } else { state.element.x = parseInt(getStyle(el, 'left')) state.element.y = parseInt(getStyle(el, 'top')) } state.mouse.x = e.clientX state.mouse.y = e.clientY state.mouse.down = true } const onMouseUp = e => { if(e.target !== state.surface) return e.preventDefault() e.stopPropagation() state.mouse.down = false el.style.willChange = null if (el instanceof SVGElement) { const translate = el.getAttribute('transform') const [ x, y ] = translate ? extractSVGTranslate(translate) : [0,0] state.element.x = x state.element.y = y } else { state.element.x = parseInt(el.style.left) || 0 state.element.y = parseInt(el.style.top) || 0 } } const onMouseMove = e => { if (!state.mouse.down) return e.preventDefault() e.stopPropagation() if (el instanceof SVGElement) { el.setAttribute('transform', `translate( ${state.element.x + e.clientX - state.mouse.x}, ${state.element.y + e.clientY - state.mouse.y} )`) } else { el.style.left = state.element.x + e.clientX - state.mouse.x + 'px' el.style.top = state.element.y + e.clientY - state.mouse.y + 'px' } } setup() el.teardown = teardown return el } export function positionElement(els, direction) { els .map(el => ensurePositionable(el)) .map(el => showHideSelected(el)) .map(el => ({ el, ...extractCurrentValueAndSide(el, direction), amount: direction.split('+').includes('shift') ? 10 : 1, negative: determineNegativity(el, direction), })) .map(payload => Object.assign(payload, { position: payload.negative ? payload.current + payload.amount : payload.current - payload.amount })) .forEach(({el, style, position}) => el instanceof SVGElement ? setTranslateOnSVG(el, direction, position) : el.style[style] = position + 'px') } const extractCurrentValueAndSide = (el, direction) => { let style, current if (el instanceof SVGElement) { const translate = el.attr('transform') const [ x, y ] = translate ? extractSVGTranslate(translate) : [0,0] style = 'transform' current = direction.includes('down') || direction.includes('up') ? y : x } else { const side = getSide(direction).toLowerCase() style = (side === 'top' || side === 'bottom') ? 'top' : 'left' current = getStyle(el, style) current === 'auto' ? current = 0 : current = parseInt(current, 10) } return { style, current } } const extractSVGTranslate = translate => translate.substring( translate.indexOf('(') + 1, translate.indexOf(')') ).split(',') .map(val => parseFloat(val)) const setTranslateOnSVG = (el, direction, position) => { const transform = el.attr('transform') const [ x, y ] = transform ? extractSVGTranslate(transform) : [0,0] const pos = direction.includes('down') || direction.includes('up') ? `${x},${position}` : `${position},${y}` el.attr('transform', `translate(${pos})`) } const determineNegativity = (el, direction) => direction.includes('right') || direction.includes('down') const ensurePositionable = el => { if (el instanceof HTMLElement) el.style.position = 'relative' return el }