UNPKG

@scidian/osui

Version:

Lightweight JavaScript UI library.

292 lines (233 loc) 9.25 kB
import { Element } from '../core/Element.js'; const _changeEvent = new Event('change', { 'bubbles': true, 'cancelable': true }); class NumberBox extends Element { constructor(number) { super(document.createElement('input')); const self = this; this.setClass('osui-input'); this.addClass('osui-number'); // // Setup // this.dom.type = 'number'; /* !! NOTE: doesn't work with custom 'unit' */ this.dom.style.cursor = 'text'; this.dom.value = '0.00'; this.dom.setAttribute('autocomplete', 'off'); // Properties this.value = 0; this.min = -Infinity; this.max = Infinity; this.precision = 3; this.step = 0.1; this.unit = ''; this.nudge = 1.0; this.setValue(number); // Events function onChange(event) { if (self.dom) self.setValue(self.dom.value); } function onKeyDown(event) { event.stopPropagation(); if (event.key === 'z' && (event.ctrlKey || event.metaKey)) { event.preventDefault(); if (event.shiftKey) editor.redo(); else editor.undo(); return; } switch (event.key) { case 'Enter': if (self.dom) self.dom.style.cursor = 'text'; if (self.dom) self.dom.blur(); break; case 'ArrowUp': event.preventDefault(); self.setValue(self.getValue() + self.nudge); if (self.dom) self.dom.dispatchEvent(_changeEvent); break; case 'ArrowDown': event.preventDefault(); self.setValue(self.getValue() - self.nudge); if (self.dom) self.dom.dispatchEvent(_changeEvent); break; } } function onKeyUp(event) { event.stopPropagation(); } function onWheel(event) { event.preventDefault(); const upOrDown = (event.deltaY < 0) ? -1.0 : 1.0; const newValue = self.getValue() - (upOrDown * self.step); self.setValue(newValue); if (self.dom) self.dom.dispatchEvent(_changeEvent); } // Add Listeners this.dom.addEventListener('keydown', onKeyDown); this.dom.addEventListener('keyup', onKeyUp); this.dom.addEventListener('wheel', onWheel); this.dom.addEventListener('change', onChange); // Remove Event Listeners this.dom.addEventListener('destroy', function() { self.dom.removeEventListener('keydown', onKeyDown); self.dom.removeEventListener('keyup', onKeyUp); self.dom.removeEventListener('wheel', onWheel); self.dom.removeEventListener('change', onChange); }, { once: true }); } // end ctor getValue() { return parseFloat(this.value); } /** Sets numeric value of input box, enforces range and precision */ setValue(value) { let valueAsFloat = parseFloat(value); if (valueAsFloat === undefined || isNaN(valueAsFloat) || !isFinite(valueAsFloat)) { return this; } if (valueAsFloat < this.min) valueAsFloat = this.min; if (valueAsFloat > this.max) valueAsFloat = this.max; valueAsFloat = parseFloat(valueAsFloat.toFixed(this.precision)); function countDecimals(number) { if (Math.floor(number.valueOf()) === number.valueOf()) return 0; return number.toString().split('.')[1].length || 0; } let decimals = Math.min(this.precision, countDecimals(valueAsFloat)); // if ((this.precision >= 1) && (decimals < 1)) decimals = 1; /* keeps one decimal no matter what */ valueAsFloat = valueAsFloat.toFixed(decimals); if (valueAsFloat !== undefined && !isNaN(valueAsFloat) && isFinite(valueAsFloat)) { this.value = valueAsFloat; if (this.dom) this.dom.value = valueAsFloat; if (this.dom && this.unit !== '') this.dom.value = valueAsFloat + ' ' + this.unit; } return this; } setPrecision(precision) { this.precision = parseFloat(precision); return this; } setStep(step) { this.step = parseFloat(step); return this; } setNudge(nudge) { this.nudge = parseFloat(nudge); return this; } setMin(min) { this.min = parseFloat(min); return this; } setMax(max) { this.max = parseFloat(max); return this; } setRange(min, max) { this.setMin(min); this.setMax(max); return this; } setUnit(unit) { this.unit = unit; return this; } } class NumberScroll extends NumberBox { constructor(number) { super(number); const self = this; this.addClass('osui-number-scroll'); this.dom.style.cursor = 'ns-resize'; let distance = 0; let onMouseDownValue = 0; const pointer = { x: 0, y: 0 }; const prevPointer = { x: 0, y: 0 }; function onMouseDown(event) { event.preventDefault(); distance = 0; onMouseDownValue = self.getValue(); prevPointer.x = event.clientX; prevPointer.y = event.clientY; } function onMouseMove(event) { const currentValue = self.getValue(); pointer.x = event.clientX; pointer.y = event.clientY; distance += (pointer.x - prevPointer.x) - (pointer.y - prevPointer.y); let value; value = onMouseDownValue + (distance / (event.shiftKey ? 0.1 : 1)) * self.step; value = Math.min(self.max, Math.max(self.min, value)); if (currentValue !== value) { self.setValue(value); if (self.dom) self.dom.dispatchEvent(_changeEvent); } prevPointer.x = event.clientX; prevPointer.y = event.clientY; } function onMouseUp() { if (Math.abs(distance) < 2) { if (self.dom) self.dom.focus(); // if (self.dom) self.dom.select(); } } function onTouchStart(event) { if (event.touches.length === 1) { distance = 0; onMouseDownValue = self.getValue(); prevPointer.x = event.touches[0].pageX; prevPointer.y = event.touches[0].pageY; } } function onTouchMove(event) { const currentValue = self.getValue(); pointer.x = event.touches[0].pageX; pointer.y = event.touches[0].pageY; distance += (pointer.x - prevPointer.x) - (pointer.y - prevPointer.y); let value; value = onMouseDownValue + (distance / (event.shiftKey ? 5 : 50)) * self.step; value = Math.min(self.max, Math.max(self.min, value)); if (currentValue !== value) { self.setValue(value); if (self.dom) self.dom.dispatchEvent(_changeEvent); } prevPointer.x = event.touches[0].pageX; prevPointer.y = event.touches[0].pageY; } function onTouchEnd(event) { if (event.touches.length === 0) { if (Math.abs(distance) < 2) { if (self.dom) self.dom.focus(); // if (self.dom) self.dom.select(); } } } function onFocus() { // if (self.dom) self.dom.style.backgroundColor = ''; if (self.dom) self.dom.style.cursor = ''; } function onBlur() { // if (self.dom) self.dom.style.backgroundColor = 'transparent'; if (self.dom) self.dom.style.cursor = 'ns-resize'; } // Add Event Listeners this.dom.addEventListener('mousedown', onMouseDown); this.dom.addEventListener('mousemove', onMouseMove); this.dom.addEventListener('mouseup', onMouseUp); this.dom.addEventListener('touchstart', onTouchStart); this.dom.addEventListener('touchmove', onTouchMove); this.dom.addEventListener('touchend', onTouchEnd); this.dom.addEventListener('focus', onFocus); this.dom.addEventListener('blur', onBlur); // Remove Event Listeners this.dom.addEventListener('destroy', function() { self.dom.removeEventListener('mousedown', onMouseDown); self.dom.removeEventListener('mousemove', onMouseMove); self.dom.removeEventListener('mouseup', onMouseUp); self.dom.removeEventListener('touchstart', onTouchStart); self.dom.removeEventListener('touchmove', onTouchMove); self.dom.removeEventListener('touchend', onTouchEnd); self.dom.removeEventListener('focus', onFocus); self.dom.removeEventListener('blur', onBlur); }, { once: true }); } } export { NumberBox, NumberScroll, };