1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (c) 2025-present FERS Contributors (see AUTHORS.md).
4import { SimpleTreeView, TreeItem } from '@mui/x-tree-view';
5import { useScenarioStore, Platform } from '@/stores/scenarioStore';
6import { Box, Typography, IconButton, Tooltip, Divider } from '@mui/material';
7import { useShallow } from 'zustand/react/shallow';
9import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
10import ChevronRightIcon from '@mui/icons-material/ChevronRight';
11import PublicIcon from '@mui/icons-material/Public';
12import WavesIcon from '@mui/icons-material/Waves';
13import TimerIcon from '@mui/icons-material/Timer';
14import SettingsInputAntennaIcon from '@mui/icons-material/SettingsInputAntenna';
15import FlightIcon from '@mui/icons-material/Flight';
16import AddIcon from '@mui/icons-material/Add';
17import RemoveIcon from '@mui/icons-material/Remove';
18import SensorsIcon from '@mui/icons-material/Sensors';
19import PodcastsIcon from '@mui/icons-material/Podcasts';
20import RssFeedIcon from '@mui/icons-material/RssFeed';
21import AdjustIcon from '@mui/icons-material/Adjust';
23import ScenarioIO from './ScenarioIO';
24import React from 'react';
26const SectionHeader = ({
36 justifyContent: 'space-between',
39 minWidth: 0, // Allow the component to shrink within its flex container
45 color: 'text.secondary',
48 textOverflow: 'ellipsis',
49 pr: 1, // Padding between text and add button
54 <Tooltip title={`Add ${title.slice(0, -1)}`}>
62 <AddIcon fontSize="inherit" />
68const getPlatformIcon = (platform: Platform) => {
69 const types = platform.components.map((c) => c.type);
71 if (types.includes('monostatic')) {
72 return <SensorsIcon sx={{ mr: 1 }} fontSize="small" />;
74 if (types.includes('transmitter')) {
75 return <PodcastsIcon sx={{ mr: 1 }} fontSize="small" />;
77 if (types.includes('receiver')) {
78 return <RssFeedIcon sx={{ mr: 1 }} fontSize="small" />;
80 if (types.includes('target')) {
81 return <AdjustIcon sx={{ mr: 1 }} fontSize="small" />;
83 return <FlightIcon sx={{ mr: 1 }} fontSize="small" />;
86export default function SceneTree() {
101 } = useScenarioStore(
102 useShallow((state) => ({
103 globalParameters: state.globalParameters,
104 waveforms: state.waveforms,
105 timings: state.timings,
106 antennas: state.antennas,
107 platforms: state.platforms,
108 selectedItemId: state.selectedItemId,
109 selectedComponentId: state.selectedComponentId,
110 selectItem: state.selectItem,
111 addWaveform: state.addWaveform,
112 addTiming: state.addTiming,
113 addAntenna: state.addAntenna,
114 addPlatform: state.addPlatform,
115 removeItem: state.removeItem,
119 const handleSelect = (
120 _event: React.SyntheticEvent | null,
121 nodeId: string | null
129 if (nodeId && rootNodes.includes(nodeId)) {
140 flexDirection: 'column',
143 {/* 1. Fixed Header */}
147 justifyContent: 'space-between',
148 alignItems: 'center',
155 <Typography variant="overline" sx={{ color: 'text.secondary' }}>
162 <Divider sx={{ mx: 2, mb: 1 }} />
164 {/* 2. Scrollable Content Area */}
167 flexGrow: 1, // Takes up all remaining space
168 overflowY: 'auto', // Enables vertical scrolling
169 minHeight: 0, // Crucial for flexbox scrolling
170 px: 2, // Apply horizontal padding inside scroll area
174 selectedItems={selectedComponentId ?? selectedItemId}
175 onSelectedItemsChange={handleSelect}
177 collapseIcon: ExpandMoreIcon,
178 expandIcon: ChevronRightIcon,
181 // Show remove button on hover
182 '& .MuiTreeItem-content .remove-button': {
183 visibility: 'hidden',
185 '& .MuiTreeItem-content:hover .remove-button': {
186 visibility: 'visible',
188 '& .MuiTreeItem-content': { py: 0.5 },
192 itemId={globalParameters.id}
194 <Box sx={{ display: 'flex', alignItems: 'center' }}>
195 <PublicIcon sx={{ mr: 1 }} fontSize="small" />
196 <Typography variant="body2">
203 itemId="waveforms-root"
208 alignItems: 'center',
212 <WavesIcon sx={{ mr: 1 }} fontSize="small" />
220 {waveforms.map((waveform) => (
228 alignItems: 'center',
240 whiteSpace: 'nowrap',
242 textOverflow: 'ellipsis',
251 className="remove-button"
254 removeItem(waveform.id);
257 <RemoveIcon fontSize="inherit" />
265 itemId="timings-root"
270 alignItems: 'center',
274 <TimerIcon sx={{ mr: 1 }} fontSize="small" />
282 {timings.map((timing) => (
290 alignItems: 'center',
302 whiteSpace: 'nowrap',
304 textOverflow: 'ellipsis',
313 className="remove-button"
316 removeItem(timing.id);
319 <RemoveIcon fontSize="inherit" />
327 itemId="antennas-root"
332 alignItems: 'center',
336 <SettingsInputAntennaIcon
347 {antennas.map((antenna) => (
355 alignItems: 'center',
359 <SettingsInputAntennaIcon
367 whiteSpace: 'nowrap',
369 textOverflow: 'ellipsis',
378 className="remove-button"
381 removeItem(antenna.id);
384 <RemoveIcon fontSize="inherit" />
392 itemId="platforms-root"
397 alignItems: 'center',
401 <FlightIcon sx={{ mr: 1 }} fontSize="small" />
409 {platforms.map((platform) => {
418 alignItems: 'center',
422 {getPlatformIcon(platform)}
427 whiteSpace: 'nowrap',
429 textOverflow: 'ellipsis',
438 className="remove-button"
441 removeItem(platform.id);
444 <RemoveIcon fontSize="inherit" />