Refactor node library drag and drop (#617)

This commit is contained in:
Chenlei Hu
2024-08-24 17:40:56 -04:00
committed by GitHub
parent f4242f8a66
commit bff1dc91fa
5 changed files with 45 additions and 119 deletions

View File

@@ -20,7 +20,7 @@ import { ref, computed, onUnmounted, watch, onMounted, watchEffect } from 'vue'
import { app as comfyApp } from '@/scripts/app'
import { useSettingStore } from '@/stores/settingStore'
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'
import { useNodeDefStore } from '@/stores/nodeDefStore'
import { ComfyNodeDefImpl, useNodeDefStore } from '@/stores/nodeDefStore'
import { useWorkspaceStore } from '@/stores/workspaceStateStore'
import {
LiteGraph,
@@ -113,14 +113,18 @@ onMounted(async () => {
element: canvasRef.value,
onDrop: (event) => {
const loc = event.location.current.input
// Add an offset on x to make sure after adding the node, the cursor
// is on the node (top left corner)
const pos = comfyApp.clientPosToCanvasPos([loc.clientX - 20, loc.clientY])
const comfyNodeName = event.source.element.getAttribute(
'data-comfy-node-name'
)
const nodeDef = nodeDefStore.nodeDefsByName[comfyNodeName]
comfyApp.addNodeOnGraph(nodeDef, { pos })
const dndData = event.source.data
if (dndData.type === 'add-node') {
const nodeDef = dndData.data as ComfyNodeDefImpl
// Add an offset on x to make sure after adding the node, the cursor
// is on the node (top left corner)
const pos = comfyApp.clientPosToCanvasPos([
loc.clientX - 20,
loc.clientY
])
comfyApp.addNodeOnGraph(nodeDef, { pos })
}
}
})
emit('ready')

View File

@@ -1,106 +0,0 @@
<!-- Tree with all leaf nodes draggable -->
<script>
import Tree from 'primevue/tree'
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'
import { h, onMounted, onBeforeUnmount, computed } from 'vue'
export default {
name: 'TreePlus',
extends: Tree,
props: {
dragSelector: {
type: String,
default: '.p-tree-node'
},
// Explicitly declare all v-model props
expandedKeys: {
type: Object,
default: () => ({})
},
selectionKeys: {
type: Object,
default: () => ({})
}
},
emits: ['update:expandedKeys', 'update:selectionKeys'],
setup(props, context) {
// Create computed properties for each v-model prop
const computedExpandedKeys = computed({
get: () => props.expandedKeys,
set: (value) => context.emit('update:expandedKeys', value)
})
const computedSelectionKeys = computed({
get: () => props.selectionKeys,
set: (value) => context.emit('update:selectionKeys', value)
})
let observer = null
const makeDraggable = (element) => {
if (!element._draggableCleanup) {
element._draggableCleanup = draggable({
element
})
}
}
const observeTreeChanges = (treeElement) => {
observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
node.querySelectorAll(props.dragSelector).forEach(makeDraggable)
}
})
}
})
})
observer.observe(treeElement, { childList: true, subtree: true })
// Make existing nodes draggable
treeElement.querySelectorAll(props.dragSelector).forEach(makeDraggable)
}
onMounted(() => {
const treeElement = document.querySelector('.p-tree')
if (treeElement) {
observeTreeChanges(treeElement)
}
})
onBeforeUnmount(() => {
if (observer) {
observer.disconnect()
}
// Clean up draggable instances if necessary
const treeElement = document.querySelector('.p-tree')
if (treeElement) {
treeElement.querySelectorAll(props.dragSelector).forEach((node) => {
if (node._draggableCleanup) {
node._draggableCleanup()
}
})
}
})
return () =>
h(
Tree,
{
...context.attrs,
...props,
expandedKeys: computedExpandedKeys.value,
selectionKeys: computedSelectionKeys.value,
'onUpdate:expandedKeys': (value) =>
(computedExpandedKeys.value = value),
'onUpdate:selectionKeys': (value) =>
(computedSelectionKeys.value = value)
},
context.slots
)
}
}
</script>

View File

@@ -20,7 +20,7 @@
@search="handleSearch"
:placeholder="$t('searchNodes') + '...'"
/>
<TreePlus
<Tree
class="node-lib-tree"
v-model:expandedKeys="expandedKeys"
selectionMode="single"
@@ -62,7 +62,7 @@
@toggleBookmark="toggleBookmark(node.data.display_name)"
/>
</template>
</TreePlus>
</Tree>
<div
v-if="hoveredComfyNode"
class="node-lib-node-preview"
@@ -89,7 +89,7 @@ import {
import { computed, ref, nextTick } from 'vue'
import type { TreeNode } from 'primevue/treenode'
import NodeTreeLeaf from './nodeLibrary/NodeTreeLeaf.vue'
import TreePlus from '@/components/primevueOverride/TreePlus.vue'
import Tree from 'primevue/tree'
import NodePreview from '@/components/node/NodePreview.vue'
import SearchBox from '@/components/common/SearchBox.vue'
import SidebarTabTemplate from '@/components/sidebar/tabs/SidebarTabTemplate.vue'

View File

@@ -1,5 +1,5 @@
<template>
<div class="node-tree-leaf">
<div class="node-tree-leaf" ref="container">
<div class="node-content">
<Tag
v-if="node.experimental"
@@ -24,6 +24,9 @@
import Button from 'primevue/button'
import Tag from 'primevue/tag'
import { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
import { onMounted, onUnmounted, ref } from 'vue'
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'
import { CanvasDragAndDropData } from '@/types/litegraphTypes'
const props = defineProps<{
node: ComfyNodeDefImpl
@@ -37,6 +40,26 @@ const emit = defineEmits<{
const toggleBookmark = () => {
emit('toggle-bookmark', props.node)
}
const container = ref<HTMLElement | null>(null)
let draggableCleanup: () => void
onMounted(() => {
const treeNodeElement = container.value?.closest(
'.p-tree-node'
) as HTMLElement
draggableCleanup = draggable({
element: treeNodeElement,
getInitialData() {
return {
type: 'add-node',
data: props.node
} as CanvasDragAndDropData<ComfyNodeDefImpl>
}
})
})
onUnmounted(() => {
draggableCleanup()
})
</script>
<style scoped>

View File

@@ -73,3 +73,8 @@ export class ConnectingLinkImpl implements ConnectingLink {
}
}
}
export type CanvasDragAndDropData<T = any> = {
type: 'add-node'
data: T
}