blob: 98e93989b9885bd6ba436b03a4ccf8a2fd7a7a41 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview
* settings-slider wraps a paper-slider. It maps the slider's values from a
* linear UI range to a range of real values. When |value| does not map exactly
* to a tick mark, it interpolates to the nearest tick.
*
* Unlike paper-slider, there is no distinction between value and
* immediateValue; when either changes, the |value| property is updated.
*/
Polymer({
is: 'settings-slider',
behaviors: [CrPolicyPrefBehavior],
properties: {
/** @type {!chrome.settingsPrivate.PrefObject} */
pref: Object,
/** @type {!Array<number>} Values corresponding to each tick. */
tickValues: {type: Array, value: []},
/**
* A scale factor used to support fractional pref values since paper-slider
* only supports integers. This is not compatible with |tickValues|,
* i.e. if |scale| is not 1 then |tickValues| must be empty.
*/
scale: {
type: Number,
value: 1,
},
min: Number,
max: Number,
labelMin: String,
labelMax: String,
disabled: Boolean,
/** @private */
disableSlider_: {
computed: 'computeDisableSlider_(pref.*, disabled)',
type: Boolean,
},
},
observers: [
'valueChanged_(pref.*, tickValues.*)',
],
/**
* Sets the |pref.value| property to the value corresponding to the knob
* position after a user action.
* @private
*/
onSliderChanged_: function() {
const sliderValue = isNaN(this.$.slider.immediateValue) ?
this.$.slider.value :
this.$.slider.immediateValue;
let newValue;
if (this.tickValues && this.tickValues.length > 0)
newValue = this.tickValues[sliderValue];
else
newValue = sliderValue / this.scale;
this.set('pref.value', newValue);
},
/** @private */
computeDisableSlider_: function() {
return this.disabled || this.isPrefEnforced();
},
/**
* Updates the knob position when |pref.value| changes. If the knob is still
* being dragged, this instead forces |pref.value| back to the current
* position.
* @private
*/
valueChanged_: function() {
// If |tickValues| is empty, simply set current value to the slider.
if (this.tickValues.length == 0) {
this.$.slider.value =
/** @type {number} */ (this.pref.value) * this.scale;
return;
}
assert(this.scale == 1);
// First update the slider settings if |tickValues| was set.
const numTicks = Math.max(1, this.tickValues.length);
this.$.slider.max = numTicks - 1;
// Limit the number of ticks to 10 to keep the slider from looking too busy.
const MAX_TICKS = 10;
this.$.slider.snaps = numTicks < MAX_TICKS;
this.$.slider.maxMarkers = numTicks < MAX_TICKS ? numTicks : 0;
if (this.$.slider.dragging && this.tickValues.length > 0 &&
this.pref.value != this.tickValues[this.$.slider.immediateValue]) {
// The value changed outside settings-slider but we're still holding the
// knob, so set the value back to where the knob was.
// Async so we don't confuse Polymer's data binding.
this.async(function() {
const newValue = this.tickValues[this.$.slider.immediateValue];
this.set('pref.value', newValue);
});
return;
}
// Convert from the public |value| to the slider index (where the knob
// should be positioned on the slider).
let sliderIndex = this.tickValues.length > 0 ?
this.tickValues.indexOf(/** @type {number} */ (this.pref.value)) :
0;
if (sliderIndex == -1) {
// No exact match.
sliderIndex = this.findNearestIndex_(
this.tickValues,
/** @type {number} */ (this.pref.value));
}
this.$.slider.value = sliderIndex;
},
/**
* Returns the index of the item in |arr| closest to |value|.
* @param {!Array<number>} arr
* @param {number} value
* @return {number}
* @private
*/
findNearestIndex_: function(arr, value) {
let closestIndex;
let minDifference = Number.MAX_VALUE;
for (let i = 0; i < arr.length; i++) {
const difference = Math.abs(arr[i] - value);
if (difference < minDifference) {
closestIndex = i;
minDifference = difference;
}
}
assert(typeof closestIndex != 'undefined');
return closestIndex;
},
/**
* TODO(scottchen): temporary fix until polymer gesture bug resolved. See:
* https://github.com/PolymerElements/paper-slider/issues/186
* @private
*/
resetTrackLock_: function() {
Polymer.Gestures.gestures.tap.reset();
},
});