mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-14 01:36:14 +00:00
## Summary
Fix part of the
#https://github.com/Comfy-Org/ComfyUI_frontend/issues/11092
A total of 4 `// @ts-expect-error` directives were removed across 3
files — all caused by PrimeVue's legacy `$el` access pattern (`const
inputElement = inputRef.value.$el`) — by replacing PrimeVue with
Reka-based UI components. 1 corresponding unit test file was added.
## Changes
### `src/components/ui/input/Input.vue`
- **Exposed APIs**: Extended `defineExpose` to include `blur()` and
`setSelectionRange()`. This allows parent components to programmatically
control input behavior without direct DOM manipulation.
### `src/components/ui/textarea/Textarea.vue`
- **Exposed APIs**: Added `focus()` via `defineExpose`.
- **Cleanup**: Removed redundant attribute spreading (`...restAttrs`) to
lean on Vue’s default `$attrs` inheritance, making the component more
predictable.
---
## Refactored Feature Components
### `WidgetMarkdown.vue` (Note/Markdown Widgets)
- **Dependency Swap**: Replaced `primevue/textarea` with local
`Textarea.vue`.
- **Logic Simplification**: Simplified focus logic from
`textareaRef.value?.$el?.focus()` to a typed
`textareaRef.value?.focus()`.
- **Code Style**: Converted arrow functions to function declarations and
removed redundant section comments.
### `PromptDialogContent.vue` (Generic Prompt Dialogs)
- **Component Update**: Replaced PrimeVue `FloatLabel` and `InputText`
with a native `<label>` and local `Input.vue`.
- **Vue 3.5 Adoption**: Implemented **Reactive Destructuring** for
props.
- **Conflict Resolution**: Renamed internal `onConfirm` handler to
`handleConfirm` to prevent collision with destructured props.
### `EditableText.vue` (Node Titles & Sidebar Items)
- **Style Modernization**: Removed `<style scoped>` block in favor of
**Tailwind CSS** utility classes (e.g., `inline`, `w-full`).
- **Clean Implementation**: Replaced PrimeVue PassThrough (`:pt`) logic
with standard `@blur` and `v-bind` attributes.
---
## Testing & Quality Assurance
### Updated Tests
- **Redundancy Removal**: Cleaned up `EditableText.test.ts` and
`WidgetMarkdown.test.ts` by removing unused PrimeVue global
registrations. All 34 existing behavioral tests remain passing.
### New Coverage
- **`PromptDialogContent.test.ts`**: Added 3 new tests to verify:
1. Correct initialization with `defaultValue`.
2. Value persistence when clicking the Confirm button.
3. Form submission via the `Enter` key.
---
## Manual Test Screenshot
All functions have passed testing.
<img width="594" height="530" alt="test5"
src="https://github.com/user-attachments/assets/46a6b3b2-1855-414e-ac78-65668052ce50"
/>
<img width="1190" height="1074" alt="test4"
src="https://github.com/user-attachments/assets/89aa61ab-9401-44c2-9eae-9ca8761df675"
/>
<img width="1154" height="1028" alt="test3"
src="https://github.com/user-attachments/assets/3f63cfdf-8fbd-4dd3-9e42-dbebe4d8d421"
/>
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Moderate risk because it swaps underlying input/textarea components
and ref handling (focus/blur/selection) in interactive UI paths
(editable labels, prompt dialogs, markdown editor), which could subtly
change keyboard/blur behavior.
>
> **Overview**
> Refactors several Vue components to stop using PrimeVue
`InputText`/`Textarea` (and `$el` access) in favor of the project’s
`Input`/`Textarea` components, updating bindings/events and Tailwind
classes accordingly.
>
> Extends the shared `Input` to expose `blur`, `setSelectionRange`, and
`selectAll`, and updates `Textarea` to expose `focus`, enabling callers
to manage focus/selection without DOM internals.
>
> Adds a new unit test suite for `PromptDialogContent` and simplifies
existing tests by removing PrimeVue plugin/component setup; the groups
e2e test replaces a screenshot assertion with a functional visibility
check for the new title input.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
9c97314d59. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11324-refactor-replace-PrimeVue-InputText-Textarea-with-project-UI-components-3456d73d36508109a18bc97a7d0487a7)
by [Unito](https://www.unito.io)
84 lines
1.9 KiB
Vue
84 lines
1.9 KiB
Vue
<template>
|
|
<div class="editable-text inline">
|
|
<component :is="labelType" v-if="!isEditing" :class="labelClass">
|
|
{{ modelValue }}
|
|
</component>
|
|
<Input
|
|
v-else
|
|
ref="inputRef"
|
|
v-model="inputValue"
|
|
v-focus
|
|
type="text"
|
|
class="h-full rounded-none p-0 focus-visible:ring-0"
|
|
v-bind="inputAttrs"
|
|
@blur="finishEditing"
|
|
@keydown.enter.capture.stop="inputRef?.blur()"
|
|
@keydown.escape.capture.stop="cancelEditing"
|
|
@click.stop
|
|
@contextmenu.stop
|
|
@pointerdown.stop.capture
|
|
@pointermove.stop.capture
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { nextTick, ref, watch } from 'vue'
|
|
|
|
import Input from '@/components/ui/input/Input.vue'
|
|
|
|
const {
|
|
modelValue,
|
|
isEditing = false,
|
|
inputAttrs = {},
|
|
labelClass = '',
|
|
labelType = 'span'
|
|
} = defineProps<{
|
|
modelValue: string
|
|
isEditing?: boolean
|
|
inputAttrs?: Record<string, string>
|
|
labelClass?: string
|
|
labelType?: string
|
|
}>()
|
|
|
|
const emit = defineEmits(['edit', 'cancel'])
|
|
const inputValue = ref<string>(modelValue)
|
|
const inputRef = ref<InstanceType<typeof Input>>()
|
|
const isCanceling = ref(false)
|
|
|
|
function finishEditing() {
|
|
if (!isCanceling.value) {
|
|
emit('edit', inputValue.value)
|
|
}
|
|
isCanceling.value = false
|
|
}
|
|
|
|
function cancelEditing() {
|
|
isCanceling.value = true
|
|
inputValue.value = modelValue
|
|
emit('cancel')
|
|
inputRef.value?.blur()
|
|
}
|
|
|
|
watch(
|
|
() => isEditing,
|
|
async (newVal) => {
|
|
if (newVal) {
|
|
inputValue.value = modelValue
|
|
await nextTick(() => {
|
|
if (!inputRef.value) return
|
|
const fileName = inputValue.value.includes('.')
|
|
? inputValue.value.split('.').slice(0, -1).join('.')
|
|
: inputValue.value
|
|
inputRef.value.setSelectionRange(0, fileName.length)
|
|
})
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
|
|
const vFocus = {
|
|
mounted: (el: HTMLElement) => el.focus()
|
|
}
|
|
</script>
|