import type { API as NoUiSliderInstance } from "nouislider"; import noUiSlider from "nouislider"; import type { Instance as WNumbInstance } from "wnumb"; import wnumb from "wnumb"; import defaultConfig from "./data"; export interface RangeSliderConfig { format?: any; [key: string]: any; } interface Output { element: HTMLElement; format?: WNumbInstance; } export default class RangeSlider { public element: HTMLElement; public config: RangeSliderConfig; public instance?: NoUiSliderInstance; public inputs: HTMLInputElement[]; public outputs: (Output | false)[]; constructor(element: HTMLElement, config?: Partial) { this.element = element; this.config = { ...defaultConfig, ...config }; this.inputs = []; this.outputs = []; this.handleSliderUpdate = this.handleSliderUpdate.bind(this); (this.element as any).ODS_RangeSlider = this; this.init(); return this; } public init(): void { const configAttr = this.element.getAttribute("data-rangeslider-config"); let elementConfig: Partial = {}; if (configAttr) { try { elementConfig = JSON.parse(configAttr); } catch { // ignore parse error } } this.config = { ...this.config, ...elementConfig }; this.config.format = wnumb(this.config.format); if ((this.element as any).noUiSlider) { (this.element as any).noUiSlider = undefined; if (this.instance?.destroy) this.instance.destroy(); } // Ensure config matches required Options type if (!this.config.range) { throw new Error("RangeSlider config must include 'range'."); } this.instance = noUiSlider.create(this.element, this.config as any); this.inputs = Array.from( document.querySelectorAll( `input[name^=${this.element.getAttribute("id")}-]`, ), ) as HTMLInputElement[]; this.outputs = this.inputs.map((input) => { const outputElement = document.getElementById( `${input.getAttribute("id")}-output`, ); if (outputElement) { const formatAttr = outputElement.getAttribute("data-format"); return { element: outputElement, format: outputElement.hasAttribute("data-format") && formatAttr ? wnumb(JSON.parse(formatAttr)) : undefined, }; } return false; }); this.instance.on( "update", (values: (string | number)[], handle: number) => { this.handleSliderUpdate(values.map(String), handle); }, ); } public update(): void { this.instance?.destroy(); this.init(); } public destroy(): void { this.instance?.off("update"); this.instance?.destroy(); (this.element as any).ODS_RangeSlider = null; } static getInstance(el: HTMLElement): RangeSlider | null { return el && (el as any).ODS_RangeSlider ? (el as any).ODS_RangeSlider : null; } private handleSliderUpdate = (values: string[], handle: number): void => { const value = values[handle]; if (this.inputs[handle]) { this.inputs[handle].value = value; } if (this.outputs[handle]) { const output = this.outputs[handle] as Output; if (output && output.format) { output.element.innerText = output.format.to( this.config.format.from(value), ); } else if (output) { output.element.innerText = value; } } }; }