fix: menu contextuel — handler natif addEventListener remplace Alpine @contextmenu
- 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)
This commit is contained in:
+1
-1
@@ -39,7 +39,7 @@ async def lifespan(_app: FastAPI):
|
|||||||
|
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="FlowDeck",
|
title="FlowDeck",
|
||||||
version="2.4.2",
|
version="2.4.3",
|
||||||
docs_url="/docs" if settings.log_level == "DEBUG" else None,
|
docs_url="/docs" if settings.log_level == "DEBUG" else None,
|
||||||
redoc_url=None,
|
redoc_url=None,
|
||||||
lifespan=lifespan,
|
lifespan=lifespan,
|
||||||
|
|||||||
@@ -341,7 +341,6 @@
|
|||||||
@dragleave="onDragLeave($event)"
|
@dragleave="onDragLeave($event)"
|
||||||
@drop.prevent="onDrop($event)"
|
@drop.prevent="onDrop($event)"
|
||||||
@keydown="onKeydown($event)"
|
@keydown="onKeydown($event)"
|
||||||
@contextmenu.prevent="onContextMenu($event)"
|
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
x-ref="layout">
|
x-ref="layout">
|
||||||
<div class="ws-main">
|
<div class="ws-main">
|
||||||
@@ -1027,6 +1026,41 @@ document.addEventListener('alpine:init', function() {
|
|||||||
var self = this;
|
var self = this;
|
||||||
window.addEventListener('online', function() { self.online = true; });
|
window.addEventListener('online', function() { self.online = true; });
|
||||||
window.addEventListener('offline', function() { self.online = false; });
|
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
|
// Load workspace tags
|
||||||
this.loadWorkspaceTags();
|
this.loadWorkspaceTags();
|
||||||
} catch(e) { console.error('Tree load error:', e); this.loaded = true; }
|
} catch(e) { console.error('Tree load error:', e); this.loaded = true; }
|
||||||
@@ -1259,7 +1293,7 @@ document.addEventListener('alpine:init', function() {
|
|||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Context menu ──
|
// ── Context menu (fallback — native handler in init is primary) ──
|
||||||
onContextMenu(ev) {
|
onContextMenu(ev) {
|
||||||
var el = ev.target.closest('[data-ws-id]');
|
var el = ev.target.closest('[data-ws-id]');
|
||||||
if (!el) { this.ctxMenu.visible = false; return; }
|
if (!el) { this.ctxMenu.visible = false; return; }
|
||||||
@@ -1267,21 +1301,17 @@ document.addEventListener('alpine:init', function() {
|
|||||||
if (!id) { this.ctxMenu.visible = false; return; }
|
if (!id) { this.ctxMenu.visible = false; return; }
|
||||||
var node = this._findNodeById(this.displayTree, id);
|
var node = this._findNodeById(this.displayTree, id);
|
||||||
if (!node) { this.ctxMenu.visible = false; return; }
|
if (!node) { this.ctxMenu.visible = false; return; }
|
||||||
this.ctxMenu = {
|
this.ctxMenu.visible = true;
|
||||||
visible: true,
|
this.ctxMenu.x = ev.clientX;
|
||||||
x: ev.clientX,
|
this.ctxMenu.y = ev.clientY;
|
||||||
y: ev.clientY,
|
this.ctxMenu.node = node;
|
||||||
node: node,
|
this.ctxMenu.tags = [...(node.tags||[])];
|
||||||
tags: [...(node.tags||[])]
|
|
||||||
};
|
|
||||||
this.ctxTagAdding = false;
|
this.ctxTagAdding = false;
|
||||||
this.ctxTagExistingOpen = false;
|
this.ctxTagExistingOpen = false;
|
||||||
this.ctxNewTagColor = '#787774';
|
this.ctxNewTagColor = '#787774';
|
||||||
// Load available tags
|
|
||||||
this.ctxAvailTags = (this.workspaceTags || []).filter(function(t) {
|
this.ctxAvailTags = (this.workspaceTags || []).filter(function(t) {
|
||||||
return !(node.tags||[]).some(function(nt) { return nt.id === t.id; });
|
return !(node.tags||[]).some(function(nt) { return nt.id === t.id; });
|
||||||
});
|
});
|
||||||
// Reposition after DOM update
|
|
||||||
var self = this;
|
var self = this;
|
||||||
self.$nextTick(function() {
|
self.$nextTick(function() {
|
||||||
var menu = self.$el.querySelector('.ctx-menu');
|
var menu = self.$el.querySelector('.ctx-menu');
|
||||||
|
|||||||
Reference in New Issue
Block a user