open-nomad/ui/app/components/stepper-input.js
Michael Lange 7ad214f34a StepperInput UX improvements
- Click label to focus input
- Focusing input selects value
- Entering an invalid value reverts selection
- Entering a fractional number floors the value
2020-06-30 11:54:11 -07:00

76 lines
1.8 KiB
JavaScript

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();
}
@action focusInput() {
this.element.querySelector('.stepper-input-input').focus();
}
update(value) {
debounce(this, sendUpdateAction, value, this.debounce);
}
}
function sendUpdateAction(value) {
return this.onChange(value);
}