mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-07 22:20:03 +00:00
Manage style of DOM widgets in Vue (#2946)
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user