mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-25 09:14:25 +00:00
Add sort button in node library sidebar tab (#259)
* Add sort button on node library * tab template
This commit is contained in:
@@ -1,63 +1,83 @@
|
||||
<template>
|
||||
<TreePlus
|
||||
class="node-lib-tree"
|
||||
v-model:expandedKeys="expandedKeys"
|
||||
selectionMode="single"
|
||||
:value="renderedRoot.children"
|
||||
:filter="true"
|
||||
filterMode="lenient"
|
||||
dragSelector=".p-tree-node-leaf"
|
||||
:pt="{
|
||||
nodeLabel: 'node-lib-tree-node-label',
|
||||
nodeChildren: ({ props }) => ({
|
||||
'data-comfy-node-name': props.node?.data?.name,
|
||||
onMouseenter: (event: MouseEvent) => {
|
||||
hoveredComfyNodeName = props.node?.data?.name
|
||||
<SideBarTabTemplate :title="$t('sideToolBar.nodeLibrary')">
|
||||
<template #tool-buttons>
|
||||
<ToggleButton
|
||||
v-model:model-value="alphabeticalSort"
|
||||
on-icon="pi pi-sort-alpha-down"
|
||||
off-icon="pi pi-sort-alt"
|
||||
aria-label="Sort"
|
||||
:pt="{
|
||||
label: { style: { display: 'none' } }
|
||||
}"
|
||||
v-tooltip="$t('sideToolBar.nodeLibraryTab.sortOrder')"
|
||||
>
|
||||
</ToggleButton>
|
||||
</template>
|
||||
<template #body>
|
||||
<TreePlus
|
||||
class="node-lib-tree"
|
||||
v-model:expandedKeys="expandedKeys"
|
||||
selectionMode="single"
|
||||
:value="renderedRoot.children"
|
||||
:filter="true"
|
||||
filterMode="lenient"
|
||||
dragSelector=".p-tree-node-leaf"
|
||||
:pt="{
|
||||
nodeLabel: 'node-lib-tree-node-label',
|
||||
nodeChildren: ({ props }) => ({
|
||||
'data-comfy-node-name': props.node?.data?.name,
|
||||
onMouseenter: (event: MouseEvent) => {
|
||||
hoveredComfyNodeName = props.node?.data?.name
|
||||
|
||||
const hoverTarget = event.target as HTMLElement
|
||||
const targetRect = hoverTarget.getBoundingClientRect()
|
||||
nodePreviewStyle.top = `${targetRect.top - 40}px`
|
||||
nodePreviewStyle.left = `${targetRect.right}px`
|
||||
},
|
||||
onMouseleave: () => {
|
||||
hoveredComfyNodeName = null
|
||||
}
|
||||
})
|
||||
}"
|
||||
>
|
||||
<template #folder="{ node }">
|
||||
<span class="folder-label">{{ node.label }}</span>
|
||||
<Badge
|
||||
:value="node.totalNodes"
|
||||
severity="secondary"
|
||||
:style="{ marginLeft: '0.5rem' }"
|
||||
></Badge>
|
||||
const hoverTarget = event.target as HTMLElement
|
||||
const targetRect = hoverTarget.getBoundingClientRect()
|
||||
nodePreviewStyle.top = `${targetRect.top - 40}px`
|
||||
nodePreviewStyle.left = `${targetRect.right}px`
|
||||
},
|
||||
onMouseleave: () => {
|
||||
hoveredComfyNodeName = null
|
||||
}
|
||||
})
|
||||
}"
|
||||
>
|
||||
<template #folder="{ node }">
|
||||
<span class="folder-label">{{ node.label }}</span>
|
||||
<Badge
|
||||
:value="node.totalNodes"
|
||||
severity="secondary"
|
||||
:style="{ marginLeft: '0.5rem' }"
|
||||
></Badge>
|
||||
</template>
|
||||
<template #node="{ node }">
|
||||
<span class="node-label">{{ node.label }}</span>
|
||||
</template>
|
||||
</TreePlus>
|
||||
<div
|
||||
v-if="hoveredComfyNode"
|
||||
class="node-lib-node-preview"
|
||||
:style="nodePreviewStyle"
|
||||
>
|
||||
<NodePreview
|
||||
:key="hoveredComfyNode.name"
|
||||
:nodeDef="hoveredComfyNode"
|
||||
></NodePreview>
|
||||
</div>
|
||||
</template>
|
||||
<template #node="{ node }">
|
||||
<span class="node-label">{{ node.label }}</span>
|
||||
</template>
|
||||
</TreePlus>
|
||||
<div
|
||||
v-if="hoveredComfyNode"
|
||||
class="node-lib-node-preview"
|
||||
:style="nodePreviewStyle"
|
||||
>
|
||||
<NodePreview
|
||||
:key="hoveredComfyNode.name"
|
||||
:nodeDef="hoveredComfyNode"
|
||||
></NodePreview>
|
||||
</div>
|
||||
</SideBarTabTemplate>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Badge from 'primevue/badge'
|
||||
import ToggleButton from 'primevue/togglebutton'
|
||||
import { ComfyNodeDefImpl, useNodeDefStore } from '@/stores/nodeDefStore'
|
||||
import { computed, ref } from 'vue'
|
||||
import { TreeNode } from 'primevue/treenode'
|
||||
import TreePlus from '@/components/primevueOverride/TreePlus.vue'
|
||||
import NodePreview from '@/components/NodePreview.vue'
|
||||
import SideBarTabTemplate from '@/components/sidebar/tabs/SideBarTabTemplate.vue'
|
||||
|
||||
const nodeDefStore = useNodeDefStore()
|
||||
const alphabeticalSort = ref(false)
|
||||
const expandedKeys = ref({})
|
||||
const hoveredComfyNodeName = ref<string | null>(null)
|
||||
const hoveredComfyNode = computed<ComfyNodeDefImpl | null>(() => {
|
||||
@@ -72,7 +92,9 @@ const nodePreviewStyle = ref<Record<string, string>>({
|
||||
left: '0px'
|
||||
})
|
||||
|
||||
const root = computed(() => nodeDefStore.nodeTree)
|
||||
const root = computed(() =>
|
||||
alphabeticalSort.value ? nodeDefStore.sortedNodeTree : nodeDefStore.nodeTree
|
||||
)
|
||||
const renderedRoot = computed(() => {
|
||||
return fillNodeInfo(root.value)
|
||||
})
|
||||
|
||||
65
src/components/sidebar/tabs/SideBarTabTemplate.vue
Normal file
65
src/components/sidebar/tabs/SideBarTabTemplate.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div class="comfy-vue-side-bar-container">
|
||||
<Toolbar class="comfy-vue-side-bar-header">
|
||||
<template #start>
|
||||
<span class="comfy-vue-side-bar-header-span">{{
|
||||
props.title.toUpperCase()
|
||||
}}</span>
|
||||
</template>
|
||||
<template #end>
|
||||
<slot name="tool-buttons"></slot>
|
||||
</template>
|
||||
</Toolbar>
|
||||
<div class="comfy-vue-side-bar-body">
|
||||
<slot name="body"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Toolbar from 'primevue/toolbar'
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.comfy-vue-side-bar-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.comfy-vue-side-bar-header {
|
||||
flex-shrink: 0;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
border-radius: 0;
|
||||
padding: 0.25rem 1rem;
|
||||
}
|
||||
|
||||
.comfy-vue-side-bar-header-span {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.comfy-vue-side-bar-body {
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: transparent transparent;
|
||||
}
|
||||
|
||||
.comfy-vue-side-bar-body::-webkit-scrollbar {
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.comfy-vue-side-bar-body::-webkit-scrollbar-thumb {
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
10
src/i18n.ts
10
src/i18n.ts
@@ -6,7 +6,10 @@ const messages = {
|
||||
settings: 'Settings',
|
||||
themeToggle: 'Toggle Theme',
|
||||
queue: 'Queue',
|
||||
nodeLibrary: 'Node Library'
|
||||
nodeLibrary: 'Node Library',
|
||||
nodeLibraryTab: {
|
||||
sortOrder: 'Sort Order'
|
||||
}
|
||||
}
|
||||
},
|
||||
zh: {
|
||||
@@ -14,7 +17,10 @@ const messages = {
|
||||
settings: '设置',
|
||||
themeToggle: '主题切换',
|
||||
queue: '队列',
|
||||
nodeLibrary: '节点库'
|
||||
nodeLibrary: '节点库',
|
||||
nodeLibraryTab: {
|
||||
sortOrder: '排序顺序'
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: Add more languages
|
||||
|
||||
@@ -218,6 +218,27 @@ export const SYSTEM_NODE_DEFS: ComfyNodeDef[] = [
|
||||
}
|
||||
]
|
||||
|
||||
function sortedTree(node: TreeNode): TreeNode {
|
||||
// Create a new node with the same label and data
|
||||
const newNode: TreeNode = {
|
||||
...node
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
// Sort the children of the current node
|
||||
const sortedChildren = [...node.children].sort((a, b) =>
|
||||
a.label.localeCompare(b.label)
|
||||
)
|
||||
// Recursively sort the children and add them to the new node
|
||||
newNode.children = []
|
||||
for (const child of sortedChildren) {
|
||||
newNode.children.push(sortedTree(child))
|
||||
}
|
||||
}
|
||||
|
||||
return newNode
|
||||
}
|
||||
|
||||
interface State {
|
||||
nodeDefsByName: Record<string, ComfyNodeDefImpl>
|
||||
widgets: Record<string, ComfyWidgetConstructor>
|
||||
@@ -261,6 +282,9 @@ export const useNodeDefStore = defineStore('nodeDef', {
|
||||
})
|
||||
}
|
||||
return root
|
||||
},
|
||||
sortedNodeTree(): TreeNode {
|
||||
return sortedTree(this.nodeTree)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
||||
Reference in New Issue
Block a user