Add filters
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Dual-handle range slider — pure JS with draggable handles.
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function initAll(force) {
|
||||
document.querySelectorAll(".range-slider").forEach(function (slider) {
|
||||
if (force) slider._rsInit = false;
|
||||
if (slider._rsInit) return;
|
||||
slider._rsInit = true;
|
||||
|
||||
var minHandle = slider.querySelector(".range-handle-min");
|
||||
var maxHandle = slider.querySelector(".range-handle-max");
|
||||
var track = slider.querySelector(".range-track-fill");
|
||||
if (!minHandle || !maxHandle) return;
|
||||
|
||||
var minTarget = document.getElementById(minHandle.getAttribute("data-target"));
|
||||
var maxTarget = document.getElementById(maxHandle.getAttribute("data-target"));
|
||||
var dMin = parseInt(slider.getAttribute("data-min"), 10);
|
||||
var dMax = parseInt(slider.getAttribute("data-max"), 10);
|
||||
var step = parseInt(slider.getAttribute("data-step"), 10) || 1;
|
||||
|
||||
function valueToPercent(v) { return ((v - dMin) / (dMax - dMin)) * 100; }
|
||||
function percentToValue(p) {
|
||||
var raw = dMin + (p / 100) * (dMax - dMin);
|
||||
return Math.round(raw / step) * step;
|
||||
}
|
||||
function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }
|
||||
|
||||
function getTargetVal(el) { return parseInt(el ? el.value : minTarget.value, 10) || dMin; }
|
||||
function setTargetVal(el, v) { if (el) el.value = v; }
|
||||
|
||||
function update() {
|
||||
var minV = getTargetVal(minTarget);
|
||||
var maxV = getTargetVal(maxTarget);
|
||||
minV = clamp(minV, dMin, dMax);
|
||||
maxV = clamp(maxV, dMin, dMax);
|
||||
if (minV > maxV) minV = maxV;
|
||||
if (maxV < minV) maxV = minV;
|
||||
setTargetVal(minTarget, minV);
|
||||
setTargetVal(maxTarget, maxV);
|
||||
var minP = valueToPercent(minV);
|
||||
var maxP = valueToPercent(maxV);
|
||||
minHandle.style.left = minP + "%";
|
||||
maxHandle.style.left = maxP + "%";
|
||||
if (track) {
|
||||
track.style.left = minP + "%";
|
||||
track.style.width = (maxP - minP) + "%";
|
||||
}
|
||||
}
|
||||
|
||||
function makeDraggable(handle, isMin) {
|
||||
handle.addEventListener("mousedown", function (e) {
|
||||
e.preventDefault();
|
||||
var rect = slider.getBoundingClientRect();
|
||||
function onMove(ev) {
|
||||
var pct = ((ev.clientX - rect.left) / rect.width) * 100;
|
||||
var v = percentToValue(clamp(pct, 0, 100));
|
||||
if (isMin) {
|
||||
minTarget.value = clamp(v, dMin, getTargetVal(maxTarget));
|
||||
} else {
|
||||
maxTarget.value = clamp(v, getTargetVal(minTarget), dMax);
|
||||
}
|
||||
update();
|
||||
// Trigger input event on the target so any listeners fire
|
||||
var tgt = isMin ? minTarget : maxTarget;
|
||||
if (tgt) tgt.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
}
|
||||
function onUp() {
|
||||
document.removeEventListener("mousemove", onMove);
|
||||
document.removeEventListener("mouseup", onUp);
|
||||
}
|
||||
document.addEventListener("mousemove", onMove);
|
||||
document.addEventListener("mouseup", onUp);
|
||||
onMove(e);
|
||||
});
|
||||
}
|
||||
|
||||
makeDraggable(minHandle, true);
|
||||
makeDraggable(maxHandle, false);
|
||||
|
||||
// Sync from inputs to slider
|
||||
function fromInputs() { update(); }
|
||||
if (minTarget) minTarget.addEventListener("input", fromInputs);
|
||||
if (maxTarget) maxTarget.addEventListener("input", fromInputs);
|
||||
|
||||
update();
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", initAll);
|
||||
document.addEventListener("htmx:afterSwap", initAll);
|
||||
// Expose for manual re-init (filter bar toggle)
|
||||
window.initRangeSliders = initAll;
|
||||
})();
|
||||
Reference in New Issue
Block a user