mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-03 12:42:01 +00:00
Categorize setting items (#338)
* Basic setting panel rework * refactor * Style the setting item * Reject invalid value * nit * nit * Sort settings by label * info chip as icon * nit
This commit is contained in:
@@ -1,116 +1,119 @@
|
||||
<template>
|
||||
<table class="comfy-modal-content comfy-table">
|
||||
<tbody>
|
||||
<tr v-for="setting in sortedSettings" :key="setting.id">
|
||||
<td>
|
||||
<span>
|
||||
{{ setting.name }}
|
||||
</span>
|
||||
<Chip
|
||||
v-if="setting.tooltip"
|
||||
icon="pi pi-info-circle"
|
||||
severity="secondary"
|
||||
v-tooltip="setting.tooltip"
|
||||
class="info-chip"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<component
|
||||
:is="markRaw(getSettingComponent(setting))"
|
||||
:id="setting.id"
|
||||
:modelValue="settingStore.get(setting.id)"
|
||||
@update:modelValue="updateSetting(setting, $event)"
|
||||
v-bind="getSettingAttrs(setting)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="settings-container">
|
||||
<div class="settings-sidebar">
|
||||
<Listbox
|
||||
v-model="activeCategory"
|
||||
:options="categories"
|
||||
optionLabel="label"
|
||||
scrollHeight="100%"
|
||||
:pt="{ root: { class: 'border-none' } }"
|
||||
/>
|
||||
</div>
|
||||
<Divider layout="vertical" />
|
||||
<div class="settings-content" v-if="activeCategory">
|
||||
<Tabs :value="activeCategory.label">
|
||||
<TabPanels>
|
||||
<TabPanel
|
||||
v-for="category in categories"
|
||||
:key="category.key"
|
||||
:value="category.label"
|
||||
>
|
||||
<SettingGroup
|
||||
v-for="group in sortedGroups(category)"
|
||||
:key="group.label"
|
||||
:group="{
|
||||
label: group.label,
|
||||
settings: flattenTree<SettingParams>(group)
|
||||
}"
|
||||
/>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type Component, computed, markRaw } from 'vue'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import InputNumber from 'primevue/inputnumber'
|
||||
import Select from 'primevue/select'
|
||||
import Chip from 'primevue/chip'
|
||||
import ToggleSwitch from 'primevue/toggleswitch'
|
||||
import { useSettingStore } from '@/stores/settingStore'
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import Listbox from 'primevue/listbox'
|
||||
import Tabs from 'primevue/tabs'
|
||||
import TabPanels from 'primevue/tabpanels'
|
||||
import TabPanel from 'primevue/tabpanel'
|
||||
import Divider from 'primevue/divider'
|
||||
import { SettingTreeNode, useSettingStore } from '@/stores/settingStore'
|
||||
import { SettingParams } from '@/types/settingTypes'
|
||||
import CustomSettingValue from '@/components/dialog/content/setting/CustomSettingValue.vue'
|
||||
import InputSlider from '@/components/dialog/content/setting/InputSlider.vue'
|
||||
import SettingGroup from './setting/SettingGroup.vue'
|
||||
import { flattenTree } from '@/utils/treeUtil'
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
const sortedSettings = computed<SettingParams[]>(() => {
|
||||
return Object.values(settingStore.settings)
|
||||
.filter((setting: SettingParams) => setting.type !== 'hidden')
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
const settingRoot = computed<SettingTreeNode>(() => settingStore.settingTree)
|
||||
const categories = computed<SettingTreeNode[]>(
|
||||
() => settingRoot.value.children || []
|
||||
)
|
||||
|
||||
const activeCategory = ref<SettingTreeNode | null>(null)
|
||||
|
||||
watch(activeCategory, (newCategory, oldCategory) => {
|
||||
if (newCategory === null) {
|
||||
activeCategory.value = oldCategory
|
||||
}
|
||||
})
|
||||
|
||||
function getSettingAttrs(setting: SettingParams) {
|
||||
const attrs = { ...(setting.attrs || {}) }
|
||||
const settingType = setting.type
|
||||
if (typeof settingType === 'function') {
|
||||
attrs['renderFunction'] = () =>
|
||||
settingType(
|
||||
setting.name,
|
||||
(v) => updateSetting(setting, v),
|
||||
settingStore.get(setting.id),
|
||||
setting.attrs
|
||||
)
|
||||
}
|
||||
switch (setting.type) {
|
||||
case 'combo':
|
||||
attrs['options'] = setting.options
|
||||
if (typeof setting.options[0] !== 'string') {
|
||||
attrs['optionLabel'] = 'text'
|
||||
attrs['optionValue'] = 'value'
|
||||
}
|
||||
break
|
||||
}
|
||||
onMounted(() => {
|
||||
activeCategory.value = categories.value[0]
|
||||
})
|
||||
|
||||
attrs['class'] += ' comfy-vue-setting-input'
|
||||
return attrs
|
||||
}
|
||||
|
||||
function getSettingComponent(setting: SettingParams): Component {
|
||||
if (typeof setting.type === 'function') {
|
||||
// return setting.type(
|
||||
// setting.name, (v) => updateSetting(setting, v), settingStore.get(setting.id), setting.attrs)
|
||||
return CustomSettingValue
|
||||
}
|
||||
switch (setting.type) {
|
||||
case 'boolean':
|
||||
return ToggleSwitch
|
||||
case 'number':
|
||||
return InputNumber
|
||||
case 'slider':
|
||||
return InputSlider
|
||||
case 'combo':
|
||||
return Select
|
||||
default:
|
||||
return InputText
|
||||
}
|
||||
}
|
||||
|
||||
const updateSetting = (setting: SettingParams, value: any) => {
|
||||
if (setting.onChange) setting.onChange(value, settingStore.get(setting.id))
|
||||
|
||||
settingStore.set(setting.id, value)
|
||||
const sortedGroups = (category: SettingTreeNode) => {
|
||||
return [...(category.children || [])].sort((a, b) =>
|
||||
a.label.localeCompare(b.label)
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.info-chip {
|
||||
background: transparent !important;
|
||||
}
|
||||
.comfy-vue-setting-input {
|
||||
width: 100%;
|
||||
/* Remove after we have tailwind setup */
|
||||
.border-none {
|
||||
border: none !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.comfy-table {
|
||||
.settings-container {
|
||||
display: flex;
|
||||
height: 80vh;
|
||||
width: 60vw;
|
||||
max-width: 1000px;
|
||||
overflow: hidden;
|
||||
/* Prevents container from scrolling */
|
||||
}
|
||||
|
||||
.settings-sidebar {
|
||||
width: 250px;
|
||||
flex-shrink: 0;
|
||||
/* Prevents sidebar from shrinking */
|
||||
overflow-y: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
/* Allows vertical scrolling */
|
||||
}
|
||||
|
||||
/* Ensure the Listbox takes full width of the sidebar */
|
||||
.settings-sidebar :deep(.p-listbox) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Optional: Style scrollbars for webkit browsers */
|
||||
.settings-sidebar::-webkit-scrollbar,
|
||||
.settings-content::-webkit-scrollbar {
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.settings-sidebar::-webkit-scrollbar-thumb,
|
||||
.settings-content::-webkit-scrollbar-thumb {
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user