mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
261 lines
10 KiB
TypeScript
261 lines
10 KiB
TypeScript
import React, { useEffect, useRef, useState } from "react";
|
|
import { OlRoundStateButton, OlStateButton, OlLockStateButton } from "../components/olstatebutton";
|
|
import { faSkull, faCamera, faFlag, faVolumeHigh, faDownload, faUpload } from "@fortawesome/free-solid-svg-icons";
|
|
import { OlDropdownItem, OlDropdown } from "../components/oldropdown";
|
|
import { OlLabelToggle } from "../components/ollabeltoggle";
|
|
import { getApp, IP } from "../../olympusapp";
|
|
import {
|
|
olButtonsVisibilityAirbase,
|
|
olButtonsVisibilityAircraft,
|
|
olButtonsVisibilityDcs,
|
|
olButtonsVisibilityGroundunit,
|
|
olButtonsVisibilityGroundunitSam,
|
|
olButtonsVisibilityHelicopter,
|
|
olButtonsVisibilityHuman,
|
|
olButtonsVisibilityNavyunit,
|
|
olButtonsVisibilityOlympus,
|
|
} from "../components/olicons";
|
|
import { FaChevronLeft, FaChevronRight, FaFloppyDisk } from "react-icons/fa6";
|
|
import { CommandModeOptionsChangedEvent, ConfigLoadedEvent, HiddenTypesChangedEvent, MapOptionsChangedEvent, MapSourceChangedEvent, SessionDataChangedEvent, SessionDataSavedEvent } from "../../events";
|
|
import { BLUE_COMMANDER, COMMAND_MODE_OPTIONS_DEFAULTS, ImportExportSubstate, MAP_HIDDEN_TYPES_DEFAULTS, MAP_OPTIONS_DEFAULTS, OlympusState } from "../../constants/constants";
|
|
import { OlympusConfig } from "../../interfaces";
|
|
import { FaCheck, FaSave, FaSpinner } from "react-icons/fa";
|
|
|
|
export function Header() {
|
|
const [mapHiddenTypes, setMapHiddenTypes] = useState(MAP_HIDDEN_TYPES_DEFAULTS);
|
|
const [mapOptions, setMapOptions] = useState(MAP_OPTIONS_DEFAULTS);
|
|
const [mapSource, setMapSource] = useState("");
|
|
const [mapSources, setMapSources] = useState([] as string[]);
|
|
const [scrolledLeft, setScrolledLeft] = useState(true);
|
|
const [scrolledRight, setScrolledRight] = useState(false);
|
|
const [audioEnabled, setAudioEnabled] = useState(false);
|
|
const [commandModeOptions, setCommandModeOptions] = useState(COMMAND_MODE_OPTIONS_DEFAULTS);
|
|
const [savingSessionData, setSavingSessionData] = useState(false);
|
|
|
|
useEffect(() => {
|
|
HiddenTypesChangedEvent.on((hiddenTypes) => setMapHiddenTypes({ ...hiddenTypes }));
|
|
MapOptionsChangedEvent.on((mapOptions) => setMapOptions({ ...mapOptions }));
|
|
MapSourceChangedEvent.on((source) => setMapSource(source));
|
|
ConfigLoadedEvent.on((config: OlympusConfig) => {
|
|
// Timeout needed to make sure the map configuration has updated
|
|
window.setTimeout(() => {
|
|
var sources = Object.keys(getApp().getMap().getMirrors()).concat(getApp().getMap().getLayers());
|
|
setMapSources(sources);
|
|
}, 200);
|
|
});
|
|
CommandModeOptionsChangedEvent.on((commandModeOptions) => {
|
|
setCommandModeOptions(commandModeOptions);
|
|
});
|
|
SessionDataChangedEvent.on(() => setSavingSessionData(true));
|
|
SessionDataSavedEvent.on(() => setSavingSessionData(false));
|
|
}, []);
|
|
|
|
/* Initialize the "scroll" position of the element */
|
|
var scrollRef = useRef(null);
|
|
useEffect(() => {
|
|
if (scrollRef.current) onScroll(scrollRef.current);
|
|
});
|
|
|
|
function onScroll(el) {
|
|
const sl = el.scrollLeft;
|
|
const sr = el.scrollWidth - el.scrollLeft - el.clientWidth;
|
|
|
|
sl < 1 && !scrolledLeft && setScrolledLeft(true);
|
|
sl > 1 && scrolledLeft && setScrolledLeft(false);
|
|
|
|
sr < 1 && !scrolledRight && setScrolledRight(true);
|
|
sr > 1 && scrolledRight && setScrolledRight(false);
|
|
}
|
|
|
|
return (
|
|
<nav
|
|
className={`
|
|
z-10 flex w-full gap-4 border-gray-200 bg-gray-300 px-3 drop-shadow-md
|
|
align-center
|
|
dark:border-gray-800 dark:bg-olympus-900
|
|
`}
|
|
>
|
|
<img src="images/icon.png" className={`my-auto h-10 w-10 rounded-md p-0`}></img>
|
|
{!scrolledLeft && (
|
|
<FaChevronLeft
|
|
className={`
|
|
absolute left-14 h-full w-6 rounded-lg px-2 py-3.5 text-gray-200
|
|
dark:bg-olympus-900
|
|
`}
|
|
/>
|
|
)}
|
|
<div
|
|
className={`
|
|
my-2 flex w-full items-center gap-3 overflow-x-scroll no-scrollbar
|
|
`}
|
|
onScroll={(ev) => onScroll(ev.target)}
|
|
ref={scrollRef}
|
|
>
|
|
<div
|
|
className={`
|
|
mr-auto hidden flex-none flex-row items-center justify-start gap-2
|
|
lg:flex
|
|
`}
|
|
>
|
|
<div className="mr-2 flex flex-col items-start">
|
|
<div
|
|
className={`
|
|
pt-1 text-xs text-gray-800
|
|
dark:text-gray-400
|
|
`}
|
|
>
|
|
Connected to
|
|
</div>
|
|
<div
|
|
className={`
|
|
flex items-center justify-center gap-2 text-sm font-extrabold
|
|
text-gray-800
|
|
dark:text-gray-200
|
|
`}
|
|
>
|
|
{IP}
|
|
</div>
|
|
</div>
|
|
{savingSessionData ? <div className="text-white"><FaSpinner className={`
|
|
animate-spin text-2xl
|
|
`}/></div> : <div className={`relative text-white`}><FaFloppyDisk className={`
|
|
absolute -top-3 text-2xl
|
|
`}/><FaCheck className={`
|
|
absolute left-[9px] top-[-6px] text-2xl text-olympus-900
|
|
`}/><FaCheck className={`absolute left-3 top-0 text-green-500`}/></div>}
|
|
<OlStateButton className="ml-8" icon={faDownload} onClick={() => {getApp().setState(OlympusState.IMPORT_EXPORT, ImportExportSubstate.EXPORT)}} checked={false}/>
|
|
<OlStateButton icon={faUpload} onClick={() => {getApp().setState(OlympusState.IMPORT_EXPORT, ImportExportSubstate.IMPORT)}} checked={false}/>
|
|
</div>
|
|
|
|
{commandModeOptions.commandMode === BLUE_COMMANDER && (
|
|
<div className={`flex h-full rounded-md bg-blue-600 px-4 text-white`}>
|
|
<span className="my-auto font-bold">BLUE Commander ({commandModeOptions.spawnPoints.blue} points)</span>
|
|
</div>
|
|
)}
|
|
<div className={`flex h-fit flex-row items-center justify-start gap-1`}>
|
|
<OlLockStateButton
|
|
checked={!mapOptions.protectDCSUnits}
|
|
onClick={() => {
|
|
getApp().getMap().setOption("protectDCSUnits", !mapOptions.protectDCSUnits);
|
|
}}
|
|
tooltip="Lock/unlock protected units (from scripted mission)"
|
|
/>
|
|
<OlRoundStateButton
|
|
checked={audioEnabled}
|
|
onClick={() => {
|
|
audioEnabled ? getApp().getAudioManager().stop() : getApp().getAudioManager().start();
|
|
setAudioEnabled(!audioEnabled);
|
|
}}
|
|
tooltip="Enable/disable audio and radio backend"
|
|
icon={faVolumeHigh}
|
|
/>
|
|
</div>
|
|
<div className={`h-8 w-0 border-l-[2px] border-gray-700`}></div>
|
|
<div className={`flex h-fit flex-row items-center justify-start gap-1`}>
|
|
{Object.entries({
|
|
human: olButtonsVisibilityHuman,
|
|
olympus: olButtonsVisibilityOlympus,
|
|
dcs: olButtonsVisibilityDcs,
|
|
}).map((entry) => {
|
|
return (
|
|
<OlRoundStateButton
|
|
key={entry[0]}
|
|
onClick={() => {
|
|
getApp().getMap().setHiddenType(entry[0], !mapHiddenTypes[entry[0]]);
|
|
}}
|
|
checked={!mapHiddenTypes[entry[0]]}
|
|
icon={entry[1]}
|
|
tooltip={"Hide/show " + entry[0] + " units"}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
<div className={`h-8 w-0 border-l-[2px] border-gray-700`}></div>
|
|
<div className={`flex h-fit flex-row items-center justify-start gap-1`}>
|
|
<OlRoundStateButton
|
|
onClick={() => getApp().getMap().setHiddenType("blue", !mapHiddenTypes["blue"])}
|
|
checked={!mapHiddenTypes["blue"]}
|
|
icon={faFlag}
|
|
className={"!text-blue-500"}
|
|
tooltip={"Hide/show blue units"}
|
|
/>
|
|
<OlRoundStateButton
|
|
onClick={() => getApp().getMap().setHiddenType("red", !mapHiddenTypes["red"])}
|
|
checked={!mapHiddenTypes["red"]}
|
|
icon={faFlag}
|
|
className={"!text-red-500"}
|
|
tooltip={"Hide/show red units"}
|
|
/>
|
|
<OlRoundStateButton
|
|
onClick={() => getApp().getMap().setHiddenType("neutral", !mapHiddenTypes["neutral"])}
|
|
checked={!mapHiddenTypes["neutral"]}
|
|
icon={faFlag}
|
|
className={"!text-gray-500"}
|
|
tooltip={"Hide/show neutral units"}
|
|
/>
|
|
</div>
|
|
<div className={`h-8 w-0 border-l-[2px] border-gray-700`}></div>
|
|
<div className={`flex h-fit flex-row items-center justify-start gap-1`}>
|
|
{Object.entries({
|
|
aircraft: olButtonsVisibilityAircraft,
|
|
helicopter: olButtonsVisibilityHelicopter,
|
|
"groundunit-sam": olButtonsVisibilityGroundunitSam,
|
|
groundunit: olButtonsVisibilityGroundunit,
|
|
navyunit: olButtonsVisibilityNavyunit,
|
|
airbase: olButtonsVisibilityAirbase,
|
|
dead: faSkull,
|
|
}).map((entry) => {
|
|
return (
|
|
<OlRoundStateButton
|
|
key={entry[0]}
|
|
onClick={() => {
|
|
getApp().getMap().setHiddenType(entry[0], !mapHiddenTypes[entry[0]]);
|
|
}}
|
|
checked={!mapHiddenTypes[entry[0]]}
|
|
icon={entry[1]}
|
|
tooltip={"Hide/show " + entry[0] + " units"}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<OlLabelToggle
|
|
toggled={mapOptions.cameraPluginMode === "map"}
|
|
leftLabel={"Live"}
|
|
rightLabel={"Map"}
|
|
onClick={() => {
|
|
getApp()
|
|
.getMap()
|
|
.setOption("cameraPluginMode", mapOptions.cameraPluginMode === "live" ? "map" : "live");
|
|
}}
|
|
/>
|
|
<OlStateButton
|
|
checked={mapOptions.cameraPluginEnabled}
|
|
icon={faCamera}
|
|
onClick={() => {
|
|
getApp().getMap().setOption("cameraPluginEnabled", !mapOptions.cameraPluginEnabled);
|
|
}}
|
|
tooltip="Activate/deactivate camera plugin"
|
|
/>
|
|
<OlDropdown label={mapSource} className="w-60">
|
|
{mapSources.map((source) => {
|
|
return (
|
|
<OlDropdownItem key={source} onClick={() => getApp().getMap().setLayerName(source)}>
|
|
<div className="truncate">{source}</div>
|
|
</OlDropdownItem>
|
|
);
|
|
})}
|
|
</OlDropdown>
|
|
</div>
|
|
{!scrolledRight && (
|
|
<FaChevronRight
|
|
className={`
|
|
absolute right-0 h-full w-6 rounded-lg px-2 py-3.5 text-gray-200
|
|
dark:bg-olympus-900
|
|
`}
|
|
/>
|
|
)}
|
|
</nav>
|
|
);
|
|
}
|