66 @mouseenter =" viewer.handleMouseEnter"
77 @mouseleave =" viewer.handleMouseLeave"
88 >
9- <div class =" w-16 flex-col py-4 space-y-4" >
10- <Button
11- v-for =" item in menuItems"
12- :key =" item.id"
13- class =" w-12 h-12 rounded-lg items-center justify-center"
14- :class =" activePanel === item.id ? 'text-white' : 'text-gray-300'"
15- @click =" activePanel = item.id"
16- >
17- <i
18- v-tooltip.right =" { value: item.title, showDelay: 300 }"
19- :class =" [item.icon, 'text-lg']"
20- />
21- </Button >
22- </div >
23-
249 <div ref =" mainContentRef" class =" flex-1 relative" >
2510 <div
2611 ref =" containerRef"
2914 />
3015 </div >
3116
32- <div class =" w-64 p-4 flex flex-col" >
33- <div class =" mb-4" >
34- {{ activePanelTitle }}
35- </div >
36-
37- <div class =" flex-1 space-y-4" >
38- <div v-show =" activePanel === 'scene'" class =" space-y-4" >
39- <SceneControls
40- v-model:background-color =" viewer.backgroundColor.value"
41- v-model:show-grid =" viewer.showGrid.value"
42- :has-background-image =" viewer.hasBackgroundImage.value"
43- @update-background-image =" viewer.handleBackgroundImageUpdate"
44- />
45- </div >
46-
47- <div v-show =" activePanel === 'model'" class =" space-y-4" >
48- <ModelControls
49- v-model:up-direction =" viewer.upDirection.value"
50- v-model:material-mode =" viewer.materialMode.value"
51- />
17+ <div class =" w-72 flex flex-col" >
18+ <div class =" flex-1 overflow-y-auto p-4" >
19+ <div class =" space-y-2" >
20+ <Panel v-model:collapsed =" panelStates.scene" toggleable >
21+ <template #header >
22+ <div class =" flex items-center gap-2" >
23+ <i class =" pi pi-image" />
24+ <span >{{ t('load3d.viewer.sceneSettings') }}</span >
25+ </div >
26+ </template >
27+ <div class =" p-4 space-y-4" >
28+ <SceneControls
29+ v-model:background-color =" viewer.backgroundColor.value"
30+ v-model:show-grid =" viewer.showGrid.value"
31+ :has-background-image =" viewer.hasBackgroundImage.value"
32+ @update-background-image =" viewer.handleBackgroundImageUpdate"
33+ />
34+ </div >
35+ </Panel >
36+
37+ <Panel v-model:collapsed =" panelStates.model" toggleable >
38+ <template #header >
39+ <div class =" flex items-center gap-2" >
40+ <i class =" pi pi-box" />
41+ <span >{{ t('load3d.viewer.modelSettings') }}</span >
42+ </div >
43+ </template >
44+ <div class =" p-4 space-y-4" >
45+ <ModelControls
46+ v-model:up-direction =" viewer.upDirection.value"
47+ v-model:material-mode =" viewer.materialMode.value"
48+ />
49+ </div >
50+ </Panel >
51+
52+ <Panel v-model:collapsed =" panelStates.camera" toggleable >
53+ <template #header >
54+ <div class =" flex items-center gap-2" >
55+ <i class =" pi pi-camera" />
56+ <span >{{ t('load3d.viewer.cameraSettings') }}</span >
57+ </div >
58+ </template >
59+ <div class =" p-4 space-y-4" >
60+ <CameraControls
61+ v-model:camera-type =" viewer.cameraType.value"
62+ v-model:fov =" viewer.fov.value"
63+ />
64+ </div >
65+ </Panel >
66+
67+ <Panel v-model:collapsed =" panelStates.light" toggleable >
68+ <template #header >
69+ <div class =" flex items-center gap-2" >
70+ <i class =" pi pi-sun" />
71+ <span >{{ t('load3d.viewer.lightSettings') }}</span >
72+ </div >
73+ </template >
74+ <div class =" p-4 space-y-4" >
75+ <LightControls
76+ v-model:light-intensity =" viewer.lightIntensity.value"
77+ />
78+ </div >
79+ </Panel >
80+
81+ <Panel v-model:collapsed =" panelStates.export" toggleable >
82+ <template #header >
83+ <div class =" flex items-center gap-2" >
84+ <i class =" pi pi-download" />
85+ <span >{{ t('load3d.viewer.exportSettings') }}</span >
86+ </div >
87+ </template >
88+ <div class =" p-4 space-y-4" >
89+ <ExportControls @export-model =" viewer.exportModel" />
90+ </div >
91+ </Panel >
5292 </div >
93+ </div >
5394
54- <div v-show =" activePanel === 'camera'" class =" space-y-4" >
55- <CameraControls
56- v-model:camera-type =" viewer.cameraType.value"
57- v-model:fov =" viewer.fov.value"
95+ <div class =" p-4" >
96+ <div class =" flex gap-2" >
97+ <Button
98+ icon =" pi pi-times"
99+ severity =" secondary"
100+ :label =" t('g.cancel')"
101+ @click =" handleCancel"
58102 />
59- </ div >
60-
61- < div v-show = " activePanel === 'light' " class = " space-y-4 " >
62- < LightControls
63- v-model:light-intensity = " viewer.lightIntensity.value "
103+ < Button
104+ icon = " pi pi-check "
105+ severity = " secondary "
106+ :label = " t('g.apply') "
107+ @click = " handleConfirm "
64108 />
65109 </div >
66-
67- <div v-show =" activePanel === 'export'" class =" space-y-4" >
68- <ExportControls @export-model =" viewer.exportModel" />
69- </div >
70- </div >
71-
72- <div class =" flex gap-2 mt-4" >
73- <Button
74- icon =" pi pi-times"
75- severity =" secondary"
76- :label =" t('g.cancel')"
77- @click =" handleCancel"
78- />
79- <Button
80- icon =" pi pi-check"
81- severity =" secondary"
82- :label =" t('g.apply')"
83- @click =" handleConfirm"
84- />
85110 </div >
86111 </div >
87112 </div >
88113</template >
89114
90115<script setup lang="ts">
91116import { LGraphNode } from ' @comfyorg/litegraph'
92- import { Tooltip } from ' primevue'
93117import Button from ' primevue/button'
94- import { computed , onBeforeUnmount , onMounted , ref , toRef } from ' vue'
118+ import Panel from ' primevue/panel'
119+ import { onBeforeUnmount , onMounted , ref , toRef } from ' vue'
95120
96121import CameraControls from ' @/components/load3d/controls/viewer/CameraControls.vue'
97122import ExportControls from ' @/components/load3d/controls/viewer/ExportControls.vue'
@@ -103,41 +128,26 @@ import { t } from '@/i18n'
103128import { useLoad3dService } from ' @/services/load3dService'
104129import { useDialogStore } from ' @/stores/dialogStore'
105130
106- const vTooltip = Tooltip
107-
108131const props = defineProps <{
109132 node: LGraphNode
110133}>()
111134
112- const activePanel = ref (' scene' )
113- const menuItems = [
114- { id: ' scene' , icon: ' pi pi-image' , title: t (' load3d.viewer.sceneSettings' ) },
115- { id: ' model' , icon: ' pi pi-box' , title: t (' load3d.viewer.modelSettings' ) },
116- {
117- id: ' camera' ,
118- icon: ' pi pi-camera' ,
119- title: t (' load3d.viewer.cameraSettings' )
120- },
121- { id: ' light' , icon: ' pi pi-sun' , title: t (' load3d.viewer.lightSettings' ) },
122- {
123- id: ' export' ,
124- icon: ' pi pi-download' ,
125- title: t (' load3d.viewer.exportSettings' )
126- }
127- ]
128-
129135const viewerContentRef = ref <HTMLDivElement >()
130136const containerRef = ref <HTMLDivElement >()
131137const mainContentRef = ref <HTMLDivElement >()
132138const maximized = ref (false )
133139const mutationObserver = ref <MutationObserver | null >(null )
134140
135- const viewer = useLoad3dViewer (toRef (props , ' node' ))
136-
137- const activePanelTitle = computed (() => {
138- return menuItems .find ((item ) => item .id === activePanel .value )?.title || ' '
141+ const panelStates = ref ({
142+ scene: false ,
143+ model: true ,
144+ camera: true ,
145+ light: true ,
146+ export: true
139147})
140148
149+ const viewer = useLoad3dViewer (toRef (props , ' node' ))
150+
141151onMounted (async () => {
142152 const source = useLoad3dService ().getLoad3d (props .node )
143153 if (source && containerRef .value ) {
@@ -196,3 +206,9 @@ onBeforeUnmount(() => {
196206 viewer .cleanup ()
197207})
198208 </script >
209+
210+ <style scoped>
211+ :deep(.p-panel-content ) {
212+ padding : 0 ;
213+ }
214+ </style >
0 commit comments