import { scheduleOnce } from '@ember/runloop'; import Component from '@ember/component'; import C from 'ui/utils/constants'; import layout from './template'; import { computed, observer, get, set } from '@ember/object'; function clientX(event) { if ( typeof event.clientX !== 'undefined' ) { return event.clientX; } var orig = event.originalEvent; if ( orig ) { if ( typeof orig.clientX !== 'undefined' ) { return orig.clientX; } if ( orig.touches && orig.touches.length ) { return orig.touches[0].clientX; } } return 0; } export default Component.extend({ layout, classNames: ['slider'], classNameBindings: ['disabled', 'active'], disabled: false, initialValue: null, value: null, // Bind something to this to get the value, or use the action to get it valueMin: 0, // The smallest and biggest value is allowed to be valueMax: 100, scaleMin: null, // The smallest and biggest values shown on the display. If these are not equal to valueMin/max then there will be scaleMax: null, // a part of the slider that the user can't select, e.g. if you want to show 0 but have a minimum value of 1. step: 1, // Increment active: false, dragFn: null, upFn: null, init() { this._super(...arguments); var initial = get(this, 'initialValue'); if ( initial !== null ) { set(this, 'value', initial); } }, didInsertElement() { scheduleOnce('afterRender', this, 'valueChanged'); }, willDestroyElement() { $('BODY').off('mousemove', get(this, 'dragFn')); // eslint-disable-line $('BODY').off('mouseup', get(this, 'upFn')); // eslint-disable-line }, valueChanged: observer('value', 'valueMin', 'valueMax', 'percent', 'elementId', function() { const { elementId } = this const originalValue = get(this, 'value'); let targetSliderBar = elementId ? `.${ elementId }.slider-bar` : '.slider-bar'; let targetSliderHandle = elementId ? `.${ elementId }.slider-handle` : '.slider-handle'; let value = Math.max(get(this, 'valueMin'), Math.min(originalValue, get(this, 'valueMax'))); if ( isNaN(value) ) { value = get(this, 'valueMin'); } this.sendAction('changed', value); if ( value && originalValue !== value ) { set(this, 'value', value); return; } var percent = get(this, 'percent'); $(targetSliderBar).css('width', `${ percent }%`); // eslint-disable-line $(targetSliderHandle).css('left', `${ percent }%`); // eslint-disable-line }), _scaleMin: computed('scaleMin', 'valueMin', function() { var min = get(this, 'scaleMin'); if ( min === null ) { min = get(this, 'valueMin'); } return min; }), _scaleMax: computed('scaleMax', 'valueMax', function() { var min = get(this, 'scaleMax'); if ( min === null ) { min = get(this, 'valueMax'); } return min; }), percent: computed('value', 'valueMin', 'valueMax', '_scaleMin', '_scaleMax', function() { var cur = get(this, 'value'); var min = Math.min(get(this, '_scaleMin'), get(this, 'valueMin')); var max = Math.max(get(this, '_scaleMax'), get(this, 'valueMax')); return (((cur - min) / (max - min)) * 100).toFixed(3); }), alignValue(val) { var step = get(this, 'step'); var min = get(this, 'valueMin'); var max = get(this, 'valueMax'); // Subtract out the minimum so that modulus will work for // determining how close val is to a valid step val -= min; var diff = val % step; if ( diff >= step / 2 ) { val = val - diff + step; } else if ( diff > 0 ) { val = val - diff; } // Add the minimum back in val += min; // Make sure the value is within range // (if `max` is not an increment of `step` it's your problem..) val = Math.max(min, Math.min(val, max)); return val; }, pointToValue(screenX) { var $elem = this.$(); var offset = $elem.offset(); var width = $elem.outerWidth(); var x = screenX - offset.left; var percent = Math.max(0, Math.min(x / width, 1)); var min = get(this, '_scaleMin'); var max = get(this, '_scaleMax'); var rawValue = min + (percent * (max - min)); var aligned = this.alignValue(rawValue); return aligned; }, click(event) { if ( get(this, 'disabled') ) { return false; } var value = this.pointToValue(clientX(event)); set(this, 'value', value); this.$('.slider-handle').focus(); }, mouseDown(event) { if ( get(this, 'disabled') ) { return false; } set(this, 'active', true); if ( !get(this, 'dragFn') ) { set(this, 'dragFn', this.drag.bind(this)); } if ( !get(this, 'upFn') ) { set(this, 'upFn', this.mouseUp.bind(this)); } $('BODY').on('mousemove', get(this, 'dragFn')); // eslint-disable-line $('BODY').on('mouseup', get(this, 'upFn')); // eslint-disable-line this.drag(event); }, drag(event) { event.preventDefault(); if ( get(this, 'disabled') ) { return false; } var value = this.pointToValue(clientX(event)); set(this, 'value', value); }, mouseUp(/* event*/) { $('BODY').off('mousemove', get(this, 'dragFn')); // eslint-disable-line $('BODY').off('mouseup', get(this, 'upFn')); // eslint-disable-line set(this, 'active', false); }, keyDown(event) { var handled = false; switch ( event.which ) { case C.KEY.LEFT: this.decrementProperty('value', get(this, 'step')); handled = true; break; case C.KEY.RIGHT: this.incrementProperty('value', get(this, 'step')); handled = true; break; case C.KEY.UP: set(this, 'value', get(this, 'valueMax')); handled = true; break; case C.KEY.DOWN: set(this, 'value', get(this, 'valueMin')); handled = true; break; } if ( handled ) { event.preventDefault(); } }, });