152 lines
4.3 KiB
JavaScript
152 lines
4.3 KiB
JavaScript
import { DotDungeonIcon } from "./icon.mjs";
|
|
import { StyledShadowElement } from "./mixins/Styles.mjs";
|
|
|
|
/**
|
|
Attributes:
|
|
@property {string} name - The path to the value to update
|
|
@property {number} value - The actual value of the input
|
|
@property {number} min - The minimum value of the input
|
|
@property {number} max - The maximum value of the input
|
|
@property {number?} smallStep - The step size used for the buttons and arrow keys
|
|
@property {number?} largeStep - The step size used for the buttons + Ctrl and page up / down
|
|
|
|
Styling:
|
|
- `--height`: Controls the height of the element + the width of the buttons (default: 1.25rem)
|
|
- `--width`: Controls the width of the number input (default 50px)
|
|
*/
|
|
export class DotDungeonIncrementer extends StyledShadowElement(HTMLElement) {
|
|
static elementName = `dd-incrementer`;
|
|
static formAssociated = true;
|
|
|
|
static _stylePath = `v3/components/incrementer.css`;
|
|
|
|
_internals;
|
|
#input;
|
|
|
|
_min;
|
|
_max;
|
|
_smallStep;
|
|
_largeStep;
|
|
|
|
constructor() {
|
|
super();
|
|
|
|
// Form internals
|
|
this._internals = this.attachInternals();
|
|
this._internals.role = `spinbutton`;
|
|
};
|
|
|
|
get form() {
|
|
return this._internals.form;
|
|
}
|
|
|
|
get name() {
|
|
return this.getAttribute(`name`);
|
|
}
|
|
set name(value) {
|
|
this.setAttribute(`name`, value);
|
|
}
|
|
|
|
get value() {
|
|
return this.getAttribute(`value`);
|
|
};
|
|
set value(value) {
|
|
this.setAttribute(`value`, value);
|
|
};
|
|
|
|
get type() {
|
|
return `number`;
|
|
}
|
|
|
|
connectedCallback() {
|
|
super.connectedCallback();
|
|
this.replaceChildren();
|
|
|
|
// Attribute parsing / registration
|
|
const value = this.getAttribute(`value`);
|
|
this._min = parseInt(this.getAttribute(`min`) ?? 0);
|
|
this._max = parseInt(this.getAttribute(`max`) ?? 0);
|
|
this._smallStep = parseInt(this.getAttribute(`smallStep`) ?? 1);
|
|
this._largeStep = parseInt(this.getAttribute(`largeStep`) ?? 5);
|
|
|
|
this._internals.ariaValueMin = this._min;
|
|
this._internals.ariaValueMax = this._max;
|
|
|
|
const container = document.createElement(`div`);
|
|
|
|
// The input that the user can see / modify
|
|
const input = document.createElement(`input`);
|
|
this.#input = input;
|
|
input.type = `number`;
|
|
input.ariaHidden = true;
|
|
input.min = this.getAttribute(`min`);
|
|
input.max = this.getAttribute(`max`);
|
|
input.addEventListener(`change`, this.#updateValue.bind(this));
|
|
input.value = value;
|
|
|
|
// plus button
|
|
const increment = document.createElement("dd-icon");
|
|
increment.setAttribute(`name`, `create`);
|
|
increment.setAttribute(`var:size`, `0.75rem`);
|
|
increment.setAttribute(`var:fill`, `currentColor`);
|
|
increment.ariaHidden = true;
|
|
increment.classList.value = `increment`;
|
|
increment.addEventListener(`mousedown`, this.#increment.bind(this));
|
|
|
|
// minus button
|
|
const decrement = document.createElement(DotDungeonIcon.elementName);
|
|
decrement.setAttribute(`name`, `minus`);
|
|
decrement.setAttribute(`var:size`, `0.75rem`);
|
|
decrement.setAttribute(`var:fill`, `currentColor`);
|
|
decrement.ariaHidden = true;
|
|
decrement.classList.value = `decrement`;
|
|
decrement.addEventListener(`mousedown`, this.#decrement.bind(this));
|
|
|
|
// Construct the DOM
|
|
container.appendChild(decrement);
|
|
container.appendChild(input);
|
|
container.appendChild(increment);
|
|
this._shadow.appendChild(container);
|
|
|
|
/*
|
|
This converts all of the namespace prefixed properties on the element to
|
|
CSS variables so that they don't all need to be provided by doing style=""
|
|
*/
|
|
for (const attrVar of this.attributes) {
|
|
if (attrVar.name?.startsWith(`var:`)) {
|
|
const prop = attrVar.name.replace(`var:`, ``);
|
|
this.style.setProperty(`--` + prop, attrVar.value);
|
|
};
|
|
};
|
|
};
|
|
|
|
#updateValue() {
|
|
let value = parseInt(this.#input.value);
|
|
if (this.getAttribute(`min`)) value = Math.max(this._min, value);
|
|
if (this.getAttribute(`max`)) value = Math.min(this._max, value);
|
|
this.#input.value = value;
|
|
this.value = value;
|
|
this.dispatchEvent(new Event(`change`, { bubbles: true }));
|
|
|
|
// NOTE: This may be really annoying, in that case, remove it later
|
|
this.blur();
|
|
};
|
|
|
|
/** @param {Event} $e */
|
|
#increment($e) {
|
|
$e.preventDefault();
|
|
let value = parseInt(this.#input.value);
|
|
value += $e.ctrlKey ? this._largeStep : this._smallStep;
|
|
this.#input.value = value;
|
|
this.#updateValue();
|
|
};
|
|
|
|
/** @param {Event} $e */
|
|
#decrement($e) {
|
|
$e.preventDefault();
|
|
let value = parseInt(this.#input.value);
|
|
value -= $e.ctrlKey ? this._largeStep : this._smallStep;
|
|
this.#input.value = value;
|
|
this.#updateValue();
|
|
};
|
|
};
|