UNPKG

soft-components

Version:

Simple soft flexible set of web components

411 lines (410 loc) 11 kB
import { Component, Host, h, Prop, Event, State, Method, Element, } from '@stencil/core'; // import { validityMessages } from '../../utils/validity-messages' import throttle from 'lodash.throttle'; export class ScDial { constructor() { this.value = 50; /** * Diameter in pixels (can be changed via CSS variable --sc-dial-size) */ this.size = 80; /** * Step value of each change */ this.step = 1; this.error = ''; this.focused = false; this.percent = 0; this.rotation = 0; this.cycles = 0; this.reachedMax = false; this.reachedMin = false; // private quadrant: number // private lastQuadrant: number this.mouseDirectionX = null; this.mouseDirectionY = null; this.mousePrevX = 0; this.mousePrevY = 0; } async setValue(value) { const { min, max, step } = this; if (!isNaN(min) && value <= min) { this.reachedMin = true; this.value = min; return; } if (!isNaN(max) && value >= max) { this.reachedMax = true; this.value = max; return; } this.value = Math.ceil(value / step) * step; } setRotationByValue(value) { this.rotation = ((value % this.total) / this.total) * 360; } handleScroll(e) { e.preventDefault(); const deltaX = -e.detail || e.wheelDeltaX; const deltaY = -e.detail || e.wheelDeltaY; const direction = deltaX > 0 || deltaY < 0 ? 1 : deltaX < 0 || deltaY > 0 ? -1 : 0; direction < 0 ? this.stepUp() : this.stepDown(); } componentDidLoad() { const elBox = this.hostEl.getBoundingClientRect(); const { min, max, step } = this; this.total = 36 * step; if (typeof min !== 'undefined' && typeof max !== 'undefined') { this.total = max - min; } this.oneStepDeg = (step / this.total) * 360; // console.log({ oneStepDeg: this.oneStepDeg }) this.centerX = Math.floor(elBox.left) + document.body.scrollLeft + elBox.width / 2; this.centerY = Math.floor(elBox.top) + document.body.scrollTop + elBox.height / 2; this.setValue(this.value); this.setRotationByValue(this.value); this.hostEl.addEventListener('mousewheel', e => this.handleScroll(e)); this.hostEl.addEventListener('touchstart', e => this.handleMoveStart(e, 'touchmove', 'touchend')); this.hostEl.addEventListener('mousedown', e => this.handleMoveStart(e, 'mousemove', 'mouseup')); } disconnectedCallback() { this.hostEl.removeEventListener('mousewheel', this.handleScroll.bind(this)); } handleMoveStart(e, onMove, onEnd) { const fnc = throttle(this.updateOnMove.bind(this), 40); const body = document.body; const { deg } = this.getDegFromPointer(e); this.startingDeg = deg; this.startingValue = this.value; body.addEventListener(onMove, fnc, false); body.addEventListener(onEnd, () => { body.removeEventListener(onMove, fnc, false); }, false); } getDegFromPointer(event) { const e = event.changedTouches ? event.changedTouches[0] : event; const x = e.pageX; const y = e.pageY; // get mouse direction if (x < this.mousePrevX) { this.mouseDirectionX = 'left'; } if (x > this.mousePrevX) { this.mouseDirectionX = 'right'; } if (y < this.mousePrevY) { this.mouseDirectionY = 'up'; } if (y > this.mousePrevY) { this.mouseDirectionY = 'down'; } this.mousePrevX = x; this.mousePrevY = y; // get angle const diffX = this.centerX - x; const diffY = this.centerY - y; const angleRad = Math.atan2(diffY, diffX); //rad let angleDeg = (angleRad * 180) / Math.PI + 90; if (angleDeg < 0) { angleDeg += 360; } return { deg: angleDeg, x, y, diffX, diffY, }; } updateOnMove(event) { event.preventDefault(); const { deg: angleDeg, x, y, diffX, diffY } = this.getDegFromPointer(event); // const degDiff = angleDeg - this.lastDeg this.degDiff = angleDeg - this.startingDeg; console.log({ x, y, diffX, diffY }); // if diff in degree is larger than 1 step, step over if (this.degDiff > this.oneStepDeg) { if (x > this.centerX && y > this.centerY && this.mouseDirectionX === 'left') { console.log(this.mouseDirectionY); console.log('yo'); // if (!isNaN(this.max)) { // return // } } this.stepUp(this.degDiff); } if (this.degDiff < -1 * this.oneStepDeg) { this.stepDown(this.degDiff); } // const newPercent = angleDeg / 360 // const newVal = newPercent * this.max // console.log({ percent: this.percent, newPercent, released: this.released }) // if (!this.released) { // this.setValue(newVal) // } } valueDiff(degDiff) { return Math.floor(degDiff / this.oneStepDeg) * this.step; } stepUp(degDiff = null) { if (this.reachedMax) { return; } const newVal = degDiff ? this.startingValue + this.valueDiff(degDiff) : this.value + this.step; this.setValue(newVal); this.setRotationByValue(newVal); this.reachedMin = false; } stepDown(degDiff = null) { if (this.reachedMin) { return; } const newVal = degDiff ? this.startingValue + this.valueDiff(degDiff) : this.value - this.step; this.setValue(newVal); this.setRotationByValue(newVal); this.reachedMax = false; } // private updateRotation(direction) { // switch (this.diff) { // case 1: // this.rotation += this.oneStepDeg // break // case -1: // this.rotation -= this.oneStepDeg // break // default: // break // } // } render() { const { value, size, rotation, reachedMax, reachedMin, } = this; return (h(Host, { style: { '--sc-dial-size': `${size}px` } }, h("div", { class: "dial-circle" }, h("div", { class: "pointer", style: { '--sc-dial-angle': `${rotation}deg` } }, h("div", { class: "pointer-circle" })), h("div", { class: "temp" }, value, h("div", null, "max? ", reachedMax ? 'true' : 'false'), h("div", null, "min? ", reachedMin ? 'true' : 'false'))))); } static get is() { return "sc-dial"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["sc-dial.scss"] }; } static get styleUrls() { return { "$": ["sc-dial.css"] }; } static get properties() { return { "value": { "type": "number", "mutable": true, "complexType": { "original": "number | null", "resolved": "number", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "" }, "attribute": "value", "reflect": false, "defaultValue": "50" }, "size": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Diameter in pixels (can be changed via CSS variable --sc-dial-size)" }, "attribute": "size", "reflect": false, "defaultValue": "80" }, "max": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Max value of dial" }, "attribute": "max", "reflect": false }, "min": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Min value of dial" }, "attribute": "min", "reflect": false }, "step": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Step value of each change" }, "attribute": "step", "reflect": false, "defaultValue": "1" } }; } static get states() { return { "error": {}, "focused": {}, "percent": {}, "rotation": {}, "degDiff": {}, "cycles": {} }; } static get events() { return [{ "method": "inputEvent", "name": "inputEvent", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when a keyboard input occurred." }, "complexType": { "original": "KeyboardEvent", "resolved": "KeyboardEvent", "references": { "KeyboardEvent": { "location": "global" } } } }, { "method": "changeEvent", "name": "changeEvent", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when the value has changed." }, "complexType": { "original": "any", "resolved": "any", "references": {} } }, { "method": "blurEvent", "name": "blurEvent", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when the input loses focus." }, "complexType": { "original": "void", "resolved": "void", "references": {} } }, { "method": "focusEvent", "name": "focusEvent", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when the input has focus." }, "complexType": { "original": "void", "resolved": "void", "references": {} } }, { "method": "keyDownEvent", "name": "keyDownEvent", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when a key is pressed down" }, "complexType": { "original": "void", "resolved": "void", "references": {} } }]; } static get methods() { return { "setValue": { "complexType": { "signature": "(value: any) => Promise<void>", "parameters": [{ "tags": [], "text": "" }], "references": { "Promise": { "location": "global" } }, "return": "Promise<void>" }, "docs": { "text": "", "tags": [] } } }; } static get elementRef() { return "hostEl"; } }