- 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.
247 lines
6.9 KiB
JavaScript
247 lines
6.9 KiB
JavaScript
// CanvasDisplay component logic
|
||
// This file defines the custom element <canvas-display> which renders a canvas
|
||
// and an SVG grid overlay. It reacts to background‑color and grid‑settings
|
||
// events from the toolbar or directly via attributes.
|
||
|
||
class CanvasDisplay extends HTMLElement {
|
||
static get observedAttributes() {
|
||
return ["bg-color", "grid-color", "grid-size", "grid-numbering"];
|
||
}
|
||
|
||
constructor() {
|
||
super();
|
||
// Attach shadow root
|
||
this.attachShadow({ mode: "open" });
|
||
|
||
// Create a template element with the canvas-display markup directly in JS
|
||
const tmpl = document.createElement("template");
|
||
tmpl.innerHTML = `
|
||
<style>
|
||
:host {
|
||
display: block;
|
||
position: relative;
|
||
font-family: sans-serif;
|
||
}
|
||
|
||
.canvas-wrapper {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
overflow: hidden;
|
||
}
|
||
|
||
canvas {
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: var(--bg-color, #ffffff);
|
||
}
|
||
|
||
/* grid overlay */
|
||
.grid-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
pointer-events: none;
|
||
}
|
||
</style>
|
||
|
||
<div class="canvas-wrapper">
|
||
<canvas id="draw-canvas"></canvas>
|
||
<svg class="grid-overlay" id="grid-overlay"></svg>
|
||
</div>
|
||
|
||
`;
|
||
this.shadowRoot.appendChild(tmpl.content.cloneNode(true));
|
||
|
||
// Elements inside the shadow DOM
|
||
this._canvas = this.shadowRoot.querySelector("#draw-canvas");
|
||
this._gridOverlay = this.shadowRoot.querySelector("#grid-overlay");
|
||
|
||
// Canvas drawing context
|
||
this._ctx = this._canvas.getContext("2d");
|
||
|
||
// Bind handlers
|
||
this._onBgColorChange = this._onBgColorChange.bind(this);
|
||
this._onGridSettingsChange = this._onGridSettingsChange.bind(this);
|
||
this._onGridToggle = this._onGridToggle.bind(this);
|
||
}
|
||
|
||
connectedCallback() {
|
||
// Initialise size – a default that can be overridden by CSS
|
||
this._canvas.width = this.clientWidth || 300;
|
||
this._canvas.height = this.clientHeight || 300;
|
||
|
||
// Apply default attribute values if none are set
|
||
if (!this.hasAttribute("bg-color"))
|
||
this.setAttribute("bg-color", "#ffffff");
|
||
if (!this.hasAttribute("grid-color"))
|
||
this.setAttribute("grid-color", "#000000");
|
||
if (!this.hasAttribute("grid-size")) this.setAttribute("grid-size", "25");
|
||
if (!this.hasAttribute("grid-numbering"))
|
||
this.setAttribute("grid-numbering", "true");
|
||
|
||
// Initial draw
|
||
this._updateCanvasBackground();
|
||
this._drawGrid();
|
||
|
||
// Listen for events bubbled up from slotted child components
|
||
this.addEventListener("bgcolorchange", this._onBgColorChange);
|
||
this.addEventListener("gridsettingschange", this._onGridSettingsChange);
|
||
this.addEventListener("gridtoggle", this._onGridToggle);
|
||
}
|
||
|
||
disconnectedCallback() {
|
||
this.removeEventListener("bgcolorchange", this._onBgColorChange);
|
||
this.removeEventListener("gridsettingschange", this._onGridSettingsChange);
|
||
this.removeEventListener("gridtoggle", this._onGridToggle);
|
||
}
|
||
|
||
attributeChangedCallback(name, oldVal, newVal) {
|
||
if (oldVal === newVal) return;
|
||
switch (name) {
|
||
case "bg-color":
|
||
this._updateCanvasBackground();
|
||
break;
|
||
case "grid-color":
|
||
case "grid-size":
|
||
case "grid-numbering":
|
||
this._drawGrid();
|
||
break;
|
||
}
|
||
}
|
||
|
||
// ----- Property getters/setters -----
|
||
get bgColor() {
|
||
return this.getAttribute("bg-color");
|
||
}
|
||
set bgColor(v) {
|
||
this.setAttribute("bg-color", v);
|
||
}
|
||
|
||
get gridColor() {
|
||
return this.getAttribute("grid-color");
|
||
}
|
||
set gridColor(v) {
|
||
this.setAttribute("grid-color", v);
|
||
}
|
||
|
||
get gridSize() {
|
||
return Number(this.getAttribute("grid-size"));
|
||
}
|
||
set gridSize(v) {
|
||
this.setAttribute("grid-size", v);
|
||
}
|
||
|
||
get gridNumbering() {
|
||
return this.getAttribute("grid-numbering") === "true";
|
||
}
|
||
set gridNumbering(v) {
|
||
this.setAttribute("grid-numbering", v);
|
||
}
|
||
|
||
// ----- Event handlers -----
|
||
_onBgColorChange(e) {
|
||
const { color } = e.detail;
|
||
this.bgColor = color;
|
||
}
|
||
|
||
_onGridSettingsChange(e) {
|
||
const { color, size, numbering } = e.detail;
|
||
this.gridColor = color;
|
||
this.gridSize = size;
|
||
this.gridNumbering = numbering;
|
||
}
|
||
|
||
_onGridToggle(e) {
|
||
const { enabled } = e.detail;
|
||
this.gridNumbering = enabled;
|
||
// Reflect the change to the attribute so CSS/DOM stays in sync
|
||
this.setAttribute("grid-numbering", enabled);
|
||
}
|
||
|
||
// ----- Rendering helpers -----
|
||
_updateCanvasBackground() {
|
||
const { _ctx, _canvas, bgColor } = this;
|
||
_ctx.save();
|
||
_ctx.fillStyle = bgColor;
|
||
_ctx.fillRect(0, 0, _canvas.width, _canvas.height);
|
||
_ctx.restore();
|
||
}
|
||
|
||
_drawGrid() {
|
||
const { _gridOverlay, _canvas, gridColor, gridSize, gridNumbering } = this;
|
||
|
||
// Clear any previous grid
|
||
while (_gridOverlay.firstChild)
|
||
_gridOverlay.removeChild(_gridOverlay.firstChild);
|
||
|
||
const width = _canvas.width;
|
||
const height = _canvas.height;
|
||
const spacing = gridSize;
|
||
|
||
// Set SVG size and viewBox to match canvas dimensions
|
||
_gridOverlay.setAttribute("width", width);
|
||
_gridOverlay.setAttribute("height", height);
|
||
_gridOverlay.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
||
|
||
// Helper to create a line
|
||
const makeLine = (x1, y1, x2, y2, thick) => {
|
||
const line = document.createElementNS(
|
||
"http://www.w3.org/2000/svg",
|
||
"line",
|
||
);
|
||
line.setAttribute("x1", x1);
|
||
line.setAttribute("y1", y1);
|
||
line.setAttribute("x2", x2);
|
||
line.setAttribute("y2", y2);
|
||
line.setAttribute("stroke", gridColor);
|
||
line.setAttribute("stroke-width", thick ? 2 : 1);
|
||
return line;
|
||
};
|
||
|
||
// Draw vertical lines
|
||
for (let x = 0; x <= width; x += spacing) {
|
||
const thick = (x / spacing) % 5 === 0;
|
||
_gridOverlay.appendChild(makeLine(x, 0, x, height, thick));
|
||
}
|
||
|
||
// Draw horizontal lines
|
||
for (let y = 0; y <= height; y += spacing) {
|
||
const thick = (y / spacing) % 5 === 0;
|
||
_gridOverlay.appendChild(makeLine(0, y, width, y, thick));
|
||
}
|
||
|
||
// Optional numbering
|
||
if (gridNumbering) {
|
||
const makeText = (x, y, txt) => {
|
||
const t = document.createElementNS(
|
||
"http://www.w3.org/2000/svg",
|
||
"text",
|
||
);
|
||
t.setAttribute("x", x + 2);
|
||
t.setAttribute("y", y + 10);
|
||
t.setAttribute("fill", gridColor);
|
||
t.setAttribute("font-size", "8");
|
||
t.textContent = txt;
|
||
return t;
|
||
};
|
||
// Number columns
|
||
for (let x = spacing; x <= width; x += spacing) {
|
||
const col = x / spacing;
|
||
if (col % 5 === 0) _gridOverlay.appendChild(makeText(x, 12, col));
|
||
}
|
||
// Number rows
|
||
for (let y = spacing; y <= height; y += spacing) {
|
||
const row = y / spacing;
|
||
if (row % 5 === 0) _gridOverlay.appendChild(makeText(2, y, row));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Register the custom element
|
||
customElements.define("canvas-display", CanvasDisplay);
|