UNPKG

grapick

Version:

Easy configurable gradient picker, with no dependencies

276 lines (254 loc) 7.78 kB
import {on, off, isFunction, isDef, getPointerEvent } from './utils' /** * Handler is the color stop of the gradient */ export default class Handler { constructor(Grapick, position = 0, color = 'black', select = 1, opts = {}) { Grapick.getHandlers().push(this); this.gp = Grapick; this.position = position; this.color = color; this.selected = 0; this.render(); select && this.select(opts); } toJSON() { return { position: this.position, selected: this.selected, color: this.color, }; } /** * Set color * @param {string} color Color string, eg. red, #123, 'rgba(30,87,153,1)', etc.. * @param {Boolean} complete Indicates if the action is complete * @example * handler.setColor('red'); */ setColor(color, complete = 1) { this.color = color; this.emit('handler:color:change', this, complete); } /** * Set color * @param {integer} position Position integer in percentage, eg. 20, 50, 100 * @param {Boolean} complete Indicates if the action is complete * @example * handler.setPosition(55); */ setPosition(position, complete = 1) { const el = this.getEl(); this.position = position; el && (el.style.left = `${position}%`); this.emit('handler:position:change', this, complete); } /** * Get color of the handler * @return {string} Color string */ getColor() { return this.color; } /** * Get position of the handler * @return {integer} Position integer */ getPosition() { const { position, gp } = this; const { onValuePos } = gp.options; return isFunction(onValuePos) ? onValuePos(position) : position; } /** * Indicates if the handler is the selected one * @return {Boolean} */ isSelected() { return !!this.selected; } /** * Get value of the handler * @return {string} * @example * handler.getValue(); // -> `black 0%` */ getValue() { return `${this.getColor()} ${this.getPosition()}%`; } /** * Select the current handler and deselect others * @param {Object} [options={}] Options * @param {Boolean} [options.keepSelect=false] Avoid deselecting other handlers */ select(opts = {}) { const el = this.getEl(); const handlers = this.gp.getHandlers(); !opts.keepSelect && handlers.forEach(handler => handler.deselect()); this.selected = 1; const clsNameSel = this.getSelectedCls(); el && (el.className += ` ${clsNameSel}`); this.emit('handler:select', this); } /** * Deselect the current handler */ deselect() { const el = this.getEl(); this.selected = 0; const clsNameSel = this.getSelectedCls(); el && (el.className = el.className.replace(clsNameSel, '').trim()); this.emit('handler:deselect', this); } getSelectedCls() { const pfx = this.gp.options.pfx; return `${pfx}-handler-selected`; } /** * Remove the current handler * @param {Object} [options={}] Options * @param {Boolean} [options.silent] Don't trigger events * @return {Handler} Removed handler (itself) */ remove(options = {}) { const { cpFn } = this; const el = this.getEl(); const handlers = this.gp.getHandlers(); const removed = handlers.splice(handlers.indexOf(this), 1)[0]; el && el.parentNode.removeChild(el); !options.silent && this.emit('handler:remove', removed); isFunction(cpFn) && cpFn(this); ['el', 'gp'].forEach(i => this[i] = 0); return removed; } /** * Get handler element * @return {HTMLElement} */ getEl() { return this.el; } initEvents() { const eventDown = 'touchstart mousedown'; const eventMove = 'touchmove mousemove'; const eventUp = 'touchend mouseup'; const el = this.getEl(); const previewEl = this.gp.previewEl; const options = this.gp.options; const min = options.min; const max = options.max; const closeEl = el.querySelector('[data-toggle=handler-close]'); const colorContEl = el.querySelector('[data-toggle=handler-color-c]'); const colorWrapEl = el.querySelector('[data-toggle=handler-color-wrap]'); const colorEl = el.querySelector('[data-toggle=handler-color]'); const dragEl = el.querySelector('[data-toggle=handler-drag]'); const upColor = (ev, complete = 1) => { const { value } = ev.target; this.setColor(value, complete); colorWrapEl && (colorWrapEl.style.backgroundColor = value); } colorContEl && on(colorContEl, 'click', e => e.stopPropagation()); closeEl && on(closeEl, 'click', e => { e.stopPropagation(); this.remove() }); if (colorEl) { on(colorEl, 'change', upColor); on(colorEl, 'input', ev => upColor(ev, 0)); } if (dragEl) { let pos = 0; let posInit = 0; let dragged = 0; const elDim = {}; const startPos = {}; const deltaPos = {}; const axis = 'x'; const drag = e => { const evP = getPointerEvent(e); dragged = 1; deltaPos.x = evP.clientX - startPos.x; deltaPos.y = evP.clientY - startPos.y; pos = (axis == 'x' ? deltaPos.x : deltaPos.y) * 100; pos = pos / (axis == 'x' ? elDim.w : elDim.h); pos = posInit + pos; pos = pos < min ? min : pos; pos = pos > max ? max : pos; this.setPosition(pos, 0); this.emit('handler:drag', this, pos); // In case the mouse button was released outside of the window isDef(e.button) && e.which === 0 && stopDrag(e); }; const stopDrag = e => { off(document, eventMove, drag); off(document, eventUp, stopDrag); if (!dragged) { return; } dragged = 0; this.setPosition(pos); this.emit('handler:drag:end', this, pos); }; const initDrag = e => { //Right or middel click if (isDef(e.button) && e.button !== 0) { return; } this.select(); const evP = getPointerEvent(e); posInit = this.position; elDim.w = previewEl.clientWidth; elDim.h = previewEl.clientHeight; startPos.x = evP.clientX; startPos.y = evP.clientY; on(document, eventMove, drag); on(document, eventUp, stopDrag); this.emit('handler:drag:start', this); }; on(dragEl, eventDown, initDrag); on(dragEl, 'click', e => e.stopPropagation()); } } emit() { this.gp.emit(...arguments); } /** * Render the handler * @return {HTMLElement} Rendered element */ render() { const gp = this.gp; const opt = gp.options; const previewEl = gp.previewEl; const colorPicker = gp.colorPicker; const pfx = opt.pfx; const colorEl = opt.colorEl; const color = this.getColor(); if (!previewEl) { return; } const hEl = document.createElement('div'); const style = hEl.style; const baseCls = `${pfx}-handler`; hEl.className = baseCls; hEl.innerHTML = ` <div class="${baseCls}-close-c"> <div class="${baseCls}-close" data-toggle="handler-close">&Cross;</div> </div> <div class="${baseCls}-drag" data-toggle="handler-drag"></div> <div class="${baseCls}-cp-c" data-toggle="handler-color-c"> ${colorEl || ` <div class="${baseCls}-cp-wrap" data-toggle="handler-color-wrap" style="background-color: ${color}"> <input type="color" data-toggle="handler-color" value="${color}"> </div>`} </div> `; style.position = 'absolute'; style.top = 0; style.left = `${this.position}%`; previewEl.appendChild(hEl); this.el = hEl; this.initEvents(); this.cpFn = colorPicker && colorPicker(this); return hEl; } }