diff --git a/src/components/panels/MosaicDashboard.tsx b/src/components/panels/MosaicDashboard.tsx index d984e79..14fa730 100644 --- a/src/components/panels/MosaicDashboard.tsx +++ b/src/components/panels/MosaicDashboard.tsx @@ -18,7 +18,6 @@ import GasSensor from './GasSensor'; import NetworkHealthTelemetryPanel from './NetworkHealthTelemetryPanel'; import VideoControls from './VideoControls'; import MotorStatusPanel from './MotorStatusPanel'; -import { v4 as uuidv4 } from 'uuid'; import AntennaControlPanel from './AntennaControlPanel'; type TileType = @@ -33,7 +32,7 @@ type TileType = | 'MotorStatusPanel' | 'antennaControlPanel'; -type TileId = `${TileType}:${string}`; +type TileId = `${TileType}:${number}`; const TILE_DISPLAY_NAMES: Record = { mapView: 'Map View', @@ -61,11 +60,6 @@ const ALL_TILE_TYPES: TileType[] = [ 'antennaControlPanel', ]; -function makeTileId(type: TileType): TileId { - const uid = uuidv4(); - return `${type}:${uid}`; -} - function tileTypeOf(id: TileId): TileType { return id.split(':', 1)[0] as TileType; } @@ -78,19 +72,81 @@ type PendingAdd = } | null; +function collectMaxTileNumber(node: MosaicNode | null): number { + if (!node) return 0; + + if (typeof node === 'string') { + const [, num] = node.split(':'); + return Number(num) || 0; + } + + return Math.max(collectMaxTileNumber(node.first), collectMaxTileNumber(node.second)); +} + +function encodeLayout(layout: MosaicNode | null): string { + return encodeURIComponent(JSON.stringify(layout)); +} + +function decodeLayout(value: string | null): MosaicNode | null { + if (!value) return null; + + try { + return JSON.parse(decodeURIComponent(value)) as MosaicNode; + } catch { + return null; + } +} + +function buildDefaultLayout(makeTileId: (type: TileType) => TileId): MosaicNode { + return { + direction: 'row', + first: { + direction: 'column', + first: makeTileId('mapView'), + second: { + direction: 'row', + first: { + direction: 'row', + first: makeTileId('rosMonitor'), + second: makeTileId('networkHealthMonitor'), + }, + second: makeTileId('orientationDisplay'), + splitPercentage: 55, + }, + splitPercentage: 55, + }, + second: { + direction: 'column', + first: makeTileId('videoControls'), + second: { + direction: 'row', + first: makeTileId('waypointList'), + second: { + direction: 'row', + first: makeTileId('gasSensor'), + second: makeTileId('goalSetter'), + }, + splitPercentage: 50, + }, + splitPercentage: 50, + }, + splitPercentage: 60, + }; +} + const Controls = memo<{ id: TileId; path: MosaicPath; pendingAdd: PendingAdd; setPendingAdd: (value: PendingAdd) => void; -}>(({ id, path, pendingAdd, setPendingAdd }) => { + makeTileId: (type: TileType) => TileId; +}>(({ id, path, pendingAdd, setPendingAdd, makeTileId }) => { const { mosaicActions } = useContext(MosaicContext); const pathKey = JSON.stringify(path); const showDropdown = pendingAdd?.pathKey === pathKey; const dropdownRef = useRef(null); const splitAndAdd = (direction: 'row' | 'column', newType: TileType) => { - const newId = makeTileId(newType); const splitNode: MosaicNode = { @@ -119,8 +175,6 @@ const Controls = memo<{ }; }, [showDropdown, setPendingAdd]); - const currentType = tileTypeOf(id); - return (