Add UI web components and adjust .gitignore

This commit is contained in:
2026-01-13 00:40:05 -05:00
parent 6f508ed194
commit b67235b93d
6 changed files with 594 additions and 1 deletions

View File

@@ -0,0 +1,94 @@
// ColorPicker component template is embedded directly in the JS file.
class ColorPicker extends HTMLElement {
static get observedAttributes() {
return ["value"];
}
constructor() {
super();
// Create a template element and embed the markup.
const tmpl = document.createElement("template");
tmpl.innerHTML = `
<style>
:host {
display: inline-block;
font-family: sans-serif;
}
.wrapper {
display: flex;
align-items: center;
gap: 0.5rem;
}
label {
font-size: 0.9rem;
user-select: none;
}
input[type="color"] {
border: none;
width: 2rem;
height: 2rem;
padding: 0;
background: none;
}
</style>
<div class="wrapper">
<label for="color-input">Background:</label>
<input type="color" id="color-input" value="#ffffff">
</div>
`;
// Attach a shadow root and clone the template into it.
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(tmpl.content.cloneNode(true));
// Reference the input element for later use.
this._input = this.shadowRoot.querySelector("#color-input");
// Bind event handler.
this._onInput = this._onInput.bind(this);
}
connectedCallback() {
// Initialise the input value from the attribute (or default).
const init = this.getAttribute("value") || "#ffffff";
this._input.value = init;
this._input.addEventListener("input", this._onInput);
}
disconnectedCallback() {
this._input.removeEventListener("input", this._onInput);
}
attributeChangedCallback(name, oldVal, newVal) {
if (name === "value" && this._input && oldVal !== newVal) {
this._input.value = newVal;
}
}
// Property getter/setter for easy JS access.
get value() {
return this._input ? this._input.value : this.getAttribute("value");
}
set value(v) {
this.setAttribute("value", v);
}
// Dispatch a custom event when the user picks a color.
_onInput(e) {
const color = e.target.value;
this.setAttribute("value", color);
this.dispatchEvent(
new CustomEvent("colorchange", {
detail: { color },
bubbles: true,
composed: true,
}),
);
}
}
// Register the custom element.
customElements.define("color-picker", ColorPicker);