Files
Foundry-VTT-Docker/resources/app/client-esm/applications/elements/range-picker.mjs
2025-01-04 00:34:03 +01:00

167 lines
4.4 KiB
JavaScript

import AbstractFormInputElement from "./form-element.mjs";
/**
* @typedef {import("../forms/fields.mjs").FormInputConfig} FormInputConfig
*/
/**
* @typedef {Object} RangePickerInputConfig
* @property {number} min
* @property {number} max
* @property {number} [step]
*/
/**
* A custom HTML element responsible selecting a value on a range slider with a linked number input field.
* @extends {AbstractFormInputElement<number>}
*/
export default class HTMLRangePickerElement extends AbstractFormInputElement {
constructor() {
super();
this.#min = Number(this.getAttribute("min")) ?? 0;
this.#max = Number(this.getAttribute("max")) ?? 1;
this.#step = Number(this.getAttribute("step")) || undefined;
this._setValue(Number(this.getAttribute("value"))); // Initialize existing value
}
/** @override */
static tagName = "range-picker";
/**
* The range input.
* @type {HTMLInputElement}
*/
#rangeInput;
/**
* The number input.
* @type {HTMLInputElement}
*/
#numberInput;
/**
* The minimum allowed value for the range.
* @type {number}
*/
#min;
/**
* The maximum allowed value for the range.
* @type {number}
*/
#max;
/**
* A required step size for the range.
* @type {number}
*/
#step;
/* -------------------------------------------- */
/**
* The value of the input element.
* @type {number}
*/
get valueAsNumber() {
return this._getValue();
}
/* -------------------------------------------- */
/** @override */
_buildElements() {
// Create range input element
const r = this.#rangeInput = document.createElement("input");
r.type = "range";
r.min = String(this.#min);
r.max = String(this.#max);
r.step = String(this.#step ?? 0.1);
this._applyInputAttributes(r);
// Create the number input element
const n = this.#numberInput = this._primaryInput = document.createElement("input");
n.type = "number";
n.min = String(this.#min);
n.max = String(this.#max);
n.step = this.#step ?? "any";
this._applyInputAttributes(n);
return [this.#rangeInput, this.#numberInput];
}
/* -------------------------------------------- */
/** @override */
_setValue(value) {
value = Math.clamp(value, this.#min, this.#max);
if ( this.#step ) value = value.toNearest(this.#step);
this._value = value;
}
/* -------------------------------------------- */
/** @override */
_refresh() {
if ( !this.#rangeInput ) return; // Not yet connected
this.#rangeInput.valueAsNumber = this.#numberInput.valueAsNumber = this._value;
}
/* -------------------------------------------- */
/** @override */
_activateListeners() {
const onChange = this.#onChangeInput.bind(this);
this.#rangeInput.addEventListener("input", this.#onDragSlider.bind(this));
this.#rangeInput.addEventListener("change", onChange);
this.#numberInput.addEventListener("change", onChange);
}
/* -------------------------------------------- */
/**
* Update display of the number input as the range slider is actively changed.
* @param {InputEvent} event The originating input event
*/
#onDragSlider(event) {
event.preventDefault();
this.#numberInput.valueAsNumber = this.#rangeInput.valueAsNumber;
}
/* -------------------------------------------- */
/**
* Handle changes to one of the inputs of the range picker element.
* @param {InputEvent} event The originating input change event
*/
#onChangeInput(event) {
event.stopPropagation();
this.value = event.currentTarget.valueAsNumber;
}
/* -------------------------------------------- */
/** @override */
_toggleDisabled(disabled) {
this.#rangeInput.toggleAttribute("disabled", disabled);
this.#numberInput.toggleAttribute("disabled", disabled);
}
/* -------------------------------------------- */
/**
* Create a HTMLRangePickerElement using provided configuration data.
* @param {FormInputConfig & RangePickerInputConfig} config
* @returns {HTMLRangePickerElement}
*/
static create(config) {
const picker = document.createElement(HTMLRangePickerElement.tagName);
picker.name = config.name;
for ( const attr of ["value", "min", "max", "step"] ) {
if ( attr in config ) picker.setAttribute(attr, config[attr]);
}
foundry.applications.fields.setInputAttributes(picker, config);
return picker;
}
}