Просмотр исходного кода

Merge pull request #31572 from overleaf/mg-context-menu-non-editable

Show context menu for empty line filler elements

GitOrigin-RevId: 9becb697e61130a623a9a00bccae015b927760a2
Malik Glossop 5 месяцев назад
Родитель
Сommit
daa7b52ef1

+ 46 - 4
services/web/frontend/js/features/source-editor/extensions/context-menu.ts

@@ -4,6 +4,7 @@ import {
   Tooltip,
   TooltipView,
   keymap,
+  ViewPlugin,
 } from '@codemirror/view'
 import {
   Extension,
@@ -230,7 +231,7 @@ function isClickOnGutter(target: HTMLElement): boolean {
 // Gutter context menu plugin
 const gutterContextMenuPlugin = (): Extension =>
   EditorView.updateListener.of(update => {
-    if (!update.view.dom.parentElement) {
+    if (isTouchOnlyInput || !update.view.dom.parentElement) {
       return
     }
 
@@ -243,9 +244,7 @@ const gutterContextMenuPlugin = (): Extension =>
     gutters.setAttribute('data-context-menu-attached', 'true')
     gutters.addEventListener('contextmenu', (event: Event) => {
       const mouseEvent = event as MouseEvent
-      if (isTouchOnlyInput) {
-        return
-      }
+
       const pos = update.view.posAtCoords({
         x: mouseEvent.clientX,
         y: mouseEvent.clientY,
@@ -269,6 +268,48 @@ const gutterContextMenuPlugin = (): Extension =>
     })
   })
 
+// Handle right-click on ol-cm-filler (empty line widget)
+// domEventHandlers doesn't fire for contenteditable="false" elements, so we use a direct DOM listener
+const emptyLineFillerContextMenuPlugin = (): Extension => {
+  if (isTouchOnlyInput) {
+    return []
+  }
+
+  return ViewPlugin.define(view => {
+    const contentDOM = view.contentDOM
+
+    const handleContextMenu = (event: Event) => {
+      const mouseEvent = event as MouseEvent
+      const target = mouseEvent.target as HTMLElement
+
+      // Only handle ol-cm-filler elements
+      if (!target.classList.contains('ol-cm-filler')) {
+        return
+      }
+
+      event.preventDefault()
+      event.stopPropagation()
+
+      // Re-dispatch on contentDOM so CodeMirror's domEventHandlers picks it up
+      const customEvent = new MouseEvent('contextmenu', {
+        bubbles: true,
+        cancelable: true,
+        clientX: mouseEvent.clientX,
+        clientY: mouseEvent.clientY,
+      })
+      contentDOM.dispatchEvent(customEvent)
+    }
+
+    contentDOM.addEventListener('contextmenu', handleContextMenu)
+
+    return {
+      destroy() {
+        contentDOM.removeEventListener('contextmenu', handleContextMenu)
+      },
+    }
+  })
+}
+
 // Editor view context menu handlers
 const editorContextMenuHandlers = (): Extension =>
   EditorView.domEventHandlers({
@@ -353,6 +394,7 @@ export const contextMenu = (enabled: boolean): Extension =>
         contextMenuContainerTheme,
         contextMenuStateField,
         gutterContextMenuPlugin(),
+        emptyLineFillerContextMenuPlugin(),
         editorContextMenuHandlers(),
         contextMenuKeymap(),
       ]