import Component from '@ember/component'; import { action } from '@ember/object'; import { debounce } from '@ember/runloop'; import { oneWay } from '@ember/object/computed'; import { classNames, classNameBindings } from '@ember-decorators/component'; import classic from 'ember-classic-decorator'; const ESC = 27; @classic @classNames('stepper-input') @classNameBindings( 'class', 'disabled:is-disabled', 'disabled:tooltip', 'disabled:multiline' ) export default class StepperInput extends Component { min = 0; max = 10; value = 0; debounce = 500; onChange() {} // Internal value changes immediately for instant visual feedback. // Value is still the public API and is expected to mutate and re-render // On onChange which is debounced. @oneWay('value') internalValue; @action increment() { if (this.internalValue < this.max) { this.incrementProperty('internalValue'); this.update(this.internalValue); } } @action decrement() { if (this.internalValue > this.min) { this.decrementProperty('internalValue'); this.update(this.internalValue); } } @action setValue(e) { if (e.target.value !== '') { const newValue = Math.floor( Math.min(this.max, Math.max(this.min, e.target.value)) ); this.set('internalValue', newValue); this.update(this.internalValue); } else { e.target.value = this.internalValue; } } @action resetTextInput(e) { if (e.keyCode === ESC) { e.target.value = this.internalValue; } } @action selectValue(e) { e.target.select(); } update(value) { debounce(this, sendUpdateAction, value, this.debounce); } } function sendUpdateAction(value) { return this.onChange(value); }