Use native context menu for focused textareas (#10454)

The custom context menu provided by the frontend exposes widget specific
options. In order to support renaming, promotion, and favoriting, there
needs to be a way to access this context menu when targeting a textarea.
However, always displaying this custom context menu will cause the user
to lose access to browser specific functionality like spell checking,
translation, and the ability to copy paste text.

This PR updates the behaviour so that the native browser context menu
will display when the text area already has focus. Our custom frontend
context menu will continue to display when it does not.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-10454-Use-native-context-menu-for-focused-textareas-32d6d73d365081909673d81d6a6ba054)
by [Unito](https://www.unito.io)
This commit is contained in:
AustinMroz
2026-03-24 12:25:35 -07:00
committed by GitHub
parent 5f14276159
commit 0ae4b78cbc
2 changed files with 23 additions and 3 deletions

View File

@@ -46,4 +46,16 @@ test.describe('Vue Multiline String Widget', () => {
await expect(textarea).toHaveValue('Keep me around')
})
test('should use native context menu when focused', async ({ comfyPage }) => {
const textarea = getFirstMultilineStringWidget(comfyPage)
const vueContextMenu = comfyPage.page.locator('.p-contextmenu')
await textarea.focus()
await textarea.click({ button: 'right' })
await expect(vueContextMenu).not.toBeVisible()
await textarea.blur()
await textarea.click({ button: 'right' })
await expect(vueContextMenu).toBeVisible()
})
})

View File

@@ -17,6 +17,7 @@
<Textarea
v-bind="filteredProps"
:id
ref="textAreaRef"
v-model="modelValue"
:class="
cn(
@@ -28,7 +29,7 @@
:placeholder
:readonly="isReadOnly"
data-capture-wheel="true"
@pointerdown.capture.stop
@pointerdown.capture.stop="trackFocus"
@pointermove.capture.stop
@pointerup.capture.stop
@contextmenu.capture="handleContextMenu"
@@ -49,7 +50,7 @@
</template>
<script setup lang="ts">
import { computed, useId } from 'vue'
import { computed, ref, useId, useTemplateRef } from 'vue'
import Button from '@/components/ui/button/Button.vue'
import Textarea from '@/components/ui/textarea/Textarea.vue'
@@ -70,8 +71,15 @@ const { widget, placeholder = '' } = defineProps<{
placeholder?: string
}>()
const textAreaRef = useTemplateRef('textAreaRef')
const modelValue = defineModel<string>({ default: '' })
const isFocused = ref(false)
function trackFocus() {
isFocused.value = document.activeElement === textAreaRef.value?.$el
}
const hideLayoutField = useHideLayoutField()
const { copyToClipboard } = useCopyToClipboard()
@@ -87,7 +95,7 @@ const isReadOnly = computed(() =>
)
function handleContextMenu(e: MouseEvent) {
if (isNodeOptionsOpen()) {
if (isNodeOptionsOpen() || isFocused.value) {
e.stopPropagation()
return
}