<div x-data="rangeSlider" x-init="init(0, 1000, 200, 800)">
<div class="relative select-none touch-none mx-6 my-3 md:mx-0 h-0.5 bg-neutral-300" x-ref="slider">
<div class="absolute bg-dark-black h-full w-full" :style="'width:' + getWidth() + '; left:' + getMinPos()"></div>
<div class="absolute -ml-2 top-1/2 -translate-y-1/2 cursor-pointer rounded-full border-2 border-dark-black bg-light-white w-6 h-6 z-30 hover:bg-neutral-200" @touchstart="dragFrom=true" @mousedown="dragFrom=true" :style="'left:' + getFromPos()"></div>
<div class="absolute -ml-2 top-1/2 -translate-y-1/2 cursor-pointer rounded-full border-2 border-dark-black bg-light-white w-6 h-6 z-30 hover:bg-neutral-200" @touchstart="dragTo=true" @mousedown="dragTo=true" :style="'left:' + getToPos()"></div>
</div>
<div class="pt-2 flex justify-between select-none mt-2">
<label class="flex gap-1 justify-center items-center py-2 px-3 border border-neutral-300 cursor-text focus-within:ring-4 focus-within:ring-blue-200 rounded-md">
<div x-data="{value:from}" class="relative h-full">
<input class="text-center border-0 p-0 w-full absolute top-0 left-0 bg-transparent focus:border-none focus:ring-0" x-model="value" type="text" name="from" x-effect="value=Math.min(from, to)" @keyup.enter="triggerChange(value,'from')" @blur="triggerChange(value,'from')">
<span class="invisible block w-auto overflow-auto text-nowrap min-w-6 h-full" x-text="value"></span>
</div>
</label>
<label class="flex gap-1 justify-center items-center py-2 px-3 border border-neutral-300 cursor-text focus-within:ring-4 focus-within:ring-blue-200 rounded-md">
<div x-data="{value:to}" class="relative h-full">
<input class="text-center border-0 p-0 w-full absolute top-0 left-0 bg-transparent focus:border-none focus:ring-0" x-model="value" type="text" name="from" x-effect="value=Math.max(from, to)" @keyup.enter="triggerChange(value,'to')" @blur="triggerChange(value,'to')">
<span class="invisible block w-auto overflow-auto text-nowrap min-w-6 h-full" x-text="value"></span>
</div>
</label>
</div>
</div>
<script>
function rangeSlider() {
return {
// slider start value
min: 0,
// slider end value
max: 100,
// range start
from: 10,
// range end
to: 80,
// flag if mouse is clicked or screen is touched
dragFrom: false,
dragTo: false,
// call on x-init
init: function(min, max, from, to) {
// register mouse/touche move events to window
window.addEventListener("mousemove", (e) => {
this.drag(e)
});
window.addEventListener("touchmove", (e) => {
this.drag(e)
});
window.addEventListener("mouseup", this.dragEnd.bind(this));
window.addEventListener("touchend", this.dragEnd.bind(this));
// set values
this.min = min || this.min;
this.max = max || this.max;
this.from = from || this.from;
this.to = to || this.to;
},
triggerChange(value, type = 'from') {
value = parseInt(value);
if (isNaN(value)) {
const {
from,
to
} = this;
this.from = this.to = 0;
this.from = from;
this.to = to;
return;
}
if (this.from > this.to && value < this.max && value > this.min) {
this[type === 'to' ? 'from' : 'to'] = this[type];
this[type] = value;
return;
}
if (type === 'to') {
this.to = 0;
this.to = Math.min(this.max, value < this.from ? this.from : value);
if (value < this.from) {
this.from = value < this.min ? this.min : value;
}
} else {
this.from = 0;
this.from = Math.max(this.min, value > this.to ? this.to : value);
if (value > this.to) {
this.to = value > this.max ? this.max : value;
}
}
return value;
},
getFromPos: function() {
// return relative slider position for 'from' value
return ((this.from - this.min) / (this.max - this.min) * 100) + '%'
},
getToPos: function() {
// return relative slider position for 'to' value
return ((this.to - this.min) / (this.max - this.min) * 100) + '%'
},
getWidth: function() {
// return relative width between 'from' and 'to' value
return ((Math.max(this.to, this.from) - Math.min(this.to, this.from)) / (this.max - this.min) * 100) + '%'
},
getMinPos: function() {
// return the smallest of 'from' or 'to' value
if (this.from < this.to) {
return this.getFromPos();
}
return this.getToPos();
},
drag: function($event) {
if (!this.dragFrom && !this.dragTo) {
return;
}
// get touch/mouse x-coordinate
let x;
const rect = this.$refs.slider.getBoundingClientRect();
if ($event.type === 'touchmove') {
x = $event.changedTouches[0].clientX - rect.left + this.$refs.slider.offsetLeft;
} else {
x = $event.clientX - rect.left + this.$refs.slider.offsetLeft; //x position within the element.
}
// calculate the value relative to the mouse/touch x-position on the document
let pos = Math.round((this.max - this.min) * (x - this.$refs.slider.offsetLeft) / this.$refs.slider.clientWidth) + this.min;
//console.log($event);
// stay in range
pos = pos > this.max ? this.max : pos;
pos = pos < this.min ? this.min : pos;
if (this.dragFrom) {
this.from = pos;
}
if (this.dragTo) {
this.to = pos;
}
},
dragEnd: function() {
this.dragFrom = false;
this.dragTo = false;
}
}
}
</script>
<div x-data="rangeSlider" x-init="init({{ minRange|default(0) }}, {{ maxRange|default(1000) }}, {{ defaultMinRange|default(200) }}, {{ defaultMaxRange|default(800) }})" >
<div class="relative select-none touch-none mx-6 my-3 md:mx-0 h-0.5 bg-neutral-300" x-ref="slider">
<div class="absolute bg-dark-black h-full w-full" :style="'width:' + getWidth() + '; left:' + getMinPos()"></div>
<div class="absolute -ml-2 top-1/2 -translate-y-1/2 cursor-pointer rounded-full border-2 border-dark-black bg-light-white w-6 h-6 z-30 hover:bg-neutral-200" @touchstart="dragFrom=true" @mousedown="dragFrom=true" :style="'left:' + getFromPos()"></div>
<div class="absolute -ml-2 top-1/2 -translate-y-1/2 cursor-pointer rounded-full border-2 border-dark-black bg-light-white w-6 h-6 z-30 hover:bg-neutral-200" @touchstart="dragTo=true" @mousedown="dragTo=true" :style="'left:' + getToPos()"></div>
</div>
<div class="pt-2 flex justify-between select-none mt-2">
<label class="flex gap-1 justify-center items-center py-2 px-3 border border-neutral-300 cursor-text focus-within:ring-4 focus-within:ring-blue-200 rounded-md">
{% if (prefix is defined) %}
<span>{{prefix}}</span>
{% endif %}
<div x-data="{value:from}" class="relative h-full">
<input class="text-center border-0 p-0 w-full absolute top-0 left-0 bg-transparent focus:border-none focus:ring-0" x-model="value" type="text" name="from" x-effect="value=Math.min(from, to)" @keyup.enter="triggerChange(value,'from')" @blur="triggerChange(value,'from')">
<span class="invisible block w-auto overflow-auto text-nowrap min-w-6 h-full" x-text="value"></span>
</div>
{% if (suffix is defined) %}
<span>{{suffix}}</span>
{% endif %}
</label>
<label class="flex gap-1 justify-center items-center py-2 px-3 border border-neutral-300 cursor-text focus-within:ring-4 focus-within:ring-blue-200 rounded-md">
{% if (prefix is defined) %}
<span>{{prefix}}</span>
{% endif %}
<div x-data="{value:to}" class="relative h-full">
<input class="text-center border-0 p-0 w-full absolute top-0 left-0 bg-transparent focus:border-none focus:ring-0" x-model="value" type="text" name="from" x-effect="value=Math.max(from, to)" @keyup.enter="triggerChange(value,'to')" @blur="triggerChange(value,'to')">
<span class="invisible block w-auto overflow-auto text-nowrap min-w-6 h-full" x-text="value"></span>
</div>
{% if (suffix is defined) %}
<span>{{suffix}}</span>
{% endif %}
</label>
</div>
</div>
<script>
function rangeSlider() {
return {
// slider start value
min: 0,
// slider end value
max: 100,
// range start
from: 10,
// range end
to: 80,
// flag if mouse is clicked or screen is touched
dragFrom: false,
dragTo: false,
// call on x-init
init: function(min, max, from, to){
// register mouse/touche move events to window
window.addEventListener("mousemove", (e) => { this.drag(e) });
window.addEventListener("touchmove", (e) => { this.drag(e) });
window.addEventListener( "mouseup", this.dragEnd.bind(this) );
window.addEventListener( "touchend", this.dragEnd.bind(this) );
// set values
this.min = min || this.min;
this.max = max || this.max;
this.from = from || this.from;
this.to = to || this.to;
},
triggerChange(value, type = 'from') {
value = parseInt(value);
if (isNaN(value)) {
const { from, to } = this;
this.from = this.to = 0;
this.from = from;
this.to = to;
return;
}
if (this.from > this.to && value < this.max && value > this.min) {
this[type === 'to' ? 'from' : 'to'] = this[type];
this[type] = value;
return;
}
if (type === 'to') {
this.to = 0;
this.to = Math.min(this.max, value < this.from ? this.from : value);
if (value < this.from) {
this.from = value < this.min ? this.min : value;
}
} else {
this.from = 0;
this.from = Math.max(this.min, value > this.to ? this.to : value);
if (value > this.to) {
this.to = value > this.max ? this.max : value;
}
}
return value;
},
getFromPos: function(){
// return relative slider position for 'from' value
return ((this.from-this.min) / (this.max-this.min) * 100) + '%'
},
getToPos: function(){
// return relative slider position for 'to' value
return ((this.to-this.min) / (this.max-this.min) * 100) + '%'
},
getWidth: function(){
// return relative width between 'from' and 'to' value
return ((Math.max(this.to, this.from) - Math.min(this.to, this.from)) / (this.max-this.min) * 100) + '%'
},
getMinPos: function(){
// return the smallest of 'from' or 'to' value
if(this.from < this.to){
return this.getFromPos();
}
return this.getToPos();
},
drag: function($event){
if(!this.dragFrom && !this.dragTo){
return;
}
// get touch/mouse x-coordinate
let x;
const rect = this.$refs.slider.getBoundingClientRect();
if($event.type==='touchmove'){
x = $event.changedTouches[0].clientX - rect.left + this.$refs.slider.offsetLeft;
}else{
x = $event.clientX - rect.left + this.$refs.slider.offsetLeft; //x position within the element.
}
// calculate the value relative to the mouse/touch x-position on the document
let pos = Math.round((this.max - this.min) * (x-this.$refs.slider.offsetLeft) / this.$refs.slider.clientWidth) + this.min;
//console.log($event);
// stay in range
pos = pos > this.max ? this.max : pos;
pos = pos < this.min ? this.min : pos;
if(this.dragFrom){
this.from = pos;
}
if(this.dragTo){
this.to = pos;
}
},
dragEnd: function(){
this.dragFrom = false;
this.dragTo = false;
}
}
}
</script>
/* No context defined. */
No notes defined.