Files
Thomas Nilles 77ba31ce8f Inline component templates into JS
- Replace HTML template lookups with programmatically created
  `<template>` elements.
- Add component‑specific styles and markup for CanvasDisplay, including
  a grid overlay SVG.
- Add SideMenu markup with toggle button, menu styling, and slot
  content.
2026-01-15 13:02:50 -05:00

143 lines
4.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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" });
// Create a template element with the sidemenu markup directly in JS
const tmpl = document.createElement("template");
tmpl.innerHTML = `
<style>
:host {
display: block;
width: var(--side-menu-width, 250px);
height: 100%;
background: var(--side-menu-bg, #f9f9f9);
border-right: 1px solid #ccc;
box-sizing: border-box;
position: relative;
transition: transform 0.3s ease;
}
.menu {
height: 100%;
overflow-y: auto;
}
.toggle {
position: absolute;
top: 0.5rem;
right: -1.5rem;
background: var(--toggle-bg, #f0f0f0);
border: 1px solid #ccc;
border-left: none;
border-radius: 0 4px 4px 0;
padding: 0.25rem 0.5rem;
cursor: pointer;
user-select: none;
}
.collapsed {
transform: translateX(-100%);
}
</style>
<div class="toggle" part="toggle">☰</div>
<nav class="menu" part="menu">
<slot></slot>
<color-picker></color-picker>
<label id="grid-toggle-label"><input type="checkbox" id="grid-toggle"> Grid</label>
</nav>
`;
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);