From 82c3ea2b90b9f60a24b624bf23a475241855e8b9 Mon Sep 17 00:00:00 2001 From: Yaro Kasear Date: Mon, 12 Jan 2026 16:35:20 -0600 Subject: [PATCH] Various bug fixes. --- inventory/static/js/components/draw.js | 106 +++++++++++++++++++------ inventory/templates/testing.html | 17 +++- 2 files changed, 97 insertions(+), 26 deletions(-) diff --git a/inventory/static/js/components/draw.js b/inventory/static/js/components/draw.js index ee50b39..4c67f2f 100644 --- a/inventory/static/js/components/draw.js +++ b/inventory/static/js/components/draw.js @@ -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; diff --git a/inventory/templates/testing.html b/inventory/templates/testing.html index 32a21dd..2f10aa8 100644 --- a/inventory/templates/testing.html +++ b/inventory/templates/testing.html @@ -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 %}
-
+
{{ draw.drawWidget('test1') }}
-
+ +
{% endblock %}