Initial
This commit is contained in:
166
resources/app/client-esm/applications/elements/range-picker.mjs
Normal file
166
resources/app/client-esm/applications/elements/range-picker.mjs
Normal file
@@ -0,0 +1,166 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user