Finish the custom incrementer component
This commit is contained in:
parent
878d278303
commit
c5c5a71587
5 changed files with 112 additions and 89 deletions
|
|
@ -15,150 +15,143 @@ export class DotDungeonIncrementer extends HTMLElement {
|
||||||
static elementName = `dd-incrementer`;
|
static elementName = `dd-incrementer`;
|
||||||
static formAssociated = true;
|
static formAssociated = true;
|
||||||
|
|
||||||
static styles = ``;
|
static #styles = ``;
|
||||||
|
_internals;
|
||||||
#min;
|
_shadow;
|
||||||
#max;
|
|
||||||
#smallStep;
|
|
||||||
#largeStep;
|
|
||||||
|
|
||||||
#input;
|
#input;
|
||||||
#publicInput;
|
|
||||||
#sr;
|
_min;
|
||||||
|
_max;
|
||||||
|
_smallStep;
|
||||||
|
_largeStep;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
this._shadow = this.attachShadow({ mode: `open`, delegatesFocus: true });
|
||||||
this._internals = this.attachInternals();
|
this._internals = this.attachInternals();
|
||||||
|
this._internals.role = `spinbutton`;
|
||||||
|
|
||||||
const value = this.getAttribute(`value`);
|
const value = this.getAttribute(`value`);
|
||||||
this.#min = parseInt(this.getAttribute(`min`) ?? 0);
|
this._min = parseInt(this.getAttribute(`min`) ?? 0);
|
||||||
this.#max = parseInt(this.getAttribute(`max`) ?? 0);
|
this._max = parseInt(this.getAttribute(`max`) ?? 0);
|
||||||
this.#smallStep = parseInt(this.getAttribute(`smallStep`) ?? 1);
|
this._smallStep = parseInt(this.getAttribute(`smallStep`) ?? 1);
|
||||||
this.#largeStep = parseInt(this.getAttribute(`largeStep`) ?? 5);
|
this._largeStep = parseInt(this.getAttribute(`largeStep`) ?? 5);
|
||||||
|
|
||||||
|
this._internals.ariaValueMin = this._min;
|
||||||
|
this._internals.ariaValueMax = this._max;
|
||||||
|
|
||||||
const sr = this.attachShadow({
|
|
||||||
mode: `open`,
|
|
||||||
delegatesFocus: true
|
|
||||||
});
|
|
||||||
this.#sr = sr;
|
|
||||||
const container = document.createElement(`div`);
|
const container = document.createElement(`div`);
|
||||||
if (DotDungeonIncrementer.styles) this.#embedStyles();
|
if (DotDungeonIncrementer.#styles) this.#embedStyles();
|
||||||
|
|
||||||
// The input that the user can see / modify
|
// The input that the user can see / modify
|
||||||
const input = document.createElement(`input`);
|
const input = document.createElement(`input`);
|
||||||
this.#input = input;
|
this.#input = input;
|
||||||
input.type = `number`;
|
input.type = `number`;
|
||||||
|
input.ariaHidden = true;
|
||||||
input.min = this.getAttribute(`min`);
|
input.min = this.getAttribute(`min`);
|
||||||
input.max = this.getAttribute(`max`);
|
input.max = this.getAttribute(`max`);
|
||||||
input.addEventListener(`change`, this.#updateValue.bind(this));
|
input.addEventListener(`change`, this.#updateValue.bind(this));
|
||||||
input.value = value;
|
input.value = value;
|
||||||
|
|
||||||
// input.id = this.id;
|
|
||||||
// this.removeAttribute(`id`);
|
|
||||||
|
|
||||||
// plus button
|
// plus button
|
||||||
const increment = document.createElement(`span`);
|
const increment = document.createElement(`span`);
|
||||||
increment.innerHTML = `+`;
|
increment.innerHTML = `+`;
|
||||||
// increment.type = `button`;
|
increment.ariaHidden = true;
|
||||||
// increment.tabIndex = -1;
|
|
||||||
increment.classList.value = `increment`;
|
increment.classList.value = `increment`;
|
||||||
increment.addEventListener(`click`, this.#increment.bind(this));
|
increment.addEventListener(`mousedown`, this.#increment.bind(this));
|
||||||
|
|
||||||
// minus button
|
// minus button
|
||||||
const decrement = document.createElement(`span`);
|
const decrement = document.createElement(`span`);
|
||||||
decrement.innerHTML = `-`;
|
decrement.innerHTML = `-`;
|
||||||
// decrement.type = `button`;
|
decrement.ariaHidden = true;
|
||||||
// decrement.tabIndex = -1;
|
|
||||||
decrement.classList.value = `decrement`;
|
decrement.classList.value = `decrement`;
|
||||||
decrement.addEventListener(`click`, this.#decrement.bind(this));
|
decrement.addEventListener(`mousedown`, this.#decrement.bind(this));
|
||||||
|
|
||||||
|
|
||||||
// Construct the DOM
|
// Construct the DOM
|
||||||
container.appendChild(decrement);
|
container.appendChild(decrement);
|
||||||
container.appendChild(input);
|
container.appendChild(input);
|
||||||
container.appendChild(increment);
|
container.appendChild(increment);
|
||||||
sr.appendChild(container);
|
this._shadow.appendChild(container);
|
||||||
};
|
};
|
||||||
|
|
||||||
connectedCallback() {
|
get form() {
|
||||||
/*
|
return this._internals.form;
|
||||||
This input exists for the sole purpose of making it so that the form data
|
}
|
||||||
works with this input without needing to do jank work arounds, as Foundry
|
|
||||||
only listens for change events from a small subset of elements which makes
|
|
||||||
this a bit a jank work around as it is.
|
|
||||||
*/
|
|
||||||
const hiddenInput = document.createElement(`input`);
|
|
||||||
this.#publicInput = hiddenInput;
|
|
||||||
hiddenInput.type = `hidden`;
|
|
||||||
hiddenInput.value = this.#input.value;
|
|
||||||
hiddenInput.name = this.getAttribute(`name`);
|
|
||||||
// this.removeAttribute(`name`);
|
|
||||||
// this.appendChild(hiddenInput);
|
|
||||||
|
|
||||||
if (!DotDungeonIncrementer.styles) {
|
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() {
|
||||||
|
this.replaceChildren();
|
||||||
|
|
||||||
|
/*
|
||||||
|
This converts all of the double-dash 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(`--`)) {
|
||||||
|
this.style.setProperty(attrVar.name, attrVar.value);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!DotDungeonIncrementer.#styles) {
|
||||||
fetch(`./systems/dotdungeon/.styles/v3/components/incrementer.css`)
|
fetch(`./systems/dotdungeon/.styles/v3/components/incrementer.css`)
|
||||||
.then(r => r.text())
|
.then(r => r.text())
|
||||||
.then(t => {
|
.then(t => {
|
||||||
DotDungeonIncrementer.styles = t;
|
DotDungeonIncrementer.#styles = t;
|
||||||
this.#embedStyles();
|
this.#embedStyles();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
get value() {
|
|
||||||
return this.#input.value;
|
|
||||||
};
|
|
||||||
get form() {
|
|
||||||
return this._internals.form;
|
|
||||||
};
|
|
||||||
get type() {
|
|
||||||
return `number`;
|
|
||||||
};
|
|
||||||
|
|
||||||
#embedStyles() {
|
#embedStyles() {
|
||||||
const style = document.createElement(`style`);
|
const style = document.createElement(`style`);
|
||||||
style.innerHTML = DotDungeonIncrementer.styles;
|
style.innerHTML = DotDungeonIncrementer.#styles;
|
||||||
this.#sr.appendChild(style);
|
this._shadow.appendChild(style);
|
||||||
};
|
};
|
||||||
|
|
||||||
#updateValue() {
|
#updateValue() {
|
||||||
let value = parseInt(this.#input.value);
|
let value = parseInt(this.#input.value);
|
||||||
if (this.getAttribute(`min`)) value = Math.max(this.#min, value);
|
if (this.getAttribute(`min`)) value = Math.max(this._min, value);
|
||||||
if (this.getAttribute(`max`)) value = Math.min(this.#max, value);
|
if (this.getAttribute(`max`)) value = Math.min(this._max, value);
|
||||||
this.#input.value = value;
|
this.#input.value = value;
|
||||||
if (this.#input.value === this.#publicInput.value) return;
|
this.value = value;
|
||||||
this.#publicInput.value = value;
|
this.dispatchEvent(new Event(`change`, { bubbles: true }));
|
||||||
const event = new Event(`change`);
|
|
||||||
// this.#publicInput.dispatchEvent(event);
|
// NOTE: This may be really annoying, in that case, remove it later
|
||||||
console.log(`#updateValue`)
|
this.blur();
|
||||||
this.dispatchEvent(event);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @param {Event} $e */
|
||||||
#increment($e) {
|
#increment($e) {
|
||||||
|
$e.preventDefault();
|
||||||
let value = parseInt(this.#input.value);
|
let value = parseInt(this.#input.value);
|
||||||
value += $e.ctrlKey ? this.#largeStep : this.#smallStep;
|
value += $e.ctrlKey ? this._largeStep : this._smallStep;
|
||||||
this.#input.value = value;
|
this.#input.value = value;
|
||||||
this.#updateValue();
|
this.#updateValue();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @param {Event} $e */
|
||||||
#decrement($e) {
|
#decrement($e) {
|
||||||
|
$e.preventDefault();
|
||||||
let value = parseInt(this.#input.value);
|
let value = parseInt(this.#input.value);
|
||||||
value -= $e.ctrlKey ? this.#largeStep : this.#smallStep;
|
value -= $e.ctrlKey ? this._largeStep : this._smallStep;
|
||||||
this.#input.value = value;
|
this.#input.value = value;
|
||||||
this.#updateValue();
|
this.#updateValue();
|
||||||
};
|
};
|
||||||
|
|
||||||
focus() {
|
|
||||||
console.log(1)
|
|
||||||
super.focus();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
if (!window.customElements.get(DotDungeonIncrementer.elementName)) {
|
|
||||||
window.customElements.define(
|
|
||||||
DotDungeonIncrementer.elementName,
|
|
||||||
DotDungeonIncrementer
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1 +1,21 @@
|
||||||
import "./incrementer.mjs";
|
import { DotDungeonIncrementer } from "./incrementer.mjs";
|
||||||
|
|
||||||
|
const components = [
|
||||||
|
DotDungeonIncrementer,
|
||||||
|
];
|
||||||
|
|
||||||
|
export function registerCustomComponents() {
|
||||||
|
(CONFIG.CACHE ??= {}).componentListeners ??= [];
|
||||||
|
for (const component of components) {
|
||||||
|
if (!window.customElements.get(component.elementName)) {
|
||||||
|
console.debug(`.dungeon | Registering component "${component.elementName}"`);
|
||||||
|
window.customElements.define(
|
||||||
|
component.elementName,
|
||||||
|
component
|
||||||
|
);
|
||||||
|
if (component.formAssociated) {
|
||||||
|
CONFIG.CACHE.componentListeners.push(component.elementName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,10 @@ import * as hbs from "./handlebars.mjs";
|
||||||
import "./hooks/hotReload.mjs";
|
import "./hooks/hotReload.mjs";
|
||||||
|
|
||||||
// Misc Imports
|
// Misc Imports
|
||||||
|
import { registerCustomComponents } from "./components/index.mjs";
|
||||||
import loadSettings from "./settings/index.mjs";
|
import loadSettings from "./settings/index.mjs";
|
||||||
import { devInit } from "./hooks/devInit.mjs";
|
import { devInit } from "./hooks/devInit.mjs";
|
||||||
import DOTDUNGEON from "./config.mjs";
|
import DOTDUNGEON from "./config.mjs";
|
||||||
import "./components/index.mjs";
|
|
||||||
|
|
||||||
|
|
||||||
Hooks.once(`init`, async () => {
|
Hooks.once(`init`, async () => {
|
||||||
|
|
@ -109,8 +109,9 @@ Hooks.once(`init`, async () => {
|
||||||
|
|
||||||
hbs.registerHandlebarsHelpers();
|
hbs.registerHandlebarsHelpers();
|
||||||
hbs.preloadHandlebarsTemplates();
|
hbs.preloadHandlebarsTemplates();
|
||||||
|
registerCustomComponents();
|
||||||
|
|
||||||
CONFIG.CACHE = {};
|
CONFIG.CACHE ??= {};
|
||||||
CONFIG.CACHE.icons = await hbs.preloadIcons();
|
CONFIG.CACHE.icons = await hbs.preloadIcons();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,17 @@ export class GenericActorSheet extends ActorSheet {
|
||||||
if (!this.isEditable) return;
|
if (!this.isEditable) return;
|
||||||
console.debug(`.dungeon | Generic sheet adding listeners`);
|
console.debug(`.dungeon | Generic sheet adding listeners`);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Custom element event listeners because Foundry doesn't listen to them by
|
||||||
|
default.
|
||||||
|
*/
|
||||||
|
html.find(
|
||||||
|
CONFIG.CACHE.componentListeners.join(`,`)
|
||||||
|
).on(`change`, this._onChangeInput.bind(this));
|
||||||
|
|
||||||
|
/*
|
||||||
|
Utility event listeners that apply
|
||||||
|
*/
|
||||||
html.find(`[data-collapse-id]`).on(`click`, this._handleSummaryToggle.bind(this));
|
html.find(`[data-collapse-id]`).on(`click`, this._handleSummaryToggle.bind(this));
|
||||||
html.find(`[data-roll-formula]`).on(`click`, this._handleRoll.bind(this));
|
html.find(`[data-roll-formula]`).on(`click`, this._handleRoll.bind(this));
|
||||||
html.find(`[data-embedded-update-on="change"]`)
|
html.find(`[data-embedded-update-on="change"]`)
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,6 @@ $default-height: 1.25rem;
|
||||||
div {
|
div {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: var(--height, $default-height) var(--width, 50px) var(--height, $default-height);
|
grid-template-columns: var(--height, $default-height) var(--width, 50px) var(--height, $default-height);
|
||||||
// I dunno why this is needed for the height to not be calculated as 17px,
|
|
||||||
// but it is for some arcane reason
|
|
||||||
grid-template-rows: var(--height, $default-height);
|
|
||||||
border-radius: var(--border-radius, $default-border-radius);
|
border-radius: var(--border-radius, $default-border-radius);
|
||||||
@include material.elevate(2);
|
@include material.elevate(2);
|
||||||
|
|
||||||
|
|
@ -34,8 +31,9 @@ span, input {
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
font-family: inherit;
|
font-family: var(--font-family, inherit);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
font-size: var(--font-size, inherit);
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
|
|
||||||
&::-webkit-inner-spin-button, &::-webkit-outer-spin-button {
|
&::-webkit-inner-spin-button, &::-webkit-outer-spin-button {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue