More adjustments to js and HTML.

This commit is contained in:
Yaro Kasear 2026-01-06 15:04:02 -06:00
parent 585c4abb25
commit ff2734fff6
2 changed files with 53 additions and 16 deletions

View file

@ -1,7 +1,3 @@
document.querySelectorAll('[data-grid-widget]').forEach((root, index) => {
initGridWidget(root, { storageKey: `gridDoc:${index}` });
});
let activeGridWidget = null; let activeGridWidget = null;
document.addEventListener('keydown', (e) => { document.addEventListener('keydown', (e) => {
@ -619,12 +615,6 @@ function initGridWidget(root, opts = {}) {
} }
function snapToGrid(x, y) { function snapToGrid(x, y) {
/*
Shapes are stored in grid units (document units), not pixels.
1 unit renders as cellSize pixels, so changing cellSize rescales (zooms) the whole drawing.
Grid modes only affect snapping/visuals; storage is always in document units for portability.
*/
const rect = gridEl.getBoundingClientRect(); const rect = gridEl.getBoundingClientRect();
const clampedX = Math.min(Math.max(x, rect.left), rect.right); const clampedX = Math.min(Math.max(x, rect.left), rect.right);
const clampedY = Math.min(Math.max(y, rect.top), rect.bottom); const clampedY = Math.min(Math.max(y, rect.top), rect.bottom);
@ -636,9 +626,6 @@ function initGridWidget(root, opts = {}) {
const maxIx = Math.floor(rect.width / grid); const maxIx = Math.floor(rect.width / grid);
const maxIy = Math.floor(rect.height / grid); const maxIy = Math.floor(rect.height / grid);
// Math.round means we actually snap to the nearest dot. Math.floor
// makes for ugly snapping behavior where it only goes to the top left
// of the cell. Cells are not where we draw to, so floor is not good.
const ix = Math.min(Math.max(Math.round(localX / grid), 0), maxIx); const ix = Math.min(Math.max(Math.round(localX / grid), 0), maxIx);
const iy = Math.min(Math.max(Math.round(localY / grid), 0), maxIy); const iy = Math.min(Math.max(Math.round(localY / grid), 0), maxIy);
@ -1029,3 +1016,53 @@ function initGridWidget(root, opts = {}) {
} }
}); });
} }
(function autoBootGridWidgets() {
function bootRoot(root) {
if (root.__gridBooted) return;
root.__gridBooted = true;
const mode = root.dataset.mode || 'editor';
const storageKey = root.dataset.storageKey || root.dataset.key || 'gridDoc';
const api = initGridWidget(root, { mode, storageKey });
root.__gridApi = api;
if (mode !== 'editor') {
const script = root.querySelector('[data-grid-doc]');
if (script?.textContent?.trim()) {
try { api.setDoc(JSON.parse(script.textContent)); } catch { }
return;
}
const src = root.dataset.src;
if (src) {
fetch(src, { credentials: 'same-origin' })
.then(r => r.ok ? r.json() : Promise.reject(new Error(`HTTP ${r.status}`)))
.then(doc => api.setDoc(doc))
.catch(() => { });
return;
}
const raw = root.dataset.doc;
if (raw) {
try { api.setDoc(JSON.parse(raw)); } catch { }
}
}
}
document.querySelectorAll('[data-grid-widget]').forEach(bootRoot);
const mo = new MutationObserver((mutations) => {
for (const m of mutations) {
for (const node of m.addedNodes) {
if (!(node instanceof Element)) continue;
if (node.matches?.('[data-grid-widget]')) bootRoot(node);
node.querySelectorAll?.('[data-grid-widget]').forEach(bootRoot);
}
}
});
mo.observe(document.documentElement, { childList: true, subtree: true });
})();

View file

@ -1,5 +1,5 @@
{% macro drawWidget(uid) %} {% macro drawWidget(uid) %}
<div class="grid-widget" data-grid-widget> <div class="grid-widget" data-grid-widget data-mode="editor" data-storage-key="gridDoc:{{ uid }}">
<div data-toolbar <div data-toolbar
class="btn-toolbar bg-light border border-bottom-0 rounded-bottom-0 border-secondary-subtle rounded p-1 align-items-center flex-nowrap overflow-auto toolbar"> class="btn-toolbar bg-light border border-bottom-0 rounded-bottom-0 border-secondary-subtle rounded p-1 align-items-center flex-nowrap overflow-auto toolbar">
<div class="toolbar-row toolbar-row--primary"> <div class="toolbar-row toolbar-row--primary">
@ -196,7 +196,7 @@
<path <path
d="M2.5 0q-.25 0-.487.048l.194.98A1.5 1.5 0 0 1 2.5 1h.458V0zm2.292 0h-.917v1h.917zm1.833 0h-.917v1h.917zm1.833 0h-.916v1h.916zm1.834 0h-.917v1h.917zm1.833 0h-.917v1h.917zM13.5 0h-.458v1h.458q.151 0 .293.029l.194-.981A2.5 2.5 0 0 0 13.5 0m2.079 1.11a2.5 2.5 0 0 0-.69-.689l-.556.831q.248.167.415.415l.83-.556zM1.11.421a2.5 2.5 0 0 0-.689.69l.831.556c.11-.164.251-.305.415-.415zM16 2.5q0-.25-.048-.487l-.98.194q.027.141.028.293v.458h1zM.048 2.013A2.5 2.5 0 0 0 0 2.5v.458h1V2.5q0-.151.029-.293zM0 3.875v.917h1v-.917zm16 .917v-.917h-1v.917zM0 5.708v.917h1v-.917zm16 .917v-.917h-1v.917zM0 7.542v.916h1v-.916zm15 .916h1v-.916h-1zM0 9.375v.917h1v-.917zm16 .917v-.917h-1v.917zm-16 .916v.917h1v-.917zm16 .917v-.917h-1v.917zm-16 .917v.458q0 .25.048.487l.98-.194A1.5 1.5 0 0 1 1 13.5v-.458zm16 .458v-.458h-1v.458q0 .151-.029.293l.981.194Q16 13.75 16 13.5M.421 14.89c.183.272.417.506.69.689l.556-.831a1.5 1.5 0 0 1-.415-.415zm14.469.689c.272-.183.506-.417.689-.69l-.831-.556c-.11.164-.251.305-.415.415l.556.83zm-12.877.373Q2.25 16 2.5 16h.458v-1H2.5q-.151 0-.293-.029zM13.5 16q.25 0 .487-.048l-.194-.98A1.5 1.5 0 0 1 13.5 15h-.458v1zm-9.625 0h.917v-1h-.917zm1.833 0h.917v-1h-.917zm1.834 0h.916v-1h-.916zm1.833 0h.917v-1h-.917zm1.833 0h.917v-1h-.917zM4.5 7.5a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1z" /> d="M2.5 0q-.25 0-.487.048l.194.98A1.5 1.5 0 0 1 2.5 1h.458V0zm2.292 0h-.917v1h.917zm1.833 0h-.917v1h.917zm1.833 0h-.916v1h.916zm1.834 0h-.917v1h.917zm1.833 0h-.917v1h.917zM13.5 0h-.458v1h.458q.151 0 .293.029l.194-.981A2.5 2.5 0 0 0 13.5 0m2.079 1.11a2.5 2.5 0 0 0-.69-.689l-.556.831q.248.167.415.415l.83-.556zM1.11.421a2.5 2.5 0 0 0-.689.69l.831.556c.11-.164.251-.305.415-.415zM16 2.5q0-.25-.048-.487l-.98.194q.027.141.028.293v.458h1zM.048 2.013A2.5 2.5 0 0 0 0 2.5v.458h1V2.5q0-.151.029-.293zM0 3.875v.917h1v-.917zm16 .917v-.917h-1v.917zM0 5.708v.917h1v-.917zm16 .917v-.917h-1v.917zM0 7.542v.916h1v-.916zm15 .916h1v-.916h-1zM0 9.375v.917h1v-.917zm16 .917v-.917h-1v.917zm-16 .916v.917h1v-.917zm16 .917v-.917h-1v.917zm-16 .917v.458q0 .25.048.487l.98-.194A1.5 1.5 0 0 1 1 13.5v-.458zm16 .458v-.458h-1v.458q0 .151-.029.293l.981.194Q16 13.75 16 13.5M.421 14.89c.183.272.417.506.69.689l.556-.831a1.5 1.5 0 0 1-.415-.415zm14.469.689c.272-.183.506-.417.689-.69l-.831-.556c-.11.164-.251.305-.415.415l.556.83zm-12.877.373Q2.25 16 2.5 16h.458v-1H2.5q-.151 0-.293-.029zM13.5 16q.25 0 .487-.048l-.194-.98A1.5 1.5 0 0 1 13.5 15h-.458v1zm-9.625 0h.917v-1h-.917zm1.833 0h.917v-1h-.917zm1.834 0h.916v-1h-.916zm1.833 0h.917v-1h-.917zm1.833 0h.917v-1h-.917zM4.5 7.5a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1z" />
</svg> </svg>
<small data-stroke-opacity-val>100%</small> <small `roke-opacity-val>100%</small>
</button> </button>
<ul class="dropdown-menu p-2"> <ul class="dropdown-menu p-2">
<li> <li>