From 7c9092993b8563586cfdf2252e8d58dfefdc6b38 Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Thu, 4 Jun 2026 07:42:11 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20menu=20AI=20attach=C3=A9=20au=20body=20p?= =?UTF-8?q?our=20=C3=A9viter=20le=20clipping=20par=20overflow:hidden?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le menu déroulant AI dans le mode édition était coupé par le overflow:hidden du .editor-container. Le menu est maintenant: - Attaché à document.body avec position:fixed - Positionné aux coordonnées du bouton via getBoundingClientRect() - Retourné au-dessus si le menu dépasse le bas de l'écran - maxHeight: 60vh avec overflow-y: auto pour les menus très longs --- frontend/js/ai.js | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/frontend/js/ai.js b/frontend/js/ai.js index 5468ce8..61a4d14 100644 --- a/frontend/js/ai.js +++ b/frontend/js/ai.js @@ -66,24 +66,45 @@ function showToast(msg, type = 'info') { // ── Dropdown menu builder ── function createMenu(items, parentEl) { // Remove any existing menu - const existing = parentEl.querySelector('.ai-dropdown-menu'); + const existing = document.querySelector('.ai-dropdown-menu'); if (existing) existing.remove(); const menu = document.createElement('div'); menu.className = 'ai-dropdown-menu'; + + // Position relative to parent, but attached to body to avoid + // being clipped by overflow:hidden on the editor container. + const rect = parentEl.getBoundingClientRect(); + const menuTop = rect.bottom + 4; + const menuLeft = rect.left; + Object.assign(menu.style, { - position: 'absolute', - top: '100%', - left: '0', + position: 'fixed', + top: menuTop + 'px', + left: menuLeft + 'px', background: 'var(--bg-primary)', border: '1px solid var(--border-color)', borderRadius: '6px', padding: '4px 0', minWidth: '200px', - zIndex: '100', + maxHeight: '60vh', + overflowY: 'auto', + zIndex: '10000', boxShadow: '0 4px 16px rgba(0,0,0,0.4)', }); + // If menu overflows bottom of viewport, flip above the button + requestAnimationFrame(() => { + const menuRect = menu.getBoundingClientRect(); + if (menuRect.bottom > window.innerHeight - 8) { + menu.style.top = Math.max(8, rect.top - menuRect.height - 4) + 'px'; + } + // If menu overflows right edge, align right + if (menuRect.right > window.innerWidth - 8) { + menu.style.left = Math.max(8, window.innerWidth - menuRect.width - 8) + 'px'; + } + }); + items.forEach(item => { const el = document.createElement('div'); el.className = 'ai-menu-item'; @@ -134,7 +155,7 @@ function createMenu(items, parentEl) { menu.appendChild(el); }); - parentEl.appendChild(menu); + document.body.appendChild(menu); // Close on outside click const closeHandler = (e) => {