blob: f84e582ee0ba358a6bc0393040d1c0b969b34711 [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.
*/
Polymer({
is: 'settings-slider',
behaviors: [CrPolicyPrefBehavior],
properties: {
/** @type {!chrome.settingsPrivate.PrefObject} */
pref: Object,
/**
* Values corresponding to each tick.
* @type {!Array<cr_slider.SliderTick>|!Array<number>}
*/
ticks: {
type: Array,
value: () => [],
},
/**
* A scale factor used to support fractional pref values since paper-slider
* only supports integers. This is not compatible with |ticks|,
* i.e. if |scale| is not 1 then |ticks| must be empty.
*/
scale: {
type: Number,
value: 1,
},
min: Number,
max: Number,
labelMin: String,
labelMax: String,
disabled: Boolean,
showMarkers: Boolean,
/** @private */
disableSlider_: {
computed: 'computeDisableSlider_(pref.*, disabled, ticks.*)',
type: Boolean,
},
updateValueInstantly: {
type: Boolean,
value: true,
observer: 'onSliderChanged_',
},
loaded_: Boolean,
},
observers: [
'valueChanged_(pref.*, ticks.*, loaded_)',
],
attached: function() {
this.loaded_ = true;
},
/**
* @param {number|cr_slider.SliderTick} tick
* @return {number|undefined}
*/
getTickValue_: function(tick) {
return typeof tick == 'object' ? tick.value : tick;
},
/**
* @param {number} index
* @return {number|undefined}
* @private
*/
getTickValueAtIndex_: function(index) {
return this.getTickValue_(this.ticks[index]);
},
/**
* Sets the |pref.value| property to the value corresponding to the knob
* position after a user action.
* @private
*/
onSliderChanged_: function() {
if (!this.loaded_)
return;
if (this.$.slider.dragging && !this.updateValueInstantly)
return;
const sliderValue = this.$.slider.value;
let newValue;
if (this.ticks && this.ticks.length > 0)
newValue = this.getTickValueAtIndex_(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 (this.pref == undefined || !this.loaded_)
return;
// First update the slider settings if |ticks| was set.
const numTicks = this.ticks.length;
if (numTicks == 1) {
this.$.slider.disabled = true;
return;
}
const prefValue = /** @type {number} */ (this.pref.value);
// If |ticks| is empty, simply set current value to the slider.
if (numTicks == 0) {
this.$.slider.value = prefValue * this.scale;
return;
}
assert(this.scale == 1);
// Limit the number of ticks to 10 to keep the slider from looking too busy.
const MAX_TICKS = 10;
this.$.slider.markerCount =
(this.showMarkers || numTicks <= MAX_TICKS) ? numTicks : 0;
const tickValue = this.getTickValueAtIndex_(this.$.slider.value);
if (this.$.slider.dragging && this.pref.value != tickValue) {
if (!this.updateValueInstantly)
return;
// 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(() => {
this.set('pref.value', tickValue);
});
return;
}
// Convert from the public |value| to the slider index (where the knob
// should be positioned on the slider).
this.$.slider.value =
this.ticks.map(tick => Math.abs(this.getTickValue_(tick) - prefValue))
.reduce(
(acc, diff, index) => diff < acc.diff ? {index, diff} : acc,
{index: -1, diff: Number.MAX_VALUE})
.index;
assert(this.$.slider.value != -1);
},
});