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:
Chenlei Hu
2024-08-08 17:52:41 -04:00
committed by GitHub
parent 02d7f91e9e
commit a5f0d2b201
10 changed files with 381 additions and 111 deletions

View File

@@ -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>