From f15f3a4456b66f18820138a9291cb9e7148bb39f Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 11 Apr 2024 22:32:04 -0600 Subject: [PATCH] Get the icon component coded and being used in the incrementer component --- module/components/icon.mjs | 79 ++++++++++++++----- module/components/incrementer.mjs | 104 ++++++++++++++------------ module/components/index.mjs | 2 + styles/v3/components/icon.scss | 21 ++++++ styles/v3/components/incrementer.scss | 5 +- 5 files changed, 141 insertions(+), 70 deletions(-) create mode 100644 styles/v3/components/icon.scss diff --git a/module/components/icon.mjs b/module/components/icon.mjs index daadaa7..3595379 100644 --- a/module/components/icon.mjs +++ b/module/components/icon.mjs @@ -7,32 +7,37 @@ export class DotDungeonIcon extends HTMLElement { static elementName = `dd-icon`; static formAssociated = false; - static #styles = ``; - _internals; - _shadow; + #shadow; - _min; - _max; - _smallStep; - _largeStep; + static #styles = ``; + static _cache = new Map(); + #style; + #container; + _name; + _path; constructor() { super(); - this._shadow = this.attachShadow({ mode: `open`, delegatesFocus: true }); + this.#shadow = this.attachShadow({ mode: `open`, delegatesFocus: true }); if (DotDungeonIcon.#styles) this.#embedStyles(); + + this.#container = document.createElement(`div`); + this.#shadow.appendChild(this.#container); }; + async connectedCallback() { - connectedCallback() { - this.replaceChildren(); + this._name = this.getAttribute(`name`); + this._path = this.getAttribute(`path`); /* 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 (attrVar.name?.startsWith(`var:`)) { + const prop = attrVar.name.replace(`var:`, ``); + this.style.setProperty(`--` + prop, attrVar.value); }; }; @@ -40,7 +45,7 @@ export class DotDungeonIcon extends HTMLElement { Style fetching if we haven't gotten them yet */ if (!DotDungeonIcon.#styles) { - fetch(`./systems/dotdungeon/.styles/v3/components/incrementer.css`) + fetch(`./systems/dotdungeon/.styles/v3/components/icon.css`) .then(r => r.text()) .then(t => { DotDungeonIcon.#styles = t; @@ -53,16 +58,50 @@ export class DotDungeonIcon extends HTMLElement { the slot content, as then we can have a default per-icon usage */ let content; + // TODO: Make name request + if (this._name) { + content = await this.#getIcon(`./systems/dotdungeon/assets/${this._name}.svg`); + }; - if (!content) { - let slot = document.createElement(`slot`); - content ??= slot; - } + // TODO: make path request + if (this._path && !content) { + content = await this.#getIcon(this._path); + }; + + // TODO: insert content into DOM + if (content) { + this.#container.appendChild(content); + }; }; #embedStyles() { - const style = document.createElement(`style`); - style.innerHTML = DotDungeonIcon.#styles; - this._shadow.appendChild(style); + this.#style = document.createElement(`style`); + this.#style.innerHTML = DotDungeonIcon.#styles; + this.#shadow.appendChild(this.#style); + }; + + async #getIcon(path) { + // Cache hit! + if (DotDungeonIcon._cache.has(path)) { + console.debug(`.dungeon | Icon ${path} cache hit`); + return DotDungeonIcon._cache.get(path); + }; + + const r = await fetch(path); + switch (r.status) { + case 200: + case 201: + break; + default: + console.error(`.dungeon | Failed to fetch icon: ${path}`); + return; + }; + + console.debug(`.dungeon | Adding icon ${path} to the cache`); + const temp = document.createElement(`div`); + temp.innerHTML = await r.text(); + const svg = temp.querySelector(`svg`); + DotDungeonIcon._cache.set(path, svg); + return svg; }; }; diff --git a/module/components/incrementer.mjs b/module/components/incrementer.mjs index 2a8ab87..b572803 100644 --- a/module/components/incrementer.mjs +++ b/module/components/incrementer.mjs @@ -1,3 +1,5 @@ +import { DotDungeonIcon } from "./icon.mjs"; + /** Attributes: @property {string} name - The path to the value to update @@ -19,6 +21,7 @@ export class DotDungeonIncrementer extends HTMLElement { _internals; _shadow; #input; + #style; _min; _max; @@ -33,48 +36,6 @@ export class DotDungeonIncrementer extends HTMLElement { // Form internals this._internals = this.attachInternals(); this._internals.role = `spinbutton`; - - // 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(`span`); - increment.innerHTML = `+`; - increment.ariaHidden = true; - increment.classList.value = `increment`; - increment.addEventListener(`mousedown`, this.#increment.bind(this)); - - // minus button - const decrement = document.createElement(`span`); - decrement.innerHTML = `-`; - 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); }; get form() { @@ -102,13 +63,60 @@ export class DotDungeonIncrementer extends HTMLElement { 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 double-dash prefixed properties on the element to + 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(`--`)) { - this.style.setProperty(attrVar.name, attrVar.value); + if (attrVar.name?.startsWith(`var:`)) { + const prop = attrVar.name.replace(`var:`, ``); + this.style.setProperty(`--` + prop, attrVar.value); }; }; @@ -123,9 +131,9 @@ export class DotDungeonIncrementer extends HTMLElement { }; #embedStyles() { - const style = document.createElement(`style`); - style.innerHTML = DotDungeonIncrementer.#styles; - this._shadow.appendChild(style); + this.#style = document.createElement(`style`); + this.#style.innerHTML = DotDungeonIncrementer.#styles; + this._shadow.appendChild(this.#style); }; #updateValue() { diff --git a/module/components/index.mjs b/module/components/index.mjs index 4912d9a..f4d39e9 100644 --- a/module/components/index.mjs +++ b/module/components/index.mjs @@ -1,6 +1,8 @@ import { DotDungeonIncrementer } from "./incrementer.mjs"; +import { DotDungeonIcon } from "./icon.mjs"; const components = [ + DotDungeonIcon, DotDungeonIncrementer, ]; diff --git a/styles/v3/components/icon.scss b/styles/v3/components/icon.scss new file mode 100644 index 0000000..22cfc17 --- /dev/null +++ b/styles/v3/components/icon.scss @@ -0,0 +1,21 @@ +/* +Disclaimer: This CSS is used by a custom web component and is scoped to JUST +the corresponding web component. Importing this into other files is forbidden. +*/ + +$default-size: 1rem; + +div { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; +} + +svg { + width: var(--size, $default-size); + height: var(--size, $default-size); + fill: var(--fill); + stroke: var(--stroke); +} diff --git a/styles/v3/components/incrementer.scss b/styles/v3/components/incrementer.scss index 80c085c..c7eb63b 100644 --- a/styles/v3/components/incrementer.scss +++ b/styles/v3/components/incrementer.scss @@ -6,11 +6,12 @@ the corresponding web component. Importing this into other files is forbidden. @use "../mixins/material"; $default-border-radius: 4px; -$default-height: 1.25rem; +$default-height: 1.5rem; div { display: grid; grid-template-columns: var(--height, $default-height) var(--width, 50px) var(--height, $default-height); + grid-template-rows: var(--height, 1fr); border-radius: var(--border-radius, $default-border-radius); @include material.elevate(2); @@ -44,7 +45,7 @@ input { } } -span { +.increment, .decrement { aspect-ratio: 1 / 1; padding: 0; display: flex;