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,102 @@
// Side menu component logic
class SideMenu extends HTMLElement {
constructor() {
super();
// Attach a shadow root and clone the template defined in side-menu.html
this.attachShadow({ mode: "open" });
const tmpl = document.getElementById("side-menu-template");
if (!tmpl) {
console.error("SideMenu: template not found");
return;
}
this.shadowRoot.appendChild(tmpl.content.cloneNode(true));
// Reference the toggle button and the menu container
this._toggleBtn = this.shadowRoot.querySelector(".toggle");
this._menu = this.shadowRoot.querySelector(".menu");
this._colorPicker = this.shadowRoot.querySelector("color-picker");
this._gridToggle = this.shadowRoot.querySelector("#grid-toggle");
// Bind event handlers
this._onToggle = this._onToggle.bind(this);
this._onColorChange = this._onColorChange.bind(this);
this._onGridToggle = this._onGridToggle.bind(this);
}
static get observedAttributes() {
return ["collapsed"];
}
connectedCallback() {
// Register click listener for the toggle button
this._toggleBtn?.addEventListener("click", this._onToggle);
this._colorPicker?.addEventListener("colorchange", this._onColorChange);
this._gridToggle?.addEventListener("change", this._onGridToggle);
// Initialise collapsed state from the attribute (if present)
if (this.hasAttribute("collapsed")) {
this._applyCollapsed(true);
}
}
disconnectedCallback() {
// Clean up listeners when the element is removed
this._toggleBtn?.removeEventListener("click", this._onToggle);
this._colorPicker?.removeEventListener("colorchange", this._onColorChange);
this._gridToggle?.removeEventListener("change", this._onGridToggle);
}
attributeChangedCallback(name, oldVal, newVal) {
if (name === "collapsed") {
const isCollapsed = this.hasAttribute("collapsed");
this._applyCollapsed(isCollapsed);
}
}
// Apply or remove the CSS class that slides the menu
_applyCollapsed(collapsed) {
const host = this.shadowRoot.host;
if (collapsed) {
host.classList.add("collapsed");
} else {
host.classList.remove("collapsed");
}
}
// Forward color picker change as bgcolorchange event
_onColorChange(e) {
const { color } = e.detail;
this.dispatchEvent(
new CustomEvent("bgcolorchange", {
detail: { color },
bubbles: true,
composed: true,
}),
);
}
// Forward grid toggle change as gridtoggle event
_onGridToggle(e) {
const enabled = e.target.checked;
this.dispatchEvent(
new CustomEvent("gridtoggle", {
detail: { enabled },
bubbles: true,
composed: true,
}),
);
}
// Toggle the collapsed attribute when the button is clicked
_onToggle() {
if (this.hasAttribute("collapsed")) {
this.removeAttribute("collapsed");
} else {
this.setAttribute("collapsed", "");
}
}
}
// Register the custom element; we follow the kebabcase convention used elsewhere
customElements.define("side-menu-component", SideMenu);