Various bug fixes.

This commit is contained in:
Yaro Kasear 2026-01-12 16:35:20 -06:00
parent cc32b2214c
commit 82c3ea2b90
2 changed files with 97 additions and 26 deletions

View file

@ -13,7 +13,7 @@
// Pointer finalize (for drawing finishing outside the element)
const forwardPointer = (e) => {
const w = window.activeGridWidget;
const w = window.__gridPointerOwner || window.activeGridWidget;
if (!w || typeof w.handleGlobalPointerUp !== 'function') return;
w.handleGlobalPointerUp(e);
};
@ -763,24 +763,30 @@ function initGridWidget(root, opts = {}) {
resizeAndSetupCanvas();
redrawAll();
// ✅ viewer mode still needs to respond to layout/resize
if (mode !== 'editor') {
const ro = new ResizeObserver(() => resizeAndSetupCanvas());
ro.observe(gridEl);
let resizeRAF = 0;
const scheduleResize = () => {
if (resizeRAF) return;
resizeRAF = requestAnimationFrame(() => {
resizeRAF = 0;
resizeAndSetupCanvas();
});
};
const ro = new ResizeObserver(scheduleResize);
ro.observe(gridWrapEl);
// optional, but helps with viewport changes
window.addEventListener('resize', resizeAndSetupCanvas, { passive: true });
window.addEventListener('resize', scheduleResize, { passive: true });
// also optional: if it's inside tabs/collapses, kick one extra redraw
requestAnimationFrame(() => resizeAndSetupCanvas());
requestAnimationFrame(scheduleResize);
return {
setDoc,
redraw: redrawAll,
destroy() {
if (window.activeGridWidget === api) window.activeGridWidget = null;
ro.disconnect();
window.removeEventListener('resize', resizeAndSetupCanvas);
window.removeEventListener('resize', scheduleResize);
},
decode
};
@ -903,11 +909,35 @@ function initGridWidget(root, opts = {}) {
},
handleGlobalPointerUp(e) {
// Only finalize if this widget is the active one (it should be)
finishPointer(e);
}
},
cancelStroke() { cancelStroke(); }
};
function destroy() {
if (window.activeGridWidget === api) window.activeGridWidget = null;
currentShape = null;
activePointerId = null;
try {
if (window.__gridPointerId != null && gridEl.hasPointerCapture?.(window.__gridPointerId)) {
gridEl.releasePointerCapture(window.__gridPointerId);
}
} catch { }
if (window.__gridPointerOwner === api) {
window.__gridPointerOwner = null;
window.__gridPointerId = null;
}
ro.disconnect();
}
api.destroy = destroy;
root.addEventListener('focusin', () => { window.activeGridWidget = api; });
root.addEventListener('pointerdown', () => {
@ -1005,12 +1035,15 @@ function initGridWidget(root, opts = {}) {
}
function finishPointer(e) {
if (window.activeGridWidget !== api) return;
if (window.__gridPointerOwner !== api) return;
if (!currentShape) return;
if (activePointerId !== null && e.pointerId !== activePointerId) return;
if (e.pointerId !== activePointerId) return;
onPointerUp(e);
activePointerId = null;
window.__gridPointerOwner = null;
window.__gridPointerId = null;
}
function renderAllWithPreview(previewShape = null, dashed = true) {
@ -1111,7 +1144,7 @@ function initGridWidget(root, opts = {}) {
const nearLeft = Math.abs(p.x - minX) <= tol && p.y >= minY - tol && p.y <= maxY + tol;
const nearRight = Math.abs(p.x - maxX) <= tol && p.y >= minY - tol && p.y <= maxY + tol;
const nearTop = Math.abs(p.y - minY) <= tol && p.x >= minX - tol && p.x <= maxX + tol;
const nearBottom = Math.abs(p.y - minX) <= tol && p.x >= minX - tol && p.x <= maxX + tol;
const nearBottom = Math.abs(p.y - maxY) <= tol && p.x >= minX - tol && p.x <= maxX + tol;
return nearLeft || nearRight || nearTop || nearBottom;
}
@ -1477,6 +1510,21 @@ function initGridWidget(root, opts = {}) {
};
}
function cancelStroke(e) {
const owns = (window.__gridPointerOwner === api) &&
(e ? window.__gridPointerId === e.pointerId : true);
if (!owns) return;
currentShape = null;
activePointerId = null;
window.__gridPointerOwner = null;
window.__gridPointerId = null;
redrawAll();
}
function onPointerUp(e) {
if (!currentShape) return;
@ -1718,15 +1766,8 @@ function initGridWidget(root, opts = {}) {
currentStrokeWidth = Math.max(0, Number(strokeWidthEl.value) || 0.12);
});
gridEl.addEventListener('pointercancel', () => {
currentShape = null;
redrawAll();
});
gridEl.addEventListener('lostpointercapture', () => {
currentShape = null;
redrawAll();
});
gridEl.addEventListener('pointercancel', (e) => cancelStroke(e));
gridEl.addEventListener('lostpointercapture', (e) => cancelStroke(e));
gridEl.addEventListener('pointermove', (e) => {
if (!ctx) return;
@ -1807,6 +1848,10 @@ function initGridWidget(root, opts = {}) {
e.preventDefault();
activePointerId = e.pointerId;
window.__gridPointerOwner = api;
window.__gridPointerId = e.pointerId;
try {
gridEl.setPointerCapture(e.pointerId);
} catch {
@ -1866,6 +1911,8 @@ function initGridWidget(root, opts = {}) {
};
}
});
return api;
}
(function autoBootGridWidgets() {
@ -1915,6 +1962,19 @@ function initGridWidget(root, opts = {}) {
const mo = new MutationObserver((mutations) => {
for (const m of mutations) {
for (const node of m.removedNodes) {
if (!(node instanceof Element)) continue;
const roots = [];
if (node.matches?.('[data-grid-widget]')) roots.push(node);
node.querySelectorAll?.('[data-grid-widget]').forEach(r => roots.push(r));
for (const r of roots) {
r.__gridApi?.destroy?.();
r.__gridApi = null;
}
}
for (const node of m.addedNodes) {
if (!(node instanceof Element)) continue;

View file

@ -14,15 +14,26 @@
{"v":1,"cs":5,"q":100,"d":{"cl":"#000000","f":false,"sw":12,"so":100,"fo":100},"s":[{"t":"s","cl":"#ffe59e","f":true},{"t":"e","p":[0,0,2500,2500]},{"t":"s","cl":"#fdc8fe"},{"t":"e","p":[100,100,2300,2300]},{"t":"s","cl":"#fbe6a1"},{"t":"e","p":[600,600,1300,1300]},{"t":"s","cl":"#ffffff"},{"t":"e","p":[700,700,1100,1100]},{"t":"s","cl":"#000000","f":false},{"t":"e","p":[0,0,2500,2500,-2400,-2400,2300,2300,-1800,-1800,1300,1300,-1200,-1200,1100,1100]},{"t":"s","sw":37,"cl":"#ff0000"},{"t":"l","p":[600,500,100,-100,500,0,100,0,500,200,100,100,200,200,100,-100,-600,-400,0,-100,-800,300,-100,100,-400,800,0,-100,100,-400,-100,-100,200,1000,100,0,-100,-600,-100,-100,500,1000,100,-100,-200,-200,0,100,500,200,100,-100,200,-200,100,100,-500,0,-100,100,800,-400,100,100,0,-400,100,100,0,-300,100,0,-200,-100,0,-100,0,-400,0,100,-400,-200,100,0,-600,-200,100,-100,-300,100,0,100,-300,500,-100,-100,0,900,100,-100,1300,400,100,-100,200,-200,0,-100,100,-200,0,-100]},{"t":"s","cl":"#00ff00"},{"t":"l","p":[400,700,100,0,500,-200,0,-100,400,-200,0,100,100,200,100,100,200,-200,100,100,300,600,100,0,-400,-300,100,100,100,400,100,0,100,200,-100,0,-200,100,100,100,0,200,100,-100,-400,100,0,-100,-100,300,100,0,-200,200,0,-100,-100,-200,0,100,-200,200,100,0,-300,0,0,-100,200,-100,100,-100,-400,0,100,0,-300,100,100,0,-200,-300,0,100,-200,-100,100,0,-200,-100,0,-100,100,-100,0,-100,-300,-100,100,0,0,-200,100,0,100,-200,0,100,100,-400,100,0]},{"t":"s","cl":"#0000ff"},{"t":"l","p":[800,400,0,100,300,0,100,0,200,-100,100,-100,200,0,0,100,300,100,100,100,0,200,0,-100,0,300,100,0,-200,300,0,-100,100,200,100,0,-300,100,0,100,0,200,0,100,-200,-100,0,100,200,200,-100,-100,0,200,-100,0,-100,-100,0,-100,-100,200,-100,0,-200,100,0,-100,-300,100,100,-100,-100,-300,0,100,-300,100,100,0,-200,-100,100,0,-200,-100,-100,-100,0,-200,100,-100,0,-200,-100,-100,100,-400,-100,0,200,300,100,-100,100,-200,-100,-100,300,-100,100,0,-500,-100,-100,100,1300,100,100,100,-1200,900,100,0]}]}
{% endset %}
<div class="row">
<div class="col" style="height: 80vh;">
<div class="col" style="height: 80vh">
{{ draw.drawWidget('test1') }}
</div>
<div class="col" style="height: 80vh;">
<!-- div class="col">
{{ draw.drawWidget('test4') }}
</div>
</div>
<div class="row">
<div class="col">
{{ draw.drawWidget('test5') }}
</div>
<div class="col">
{{ draw.drawWidget('test6') }}
</div -->
<!-- div class="col" style="height: 80vh;">
I am testing a thing.
{{ draw.viewWidget('test2', jsonImage) }}
{{ draw.viewWidget('test3', jsonImage2) }}
The thing has been tested.
</div>
</div -->
</div>
{% endblock %}