mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-01 05:49:54 +00:00
Vue component multi-select widget (#2987)
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
:key="widget.id"
|
||||
:widget="widget"
|
||||
:widget-state="domWidgetStore.widgetStates.get(widget.id)"
|
||||
@update:widget-value="widget.value = $event"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -16,7 +17,7 @@ import { computed, watch } from 'vue'
|
||||
|
||||
import DomWidget from '@/components/graph/widgets/DomWidget.vue'
|
||||
import { useChainCallback } from '@/composables/functional/useChainCallback'
|
||||
import { DOMWidget } from '@/scripts/domWidget'
|
||||
import { BaseDOMWidget } from '@/scripts/domWidget'
|
||||
import { useDomWidgetStore } from '@/stores/domWidgetStore'
|
||||
import { useCanvasStore } from '@/stores/graphStore'
|
||||
|
||||
@@ -24,7 +25,7 @@ const domWidgetStore = useDomWidgetStore()
|
||||
const widgets = computed(() =>
|
||||
Array.from(
|
||||
domWidgetStore.widgetInstances.values() as Iterable<
|
||||
DOMWidget<HTMLElement, object | string>
|
||||
BaseDOMWidget<string | object>
|
||||
>
|
||||
)
|
||||
)
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
ref="widgetElement"
|
||||
:style="style"
|
||||
v-show="widgetState.visible"
|
||||
/>
|
||||
>
|
||||
<component
|
||||
v-if="isComponentWidget(widget)"
|
||||
:is="widget.component"
|
||||
:modelValue="widget.value"
|
||||
@update:modelValue="emit('update:widgetValue', $event)"
|
||||
:widget="widget"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -15,16 +23,24 @@ import { CSSProperties, computed, onMounted, ref, watch } from 'vue'
|
||||
|
||||
import { useAbsolutePosition } from '@/composables/element/useAbsolutePosition'
|
||||
import { useDomClipping } from '@/composables/element/useDomClipping'
|
||||
import type { DOMWidget } from '@/scripts/domWidget'
|
||||
import {
|
||||
type BaseDOMWidget,
|
||||
isComponentWidget,
|
||||
isDOMWidget
|
||||
} from '@/scripts/domWidget'
|
||||
import { DomWidgetState } from '@/stores/domWidgetStore'
|
||||
import { useCanvasStore } from '@/stores/graphStore'
|
||||
import { useSettingStore } from '@/stores/settingStore'
|
||||
|
||||
const { widget, widgetState } = defineProps<{
|
||||
widget: DOMWidget<HTMLElement, any>
|
||||
widget: BaseDOMWidget<string | object>
|
||||
widgetState: DomWidgetState
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:widgetValue', value: string | object): void
|
||||
}>()
|
||||
|
||||
const widgetElement = ref<HTMLElement>()
|
||||
|
||||
const { style: positionStyle, updatePositionWithTransform } =
|
||||
@@ -92,27 +108,31 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
if (widget.element.blur) {
|
||||
useEventListener(document, 'mousedown', (event) => {
|
||||
if (!widget.element.contains(event.target as HTMLElement)) {
|
||||
widget.element.blur()
|
||||
}
|
||||
})
|
||||
}
|
||||
if (isDOMWidget(widget)) {
|
||||
if (widget.element.blur) {
|
||||
useEventListener(document, 'mousedown', (event) => {
|
||||
if (!widget.element.contains(event.target as HTMLElement)) {
|
||||
widget.element.blur()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
for (const evt of widget.options.selectOn ?? ['focus', 'click']) {
|
||||
useEventListener(widget.element, evt, () => {
|
||||
const lgCanvas = canvasStore.canvas
|
||||
lgCanvas?.selectNode(widget.node)
|
||||
lgCanvas?.bringToFront(widget.node)
|
||||
})
|
||||
for (const evt of widget.options.selectOn ?? ['focus', 'click']) {
|
||||
useEventListener(widget.element, evt, () => {
|
||||
const lgCanvas = canvasStore.canvas
|
||||
lgCanvas?.selectNode(widget.node)
|
||||
lgCanvas?.bringToFront(widget.node)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const inputSpec = widget.node.constructor.nodeData
|
||||
const tooltip = inputSpec?.inputs?.[widget.name]?.tooltip
|
||||
|
||||
onMounted(() => {
|
||||
widgetElement.value.appendChild(widget.element)
|
||||
if (isDOMWidget(widget)) {
|
||||
widgetElement.value.appendChild(widget.element)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
30
src/components/graph/widgets/MultiSelectWidget.vue
Normal file
30
src/components/graph/widgets/MultiSelectWidget.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div>
|
||||
<MultiSelect
|
||||
v-model="selectedItems"
|
||||
:options="options"
|
||||
filter
|
||||
:placeholder="placeholder"
|
||||
:maxSelectedLabels="3"
|
||||
:display="display"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MultiSelect from 'primevue/multiselect'
|
||||
|
||||
import type { ComboInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
import type { ComponentWidget } from '@/scripts/domWidget'
|
||||
|
||||
const selectedItems = defineModel<string[]>({ required: true })
|
||||
const { widget } = defineProps<{
|
||||
widget: ComponentWidget<string[]>
|
||||
}>()
|
||||
|
||||
const inputSpec = widget.inputSpec as ComboInputSpec
|
||||
const options = inputSpec.options ?? []
|
||||
const placeholder = inputSpec.multi_select?.placeholder ?? 'Select items'
|
||||
const display = inputSpec.multi_select?.chip ? 'chip' : 'comma'
|
||||
</script>
|
||||
@@ -1,16 +1,24 @@
|
||||
import type { LGraphNode } from '@comfyorg/litegraph'
|
||||
import type { IComboWidget } from '@comfyorg/litegraph/dist/types/widgets'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import MultiSelectWidget from '@/components/graph/widgets/MultiSelectWidget.vue'
|
||||
import { transformInputSpecV2ToV1 } from '@/schemas/nodeDef/migration'
|
||||
import {
|
||||
ComboInputSpec,
|
||||
type InputSpec,
|
||||
isComboInputSpec
|
||||
} from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
import {
|
||||
type BaseDOMWidget,
|
||||
ComponentWidgetImpl,
|
||||
addWidget
|
||||
} from '@/scripts/domWidget'
|
||||
import {
|
||||
type ComfyWidgetConstructorV2,
|
||||
addValueControlWidgets
|
||||
} from '@/scripts/widgets'
|
||||
import { generateUUID } from '@/utils/formatUtil'
|
||||
|
||||
import { useRemoteWidget } from './useRemoteWidget'
|
||||
|
||||
@@ -21,6 +29,71 @@ const getDefaultValue = (inputSpec: ComboInputSpec) => {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const addMultiSelectWidget = (node: LGraphNode, inputSpec: ComboInputSpec) => {
|
||||
const widgetValue = ref<string[]>([])
|
||||
const widget = new ComponentWidgetImpl({
|
||||
id: generateUUID(),
|
||||
node,
|
||||
name: inputSpec.name,
|
||||
component: MultiSelectWidget,
|
||||
inputSpec,
|
||||
options: {
|
||||
getValue: () => widgetValue.value,
|
||||
setValue: (value: string[]) => {
|
||||
widgetValue.value = value
|
||||
}
|
||||
}
|
||||
})
|
||||
addWidget(node, widget as BaseDOMWidget<object | string>)
|
||||
// TODO: Add remote support to multi-select widget
|
||||
// https://github.com/Comfy-Org/ComfyUI_frontend/issues/3003
|
||||
return widget
|
||||
}
|
||||
|
||||
const addComboWidget = (node: LGraphNode, inputSpec: ComboInputSpec) => {
|
||||
const defaultValue = getDefaultValue(inputSpec)
|
||||
const comboOptions = inputSpec.options ?? []
|
||||
const widget = node.addWidget(
|
||||
'combo',
|
||||
inputSpec.name,
|
||||
defaultValue,
|
||||
() => {},
|
||||
{
|
||||
values: comboOptions
|
||||
}
|
||||
) as IComboWidget
|
||||
|
||||
if (inputSpec.remote) {
|
||||
const remoteWidget = useRemoteWidget({
|
||||
remoteConfig: inputSpec.remote,
|
||||
defaultValue,
|
||||
node,
|
||||
widget
|
||||
})
|
||||
if (inputSpec.remote.refresh_button) remoteWidget.addRefreshButton()
|
||||
|
||||
const origOptions = widget.options
|
||||
widget.options = new Proxy(origOptions as Record<string | symbol, any>, {
|
||||
get(target, prop: string | symbol) {
|
||||
if (prop !== 'values') return target[prop]
|
||||
return remoteWidget.getValue()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (inputSpec.control_after_generate) {
|
||||
widget.linkedWidgets = addValueControlWidgets(
|
||||
node,
|
||||
widget,
|
||||
undefined,
|
||||
undefined,
|
||||
transformInputSpecV2ToV1(inputSpec)
|
||||
)
|
||||
}
|
||||
|
||||
return widget
|
||||
}
|
||||
|
||||
export const useComboWidget = () => {
|
||||
const widgetConstructor: ComfyWidgetConstructorV2 = (
|
||||
node: LGraphNode,
|
||||
@@ -29,48 +102,9 @@ export const useComboWidget = () => {
|
||||
if (!isComboInputSpec(inputSpec)) {
|
||||
throw new Error(`Invalid input data: ${inputSpec}`)
|
||||
}
|
||||
|
||||
const comboOptions = inputSpec.options ?? []
|
||||
const defaultValue = getDefaultValue(inputSpec)
|
||||
|
||||
const widget = node.addWidget(
|
||||
'combo',
|
||||
inputSpec.name,
|
||||
defaultValue,
|
||||
() => {},
|
||||
{
|
||||
values: comboOptions
|
||||
}
|
||||
) as IComboWidget
|
||||
|
||||
if (inputSpec.remote) {
|
||||
const remoteWidget = useRemoteWidget({
|
||||
remoteConfig: inputSpec.remote,
|
||||
defaultValue,
|
||||
node,
|
||||
widget
|
||||
})
|
||||
if (inputSpec.remote.refresh_button) remoteWidget.addRefreshButton()
|
||||
|
||||
const origOptions = widget.options
|
||||
widget.options = new Proxy(origOptions as Record<string | symbol, any>, {
|
||||
get(target, prop: string | symbol) {
|
||||
if (prop !== 'values') return target[prop]
|
||||
return remoteWidget.getValue()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (inputSpec.control_after_generate) {
|
||||
widget.linkedWidgets = addValueControlWidgets(
|
||||
node,
|
||||
widget,
|
||||
undefined,
|
||||
undefined,
|
||||
transformInputSpecV2ToV1(inputSpec)
|
||||
)
|
||||
}
|
||||
return widget
|
||||
return inputSpec.multi_select
|
||||
? addMultiSelectWidget(node, inputSpec)
|
||||
: addComboWidget(node, inputSpec)
|
||||
}
|
||||
|
||||
return widgetConstructor
|
||||
|
||||
@@ -12,6 +12,10 @@ const zRemoteWidgetConfig = z.object({
|
||||
timeout: z.number().gte(0).optional(),
|
||||
max_retries: z.number().gte(0).optional()
|
||||
})
|
||||
const zMultiSelectOption = z.object({
|
||||
placeholder: z.string().optional(),
|
||||
chip: z.boolean().optional()
|
||||
})
|
||||
|
||||
export const zBaseInputOptions = z
|
||||
.object({
|
||||
@@ -72,7 +76,9 @@ export const zComboInputOptions = zBaseInputOptions.extend({
|
||||
allow_batch: z.boolean().optional(),
|
||||
video_upload: z.boolean().optional(),
|
||||
options: z.array(zComboOption).optional(),
|
||||
remote: zRemoteWidgetConfig.optional()
|
||||
remote: zRemoteWidgetConfig.optional(),
|
||||
/** Whether the widget is a multi-select widget. */
|
||||
multi_select: zMultiSelectOption.optional()
|
||||
})
|
||||
|
||||
const zIntInputSpec = z.tuple([z.literal('INT'), zIntInputOptions.optional()])
|
||||
|
||||
@@ -5,47 +5,64 @@ import type {
|
||||
IWidgetOptions
|
||||
} from '@comfyorg/litegraph/dist/types/widgets'
|
||||
import _ from 'lodash'
|
||||
import { type Component, toRaw } from 'vue'
|
||||
|
||||
import { useChainCallback } from '@/composables/functional/useChainCallback'
|
||||
import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
import { useDomWidgetStore } from '@/stores/domWidgetStore'
|
||||
import { generateUUID } from '@/utils/formatUtil'
|
||||
|
||||
export interface DOMWidget<T extends HTMLElement, V extends object | string>
|
||||
export interface BaseDOMWidget<V extends object | string>
|
||||
extends ICustomWidget {
|
||||
// ICustomWidget properties
|
||||
type: 'custom'
|
||||
element: T
|
||||
options: DOMWidgetOptions<T, V>
|
||||
options: DOMWidgetOptions<V>
|
||||
value: V
|
||||
/**
|
||||
* @deprecated Legacy property used by some extensions for customtext
|
||||
* (textarea) widgets. Use `element` instead as it provides the same
|
||||
* functionality and works for all DOMWidget types.
|
||||
*/
|
||||
inputEl?: T
|
||||
callback?: (value: V) => void
|
||||
// DOMWidget properties
|
||||
|
||||
// BaseDOMWidget properties
|
||||
/** The unique ID of the widget. */
|
||||
id: string
|
||||
readonly id: string
|
||||
/** The node that the widget belongs to. */
|
||||
node: LGraphNode
|
||||
readonly node: LGraphNode
|
||||
/** Whether the widget is visible. */
|
||||
isVisible(): boolean
|
||||
}
|
||||
|
||||
export interface DOMWidgetOptions<
|
||||
T extends HTMLElement,
|
||||
V extends object | string
|
||||
> extends IWidgetOptions {
|
||||
/**
|
||||
* A DOM widget that wraps a custom HTML element as a litegraph widget.
|
||||
*/
|
||||
export interface DOMWidget<T extends HTMLElement, V extends object | string>
|
||||
extends BaseDOMWidget<V> {
|
||||
element: T
|
||||
/**
|
||||
* @deprecated Legacy property used by some extensions for customtext
|
||||
* (textarea) widgets. Use {@link element} instead as it provides the same
|
||||
* functionality and works for all DOMWidget types.
|
||||
*/
|
||||
inputEl?: T
|
||||
}
|
||||
|
||||
/**
|
||||
* A DOM widget that wraps a Vue component as a litegraph widget.
|
||||
*/
|
||||
export interface ComponentWidget<V extends object | string>
|
||||
extends BaseDOMWidget<V> {
|
||||
readonly component: Component
|
||||
readonly inputSpec: InputSpec
|
||||
}
|
||||
|
||||
export interface DOMWidgetOptions<V extends object | string>
|
||||
extends IWidgetOptions {
|
||||
hideOnZoom?: boolean
|
||||
selectOn?: string[]
|
||||
onHide?: (widget: DOMWidget<T, V>) => void
|
||||
onHide?: (widget: BaseDOMWidget<V>) => void
|
||||
getValue?: () => V
|
||||
setValue?: (value: V) => void
|
||||
getMinHeight?: () => number
|
||||
getMaxHeight?: () => number
|
||||
getHeight?: () => string | number
|
||||
onDraw?: (widget: DOMWidget<T, V>) => void
|
||||
onDraw?: (widget: BaseDOMWidget<V>) => void
|
||||
/**
|
||||
* @deprecated Use `afterResize` instead. This callback is a legacy API
|
||||
* that fires before resize happens, but it is no longer supported. Now it
|
||||
@@ -53,21 +70,24 @@ export interface DOMWidgetOptions<
|
||||
* The resize logic has been upstreamed to litegraph in
|
||||
* https://github.com/Comfy-Org/ComfyUI_frontend/pull/2557
|
||||
*/
|
||||
beforeResize?: (this: DOMWidget<T, V>, node: LGraphNode) => void
|
||||
afterResize?: (this: DOMWidget<T, V>, node: LGraphNode) => void
|
||||
beforeResize?: (this: BaseDOMWidget<V>, node: LGraphNode) => void
|
||||
afterResize?: (this: BaseDOMWidget<V>, node: LGraphNode) => void
|
||||
}
|
||||
|
||||
export const isDOMWidget = <T extends HTMLElement, V extends object | string>(
|
||||
widget: IWidget
|
||||
): widget is DOMWidget<T, V> => 'element' in widget && !!widget.element
|
||||
|
||||
export class DOMWidgetImpl<T extends HTMLElement, V extends object | string>
|
||||
implements DOMWidget<T, V>
|
||||
export const isComponentWidget = <V extends object | string>(
|
||||
widget: IWidget
|
||||
): widget is ComponentWidget<V> => 'component' in widget && !!widget.component
|
||||
|
||||
abstract class BaseDOMWidgetImpl<V extends object | string>
|
||||
implements BaseDOMWidget<V>
|
||||
{
|
||||
readonly type: 'custom'
|
||||
readonly name: string
|
||||
readonly element: T
|
||||
readonly options: DOMWidgetOptions<T, V>
|
||||
readonly options: DOMWidgetOptions<V>
|
||||
computedHeight?: number
|
||||
y: number = 0
|
||||
callback?: (value: V) => void
|
||||
@@ -80,13 +100,11 @@ export class DOMWidgetImpl<T extends HTMLElement, V extends object | string>
|
||||
node: LGraphNode
|
||||
name: string
|
||||
type: string
|
||||
element: T
|
||||
options: DOMWidgetOptions<T, V>
|
||||
options: DOMWidgetOptions<V>
|
||||
}) {
|
||||
// @ts-expect-error custom widget type
|
||||
this.type = obj.type
|
||||
this.name = obj.name
|
||||
this.element = obj.element
|
||||
this.options = obj.options
|
||||
|
||||
this.id = obj.id
|
||||
@@ -102,6 +120,42 @@ export class DOMWidgetImpl<T extends HTMLElement, V extends object | string>
|
||||
this.callback?.(this.value)
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
return (
|
||||
!_.isNil(this.computedHeight) &&
|
||||
this.computedHeight > 0 &&
|
||||
!['converted-widget', 'hidden'].includes(this.type) &&
|
||||
!this.node.collapsed
|
||||
)
|
||||
}
|
||||
|
||||
draw(): void {
|
||||
this.options.onDraw?.(this)
|
||||
}
|
||||
|
||||
onRemove(): void {
|
||||
useDomWidgetStore().unregisterWidget(this.id)
|
||||
}
|
||||
}
|
||||
|
||||
export class DOMWidgetImpl<T extends HTMLElement, V extends object | string>
|
||||
extends BaseDOMWidgetImpl<V>
|
||||
implements DOMWidget<T, V>
|
||||
{
|
||||
readonly element: T
|
||||
|
||||
constructor(obj: {
|
||||
id: string
|
||||
node: LGraphNode
|
||||
name: string
|
||||
type: string
|
||||
element: T
|
||||
options: DOMWidgetOptions<V>
|
||||
}) {
|
||||
super(obj)
|
||||
this.element = obj.element
|
||||
}
|
||||
|
||||
/** Extract DOM widget size info */
|
||||
computeLayoutSize(node: LGraphNode) {
|
||||
// @ts-expect-error custom widget type
|
||||
@@ -144,25 +198,63 @@ export class DOMWidgetImpl<T extends HTMLElement, V extends object | string>
|
||||
minWidth: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
return (
|
||||
!_.isNil(this.computedHeight) &&
|
||||
this.computedHeight > 0 &&
|
||||
!['converted-widget', 'hidden'].includes(this.type) &&
|
||||
!this.node.collapsed
|
||||
)
|
||||
export class ComponentWidgetImpl<V extends object | string>
|
||||
extends BaseDOMWidgetImpl<V>
|
||||
implements ComponentWidget<V>
|
||||
{
|
||||
readonly component: Component
|
||||
readonly inputSpec: InputSpec
|
||||
|
||||
constructor(obj: {
|
||||
id: string
|
||||
node: LGraphNode
|
||||
name: string
|
||||
component: Component
|
||||
inputSpec: InputSpec
|
||||
options: DOMWidgetOptions<V>
|
||||
}) {
|
||||
super({
|
||||
...obj,
|
||||
type: 'custom'
|
||||
})
|
||||
this.component = obj.component
|
||||
this.inputSpec = obj.inputSpec
|
||||
}
|
||||
|
||||
draw(): void {
|
||||
this.options.onDraw?.(this)
|
||||
computeLayoutSize() {
|
||||
const minHeight = this.options.getMinHeight?.() ?? 50
|
||||
const maxHeight = this.options.getMaxHeight?.()
|
||||
return {
|
||||
minHeight,
|
||||
maxHeight,
|
||||
minWidth: 0
|
||||
}
|
||||
}
|
||||
|
||||
onRemove(): void {
|
||||
useDomWidgetStore().unregisterWidget(this.id)
|
||||
serializeValue(): V {
|
||||
return toRaw(this.value)
|
||||
}
|
||||
}
|
||||
|
||||
export const addWidget = <W extends BaseDOMWidget<object | string>>(
|
||||
node: LGraphNode,
|
||||
widget: W
|
||||
) => {
|
||||
node.addCustomWidget(widget)
|
||||
node.onRemoved = useChainCallback(node.onRemoved, () => {
|
||||
widget.onRemove?.()
|
||||
})
|
||||
|
||||
node.onResize = useChainCallback(node.onResize, () => {
|
||||
widget.options.beforeResize?.call(widget, node)
|
||||
widget.options.afterResize?.call(widget, node)
|
||||
})
|
||||
|
||||
useDomWidgetStore().registerWidget(widget)
|
||||
}
|
||||
|
||||
LGraphNode.prototype.addDOMWidget = function <
|
||||
T extends HTMLElement,
|
||||
V extends object | string
|
||||
@@ -171,22 +263,18 @@ LGraphNode.prototype.addDOMWidget = function <
|
||||
name: string,
|
||||
type: string,
|
||||
element: T,
|
||||
options: DOMWidgetOptions<T, V> = {}
|
||||
options: DOMWidgetOptions<V> = {}
|
||||
): DOMWidget<T, V> {
|
||||
const widget = new DOMWidgetImpl({
|
||||
id: generateUUID(),
|
||||
node: this,
|
||||
name,
|
||||
type,
|
||||
element,
|
||||
options: { hideOnZoom: true, ...options }
|
||||
})
|
||||
// Note: Before `LGraphNode.configure` is called, `this.id` is always `-1`.
|
||||
const widget = this.addCustomWidget(
|
||||
new DOMWidgetImpl({
|
||||
id: generateUUID(),
|
||||
node: this,
|
||||
name,
|
||||
type,
|
||||
element,
|
||||
options: {
|
||||
hideOnZoom: true,
|
||||
...options
|
||||
}
|
||||
})
|
||||
)
|
||||
addWidget(this, widget as unknown as BaseDOMWidget<object | string>)
|
||||
|
||||
// Workaround for https://github.com/Comfy-Org/ComfyUI_frontend/issues/2493
|
||||
// Some custom nodes are explicitly expecting getter and setter of `value`
|
||||
@@ -201,16 +289,5 @@ LGraphNode.prototype.addDOMWidget = function <
|
||||
}
|
||||
})
|
||||
|
||||
this.onRemoved = useChainCallback(this.onRemoved, () => {
|
||||
widget.onRemove()
|
||||
})
|
||||
|
||||
this.onResize = useChainCallback(this.onResize, () => {
|
||||
options.beforeResize?.call(widget, this)
|
||||
options.afterResize?.call(widget, this)
|
||||
})
|
||||
|
||||
useDomWidgetStore().registerWidget(widget)
|
||||
|
||||
return widget
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { defineStore } from 'pinia'
|
||||
import { markRaw, ref } from 'vue'
|
||||
|
||||
import type { PositionConfig } from '@/composables/element/useAbsolutePosition'
|
||||
import type { DOMWidget } from '@/scripts/domWidget'
|
||||
import type { BaseDOMWidget } from '@/scripts/domWidget'
|
||||
|
||||
export interface DomWidgetState extends PositionConfig {
|
||||
visible: boolean
|
||||
@@ -18,17 +18,15 @@ export const useDomWidgetStore = defineStore('domWidget', () => {
|
||||
|
||||
// Map to reference actual widget instances
|
||||
// Widgets are stored as raw values to avoid reactivity issues
|
||||
const widgetInstances = ref(
|
||||
new Map<string, DOMWidget<HTMLElement, object | string>>()
|
||||
)
|
||||
const widgetInstances = ref(new Map<string, BaseDOMWidget<object | string>>())
|
||||
|
||||
// Register a widget with the store
|
||||
const registerWidget = <T extends HTMLElement, V extends object | string>(
|
||||
widget: DOMWidget<T, V>
|
||||
const registerWidget = <V extends object | string>(
|
||||
widget: BaseDOMWidget<V>
|
||||
) => {
|
||||
widgetInstances.value.set(
|
||||
widget.id,
|
||||
markRaw(widget as unknown as DOMWidget<HTMLElement, object | string>)
|
||||
markRaw(widget) as unknown as BaseDOMWidget<object | string>
|
||||
)
|
||||
widgetStates.value.set(widget.id, {
|
||||
visible: true,
|
||||
|
||||
2
src/types/litegraph-augmentation.d.ts
vendored
2
src/types/litegraph-augmentation.d.ts
vendored
@@ -134,7 +134,7 @@ declare module '@comfyorg/litegraph' {
|
||||
name: string,
|
||||
type: string,
|
||||
element: T,
|
||||
options?: DOMWidgetOptions<T, V>
|
||||
options?: DOMWidgetOptions<V>
|
||||
): DOMWidget<T, V>
|
||||
|
||||
animatedImages?: boolean
|
||||
|
||||
Reference in New Issue
Block a user