Node library custom bookmark folder (#631)

* Add new folder button

* Add tree util test

* nit

* Support empty folder in node library

* Drag to bookmark folder

* Use bookmark icon for bookmark folder

* Highlight on dragover

* nit

* Auto-expand on item added

* Extract bookmark system as store

* Add context menu on bookmark folder

* Add editable text

* Fix reactivity

* Plumb editable text

* refactor

* Rename node

* Fix focus

* Prevent name collision

* nit

* Add new folder

* nested folder support

* Change drag behavior

* Add basic playwright tests

* nit

* Target tree-node-content instead of tree-node
This commit is contained in:
Chenlei Hu
2024-08-25 21:53:58 -04:00
committed by GitHub
parent f36c934d37
commit 090fda2f22
10 changed files with 692 additions and 82 deletions

View File

@@ -0,0 +1,90 @@
<template>
<div
:class="[
'node-tree-folder',
{ bookmark: props.isBookmarkFolder, 'can-drop': canDrop }
]"
ref="container"
>
<span class="folder-label">
<slot name="folder-label" :node="props.node">
{{ props.node.label }}
</slot>
</span>
<Badge
:value="props.node.totalNodes"
severity="secondary"
:style="{ marginLeft: '0.5rem' }"
/>
</div>
</template>
<script setup lang="ts">
import { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
import { useNodeBookmarkStore } from '@/stores/nodeBookmarkStore'
import type { CanvasDragAndDropData } from '@/types/litegraphTypes'
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'
import Badge from 'primevue/badge'
import type { TreeNode } from 'primevue/treenode'
import { onMounted, onUnmounted, ref } from 'vue'
const props = defineProps<{
node: TreeNode
isBookmarkFolder: boolean
}>()
const emit = defineEmits<{
(e: 'itemDropped', node: TreeNode): void
}>()
const nodeBookmarkStore = useNodeBookmarkStore()
const addNodeToBookmarkFolder = (node: ComfyNodeDefImpl) => {
if (!props.node.data) {
console.error('Bookmark folder does not have data!')
return
}
if (nodeBookmarkStore.isBookmarked(node)) {
nodeBookmarkStore.toggleBookmark(node)
}
const folderNodeDef = props.node.data as ComfyNodeDefImpl
const nodePath = folderNodeDef.category + '/' + node.display_name
nodeBookmarkStore.addBookmark(nodePath)
}
const container = ref<HTMLElement | null>(null)
const canDrop = ref(false)
let dropTargetCleanup = () => {}
onMounted(() => {
if (!props.isBookmarkFolder) return
const treeNodeElement = container.value?.closest(
'.p-tree-node-content'
) as HTMLElement
dropTargetCleanup = dropTargetForElements({
element: treeNodeElement,
onDrop: (event) => {
const dndData = event.source.data as CanvasDragAndDropData
if (dndData.type === 'add-node') {
addNodeToBookmarkFolder(dndData.data)
canDrop.value = false
emit('itemDropped', props.node)
}
},
onDragEnter: (event) => {
const dndData = event.source.data as CanvasDragAndDropData
if (dndData.type === 'add-node') {
canDrop.value = true
}
},
onDragLeave: (event) => {
canDrop.value = false
}
})
})
onUnmounted(() => {
dropTargetCleanup()
})
</script>