Get the icon component coded and being used in the incrementer component
This commit is contained in:
parent
3ace1df5a2
commit
f15f3a4456
5 changed files with 141 additions and 70 deletions
|
|
@ -7,32 +7,37 @@ export class DotDungeonIcon extends HTMLElement {
|
||||||
static elementName = `dd-icon`;
|
static elementName = `dd-icon`;
|
||||||
static formAssociated = false;
|
static formAssociated = false;
|
||||||
|
|
||||||
static #styles = ``;
|
#shadow;
|
||||||
_internals;
|
|
||||||
_shadow;
|
|
||||||
|
|
||||||
_min;
|
static #styles = ``;
|
||||||
_max;
|
static _cache = new Map();
|
||||||
_smallStep;
|
#style;
|
||||||
_largeStep;
|
#container;
|
||||||
|
_name;
|
||||||
|
_path;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._shadow = this.attachShadow({ mode: `open`, delegatesFocus: true });
|
this.#shadow = this.attachShadow({ mode: `open`, delegatesFocus: true });
|
||||||
if (DotDungeonIcon.#styles) this.#embedStyles();
|
if (DotDungeonIcon.#styles) this.#embedStyles();
|
||||||
|
|
||||||
|
this.#container = document.createElement(`div`);
|
||||||
|
this.#shadow.appendChild(this.#container);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async connectedCallback() {
|
||||||
|
|
||||||
connectedCallback() {
|
this._name = this.getAttribute(`name`);
|
||||||
this.replaceChildren();
|
this._path = this.getAttribute(`path`);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This converts all of the double-dash prefixed properties on the element to
|
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=""
|
CSS variables so that they don't all need to be provided by doing style=""
|
||||||
*/
|
*/
|
||||||
for (const attrVar of this.attributes) {
|
for (const attrVar of this.attributes) {
|
||||||
if (attrVar.name?.startsWith(`--`)) {
|
if (attrVar.name?.startsWith(`var:`)) {
|
||||||
this.style.setProperty(attrVar.name, attrVar.value);
|
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
|
Style fetching if we haven't gotten them yet
|
||||||
*/
|
*/
|
||||||
if (!DotDungeonIcon.#styles) {
|
if (!DotDungeonIcon.#styles) {
|
||||||
fetch(`./systems/dotdungeon/.styles/v3/components/incrementer.css`)
|
fetch(`./systems/dotdungeon/.styles/v3/components/icon.css`)
|
||||||
.then(r => r.text())
|
.then(r => r.text())
|
||||||
.then(t => {
|
.then(t => {
|
||||||
DotDungeonIcon.#styles = 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
|
the slot content, as then we can have a default per-icon usage
|
||||||
*/
|
*/
|
||||||
let content;
|
let content;
|
||||||
|
// TODO: Make name request
|
||||||
|
if (this._name) {
|
||||||
|
content = await this.#getIcon(`./systems/dotdungeon/assets/${this._name}.svg`);
|
||||||
|
};
|
||||||
|
|
||||||
if (!content) {
|
// TODO: make path request
|
||||||
let slot = document.createElement(`slot`);
|
if (this._path && !content) {
|
||||||
content ??= slot;
|
content = await this.#getIcon(this._path);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// TODO: insert content into DOM
|
||||||
|
if (content) {
|
||||||
|
this.#container.appendChild(content);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#embedStyles() {
|
#embedStyles() {
|
||||||
const style = document.createElement(`style`);
|
this.#style = document.createElement(`style`);
|
||||||
style.innerHTML = DotDungeonIcon.#styles;
|
this.#style.innerHTML = DotDungeonIcon.#styles;
|
||||||
this._shadow.appendChild(style);
|
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;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { DotDungeonIcon } from "./icon.mjs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Attributes:
|
Attributes:
|
||||||
@property {string} name - The path to the value to update
|
@property {string} name - The path to the value to update
|
||||||
|
|
@ -19,6 +21,7 @@ export class DotDungeonIncrementer extends HTMLElement {
|
||||||
_internals;
|
_internals;
|
||||||
_shadow;
|
_shadow;
|
||||||
#input;
|
#input;
|
||||||
|
#style;
|
||||||
|
|
||||||
_min;
|
_min;
|
||||||
_max;
|
_max;
|
||||||
|
|
@ -33,48 +36,6 @@ export class DotDungeonIncrementer extends HTMLElement {
|
||||||
// Form internals
|
// Form internals
|
||||||
this._internals = this.attachInternals();
|
this._internals = this.attachInternals();
|
||||||
this._internals.role = `spinbutton`;
|
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() {
|
get form() {
|
||||||
|
|
@ -102,13 +63,60 @@ export class DotDungeonIncrementer extends HTMLElement {
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.replaceChildren();
|
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=""
|
CSS variables so that they don't all need to be provided by doing style=""
|
||||||
*/
|
*/
|
||||||
for (const attrVar of this.attributes) {
|
for (const attrVar of this.attributes) {
|
||||||
if (attrVar.name?.startsWith(`--`)) {
|
if (attrVar.name?.startsWith(`var:`)) {
|
||||||
this.style.setProperty(attrVar.name, attrVar.value);
|
const prop = attrVar.name.replace(`var:`, ``);
|
||||||
|
this.style.setProperty(`--` + prop, attrVar.value);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -123,9 +131,9 @@ export class DotDungeonIncrementer extends HTMLElement {
|
||||||
};
|
};
|
||||||
|
|
||||||
#embedStyles() {
|
#embedStyles() {
|
||||||
const style = document.createElement(`style`);
|
this.#style = document.createElement(`style`);
|
||||||
style.innerHTML = DotDungeonIncrementer.#styles;
|
this.#style.innerHTML = DotDungeonIncrementer.#styles;
|
||||||
this._shadow.appendChild(style);
|
this._shadow.appendChild(this.#style);
|
||||||
};
|
};
|
||||||
|
|
||||||
#updateValue() {
|
#updateValue() {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { DotDungeonIncrementer } from "./incrementer.mjs";
|
import { DotDungeonIncrementer } from "./incrementer.mjs";
|
||||||
|
import { DotDungeonIcon } from "./icon.mjs";
|
||||||
|
|
||||||
const components = [
|
const components = [
|
||||||
|
DotDungeonIcon,
|
||||||
DotDungeonIncrementer,
|
DotDungeonIncrementer,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
21
styles/v3/components/icon.scss
Normal file
21
styles/v3/components/icon.scss
Normal file
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -6,11 +6,12 @@ the corresponding web component. Importing this into other files is forbidden.
|
||||||
@use "../mixins/material";
|
@use "../mixins/material";
|
||||||
|
|
||||||
$default-border-radius: 4px;
|
$default-border-radius: 4px;
|
||||||
$default-height: 1.25rem;
|
$default-height: 1.5rem;
|
||||||
|
|
||||||
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);
|
||||||
|
grid-template-rows: var(--height, 1fr);
|
||||||
border-radius: var(--border-radius, $default-border-radius);
|
border-radius: var(--border-radius, $default-border-radius);
|
||||||
@include material.elevate(2);
|
@include material.elevate(2);
|
||||||
|
|
||||||
|
|
@ -44,7 +45,7 @@ input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
.increment, .decrement {
|
||||||
aspect-ratio: 1 / 1;
|
aspect-ratio: 1 / 1;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue