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() {
100 } = useScenarioStore(
101 useShallow((state) => ({
102 globalParameters: state.globalParameters,
103 waveforms: state.waveforms,
104 timings: state.timings,
105 antennas: state.antennas,
106 platforms: state.platforms,
107 selectedItemId: state.selectedItemId,
108 selectItem: state.selectItem,
109 addWaveform: state.addWaveform,
110 addTiming: state.addTiming,
111 addAntenna: state.addAntenna,
112 addPlatform: state.addPlatform,
113 removeItem: state.removeItem,
117 const handleSelect = (
118 _event: React.SyntheticEvent | null,
119 nodeId: string | null
127 if (nodeId && rootNodes.includes(nodeId)) {
138 flexDirection: 'column',
141 {/* 1. Fixed Header */}
145 justifyContent: 'space-between',
146 alignItems: 'center',
153 <Typography variant="overline" sx={{ color: 'text.secondary' }}>
160 <Divider sx={{ mx: 2, mb: 1 }} />
162 {/* 2. Scrollable Content Area */}
165 flexGrow: 1, // Takes up all remaining space
166 overflowY: 'auto', // Enables vertical scrolling
167 minHeight: 0, // Crucial for flexbox scrolling
168 px: 2, // Apply horizontal padding inside scroll area
172 selectedItems={selectedItemId}
173 onSelectedItemsChange={handleSelect}
175 collapseIcon: ExpandMoreIcon,
176 expandIcon: ChevronRightIcon,
179 // Show remove button on hover
180 '& .MuiTreeItem-content .remove-button': {
181 visibility: 'hidden',
183 '& .MuiTreeItem-content:hover .remove-button': {
184 visibility: 'visible',
186 '& .MuiTreeItem-content': { py: 0.5 },
190 itemId={globalParameters.id}
192 <Box sx={{ display: 'flex', alignItems: 'center' }}>
193 <PublicIcon sx={{ mr: 1 }} fontSize="small" />
194 <Typography variant="body2">
201 itemId="waveforms-root"
206 alignItems: 'center',
210 <WavesIcon sx={{ mr: 1 }} fontSize="small" />
218 {waveforms.map((waveform) => (
226 alignItems: 'center',
238 whiteSpace: 'nowrap',
240 textOverflow: 'ellipsis',
249 className="remove-button"
252 removeItem(waveform.id);
255 <RemoveIcon fontSize="inherit" />
263 itemId="timings-root"
268 alignItems: 'center',
272 <TimerIcon sx={{ mr: 1 }} fontSize="small" />
280 {timings.map((timing) => (
288 alignItems: 'center',
300 whiteSpace: 'nowrap',
302 textOverflow: 'ellipsis',
311 className="remove-button"
314 removeItem(timing.id);
317 <RemoveIcon fontSize="inherit" />
325 itemId="antennas-root"
330 alignItems: 'center',
334 <SettingsInputAntennaIcon
345 {antennas.map((antenna) => (
353 alignItems: 'center',
357 <SettingsInputAntennaIcon
365 whiteSpace: 'nowrap',
367 textOverflow: 'ellipsis',
376 className="remove-button"
379 removeItem(antenna.id);
382 <RemoveIcon fontSize="inherit" />
390 itemId="platforms-root"
395 alignItems: 'center',
399 <FlightIcon sx={{ mr: 1 }} fontSize="small" />
407 {platforms.map((platform) => {
416 alignItems: 'center',
420 {getPlatformIcon(platform)}
425 whiteSpace: 'nowrap',
427 textOverflow: 'ellipsis',
436 className="remove-button"
439 removeItem(platform.id);
442 <RemoveIcon fontSize="inherit" />