From 04b81ad2f28a9050b9c1afebd508ff7fad764ed7 Mon Sep 17 00:00:00 2001 From: "Adriano (daitarn)" Date: Wed, 28 Oct 2026 12:39:06 +0100 Subject: [PATCH] Wizard DXF: correzioni revisione zoom anteprime (style statico, x-show, altezza utile). Refs #113 Co-Authored-By: Claude Sonnet 5.5 --- .../flask_app/static/js/dxf_recipe_wizard.js | 4 +++- .../flask_app/templates/maker/task_editor.html | 10 +++++----- .../flask_app/tests/js/test_dxf_zoom.js | 18 +++++++++++------- .../flask_app/tests/test_dxf_recipe_routes.py | 6 ++++++ 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/frontend/flask_app/static/js/dxf_recipe_wizard.js b/src/frontend/flask_app/static/js/dxf_recipe_wizard.js index cffa4db..eec8c60 100644 --- a/src/frontend/flask_app/static/js/dxf_recipe_wizard.js +++ b/src/frontend/flask_app/static/js/dxf_recipe_wizard.js @@ -514,7 +514,9 @@ function dxfRecipeWizard(taskId, csrf, msgs) { vFit(k) { const vp = this._vpEl(k), v = this.views[k]; if (!vp || !v.w || !v.h || !vp.clientWidth) return; // nascosto o vuoto: niente - let z = Math.min(vp.clientWidth / v.w, 240 / v.h); + // altezza utile del viewport (senza bordo), ripiego 240; con 240 secco il contenuto sborda e compare lo scroll + const ch = vp.clientHeight || 240; + let z = Math.min(vp.clientWidth / v.w, ch / v.h); if (k === 'img') z = Math.min(8, Math.max(0.05, z)); v.zoom = z; v.fit = z; diff --git a/src/frontend/flask_app/templates/maker/task_editor.html b/src/frontend/flask_app/templates/maker/task_editor.html index 140cb47..bfc985e 100644 --- a/src/frontend/flask_app/templates/maker/task_editor.html +++ b/src/frontend/flask_app/templates/maker/task_editor.html @@ -730,8 +730,8 @@
- -
+ +
@@ -740,12 +740,12 @@
-
-
+
{# #113: tutto in :style, Alpine con stringa sostituisce l'attributo style #} @@ -771,7 +771,7 @@ @pointerup="vPanEnd()" @pointercancel="vPanEnd()" class="rounded border border-[var(--border-color)]" :style="'overflow:auto;touch-action:none;user-select:none;display:flex;height:240px;background:var(--bg-primary);cursor:' + vCursor('dxf')"> -
+
diff --git a/src/frontend/flask_app/tests/js/test_dxf_zoom.js b/src/frontend/flask_app/tests/js/test_dxf_zoom.js index 54bd107..c064491 100644 --- a/src/frontend/flask_app/tests/js/test_dxf_zoom.js +++ b/src/frontend/flask_app/tests/js/test_dxf_zoom.js @@ -23,18 +23,18 @@ const near = (a, b) => Math.abs(a - b) < 1e-9 * Math.max(1, Math.abs(b)); // Wizard con viewport finti e $nextTick sincrono function make(cw) { const w = dxfRecipeWizard(1, () => 'tok', {}); - const vp = () => ({ clientWidth: cw, clientHeight: 240, scrollLeft: 5, scrollTop: 5 }); + const vp = () => ({ clientWidth: cw, clientHeight: 238, scrollLeft: 5, scrollTop: 5 }); w.$refs = { vpImg: vp(), vpDxf: vp() }; w.$nextTick = fn => fn(); return w; } -// img: viewport 400 x 240, immagine 2856 x 2848 +// img: viewport 400 x 238 utili, immagine 2856 x 2848 let w = make(400); w.vSetContent('img', 2856, 2848); -check('#113 img: Adatta = 240/2848', near(w.views.img.zoom, 240 / 2848) && near(w.views.img.fit, 240 / 2848), String(w.views.img.zoom)); +check('#113 img: Adatta = clientHeight/2848', near(w.views.img.zoom, 238 / 2848) && near(w.views.img.fit, 238 / 2848), String(w.views.img.zoom)); check('#113 img: scroll azzerato dopo Adatta', w.$refs.vpImg.scrollLeft === 0 && w.$refs.vpImg.scrollTop === 0); -check('#113 img: percentuale = zoom*100', w.vPct('img') === Math.round(240 / 2848 * 100) + ' %', w.vPct('img')); +check('#113 img: percentuale = zoom*100', w.vPct('img') === Math.round(238 / 2848 * 100) + ' %', w.vPct('img')); const z0 = w.views.img.zoom; w.vZoomBy('img', 1.25); check('#113 img: zoomBy moltiplica', near(w.views.img.zoom, z0 * 1.25)); @@ -45,13 +45,17 @@ w.vZoomBy('img', 1e-9); check('#113 img: limite basso 0.05', w.views.img.zoom === 0.05); w.vFit('img'); check('#113 img: Adatta torna al fit', near(w.views.img.zoom, z0) && w.vCanPan('img') === false); +// clientHeight 0 (non misurabile): ripiego 240 +w = make(400); w.$refs.vpImg.clientHeight = 0; w.vSetContent('img', 2856, 2848); +check('#113 img: clientHeight 0 usa 240', near(w.views.img.fit, 240 / 2848)); +w = make(400); w.vSetContent('img', 2856, 2848); check('#113 img: l\'altra vista non si muove', w.views.dxf.w === 0 && w.views.dxf.zoom === 1); check('#113 img: wrapper w*zoom', near(w.vW('img'), 2856 * z0) && near(w.vH('img'), 2848 * z0)); -// dxf: viewBox 20 x 10 mm, viewport 400 x 240 -> fit = min(20, 24) = 20 +// dxf: viewBox 20 x 10 mm, viewport 400 x 238 -> fit = min(20, 23.8) = 20 w = make(400); w.vSetContent('dxf', 20, 10); -check('#113 dxf: fit = min(vpW/w, 240/h)', near(w.views.dxf.fit, 20), String(w.views.dxf.fit)); +check('#113 dxf: fit = min(vpW/w, clientHeight/h)', near(w.views.dxf.fit, 20), String(w.views.dxf.fit)); check('#113 dxf: dopo Adatta 100 %', w.vPct('dxf') === '100 %', w.vPct('dxf')); w.vZoomBy('dxf', 2); check('#113 dxf: zoom 2x = 200 %', w.vPct('dxf') === '200 %', w.vPct('dxf')); @@ -66,7 +70,7 @@ w.vSetContent('img', 1000, 500); check('#113 clientWidth 0: nessun fit, stato invariato', w.views.img.zoom === 1 && w.views.img.fit === 1 && w.views.img.w === 1000); w.$refs.vpImg.clientWidth = 500; w.vFit('img'); -check('#113 viewport visibile: il fit parte', near(w.views.img.fit, 0.48), String(w.views.img.fit)); +check('#113 viewport visibile: il fit parte', near(w.views.img.fit, 238 / 500), String(w.views.img.fit)); // Reset: nuova immagine / DXF tolto w.vReset('img'); diff --git a/src/frontend/flask_app/tests/test_dxf_recipe_routes.py b/src/frontend/flask_app/tests/test_dxf_recipe_routes.py index 931b490..9eef3fc 100644 --- a/src/frontend/flask_app/tests/test_dxf_recipe_routes.py +++ b/src/frontend/flask_app/tests/test_dxf_recipe_routes.py @@ -362,5 +362,11 @@ class TestPages: assert "vFit('img')" in html and "vFit('dxf')" in html assert "vZoomBy('img', 1.25)" in html and "vZoomBy('dxf', 1.25)" in html assert "vPanDown('img', $event)" in html and "vWheel('dxf', $event)" in html + # #113: Alpine :style a stringa sostituisce l'attributo style: niente style statico sullo stesso tag + assert 'style="margin:auto;flex:none;"' not in html + assert html.count("margin:auto;flex:none;") == 2 and html.count(":style=\"'margin:auto;flex:none;") == 2 + # #113: x-show e :style (display:flex) si pestano: x-ref="vpImg" non sta sul tag con x-show + tag = html[html.rindex("