From 6e05f9e7000da966bcd3fac96cc4efd063414122 Mon Sep 17 00:00:00 2001 From: bruno Date: Sun, 12 Jul 2026 17:42:18 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20menu=20contextuel=20=E2=80=94=20handler?= =?UTF-8?q?=20natif=20addEventListener=20remplace=20Alpine=20@contextmenu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Le handler Alpine @contextmenu.prevent="onContextMenu" ne fonctionnait pas (problème de scope/resolution Alpine sur le clic droit) - Remplacement par addEventListener('contextmenu', ...) natif dans init() → événement capturé directement sur le DOM, bypass complet Alpine - Propriétés ctxMenu modifiées individuellement (pas d'objet remplacé) pour garantir la réactivité Alpine sur les nested properties - L'ancien handler onContextMenu est gardé en fallback avec le même pattern - Suppression du @contextmenu.prevent du template HTML (évite double-fire) --- app/main.py | 2 +- app/templates/local_workspace.html | 52 +++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/app/main.py b/app/main.py index a054f1c..cf3370f 100644 --- a/app/main.py +++ b/app/main.py @@ -39,7 +39,7 @@ async def lifespan(_app: FastAPI): app = FastAPI( title="FlowDeck", - version="2.4.2", + version="2.4.3", docs_url="/docs" if settings.log_level == "DEBUG" else None, redoc_url=None, lifespan=lifespan, diff --git a/app/templates/local_workspace.html b/app/templates/local_workspace.html index d490e49..26908ba 100644 --- a/app/templates/local_workspace.html +++ b/app/templates/local_workspace.html @@ -341,7 +341,6 @@ @dragleave="onDragLeave($event)" @drop.prevent="onDrop($event)" @keydown="onKeydown($event)" - @contextmenu.prevent="onContextMenu($event)" tabindex="0" x-ref="layout">
@@ -1027,6 +1026,41 @@ document.addEventListener('alpine:init', function() { var self = this; window.addEventListener('online', function() { self.online = true; }); window.addEventListener('offline', function() { self.online = false; }); + // Native context menu handler (bypasses Alpine scoping issues) + self.$el.addEventListener('contextmenu', function(ev) { + ev.preventDefault(); + var el = ev.target.closest('[data-ws-id]'); + if (!el) { self.ctxMenu.visible = false; return; } + var id = parseInt(el.getAttribute('data-ws-id')); + if (!id) { self.ctxMenu.visible = false; return; } + var node = self._findNodeById(self.displayTree, id); + if (!node) { self.ctxMenu.visible = false; return; } + self.ctxMenu.visible = true; + self.ctxMenu.x = ev.clientX; + self.ctxMenu.y = ev.clientY; + self.ctxMenu.node = node; + self.ctxMenu.tags = [...(node.tags||[])]; + self.ctxTagAdding = false; + self.ctxTagExistingOpen = false; + self.ctxNewTagColor = '#787774'; + self.ctxAvailTags = (self.workspaceTags || []).filter(function(t) { + return !(node.tags||[]).some(function(nt) { return nt.id === t.id; }); + }); + // Reposition after DOM update + self.$nextTick(function() { + var menu = self.$el.querySelector('.ctx-menu'); + if (!menu) return; + var rect = menu.getBoundingClientRect(); + var mx = self.ctxMenu.x; + var my = self.ctxMenu.y; + if (mx + rect.width > window.innerWidth - 4) mx = window.innerWidth - rect.width - 4; + if (my + rect.height > window.innerHeight - 4) my = window.innerHeight - rect.height - 4; + if (mx < 4) mx = 4; + if (my < 4) my = 4; + self.ctxMenu.x = mx; + self.ctxMenu.y = my; + }); + }); // Load workspace tags this.loadWorkspaceTags(); } catch(e) { console.error('Tree load error:', e); this.loaded = true; } @@ -1259,7 +1293,7 @@ document.addEventListener('alpine:init', function() { return {}; }, - // ── Context menu ── + // ── Context menu (fallback — native handler in init is primary) ── onContextMenu(ev) { var el = ev.target.closest('[data-ws-id]'); if (!el) { this.ctxMenu.visible = false; return; } @@ -1267,21 +1301,17 @@ document.addEventListener('alpine:init', function() { if (!id) { this.ctxMenu.visible = false; return; } var node = this._findNodeById(this.displayTree, id); if (!node) { this.ctxMenu.visible = false; return; } - this.ctxMenu = { - visible: true, - x: ev.clientX, - y: ev.clientY, - node: node, - tags: [...(node.tags||[])] - }; + this.ctxMenu.visible = true; + this.ctxMenu.x = ev.clientX; + this.ctxMenu.y = ev.clientY; + this.ctxMenu.node = node; + this.ctxMenu.tags = [...(node.tags||[])]; this.ctxTagAdding = false; this.ctxTagExistingOpen = false; this.ctxNewTagColor = '#787774'; - // Load available tags this.ctxAvailTags = (this.workspaceTags || []).filter(function(t) { return !(node.tags||[]).some(function(nt) { return nt.id === t.id; }); }); - // Reposition after DOM update var self = this; self.$nextTick(function() { var menu = self.$el.querySelector('.ctx-menu');