File: //var/www/innodrive/src/js/modules/range-filter.js
class FilterRangeSlider {
constructor (id, onChange, didChanged) {
this.id = id;
this.state = 0;
this.slider = document.getElementById(id);
this.container = this.slider.closest('.filter-fieldset__range-box');
this.touchLeft = this.slider.querySelector('.filter-range__touch-left');
this.touchRight = this.slider.querySelector('.filter-range__touch-right');
this.lineSpan = this.slider.querySelector('.filter-range__line span');
this.valueState = this.slider.querySelector('.js-state');
this.body = document.querySelector('body');
this.breakpoint = window.matchMedia('(min-width: 768px)');
this.onChange = onChange;
this.didChanged = didChanged;
this.startX = 0;
this.x = 0;
this.min = parseFloat(this.slider.getAttribute('se-min'));
this.max = parseFloat(this.slider.getAttribute('se-max'));
this.defaultMinValue = this.min;
if (this.slider.hasAttribute('se-min-value')) {
this.defaultMinValue = parseFloat(this.slider.getAttribute('se-min-value'));
}
this.defaultMaxValue = this.max;
if (this.slider.hasAttribute('se-max-value')) {
this.defaultMaxValue = parseFloat(this.slider.getAttribute('se-max-value'));
}
if (this.defaultMinValue < this.min) {
this.defaultMinValue = this.min;
}
if (this.defaultMaxValue > this.max) {
this.defaultMaxValue = this.max;
}
if (this.defaultMinValue > this.defaultMaxValue) {
this.defaultMinValue = this.defaultMaxValue;
}
this.step = 0.0;
if (this.slider.getAttribute('se-step')) {
this.step = Math.abs(parseFloat(this.slider.getAttribute('se-step')));
}
this.normalizeFact = 0;
//this.reset(true);
this.selectedTouch = null;
// this.maxX = this.slider.offsetWidth - this.touchRight.offsetWidth;
// this.initialValue = (this.lineSpan.offsetWidth - this.normalizeFact);
this.setMinValue(this.defaultMinValue);
this.setMaxValue(this.defaultMaxValue);
this.onMove = this.onMove.bind(this);
this.onStart = this.onStart.bind(this);
this.onMove = this.onMove.bind(this);
this.onStop = this.onStop.bind(this);
this.update = this.update.bind(this);
this.toggleDefaultClass = this.toggleDefaultClass.bind(this);
// link events
this.touchLeft.addEventListener('mousedown', this.onStart);
this.touchRight.addEventListener('mousedown', this.onStart);
this.touchLeft.addEventListener('touchstart', this.onStart);
this.touchRight.addEventListener('touchstart', this.onStart);
}
get initialValue () {
return this.slider.offsetWidth - this.touchRight.offsetWidth;
}
get maxX () {
return this.slider.offsetWidth - this.touchRight.offsetWidth;
}
get defaultState () {
return this.min === this.defaultMinValue && this.max === this.defaultMaxValue;
}
update () {
this.setMaxValue(this.max);
this.setMinValue(this.min);
}
reset () {
this.touchLeft.style.left = '0px';
this.touchRight.style.left = (this.slider.offsetWidth - this.touchLeft.offsetWidth) + 'px';
this.lineSpan.style.marginLeft = '0px';
this.lineSpan.style.width = (this.slider.offsetWidth - this.touchLeft.offsetWidth) + 'px';
this.startX = 0;
this.x = 0;
this.state = 0;
this.valueState = 0;
this.defaultMinValue = this.min;
this.defaultMaxValue = this.max;
this.update();
this.onChange(this.min, this.max);
this.toggleDefaultClass();
};
setMinValue (minValue) {
const nextMinValue = minValue < this.defaultMinValue ? this.defaultMinValue : (minValue > this.defaultMaxValue ? this.defaultMaxValue : minValue);
const ratio = ((nextMinValue - this.min) / (this.max - this.min));
this.touchLeft.style.left = Math.ceil(ratio * (this.slider.offsetWidth - (this.touchLeft.offsetWidth + this.normalizeFact))) + 'px';
this.lineSpan.style.marginLeft = this.touchLeft.offsetLeft + 'px';
this.lineSpan.style.width = (this.touchRight.offsetLeft - this.touchLeft.offsetLeft) + 'px';
this.slider.setAttribute('se-min-value', nextMinValue);
}
setMaxValue (maxValue) {
const nextMaxValue = maxValue > this.defaultMaxValue ? this.defaultMaxValue : (maxValue < this.defaultMinValue ? this.defaultMaxValue : maxValue);
const ratio = ((nextMaxValue - this.min) / (this.max - this.min));
this.touchRight.style.left = Math.ceil(ratio * (this.slider.offsetWidth - (this.touchLeft.offsetWidth + this.normalizeFact)) + this.normalizeFact) + 'px';
this.lineSpan.style.marginLeft = this.touchLeft.offsetLeft + 'px';
this.lineSpan.style.width = (this.touchRight.offsetLeft - this.touchLeft.offsetLeft) + 'px';
this.slider.setAttribute('se-max-value', nextMaxValue);
}
onStart (event) {
if (event.cancelable) event.preventDefault();
let eventTouch = event;
if (event.touches) {
eventTouch = event.touches[0];
}
if (event.currentTarget === this.touchLeft) {
this.x = this.touchLeft.offsetLeft;
} else {
this.x = this.touchRight.offsetLeft;
}
this.startX = eventTouch.pageX - this.x;
this.selectedTouch = event.currentTarget;
if (this.breakpoint.matches) {
this.body.addEventListener('mousemove', this.onMove);
this.body.addEventListener('mouseup', this.onStop);
this.body.addEventListener('touchmove', this.onMove);
this.body.addEventListener('touchend', this.onStop);
} else {
event.currentTarget.addEventListener('mousemove', this.onMove);
event.currentTarget.addEventListener('mouseup', this.onStop);
event.currentTarget.addEventListener('touchmove', this.onMove);
event.currentTarget.addEventListener('touchend', this.onStop);
}
}
onMove (event) {
let eventTouch = event;
if (event.touches) {
eventTouch = event.touches[0];
}
this.x = eventTouch.pageX - this.startX;
if (this.selectedTouch === this.touchLeft) {
if (this.x > (this.touchRight.offsetLeft - this.selectedTouch.offsetWidth + this.touchLeft.offsetWidth)) {
this.x = (this.touchRight.offsetLeft - this.selectedTouch.offsetWidth + this.touchLeft.offsetWidth);
} else if (this.x < 0) {
this.x = 0;
}
this.selectedTouch.style.left = this.x + 'px';
} else if (this.selectedTouch === this.touchRight) {
if (this.x < (this.touchLeft.offsetLeft + this.touchLeft.offsetWidth - this.touchLeft.offsetWidth)) {
this.x = (this.touchLeft.offsetLeft + this.touchLeft.offsetWidth - this.touchLeft.offsetWidth);
} else if (this.x > this.maxX) {
this.x = this.maxX;
}
this.selectedTouch.style.left = this.x + 'px';
}
// update line span
this.lineSpan.style.marginLeft = this.touchLeft.offsetLeft + 'px';
this.lineSpan.style.width = (this.touchRight.offsetLeft - this.touchLeft.offsetLeft) + 'px';
// write new value
this.calculateValue();
if (this.onChange) {
this.onChange(this.slider.getAttribute('se-min-value'), this.slider.getAttribute('se-max-value'));
}
}
onStop (event) {
if (this.breakpoint.matches) {
this.body.removeEventListener('mousemove', this.onMove);
this.body.removeEventListener('mouseup', this.onStop);
this.body.removeEventListener('touchmove', this.onMove);
this.body.removeEventListener('touchend', this.onStop);
} else {
event.currentTarget.removeEventListener('mousemove', this.onMove);
event.currentTarget.removeEventListener('mouseup', this.onStop);
event.currentTarget.removeEventListener('touchmove', this.onMove);
event.currentTarget.removeEventListener('touchend', this.onStop);
}
this.selectedTouch = null;
// write new value
this.calculateValue();
if (this.didChanged) {
this.didChanged(this.min, this.max);
}
this.toggleDefaultClass();
}
toggleDefaultClass () {
if (this.defaultState) {
this.container.classList.add('default');
} else {
this.container.classList.remove('default');
}
}
calculateValue () {
const newValue = (this.lineSpan.offsetWidth - this.normalizeFact) / this.initialValue;
let minValue = this.lineSpan.offsetLeft / this.initialValue;
let maxValue = minValue + newValue;
const scale = this.max - this.min;
minValue = minValue * scale + this.min;
maxValue = maxValue * scale + this.min;
if (this.step !== 0.0) {
const minMulti = Math.floor((minValue / this.step));
const maxMulti = Math.ceil((maxValue / this.step));
minValue = this.step * minMulti;
maxValue = this.step * maxMulti;
}
if (this.selectedTouch === this.touchLeft) {
this.slider.setAttribute('se-min-value', minValue);
} else {
this.slider.setAttribute('se-max-value', maxValue);
}
}
}
const initFilterRanges = () => {
const rangeBoxes = document.querySelectorAll('.filter-fieldset__range-box');
const setStateValue = (box, minValue, maxValue) => {
const valueState = box.querySelector('.js-state');
if (minValue === box.rangeSlider.min && maxValue === box.rangeSlider.max) {
valueState.value = 0;
} else if (minValue === box.rangeSlider.min) {
valueState.value = 1;
} else if (maxValue === box.rangeSlider.max) {
valueState.value = 2;
} else {
valueState.value = 3;
}
};
if (!rangeBoxes.length) { return }
[...rangeBoxes].forEach((box) => {
const rangeSliderContainer = box.querySelector('.filter-range__slider');
const inboxMin = box.querySelector('.filter-range__inbox.js-min');
const inboxMax = box.querySelector('.filter-range__inbox.js-max');
const valueMin = box.querySelector('.filter-range__value.js-min');
const valueMax = box.querySelector('.filter-range__value.js-max');
let changeLatency = 0;
const onChange = (minValue, maxValue) => {
if (!box.rangeSlider) { return }
inboxMin.value = minValue;
inboxMax.value = maxValue;
valueMin.textContent = minValue;
valueMax.textContent = maxValue;
setStateValue(box, minValue, maxValue);
clearInterval(changeLatency);
changeLatency = setTimeout(() => {
const evt = new Event('change');
inboxMin.dispatchEvent(evt);
}, 300);
};
inboxMin.addEventListener('change', (e) => {
box.rangeSlider.setMinValue(parseFloat(e.currentTarget.value));
setStateValue(box, parseInt(inboxMin.value), parseInt(inboxMax.value));
});
inboxMax.addEventListener('change', (e) => {
box.rangeSlider.setMaxValue(parseFloat(e.currentTarget.value));
setStateValue(box, parseInt(inboxMin.value), parseInt(inboxMax.value));
});
box.rangeSlider = new FilterRangeSlider(rangeSliderContainer.id, onChange);
});
};
export default initFilterRanges;