Manage style of DOM widgets in Vue (#2946)

This commit is contained in:
Chenlei Hu
2025-03-09 16:51:42 -04:00
committed by GitHub
parent 97d9f90374
commit 3a0b337d0c
6 changed files with 339 additions and 167 deletions

View File

@@ -1,15 +1,24 @@
<template>
<div>
<DomWidget v-for="widget in widgets" :key="widget.id" :widget="widget" />
<!-- Create a new stacking context for widgets to avoid z-index issues -->
<div class="isolate">
<DomWidget
v-for="widget in widgets"
:key="widget.id"
:widget="widget"
:widget-state="domWidgetStore.widgetStates.get(widget.id)"
/>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import type { LGraphNode } from '@comfyorg/litegraph'
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 { useDomWidgetStore } from '@/stores/domWidgetStore'
import { useCanvasStore } from '@/stores/graphStore'
const domWidgetStore = useDomWidgetStore()
const widgets = computed(() =>
@@ -19,4 +28,47 @@ const widgets = computed(() =>
>
)
)
const MARGIN = 10
const updateWidgets = () => {
const lgCanvas = canvasStore.canvas
if (!lgCanvas) return
const lowQuality = lgCanvas.low_quality
for (const widget of domWidgetStore.widgetInstances.values()) {
const node = widget.node as LGraphNode
const widgetState = domWidgetStore.widgetStates.get(widget.id)
if (!widgetState) continue
widgetState.visible =
lgCanvas.isNodeVisible(node) &&
!(widget.options.hideOnZoom && lowQuality) &&
widget.isVisible()
widgetState.pos = [node.pos[0] + MARGIN, node.pos[1] + MARGIN + widget.y]
widgetState.size = [
(widget.width ?? node.width) - MARGIN * 2,
(widget.computedHeight ?? 50) - MARGIN * 2
]
// TODO: optimize this logic as it's O(n), where n is the number of nodes
widgetState.zIndex = lgCanvas.graph.nodes.indexOf(node)
widgetState.readonly = lgCanvas.read_only
}
}
const canvasStore = useCanvasStore()
watch(
() => canvasStore.canvas,
(lgCanvas) => {
if (!lgCanvas) return
lgCanvas.onDrawForeground = useChainCallback(
lgCanvas.onDrawForeground,
() => {
updateWidgets()
}
)
}
)
</script>

View File

@@ -1,19 +1,102 @@
<template>
<div ref="widgetElement" />
<div
class="dom-widget"
ref="widgetElement"
:style="style"
v-show="widgetState.visible"
/>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import type { LGraphNode } from '@comfyorg/litegraph'
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 { DomWidgetState } from '@/stores/domWidgetStore'
import { useCanvasStore } from '@/stores/graphStore'
import { useSettingStore } from '@/stores/settingStore'
const { widget } = defineProps<{
const { widget, widgetState } = defineProps<{
widget: DOMWidget<HTMLElement, any>
widgetState: DomWidgetState
}>()
const widgetElement = ref<HTMLElement>()
const { style: positionStyle, updatePositionWithTransform } =
useAbsolutePosition()
const { style: clippingStyle, updateClipPath } = useDomClipping()
const style = computed<CSSProperties>(() => ({
...positionStyle.value,
...(enableDomClipping.value ? clippingStyle.value : {}),
zIndex: widgetState.zIndex,
pointerEvents: widgetState.readonly ? 'none' : 'auto'
}))
const canvasStore = useCanvasStore()
const settingStore = useSettingStore()
const enableDomClipping = computed(() =>
settingStore.get('Comfy.DOMClippingEnabled')
)
const updateDomClipping = () => {
const lgCanvas = canvasStore.canvas
const selectedNode = Object.values(
lgCanvas.selected_nodes ?? {}
)[0] as LGraphNode
const node = widget.node
const isSelected = selectedNode === node
const renderArea = selectedNode?.renderArea
const offset = lgCanvas.ds.offset
const scale = lgCanvas.ds.scale
const selectedAreaConfig = renderArea
? {
x: renderArea[0],
y: renderArea[1],
width: renderArea[2],
height: renderArea[3],
scale,
offset: [offset[0], offset[1]] as [number, number]
}
: undefined
updateClipPath(
widgetElement.value,
lgCanvas.canvas,
isSelected,
selectedAreaConfig
)
}
watch(
() => widgetState,
(newState) => {
updatePositionWithTransform(newState)
if (enableDomClipping.value) {
updateDomClipping()
}
},
{ deep: true }
)
watch(
() => widgetState.visible,
(newVisible, oldVisible) => {
if (!newVisible && oldVisible) {
widget.options.onHide?.(widget)
}
}
)
onMounted(() => {
widgetElement.value.appendChild(widget.element)
})
</script>
<style scoped>
.dom-widget > * {
@apply h-full w-full;
}
</style>