Add UI web components and adjust .gitignore
This commit is contained in:
211
static/components/canvas-display/canvas-display.js
Normal file
211
static/components/canvas-display/canvas-display.js
Normal file
@@ -0,0 +1,211 @@
|
||||
// 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" });
|
||||
|
||||
// Grab the template defined in canvas-display.html
|
||||
const tmpl = document.getElementById("canvas-display-template");
|
||||
if (!tmpl) {
|
||||
console.error("CanvasDisplay: template not found");
|
||||
return;
|
||||
}
|
||||
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);
|
||||
Reference in New Issue
Block a user