11import { Accordion , Button , ScrollArea , Tabs , Tooltip } from "@mantine/core"
2- import { useDisclosure } from "@mantine/hooks"
2+ import { useDebouncedCallback , useDisclosure } from "@mantine/hooks"
33import { Fragment } from "react"
4- import { useSelector } from "react-redux"
4+ import { useDispatch , useSelector } from "react-redux"
5+ import { Panel , PanelGroup , PanelResizeHandle } from "react-resizable-panels"
56import {
67 selectAircraftType ,
78 selectFile ,
89 selectFirmwareVersion ,
910 selectMessageMeans ,
11+ selectPanelLayout ,
12+ setHorizontalLayout ,
13+ setVerticalLayout ,
1014} from "../../redux/slices/logAnalyserSlice.js"
1115import ChartDataCard from "./chartDataCard.jsx"
1216import DataTable from "./dataTable.jsx"
@@ -20,11 +24,25 @@ import SavePresetModal from "./savePresetModal.jsx"
2024 * Main display component for the Falcon Log Analyser (FLA).
2125 */
2226export default function MainDisplay ( { closeLogFile, chartData, customColors } ) {
27+ const dispatch = useDispatch ( )
28+
2329 // Redux selectors
2430 const file = useSelector ( selectFile )
2531 const aircraftType = useSelector ( selectAircraftType )
2632 const messageMeans = useSelector ( selectMessageMeans )
2733 const firmwareVersion = useSelector ( selectFirmwareVersion )
34+ const panelLayout = useSelector ( selectPanelLayout )
35+
36+ // Debounced layout update handlers (300ms delay)
37+ const debouncedSetHorizontalLayout = useDebouncedCallback (
38+ ( sizes ) => dispatch ( setHorizontalLayout ( sizes ) ) ,
39+ 300 ,
40+ )
41+
42+ const debouncedSetVerticalLayout = useDebouncedCallback (
43+ ( sizes ) => dispatch ( setVerticalLayout ( sizes ) ) ,
44+ 300 ,
45+ )
2846
2947 // Shared presets state for all children (presets + modal)
3048 const {
@@ -40,117 +58,154 @@ export default function MainDisplay({ closeLogFile, chartData, customColors }) {
4058 ] = useDisclosure ( false )
4159
4260 return (
43- < div className = "flex h-full gap-4 px-2 py-4 mb-4 overflow-hidden" >
44- { /* Message selection column */ }
45- < div className = "w-1/4 pb-6 flex flex-col min-h-0" >
46- < div className = "flex flex-col mb-2 text-sm gap-y-2 flex-shrink-0" >
47- < div className = "flex flex-row justify-between" >
48- < Tooltip label = { file . path } >
49- < div className = "px-4 py-2 text-gray-200 bg-falcongrey-700 rounded truncate max-w-[400px] inline-block" >
50- File Name:
51- < span
52- className = "ml-2 text-white underline cursor-pointer"
53- onClick = { ( ) => {
54- window . ipcRenderer . send (
55- "window:open-file-in-explorer" ,
56- file . path ,
57- )
58- } }
61+ < div className = "flex h-full px-2 py-4 mb-4 overflow-hidden" >
62+ < PanelGroup
63+ direction = "horizontal"
64+ onLayout = { debouncedSetHorizontalLayout }
65+ >
66+ { /* Left Panel - Message selection column */ }
67+ < Panel defaultSize = { panelLayout . leftSidebar } minSize = { 10 } >
68+ < div className = "pb-6 flex flex-col min-h-0 h-full pr-2" >
69+ < div className = "flex flex-col mb-2 text-sm gap-y-2 flex-shrink-0" >
70+ < div className = "flex flex-row justify-between" >
71+ < Tooltip label = { file . path } >
72+ < div className = "px-4 py-2 text-gray-200 bg-falcongrey-700 rounded truncate max-w-[400px] inline-block" >
73+ File Name:
74+ < span
75+ className = "ml-2 text-white underline cursor-pointer"
76+ onClick = { ( ) => {
77+ window . ipcRenderer . send (
78+ "window:open-file-in-explorer" ,
79+ file . path ,
80+ )
81+ } }
82+ >
83+ { file . name }
84+ </ span >
85+ </ div >
86+ </ Tooltip >
87+ < Button
88+ className = "ml-2"
89+ size = "sm"
90+ color = "red"
91+ onClick = { closeLogFile }
5992 >
60- { file . name }
61- </ span >
93+ Close file
94+ </ Button >
6295 </ div >
63- </ Tooltip >
64- < Button
65- className = "ml-2"
66- size = "sm"
67- color = "red"
68- onClick = { closeLogFile }
69- >
70- Close file
71- </ Button >
72- </ div >
73- < div className = "flex justify-between px-4 py-2 text-gray-200 rounded bg-falcongrey-700" >
74- < div className = "whitespace-nowrap" > Aircraft Type:</ div >
75- < div className = "text-white ml-auto capitalize" >
76- { aircraftType ?? "No Aircraft Type" }
96+ < div className = "flex justify-between px-4 py-2 text-gray-200 rounded bg-falcongrey-700" >
97+ < div className = "whitespace-nowrap" > Aircraft Type:</ div >
98+ < div className = "text-white ml-auto capitalize" >
99+ { aircraftType ?? "No Aircraft Type" }
100+ </ div >
101+ </ div >
102+ { firmwareVersion !== null && (
103+ < div className = "flex justify-between px-4 py-2 text-gray-200 rounded bg-falcongrey-700" >
104+ < div className = "whitespace-nowrap" > Firmware Version:</ div >
105+ < div className = "text-white ml-auto" > { firmwareVersion } </ div >
106+ </ div >
107+ ) }
77108 </ div >
109+ < ScrollArea className = "flex-1 min-h-0" >
110+ < Accordion multiple = { true } defaultValue = { [ "messages" ] } >
111+ { /* Presets */ }
112+ < Accordion . Item key = "presets" value = "presets" >
113+ < Accordion . Control className = "rounded-md" >
114+ Presets
115+ </ Accordion . Control >
116+ < Accordion . Panel >
117+ < PresetsAccordion
118+ presetCategories = { presetCategories }
119+ deleteCustomPreset = { deleteCustomPreset }
120+ />
121+ </ Accordion . Panel >
122+ </ Accordion . Item >
123+
124+ { /* All messages (checkboxes in accordions) */ }
125+ < Accordion . Item
126+ key = "messages"
127+ value = "messages"
128+ styles = { { item : { borderBottom : "none" } } }
129+ >
130+ < Accordion . Control className = "rounded-md" >
131+ Messages
132+ </ Accordion . Control >
133+ < Accordion . Panel >
134+ < MessagesFiltersAccordion />
135+ </ Accordion . Panel >
136+ </ Accordion . Item >
137+ </ Accordion >
138+ </ ScrollArea >
78139 </ div >
79- { firmwareVersion !== null && (
80- < div className = "flex justify-between px-4 py-2 text-gray-200 rounded bg-falcongrey-700" >
81- < div className = "whitespace-nowrap" > Firmware Version:</ div >
82- < div className = "text-white ml-auto" > { firmwareVersion } </ div >
83- </ div >
84- ) }
85- </ div >
86- < ScrollArea className = "flex-1 min-h-0" >
87- < Accordion multiple = { true } defaultValue = { [ "messages" ] } >
88- { /* Presets */ }
89- < Accordion . Item key = "presets" value = "presets" >
90- < Accordion . Control className = "rounded-md" >
91- Presets
92- </ Accordion . Control >
93- < Accordion . Panel >
94- < PresetsAccordion
95- presetCategories = { presetCategories }
96- deleteCustomPreset = { deleteCustomPreset }
140+ </ Panel >
141+
142+ { /* Resize Handle */ }
143+ < PanelResizeHandle className = 'w-1 bg-falcongrey-700 hover:bg-falconred-500 data-[resize-handle-state="hover"]:bg-falconred-500 data-[resize-handle-state="drag"]:bg-falconred-500 transition-colors cursor-col-resize' />
144+
145+ { /* Right Panel - Graph and Tabs */ }
146+ < Panel defaultSize = { panelLayout . rightContent } minSize = { 65 } >
147+ < PanelGroup
148+ direction = "vertical"
149+ className = "h-full"
150+ onLayout = { debouncedSetVerticalLayout }
151+ >
152+ { /* Top Panel - Graph */ }
153+ < Panel defaultSize = { panelLayout . graph } minSize = { 30 } >
154+ < div className = "h-full pl-2 min-w-0" >
155+ < Graph
156+ data = { chartData }
157+ customColors = { customColors }
158+ openPresetModal = { openSavePresetModal }
97159 />
98- </ Accordion . Panel >
99- </ Accordion . Item >
160+ </ div >
161+ </ Panel >
100162
101- { /* All messages (checkboxes in accordions) */ }
102- < Accordion . Item
103- key = "messages"
104- value = "messages"
105- styles = { { item : { borderBottom : "none" } } }
106- >
107- < Accordion . Control className = "rounded-md" >
108- Messages
109- </ Accordion . Control >
110- < Accordion . Panel >
111- < MessagesFiltersAccordion />
112- </ Accordion . Panel >
113- </ Accordion . Item >
114- </ Accordion >
115- </ ScrollArea >
116- </ div >
117- { /* Graph column */ }
118- < div className = "w-full h-full pr-4 min-w-0 flex flex-col" >
119- < Graph
120- data = { chartData }
121- customColors = { customColors }
122- openPresetModal = { openSavePresetModal }
123- />
163+ { /* Vertical Resize Handle */ }
164+ < PanelResizeHandle className = 'h-1 bg-falcongrey-700 hover:bg-falconred-500 data-[resize-handle-state="hover"]:bg-falconred-500 data-[resize-handle-state="drag"]:bg-falconred-500 transition-colors cursor-row-resize' />
124165
125- < Tabs
126- defaultValue = { "chart_cards" }
127- className = "flex flex-col flex-1 min-h-0"
128- >
129- < Tabs . List >
130- < Tabs . Tab value = "chart_cards" > Chart Cards</ Tabs . Tab >
131- < Tabs . Tab value = "data_table" > Data Table</ Tabs . Tab >
132- </ Tabs . List >
166+ { /* Bottom Panel - Tabs */ }
167+ < Panel defaultSize = { panelLayout . tabs } minSize = { 15 } >
168+ < div className = "h-full px-2 py-1 min-w-0 flex flex-col" >
169+ < Tabs
170+ defaultValue = { "chart_cards" }
171+ className = "flex flex-col flex-1 min-h-0"
172+ >
173+ < Tabs . List >
174+ < Tabs . Tab value = "chart_cards" > Chart Cards</ Tabs . Tab >
175+ < Tabs . Tab value = "data_table" > Data Table</ Tabs . Tab >
176+ </ Tabs . List >
133177
134- < Tabs . Panel value = "chart_cards" className = "min-h-0 h-full pb-4" >
135- < ScrollArea className = "h-full" >
136- < div className = "grid grid-cols-6 gap-2 py-2" >
137- { chartData . datasets . map ( ( item ) => (
138- < Fragment key = { item . label } >
139- < ChartDataCard
140- item = { item }
141- unit = { item . yAxisID } // item.yAxisID is the unit
142- messageMeans = { messageMeans }
143- />
144- </ Fragment >
145- ) ) }
178+ < Tabs . Panel
179+ value = "chart_cards"
180+ className = "min-h-0 h-full pb-4"
181+ >
182+ < ScrollArea className = "h-full" >
183+ < div className = "grid grid-cols-6 gap-2 py-2" >
184+ { chartData . datasets . map ( ( item ) => (
185+ < Fragment key = { item . label } >
186+ < ChartDataCard
187+ item = { item }
188+ unit = { item . yAxisID } // item.yAxisID is the unit
189+ messageMeans = { messageMeans }
190+ />
191+ </ Fragment >
192+ ) ) }
193+ </ div >
194+ </ ScrollArea >
195+ </ Tabs . Panel >
196+ < Tabs . Panel
197+ value = "data_table"
198+ className = "min-h-0 h-full pb-4"
199+ >
200+ < DataTable />
201+ </ Tabs . Panel >
202+ </ Tabs >
146203 </ div >
147- </ ScrollArea >
148- </ Tabs . Panel >
149- < Tabs . Panel value = "data_table" className = "min-h-0 h-full pb-4" >
150- < DataTable />
151- </ Tabs . Panel >
152- </ Tabs >
153- </ div >
204+ </ Panel >
205+ </ PanelGroup >
206+ </ Panel >
207+ </ PanelGroup >
208+
154209 < SavePresetModal
155210 isSavePresetModalOpen = { isSavePresetModalOpen }
156211 closeSavePresetModal = { closeSavePresetModal }
0 commit comments