ember-paper
Version:
The Ember approach to Material Design.
224 lines (179 loc) • 4.87 kB
JavaScript
/* eslint-disable ember/classic-decorator-no-classic-methods, ember/no-classic-components, ember/no-computed-properties-in-native-classes, prettier/prettier */
/**
* @module ember-paper
*/
import Component from '@ember/component';
import { computed, action } from '@ember/object';
import { bind } from '@ember/runloop';
import { htmlSafe } from '@ember/template';
import template from './template';
import clamp from 'ember-paper/utils/clamp';
import { tagName, layout } from '@ember-decorators/component';
/* global Hammer */
/**
* @class PaperSlider
* @extends Ember.Component
*/
class PaperSlider extends Component {
min = 0;
max = 100;
step = 1;
tabindex = 0;
active = false;
dragging = false;
focused = false;
element = null;
get percent() {
let min = parseFloat(this.min, 10);
let max = parseFloat(this.max, 10);
return clamp((this.value - min) / (max - min), 0, 1);
}
get activeTrackStyle() {
let percent = this.percent || 0;
return htmlSafe(`width: ${percent * 100}%`);
}
get thumbContainerStyle() {
let percent = this.percent || 0;
return htmlSafe(`left: ${percent * 100}%`);
}
get isMinimum() {
return this.percent === this.min;
}
onDidInsert(element) {
this.element = element;
if (!this.disabled) {
this._setupHammer();
}
}
onDidUpdate() {
if (!this.disabled && !this._hammer) {
// if it is enabled and we didn't init hammer yet
this._setupHammer();
} else if (this.disabled && this._hammer) {
// if it is disabled and we did init hammer already
this._teardownHammer();
}
}
onWillDestroy() {
if (this._hammer) {
this._teardownHammer();
}
}
_setupHammer() {
// Enable dragging the slider
let containerManager = new Hammer.Manager(this.element);
let pan = new Hammer.Pan({ direction: Hammer.DIRECTION_HORIZONTAL, threshold: 10 });
containerManager.add(pan);
let tap = new Hammer.Tap();
containerManager.add(tap);
containerManager
.on('panstart', bind(this, this.onDragStart))
.on('panmove', bind(this, this.onDrag))
.on('panend', bind(this, this.onDragEnd))
.on('tap', bind(this, this.onTap));
this._hammer = containerManager;
}
_teardownHammer() {
this._hammer.destroy();
delete this._hammer;
}
positionToPercent(x) {
let { left, width } = this.sliderDimensions();
return Math.max(0, Math.min(1, (x - left) / width));
}
percentToValue(x) {
let min = parseFloat(this.min, 10);
let max = parseFloat(this.max, 10);
return (min + x * (max - min));
}
minMaxValidator(value) {
let min = parseFloat(this.min, 10);
let max = parseFloat(this.max, 10);
return Math.max(min, Math.min(max, value));
}
stepValidator(value) {
let step = parseFloat(this.step, 10);
return Math.round(value / step) * step;
}
sliderDimensions() {
return this.element.querySelector('.md-track-container').getBoundingClientRect();
}
setValueFromEvent(value) {
let exactVal = this.percentToValue(this.positionToPercent(value));
let closestVal = this.minMaxValidator(this.stepValidator(exactVal));
if (this.onChange) {
this.onChange(closestVal);
}
}
onTap(event) {
if (this.disabled) {
return;
}
this.setValueFromEvent(event.center.x);
}
onDragStart(event) {
if (this.disabled) {
return;
}
this.set('active', true);
this.set('dragging', true);
this.element.focus();
this.setValueFromEvent(event.center.x);
}
onDrag(event) {
if (this.disabled || !this.dragging) {
return;
}
this.setValueFromEvent(event.center.x);
}
onDragEnd() {
if (this.disabled) {
return;
}
this.set('active', false);
this.set('dragging', false);
}
handleKeyDown(event) {
if (this.disabled) {
return;
}
let changeAmount, newValue;
if (['ArrowLeft', 'Left'].includes(event.key)) {
changeAmount = parseFloat(this.step) * -1;
} else if (['ArrowRight', 'Right'].includes(event.key)) {
changeAmount = parseFloat(this.step);
}
if (changeAmount) {
if (event.metaKey || event.ctrlKey || event.altKey) {
changeAmount *= 4;
}
newValue = this.value + changeAmount;
if (this.onChange) {
this.onChange(this.minMaxValidator(newValue));
}
event.preventDefault();
event.stopPropagation();
}
}
handleFocusIn() {
if (!this.disabled) {
this.set('focused', true);
}
}
handleFocusOut() {
this.set('focused', false);
}
}
export default PaperSlider;