mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-26 17:30:07 +00:00
Cleanup: YAGNI readonly props, private swap on ComfyApp, Canvas resize events simplification, v-memos on individual instances (#5869)
## Summary Assorted cleanup opportunities found while working through the Vue node rendering logic cleanup. ## Review Focus Am I wrong that the readonly logic was never actually executing because it was defined as False in GraphCanvas when making each LGraphNode? Is there an edge case or some other reason that the ResizeObserver wouldn't work as a single signal to resize the canvas? ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5869-Cleanup-YAGNI-readonly-props-private-swap-on-ComfyApp-Canvas-resize-events-simplificat-27e6d73d3650811ba1dcf29e8d43091e) by [Unito](https://www.unito.io) --------- Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
@@ -3,12 +3,7 @@
|
||||
<label v-if="widget.name" class="text-sm opacity-80">{{
|
||||
widget.name
|
||||
}}</label>
|
||||
<Button
|
||||
v-bind="filteredProps"
|
||||
:disabled="readonly"
|
||||
size="small"
|
||||
@click="handleClick"
|
||||
/>
|
||||
<Button v-bind="filteredProps" size="small" @click="handleClick" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -25,7 +20,6 @@ import {
|
||||
// Button widgets don't have a v-model value, they trigger actions
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<void>
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
// Button specific excluded props
|
||||
@@ -36,7 +30,7 @@ const filteredProps = computed(() =>
|
||||
)
|
||||
|
||||
const handleClick = () => {
|
||||
if (!props.readonly && props.widget.callback) {
|
||||
if (props.widget.callback) {
|
||||
props.widget.callback()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ const value = defineModel<ChartData>({ required: true })
|
||||
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<ChartData, ChartWidgetOptions>
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const chartType = computed(() => props.widget.options?.type ?? 'line')
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
<ColorPicker
|
||||
v-model="localValue"
|
||||
v-bind="filteredProps"
|
||||
:disabled="readonly"
|
||||
class="w-8 h-4 !rounded-full overflow-hidden border-none"
|
||||
:pt="{
|
||||
preview: '!w-full !h-full !border-none'
|
||||
@@ -48,7 +47,6 @@ type WidgetOptions = { format?: ColorFormat } & Record<string, unknown>
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<string, WidgetOptions>
|
||||
modelValue: string
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
icon="pi pi-folder"
|
||||
size="small"
|
||||
class="!w-8 !h-8"
|
||||
:disabled="readonly"
|
||||
@click="triggerFileInput"
|
||||
/>
|
||||
</div>
|
||||
@@ -47,7 +46,6 @@
|
||||
/>
|
||||
<!-- Control buttons in top right on hover -->
|
||||
<div
|
||||
v-if="!readonly"
|
||||
class="absolute top-2 right-2 flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200"
|
||||
>
|
||||
<!-- Edit button -->
|
||||
@@ -100,7 +98,6 @@
|
||||
icon="pi pi-folder"
|
||||
size="small"
|
||||
class="!w-8 !h-8"
|
||||
:disabled="readonly"
|
||||
@click="triggerFileInput"
|
||||
/>
|
||||
</div>
|
||||
@@ -128,7 +125,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Control buttons -->
|
||||
<div v-if="!readonly" class="flex gap-1">
|
||||
<div class="flex gap-1">
|
||||
<!-- Delete button -->
|
||||
<button
|
||||
class="w-8 h-8 rounded flex items-center justify-center transition-all duration-150 focus:outline-none border-none hover:bg-[#262729]"
|
||||
@@ -159,7 +156,6 @@
|
||||
size="small"
|
||||
severity="secondary"
|
||||
class="text-xs"
|
||||
:disabled="readonly"
|
||||
@click="triggerFileInput"
|
||||
/>
|
||||
</div>
|
||||
@@ -173,7 +169,6 @@
|
||||
class="hidden"
|
||||
:accept="widget.options?.accept"
|
||||
:multiple="false"
|
||||
:disabled="readonly"
|
||||
@change="handleFileChange"
|
||||
/>
|
||||
</template>
|
||||
@@ -187,14 +182,9 @@ import { useWidgetValue } from '@/composables/graph/useWidgetValue'
|
||||
import { useTransformCompatOverlayProps } from '@/composables/useTransformCompatOverlayProps'
|
||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||
|
||||
const {
|
||||
widget,
|
||||
modelValue,
|
||||
readonly = false
|
||||
} = defineProps<{
|
||||
const { widget, modelValue } = defineProps<{
|
||||
widget: SimplifiedWidget<File[] | null>
|
||||
modelValue: File[] | null
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -284,7 +274,7 @@ const triggerFileInput = () => {
|
||||
|
||||
const handleFileChange = (event: Event) => {
|
||||
const target = event.target as HTMLInputElement
|
||||
if (!readonly && target.files && target.files.length > 0) {
|
||||
if (target.files && target.files.length > 0) {
|
||||
// Since we only support single file, take the first one
|
||||
const file = target.files[0]
|
||||
|
||||
|
||||
@@ -61,8 +61,7 @@ function createMockWidget(
|
||||
|
||||
function mountComponent(
|
||||
widget: SimplifiedWidget<GalleryValue>,
|
||||
modelValue: GalleryValue,
|
||||
readonly = false
|
||||
modelValue: GalleryValue
|
||||
) {
|
||||
return mount(WidgetGalleria, {
|
||||
global: {
|
||||
@@ -71,7 +70,6 @@ function mountComponent(
|
||||
},
|
||||
props: {
|
||||
widget,
|
||||
readonly,
|
||||
modelValue
|
||||
}
|
||||
})
|
||||
@@ -87,11 +85,10 @@ function createImageStrings(count: number): string[] {
|
||||
// Factory function that takes images, creates widget internally, returns wrapper
|
||||
function createGalleriaWrapper(
|
||||
images: GalleryValue,
|
||||
options: Partial<GalleriaProps> = {},
|
||||
readonly = false
|
||||
options: Partial<GalleriaProps> = {}
|
||||
) {
|
||||
const widget = createMockWidget(images, options)
|
||||
return mountComponent(widget, images, readonly)
|
||||
return mountComponent(widget, images)
|
||||
}
|
||||
|
||||
describe('WidgetGalleria Image Display', () => {
|
||||
@@ -249,25 +246,6 @@ describe('WidgetGalleria Image Display', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('Readonly Mode', () => {
|
||||
it('passes readonly state to galleria when readonly', () => {
|
||||
const images = createImageStrings(3)
|
||||
const widget = createMockWidget(images)
|
||||
const wrapper = mountComponent(widget, images, true)
|
||||
|
||||
// Galleria component should receive readonly state (though it may not support disabled)
|
||||
expect(wrapper.props('readonly')).toBe(true)
|
||||
})
|
||||
|
||||
it('passes readonly state to galleria when not readonly', () => {
|
||||
const images = createImageStrings(3)
|
||||
const widget = createMockWidget(images)
|
||||
const wrapper = mountComponent(widget, images, false)
|
||||
|
||||
expect(wrapper.props('readonly')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Widget Options Handling', () => {
|
||||
it('passes through valid widget options', () => {
|
||||
const images = createImageStrings(2)
|
||||
|
||||
@@ -72,7 +72,6 @@ const value = defineModel<GalleryValue>({ required: true })
|
||||
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<GalleryValue>
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const activeIndex = ref(0)
|
||||
|
||||
@@ -41,7 +41,6 @@ export interface ImageCompareValue {
|
||||
// Image compare widgets typically don't have v-model, they display comparison
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<ImageCompareValue | string>
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const beforeImage = computed(() => {
|
||||
|
||||
@@ -6,7 +6,6 @@ import WidgetInputNumberSlider from './WidgetInputNumberSlider.vue'
|
||||
|
||||
defineProps<{
|
||||
widget: SimplifiedWidget<number>
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const modelValue = defineModel<number>({ default: 0 })
|
||||
@@ -21,7 +20,6 @@ const modelValue = defineModel<number>({ default: 0 })
|
||||
"
|
||||
v-model="modelValue"
|
||||
:widget="widget"
|
||||
:readonly="readonly"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -16,7 +16,6 @@ import WidgetLayoutField from './layout/WidgetLayoutField.vue'
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<number>
|
||||
modelValue: number
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -72,7 +71,6 @@ const buttonsDisabled = computed(() => {
|
||||
|
||||
// Tooltip message for disabled buttons
|
||||
const buttonTooltip = computed(() => {
|
||||
if (props.readonly) return null
|
||||
if (buttonsDisabled.value) {
|
||||
return 'Increment/decrement disabled: value exceeds JavaScript precision limit (±2^53)'
|
||||
}
|
||||
@@ -89,7 +87,6 @@ const buttonTooltip = computed(() => {
|
||||
:show-buttons="!buttonsDisabled"
|
||||
button-layout="horizontal"
|
||||
size="small"
|
||||
:disabled="readonly"
|
||||
:step="stepValue"
|
||||
:use-grouping="useGrouping"
|
||||
:class="cn(WidgetInputBaseClass, 'w-full text-xs')"
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<Slider
|
||||
:model-value="[localValue]"
|
||||
v-bind="filteredProps"
|
||||
:disabled="readonly"
|
||||
class="flex-grow text-xs"
|
||||
:step="stepValue"
|
||||
@update:model-value="updateLocalValue"
|
||||
@@ -17,7 +16,6 @@
|
||||
:key="timesEmptied"
|
||||
:model-value="localValue"
|
||||
v-bind="filteredProps"
|
||||
:disabled="readonly"
|
||||
:step="stepValue"
|
||||
:min-fraction-digits="precision"
|
||||
:max-fraction-digits="precision"
|
||||
@@ -46,10 +44,9 @@ import {
|
||||
import { WidgetInputBaseClass } from './layout'
|
||||
import WidgetLayoutField from './layout/WidgetLayoutField.vue'
|
||||
|
||||
const { widget, modelValue, readonly } = defineProps<{
|
||||
const { widget, modelValue } = defineProps<{
|
||||
widget: SimplifiedWidget<number>
|
||||
modelValue: number
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<InputText
|
||||
v-model="localValue"
|
||||
v-bind="filteredProps"
|
||||
:disabled="readonly"
|
||||
:class="cn(WidgetInputBaseClass, 'w-full text-xs py-2 px-4')"
|
||||
size="small"
|
||||
@update:model-value="onChange"
|
||||
@@ -29,7 +28,6 @@ import WidgetLayoutField from './layout/WidgetLayoutField.vue'
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<string>
|
||||
modelValue: string
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
v-show="isEditing"
|
||||
ref="textareaRef"
|
||||
v-model="localValue"
|
||||
:disabled="readonly"
|
||||
class="w-full min-h-[60px] absolute inset-0 resize-none"
|
||||
:pt="{
|
||||
root: {
|
||||
@@ -45,7 +44,6 @@ import LODFallback from '../../components/LODFallback.vue'
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<string>
|
||||
modelValue: string
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -70,7 +68,7 @@ const renderedHtml = computed(() => {
|
||||
|
||||
// Methods
|
||||
const startEditing = async () => {
|
||||
if (props.readonly || isEditing.value) return
|
||||
if (isEditing.value) return
|
||||
|
||||
isEditing.value = true
|
||||
await nextTick()
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
v-model="localValue"
|
||||
:options="multiSelectOptions"
|
||||
v-bind="combinedProps"
|
||||
:disabled="readonly"
|
||||
class="w-full text-xs"
|
||||
size="small"
|
||||
display="chip"
|
||||
@@ -33,7 +32,6 @@ import WidgetLayoutField from './layout/WidgetLayoutField.vue'
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<T[]>
|
||||
modelValue: T[]
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -31,7 +31,6 @@ import WidgetSelectDropdown from './WidgetSelectDropdown.vue'
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<string | number | undefined>
|
||||
modelValue: string | number | undefined
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<FormSelectButton
|
||||
v-model="localValue"
|
||||
:options="widget.options?.values || []"
|
||||
:disabled="readonly"
|
||||
class="w-full"
|
||||
@update:model-value="onChange"
|
||||
/>
|
||||
@@ -20,7 +19,6 @@ import WidgetLayoutField from './layout/WidgetLayoutField.vue'
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<string>
|
||||
modelValue: string
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
v-model="localValue"
|
||||
:options="selectOptions"
|
||||
v-bind="combinedProps"
|
||||
:disabled="readonly"
|
||||
:class="cn(WidgetInputBaseClass, 'w-full text-xs')"
|
||||
size="small"
|
||||
:pt="{
|
||||
@@ -35,7 +34,6 @@ import WidgetLayoutField from './layout/WidgetLayoutField.vue'
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<string | number | undefined>
|
||||
modelValue: string | number | undefined
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -25,7 +25,6 @@ import WidgetLayoutField from './layout/WidgetLayoutField.vue'
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<string | number | undefined>
|
||||
modelValue: string | number | undefined
|
||||
readonly?: boolean
|
||||
assetKind?: AssetKind
|
||||
allowUpload?: boolean
|
||||
uploadFolder?: ResultItemType
|
||||
@@ -222,7 +221,6 @@ const filterOptions = ref<FilterOption[]>([
|
||||
:placeholder="mediaPlaceholder"
|
||||
:multiple="false"
|
||||
:uploadable="uploadable"
|
||||
:disabled="readonly"
|
||||
:filter-options="filterOptions"
|
||||
v-bind="combinedProps"
|
||||
class="w-full"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<Textarea
|
||||
v-model="localValue"
|
||||
v-bind="filteredProps"
|
||||
:disabled="readonly"
|
||||
:class="cn(WidgetInputBaseClass, 'w-full text-xs lod-toggle')"
|
||||
:placeholder="placeholder || widget.name || ''"
|
||||
size="small"
|
||||
@@ -33,7 +32,6 @@ import { WidgetInputBaseClass } from './layout'
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<string>
|
||||
modelValue: string
|
||||
readonly?: boolean
|
||||
placeholder?: string
|
||||
}>()
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<ToggleSwitch
|
||||
v-model="localValue"
|
||||
v-bind="filteredProps"
|
||||
:disabled="readonly"
|
||||
@update:model-value="onChange"
|
||||
/>
|
||||
</WidgetLayoutField>
|
||||
@@ -25,7 +24,6 @@ import WidgetLayoutField from './layout/WidgetLayoutField.vue'
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<boolean>
|
||||
modelValue: boolean
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<TreeSelect
|
||||
v-model="localValue"
|
||||
v-bind="combinedProps"
|
||||
:disabled="readonly"
|
||||
class="w-full text-xs"
|
||||
size="small"
|
||||
@update:model-value="onChange"
|
||||
@@ -37,7 +36,6 @@ export type TreeNode = {
|
||||
const props = defineProps<{
|
||||
widget: SimplifiedWidget<any>
|
||||
modelValue: any
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
Reference in New Issue
Block a user