|
1 | 1 | <template> |
2 | | - <table class="comfy-modal-content comfy-table"> |
3 | | - <tbody> |
4 | | - <tr v-for="setting in sortedSettings" :key="setting.id"> |
5 | | - <td> |
6 | | - <span> |
7 | | - {{ setting.name }} |
8 | | - </span> |
9 | | - <Chip |
10 | | - v-if="setting.tooltip" |
11 | | - icon="pi pi-info-circle" |
12 | | - severity="secondary" |
13 | | - v-tooltip="setting.tooltip" |
14 | | - class="info-chip" |
15 | | - /> |
16 | | - </td> |
17 | | - <td> |
18 | | - <component |
19 | | - :is="markRaw(getSettingComponent(setting))" |
20 | | - :id="setting.id" |
21 | | - :modelValue="settingStore.get(setting.id)" |
22 | | - @update:modelValue="updateSetting(setting, $event)" |
23 | | - v-bind="getSettingAttrs(setting)" |
24 | | - /> |
25 | | - </td> |
26 | | - </tr> |
27 | | - </tbody> |
28 | | - </table> |
| 2 | + <div class="settings-container"> |
| 3 | + <div class="settings-sidebar"> |
| 4 | + <Listbox |
| 5 | + v-model="activeCategory" |
| 6 | + :options="categories" |
| 7 | + optionLabel="label" |
| 8 | + scrollHeight="100%" |
| 9 | + :pt="{ root: { class: 'border-none' } }" |
| 10 | + /> |
| 11 | + </div> |
| 12 | + <Divider layout="vertical" /> |
| 13 | + <div class="settings-content" v-if="activeCategory"> |
| 14 | + <Tabs :value="activeCategory.label"> |
| 15 | + <TabPanels> |
| 16 | + <TabPanel |
| 17 | + v-for="category in categories" |
| 18 | + :key="category.key" |
| 19 | + :value="category.label" |
| 20 | + > |
| 21 | + <SettingGroup |
| 22 | + v-for="group in sortedGroups(category)" |
| 23 | + :key="group.label" |
| 24 | + :group="{ |
| 25 | + label: group.label, |
| 26 | + settings: flattenTree<SettingParams>(group) |
| 27 | + }" |
| 28 | + /> |
| 29 | + </TabPanel> |
| 30 | + </TabPanels> |
| 31 | + </Tabs> |
| 32 | + </div> |
| 33 | + </div> |
29 | 34 | </template> |
30 | 35 |
|
31 | 36 | <script setup lang="ts"> |
32 | | -import { type Component, computed, markRaw } from 'vue' |
33 | | -import InputText from 'primevue/inputtext' |
34 | | -import InputNumber from 'primevue/inputnumber' |
35 | | -import Select from 'primevue/select' |
36 | | -import Chip from 'primevue/chip' |
37 | | -import ToggleSwitch from 'primevue/toggleswitch' |
38 | | -import { useSettingStore } from '@/stores/settingStore' |
| 37 | +import { ref, computed, onMounted, watch } from 'vue' |
| 38 | +import Listbox from 'primevue/listbox' |
| 39 | +import Tabs from 'primevue/tabs' |
| 40 | +import TabPanels from 'primevue/tabpanels' |
| 41 | +import TabPanel from 'primevue/tabpanel' |
| 42 | +import Divider from 'primevue/divider' |
| 43 | +import { SettingTreeNode, useSettingStore } from '@/stores/settingStore' |
39 | 44 | import { SettingParams } from '@/types/settingTypes' |
40 | | -import CustomSettingValue from '@/components/dialog/content/setting/CustomSettingValue.vue' |
41 | | -import InputSlider from '@/components/dialog/content/setting/InputSlider.vue' |
| 45 | +import SettingGroup from './setting/SettingGroup.vue' |
| 46 | +import { flattenTree } from '@/utils/treeUtil' |
42 | 47 |
|
43 | 48 | const settingStore = useSettingStore() |
44 | | -const sortedSettings = computed<SettingParams[]>(() => { |
45 | | - return Object.values(settingStore.settings) |
46 | | - .filter((setting: SettingParams) => setting.type !== 'hidden') |
47 | | - .sort((a, b) => a.name.localeCompare(b.name)) |
48 | | -}) |
49 | | -
|
50 | | -function getSettingAttrs(setting: SettingParams) { |
51 | | - const attrs = { ...(setting.attrs || {}) } |
52 | | - const settingType = setting.type |
53 | | - if (typeof settingType === 'function') { |
54 | | - attrs['renderFunction'] = () => |
55 | | - settingType( |
56 | | - setting.name, |
57 | | - (v) => updateSetting(setting, v), |
58 | | - settingStore.get(setting.id), |
59 | | - setting.attrs |
60 | | - ) |
61 | | - } |
62 | | - switch (setting.type) { |
63 | | - case 'combo': |
64 | | - attrs['options'] = setting.options |
65 | | - if (typeof setting.options[0] !== 'string') { |
66 | | - attrs['optionLabel'] = 'text' |
67 | | - attrs['optionValue'] = 'value' |
68 | | - } |
69 | | - break |
70 | | - } |
| 49 | +const settingRoot = computed<SettingTreeNode>(() => settingStore.settingTree) |
| 50 | +const categories = computed<SettingTreeNode[]>( |
| 51 | + () => settingRoot.value.children || [] |
| 52 | +) |
71 | 53 |
|
72 | | - attrs['class'] += ' comfy-vue-setting-input' |
73 | | - return attrs |
74 | | -} |
| 54 | +const activeCategory = ref<SettingTreeNode | null>(null) |
75 | 55 |
|
76 | | -function getSettingComponent(setting: SettingParams): Component { |
77 | | - if (typeof setting.type === 'function') { |
78 | | - // return setting.type( |
79 | | - // setting.name, (v) => updateSetting(setting, v), settingStore.get(setting.id), setting.attrs) |
80 | | - return CustomSettingValue |
81 | | - } |
82 | | - switch (setting.type) { |
83 | | - case 'boolean': |
84 | | - return ToggleSwitch |
85 | | - case 'number': |
86 | | - return InputNumber |
87 | | - case 'slider': |
88 | | - return InputSlider |
89 | | - case 'combo': |
90 | | - return Select |
91 | | - default: |
92 | | - return InputText |
| 56 | +watch(activeCategory, (newCategory, oldCategory) => { |
| 57 | + if (newCategory === null) { |
| 58 | + activeCategory.value = oldCategory |
93 | 59 | } |
94 | | -} |
| 60 | +}) |
95 | 61 |
|
96 | | -const updateSetting = (setting: SettingParams, value: any) => { |
97 | | - if (setting.onChange) setting.onChange(value, settingStore.get(setting.id)) |
| 62 | +onMounted(() => { |
| 63 | + activeCategory.value = categories.value[0] |
| 64 | +}) |
98 | 65 |
|
99 | | - settingStore.set(setting.id, value) |
| 66 | +const sortedGroups = (category: SettingTreeNode) => { |
| 67 | + return [...(category.children || [])].sort((a, b) => |
| 68 | + a.label.localeCompare(b.label) |
| 69 | + ) |
100 | 70 | } |
101 | 71 | </script> |
102 | 72 |
|
103 | 73 | <style> |
104 | | -.info-chip { |
105 | | - background: transparent !important; |
106 | | -} |
107 | | -.comfy-vue-setting-input { |
108 | | - width: 100%; |
| 74 | +/* Remove after we have tailwind setup */ |
| 75 | +.border-none { |
| 76 | + border: none !important; |
109 | 77 | } |
110 | 78 | </style> |
111 | 79 |
|
112 | 80 | <style scoped> |
113 | | -.comfy-table { |
| 81 | +.settings-container { |
| 82 | + display: flex; |
| 83 | + height: 80vh; |
| 84 | + width: 60vw; |
| 85 | + max-width: 1000px; |
| 86 | + overflow: hidden; |
| 87 | + /* Prevents container from scrolling */ |
| 88 | +} |
| 89 | +
|
| 90 | +.settings-sidebar { |
| 91 | + width: 250px; |
| 92 | + flex-shrink: 0; |
| 93 | + /* Prevents sidebar from shrinking */ |
| 94 | + overflow-y: auto; |
| 95 | + padding: 10px; |
| 96 | +} |
| 97 | +
|
| 98 | +.settings-content { |
| 99 | + flex-grow: 1; |
| 100 | + overflow-y: auto; |
| 101 | + /* Allows vertical scrolling */ |
| 102 | +} |
| 103 | +
|
| 104 | +/* Ensure the Listbox takes full width of the sidebar */ |
| 105 | +.settings-sidebar :deep(.p-listbox) { |
114 | 106 | width: 100%; |
115 | 107 | } |
| 108 | +
|
| 109 | +/* Optional: Style scrollbars for webkit browsers */ |
| 110 | +.settings-sidebar::-webkit-scrollbar, |
| 111 | +.settings-content::-webkit-scrollbar { |
| 112 | + width: 1px; |
| 113 | +} |
| 114 | +
|
| 115 | +.settings-sidebar::-webkit-scrollbar-thumb, |
| 116 | +.settings-content::-webkit-scrollbar-thumb { |
| 117 | + background-color: transparent; |
| 118 | +} |
116 | 119 | </style> |
0 commit comments