// 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 side‑menu markup directly in JS
const tmpl = document.createElement("template");
tmpl.innerHTML = `
☰
`;
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 kebab‑case convention used elsewhere
customElements.define("side-menu-component", SideMenu);