More work on unit control panel

This commit is contained in:
Davide Passoni
2024-04-12 17:53:03 +02:00
parent c7ecd2422a
commit 40df2ebb7d
23 changed files with 820 additions and 453 deletions

View File

@@ -0,0 +1,23 @@
import { IconProp } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React from "react";
export function OlButtonGroup(props: {
children?: JSX.Element | JSX.Element[]
}) {
return <div className="inline-flex rounded-md shadow-sm" >
{props.children}
</div>
}
export function OlButtonGroupItem(props: {
icon: IconProp
active: boolean,
onClick: () => void
}) {
return <button onClick={props.onClick} type="button" data-active={props.active} className="h-11 w-11 first-of-type:rounded-s-md last-of-type:rounded-e-md py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-700 dark:data-[active='true']:bg-blue-500 dark:border-gray-600 dark:text-white dark:hover:text-white dark:hover:bg-gray-500 dark:focus:ring-blue-500 dark:focus:text-white">
<FontAwesomeIcon icon={props.icon} />
</button>
}

View File

@@ -2,18 +2,21 @@ import React from "react";
import { Coalition } from "../../types/types";
export function OlCoalitionToggle(props: {
coalition: Coalition,
coalition: Coalition | undefined,
onClick: () => void
}) {
return <div className="inline-flex items-center cursor-pointer" onClick={props.onClick}>
<button className="sr-only peer" />
<div data-coalition={props.coalition} className={"relative w-14 h-7 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-300 " +
<div data-flash={props.coalition === undefined} data-coalition={props.coalition ?? 'blue'} className={"relative w-14 h-7 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-300 " +
"dark:peer-focus:ring-blue-800 rounded-full peer " +
" after:content-[''] after:absolute after:top-0.5 after:start-[4px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-6 " +
"after:w-6 after:transition-all dark:border-gray-600 " +
"data-[coalition='neutral']:after:translate-x-[50%] rtl:data-[coalition='neutral']:after:-translate-x-[50%] data-[coalition='neutral']:after:border-white " +
"data-[coalition='red']:after:translate-x-full rtl:data-[coalition='red']:after:-translate-x-full data-[coalition='red']:after:border-white " +
" data-[coalition='blue']:bg-blue-600 data-[coalition='neutral']:bg-gray-400 data-[coalition='red']:bg-red-500"}></div>
<span className="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300">Coalition ({props.coalition[0].toLocaleUpperCase() + props.coalition.substring(1)})</span>
" data-[coalition='blue']:bg-blue-600 data-[coalition='neutral']:bg-gray-400 data-[coalition='red']:bg-red-500"}>
</div>
<span className="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300 data-[flash='true']:after:animate-pulse">
{props.coalition? `Coalition (${props.coalition[0].toLocaleUpperCase() + props.coalition.substring(1)})`: "Different values"}
</span>
</div>
}

View File

@@ -74,7 +74,7 @@ export function OlDropdown(props: {
})
return <div className={(props.className ?? "") + " relative"}>
<button ref={buttonRef} onClick={() => { setOpen(!open) }} className={"w-full text-white bg-blue-700 hover:bg-blue-800 focus:ring-2 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center justify-between border-[1px] dark:border-gray-600 dark:text-gray-100 dark:bg-gray-700 dark:hover:bg-gray-800 dark:focus:ring-blue-800"} type="button">
<button ref={buttonRef} onClick={() => { setOpen(!open) }} className={"w-full text-white bg-blue-700 hover:bg-blue-800 focus:ring-2 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center justify-between border dark:border-gray-600 dark:text-gray-100 dark:bg-gray-700 dark:hover:bg-gray-800 dark:focus:ring-blue-800"} type="button">
{props.leftIcon && <FontAwesomeIcon icon={props.leftIcon} className="mr-3" />}
<span className="text-nowrap text-ellipsis overflow-hidden">
{props.label}

File diff suppressed because one or more lines are too long

View File

@@ -6,9 +6,9 @@ export function OlLabelToggle(props: {
rightLabel: string,
onClick: () => void
}) {
return <button onClick={props.onClick} className=" relative flex flex-row flex-none my-auto contents-center justify-between w-32 h-10 border-[1px] dark:border-transparent dark:bg-[#2A3949] rounded-lg py-[5px] px-1 select-none cursor-pointer focus:ring-2 focus:outline-none focus:ring-blue-300 dark:hover:bg-gray-800 dark:focus:ring-blue-800">
<span data-toggled={props.toggled} className="absolute my-auto h-[28px] w-[54px] bg-blue-500 rounded-md data-[toggled='true']:translate-x-16 transition-transform"></span>
<span data-active={!props.toggled} className="my-auto dark:data-[active='true']:text-white font-normal dark:data-[active='false']:text-gray-400 pl-3 z-ui-2 transition-colors">{props.leftLabel}</span>
<span data-active={props.toggled} className="my-auto dark:data-[active='true']:text-white font-normal dark:data-[active='false']:text-gray-400 pr-3 z-ui-2 transition-colors">{props.rightLabel}</span>
return <button onClick={props.onClick} className=" relative flex flex-row flex-none my-auto contents-center justify-between w-32 h-10 border dark:border-transparent dark:bg-[#2A3949] rounded-lg py-[5px] px-1 select-none cursor-pointer focus:ring-2 focus:outline-none focus:ring-blue-300 dark:hover:bg-gray-800 dark:focus:ring-blue-800">
<span data-flash={props.toggled === undefined} data-toggled={props.toggled ?? false} className="data-[flash='true']:animate-pulse absolute my-auto h-[28px] w-[54px] bg-blue-500 rounded-md data-[toggled='true']:translate-x-16 transition-transform"></span>
<span data-active={!(props.toggled ?? false)} className="my-auto dark:data-[active='true']:text-white font-normal dark:data-[active='false']:text-gray-400 pl-3 z-ui-2 transition-colors">{props.leftLabel}</span>
<span data-active={props.toggled ?? false} className="my-auto dark:data-[active='true']:text-white font-normal dark:data-[active='false']:text-gray-400 pr-3 z-ui-2 transition-colors">{props.rightLabel}</span>
</button>
}

View File

@@ -21,7 +21,7 @@ export function OlRangeSlider(props: {
return <input type="range"
ref={elementRef}
onChange={props.onChange}
value={props.value}
value={props.value ?? 0}
min={props.min ?? 0}
max={props.max ?? 100}
step={props.step ?? 1}

View File

@@ -0,0 +1,16 @@
import React from "react";
export function OlToggle(props: {
toggled: boolean | undefined,
onClick: () => void
}) {
return <div className="inline-flex items-center cursor-pointer" onClick={props.onClick}>
<button className="sr-only peer" />
<div data-flash={props.toggled === undefined} data-toggled={props.toggled ?? false} className={"relative w-14 h-7 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-300 " +
"dark:peer-focus:ring-blue-800 rounded-full peer " +
" after:content-[''] after:absolute after:top-0.5 after:start-[4px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-6 " +
"after:w-6 after:transition-all dark:border-gray-600 " +
"data-[toggled='true']:after:translate-x-full rtl:data-[toggled='true']:after:-translate-x-full data-[toggled='true']:after:border-white " +
"data-[toggled='false']:bg-gray-500 dark:data-[toggled='true']:bg-blue-500 data-[flash='true']:after:animate-pulse"}></div>
</div>
}

View File

@@ -6,6 +6,7 @@ import { StateConsumer } from '../../statecontext';
import { OlDropdownItem, OlDropdown } from '../components/oldropdown';
import { OlLabelToggle } from '../components/ollabeltoggle';
import { getApp } from '../../olympusapp';
import { olButtonsVisibilityAirbase, olButtonsVisibilityAircraft, olButtonsVisibilityDcs, olButtonsVisibilityGroundunit, olButtonsVisibilityGroundunitSam, olButtonsVisibilityHelicopter, olButtonsVisibilityHuman, olButtonsVisibilityNavyunit, olButtonsVisibilityOlympus } from '../components/olicons';
export function Header() {
return <StateConsumer>
@@ -23,7 +24,7 @@ export function Header() {
<div className="flex flex-row h-fit items-center justify-start gap-2">
{
Object.entries({
'human': faPerson,'olympus': faBrain, 'dcs': faRobot
'human': olButtonsVisibilityHuman,'olympus': olButtonsVisibilityOlympus, 'dcs': olButtonsVisibilityDcs
}).map((entry) => {
return <OlRoundStateButton
onClick={() => {
@@ -38,8 +39,8 @@ export function Header() {
<div className="flex flex-row h-fit items-center justify-start gap-1">
{
Object.entries({
'aircraft': faJetFighter,'helicopter': faHelicopter, 'groundunit-sam': faShieldAlt,
'groundunit': faTruck, 'navyunit': faShip, 'airbase': faPlaneDeparture, 'dead': faSkull
'aircraft': olButtonsVisibilityAircraft,'helicopter': olButtonsVisibilityHelicopter, 'groundunit-sam': olButtonsVisibilityGroundunitSam,
'groundunit': olButtonsVisibilityGroundunit, 'navyunit': olButtonsVisibilityNavyunit, 'airbase': olButtonsVisibilityAirbase, 'dead': faSkull
}).map((entry) => {
return <OlRoundStateButton
onClick={() => {

View File

@@ -8,6 +8,7 @@ import { getApp } from "../../olympusapp";
import { OlUnitEntryList } from "../components/olunitlistentry";
import { UnitSpawnMenu } from "./unitspawnmenu";
import { UnitBlueprint } from "../../interfaces";
import { olButtonsVisibilityAircraft, olButtonsVisibilityGroundunit, olButtonsVisibilityGroundunitSam, olButtonsVisibilityHelicopter, olButtonsVisibilityNavyunit } from "../components/olicons";
library.add(faPlus);
@@ -62,7 +63,7 @@ export function SpawnMenu(props: {
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
{Object.keys(filteredAircraft).map((key) => {
const blueprint = getApp().getAircraftDatabase().blueprints[key];
return <OlUnitEntryList key={key} icon={faJetFighter} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
return <OlUnitEntryList key={key} icon={olButtonsVisibilityAircraft} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
})}
</div>
</OlAccordion>
@@ -70,7 +71,7 @@ export function SpawnMenu(props: {
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
{Object.keys(filteredHelicopters).map((key) => {
const blueprint = getApp().getHelicopterDatabase().blueprints[key];
return <OlUnitEntryList key={key} icon={faHelicopter} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
return <OlUnitEntryList key={key} icon={olButtonsVisibilityHelicopter} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
})}
</div>
</OlAccordion>
@@ -78,7 +79,7 @@ export function SpawnMenu(props: {
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
{Object.keys(filteredAirDefense).map((key) => {
const blueprint = getApp().getGroundUnitDatabase().blueprints[key];
return <OlUnitEntryList key={key} icon={faShieldAlt} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
return <OlUnitEntryList key={key} icon={olButtonsVisibilityGroundunitSam} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
})}
</div>
</OlAccordion>
@@ -86,7 +87,7 @@ export function SpawnMenu(props: {
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
{Object.keys(filteredGroundUnits).map((key) => {
const blueprint = getApp().getGroundUnitDatabase().blueprints[key];
return <OlUnitEntryList key={key} icon={faTruck} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
return <OlUnitEntryList key={key} icon={olButtonsVisibilityGroundunit} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
})}
</div>
</OlAccordion>
@@ -94,7 +95,7 @@ export function SpawnMenu(props: {
<div className="flex flex-col gap-1 max-h-80 overflow-y-scroll">
{Object.keys(filteredNavyUnits).map((key) => {
const blueprint = getApp().getNavyUnitDatabase().blueprints[key];
return <OlUnitEntryList key={key} icon={faShip} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
return <OlUnitEntryList key={key} icon={olButtonsVisibilityNavyunit} blueprint={blueprint} onClick={() => setBlueprint(blueprint)} />
})}
</div>
</OlAccordion>

View File

@@ -1,38 +1,57 @@
import React, { useState } from "react";
import { Menu } from "./components/menu";
import { faGamepad } from '@fortawesome/free-solid-svg-icons';
import { library } from '@fortawesome/fontawesome-svg-core'
import { Unit } from "../../unit/unit";
import { OlLabelToggle } from "../components/ollabeltoggle";
import { OlRangeSlider } from "../components/olrangeslider";
import { getApp } from "../../olympusapp";
const defaultUnitControlPanelData = {
desiredAltitude: undefined as undefined | number,
desiredAltitudeType: undefined as undefined | boolean
}
import { OlButtonGroup, OlButtonGroupItem } from "../components/olbuttongroup";
import { ROEs, emissionsCountermeasures, reactionsToThreat } from "../../constants/constants";
import { OlToggle } from "../components/oltoggle";
import { OlCoalitionToggle } from "../components/olcoalitiontoggle";
import { olButtonsEmissionsAttack, olButtonsEmissionsDefend, olButtonsEmissionsFree, olButtonsEmissionsSilent, olButtonsIntensity1, olButtonsIntensity2, olButtonsIntensity3, olButtonsRoeDesignated, olButtonsRoeFree, olButtonsRoeHold, olButtonsRoeReturn, olButtonsScatter1, olButtonsScatter2, olButtonsScatter3, olButtonsThreatEvade, olButtonsThreatManoeuvre, olButtonsThreatNone, olButtonsThreatPassive } from "../components/olicons";
import { Coalition } from "../../types/types";
export function UnitControlMenu() {
var [open, setOpen] = useState(false);
var [selectedUnits, setSelectedUnits] = useState([] as Unit[]);
var [selectedUnitsData, setSelectedUnitsData] = useState(defaultUnitControlPanelData);
var [selectedUnitsRequestedData, setSelectedUnitsRequestedData] = useState(defaultUnitControlPanelData);
var [selectedUnitsData, setSelectedUnitsData] = useState({
desiredAltitude: undefined as undefined | number,
desiredAltitudeType: undefined as undefined | string,
desiredSpeed: undefined as undefined | number,
desiredSpeedType: undefined as undefined | string,
ROE: undefined as undefined | string,
reactionToThreat: undefined as undefined | string,
emissionsCountermeasures: undefined as undefined | string,
shotsScatter: undefined as undefined | number,
shotsIntensity: undefined as undefined | number,
operateAs: undefined as undefined | string,
followRoads: undefined as undefined | boolean,
isActiveAWACS: undefined as undefined | boolean,
isActiveTanker: undefined as undefined | boolean,
onOff: undefined as undefined | boolean
});
/* */
const minAltitude = 0;
const maxAltitude = 60000;
const altitudeStep = 500;
const maxAltitude = getApp()?.getUnitsManager()?.getSelectedUnitsCategories().every((category) => { return category === 'Helicopter'}) ? 20000 : 60000;
const altitudeStep = getApp()?.getUnitsManager()?.getSelectedUnitsCategories().every((category) => { return category === 'Helicopter'}) ? 100 : 500;
const minSpeed = 0;
const maxSpeed = getApp()?.getUnitsManager()?.getSelectedUnitsCategories().every((category) => { return category === 'Helicopter'}) ? 200 : 800;
const speedStep = getApp()?.getUnitsManager()?.getSelectedUnitsCategories().every((category) => { return category === 'Helicopter'}) ? 5 : 10;;
/* When a unit is selected, open the menu */
document.addEventListener("unitsSelection", (ev: CustomEventInit) => {
setOpen(true);
setSelectedUnits(ev.detail as Unit[])
setSelectedUnits(ev.detail as Unit[]);
updateData();
})
/* When a unit is deselected, refresh the view */
document.addEventListener("unitDeselection", (ev: CustomEventInit) => {
/* TODO add delay to avoid doing it too many times */
updateData();
})
/* When all units are selected clean the view */
@@ -41,9 +60,31 @@ export function UnitControlMenu() {
setSelectedUnits([])
})
document.addEventListener("unitUpdated", () => {
/* Update the current values of the shown data */
function updateData() {
const getters = {
desiredAltitude: (unit: Unit) => { return unit.getDesiredAltitude(); },
desiredAltitudeType: (unit: Unit) => { return unit.getDesiredAltitudeType(); },
desiredSpeed: (unit: Unit) => { return unit.getDesiredSpeed(); },
desiredSpeedType: (unit: Unit) => { return unit.getDesiredSpeedType(); },
ROE: (unit: Unit) => { return unit.getROE(); },
reactionToThreat: (unit: Unit) => { return unit.getReactionToThreat(); },
emissionsCountermeasures: (unit: Unit) => { return unit.getROE(); },
shotsScatter: (unit: Unit) => { return unit.getShotsScatter(); },
shotsIntensity: (unit: Unit) => { return unit.getShotsIntensity(); },
operateAs: (unit: Unit) => { return unit.getOperateAs(); },
followRoads: (unit: Unit) => { return unit.getFollowRoads(); },
isActiveAWACS: (unit: Unit) => { return unit.getIsActiveAWACS(); },
isActiveTanker: (unit: Unit) => { return unit.getIsActiveTanker(); },
onOff: (unit: Unit) => { return unit.getOnOff(); },
} as { [key in keyof (typeof selectedUnitsData)]: (unit: Unit) => void }
})
var updatedData = selectedUnitsData;
Object.entries(getters).forEach(([key, getter]) => {
updatedData[key] = getApp()?.getUnitsManager()?.getSelectedUnitsVariable(getter);
});
setSelectedUnitsData(updatedData);
}
/* Count how many units are selected of each type, divided by coalition */
var unitOccurences = {
@@ -51,6 +92,7 @@ export function UnitControlMenu() {
red: {},
neutral: {}
}
selectedUnits.forEach((unit) => {
if (!(unit.getName() in unitOccurences[unit.getCoalition()]))
unitOccurences[unit.getCoalition()][unit.getName()] = 1;
@@ -58,11 +100,14 @@ export function UnitControlMenu() {
unitOccurences[unit.getCoalition()][unit.getName()]++;
})
const selectedCategories = getApp()?.getUnitsManager()?.getSelectedUnitsCategories() ?? [];
return <Menu
open={open}
title="Unit control menu"
onClose={() => { }}
>
{/* Units list */}
<div className="dark:bg-[#243141] h-fit p-0 flex flex-col gap-0">
<div>
{
@@ -85,43 +130,267 @@ export function UnitControlMenu() {
}
</div>
</div>
<div className="p-5">
<div className="p-5 flex flex-col gap-5">
{/* Altitude selector */
selectedCategories.every((category) => { return ['Aircraft', 'Helicopter'].includes(category) }) && <div>
<div className="flex flex-row content-center justify-between">
<div className="flex flex-col">
<span className="font-normal dark:text-white">Altitude</span>
<span
data-flash={selectedUnitsData.desiredAltitude === undefined}
className="data-[flash='true']:animate-pulse dark:text-blue-500">{selectedUnitsData.desiredAltitude !== undefined ? (selectedUnitsData.desiredAltitude + " FT") : "Different values"}
</span>
</div>
<OlLabelToggle
toggled={selectedUnitsData.desiredAltitudeType === undefined ? undefined : selectedUnitsData.desiredAltitudeType === "AGL"}
leftLabel={"AGL"}
rightLabel={"ASL"}
onClick={() => {
selectedUnits.forEach((unit) => {
unit.setAltitudeType((selectedUnitsData.desiredAltitudeType === "ASL") ? "AGL" : "ASL");
setSelectedUnitsData({
...selectedUnitsData,
desiredAltitudeType: (selectedUnitsData.desiredAltitudeType === "ASL") ? "AGL" : "ASL"
})
})
}} />
</div>
<OlRangeSlider
onChange={(ev) => {
selectedUnits.forEach((unit) => {
unit.setAltitude(Number(ev.target.value));
setSelectedUnitsData({
...selectedUnitsData,
desiredAltitude: Number(ev.target.value)
})
})
}}
value={selectedUnitsData.desiredAltitude}
min={minAltitude}
max={maxAltitude}
step={altitudeStep}
/>
</div>
}
{/* Airspeed selector */}
<div>
<div className="flex flex-row content-center justify-between">
<div className="flex flex-col">
<span className="font-normal dark:text-white">Altitude</span>
<span className="dark:text-blue-500">{`${selectedUnitsRequestedData.desiredAltitude} FT`}</span>
<span className="font-normal dark:text-white">Speed</span>
<span
data-flash={selectedUnitsData.desiredSpeed === undefined}
className="data-[flash='true']:animate-pulse dark:text-blue-500">{selectedUnitsData.desiredSpeed !== undefined ? (selectedUnitsData.desiredSpeed + " KTS") : "Different values"}
</span>
</div>
<OlLabelToggle
toggled={selectedUnitsRequestedData.desiredAltitudeType}
leftLabel={"AGL"}
rightLabel={"ASL"}
toggled={selectedUnitsData.desiredSpeedType === undefined ? undefined : selectedUnitsData.desiredSpeedType === "GS"}
leftLabel={"GS"}
rightLabel={"CAS"}
onClick={() => {
selectedUnits.forEach((unit) => {
unit.setAltitudeType((!selectedUnitsRequestedData.desiredAltitudeType) ? "AGL" : "ASL");
setSelectedUnitsRequestedData({
...selectedUnitsRequestedData,
desiredAltitudeType: !selectedUnitsRequestedData.desiredAltitudeType
unit.setSpeedType((selectedUnitsData.desiredSpeedType === "CAS") ? "GS" : "CAS");
setSelectedUnitsData({
...selectedUnitsData,
desiredSpeedType: (selectedUnitsData.desiredSpeedType === "CAS") ? "GS" : "CAS"
})
})
}} />
}}
/>
</div>
<OlRangeSlider
onChange={(ev) => {
selectedUnits.forEach((unit) => {
unit.setAltitude(Number(ev.target.value));
setSelectedUnitsRequestedData({
...selectedUnitsRequestedData,
desiredAltitude: Number(ev.target.value)
unit.setSpeed(Number(ev.target.value));
setSelectedUnitsData({
...selectedUnitsData,
desiredSpeed: Number(ev.target.value)
})
})
}}
value={selectedUnitsRequestedData.desiredAltitude}
min={minAltitude}
max={maxAltitude}
step={altitudeStep} />
value={selectedUnitsData.desiredSpeed}
min={minSpeed}
max={maxSpeed}
step={speedStep}
/>
</div>
</div>
<div className="flex flex-col gap-2">
<span className="font-normal dark:text-white">Rules of engagement</span>
<OlButtonGroup>
{
[olButtonsRoeHold, olButtonsRoeReturn, olButtonsRoeDesignated, olButtonsRoeFree].map((icon, idx) => {
return <OlButtonGroupItem
onClick={() => {
selectedUnits.forEach((unit) => {
unit.setROE(ROEs[idx]);
setSelectedUnitsData({
...selectedUnitsData,
ROE: ROEs[idx]
})
})
}}
active={selectedUnitsData.ROE === ROEs[idx]}
icon={icon} />
})
}
</OlButtonGroup>
</div>
{
selectedCategories.every((category) => { return ['Aircraft', 'Helicopter'].includes(category) }) && <> <div className="flex flex-col gap-2">
<span className="font-normal dark:text-white">Threat reaction</span>
<OlButtonGroup>
{
[olButtonsThreatNone, olButtonsThreatPassive, olButtonsThreatManoeuvre, olButtonsThreatEvade].map((icon, idx) => {
return <OlButtonGroupItem
onClick={() => {
selectedUnits.forEach((unit) => {
unit.setReactionToThreat(reactionsToThreat[idx]);
setSelectedUnitsData({
...selectedUnitsData,
reactionToThreat: reactionsToThreat[idx]
})
})
}}
active={selectedUnitsData.reactionToThreat === reactionsToThreat[idx]}
icon={icon} />
})
}
</OlButtonGroup>
</div>
<div className="flex flex-col gap-2">
<span className="font-normal dark:text-white">Radar and ECM</span>
<OlButtonGroup>
{
[olButtonsEmissionsSilent, olButtonsEmissionsDefend, olButtonsEmissionsAttack, olButtonsEmissionsFree].map((icon, idx) => {
return <OlButtonGroupItem
onClick={() => {
selectedUnits.forEach((unit) => {
unit.setEmissionsCountermeasures(emissionsCountermeasures[idx]);
setSelectedUnitsData({
...selectedUnitsData,
emissionsCountermeasures: emissionsCountermeasures[idx]
})
})
}}
active={selectedUnitsData.emissionsCountermeasures === emissionsCountermeasures[idx]}
icon={icon} />
})
}
</OlButtonGroup>
</div>
</>
}
{
getApp()?.getUnitsManager()?.getSelectedUnitsVariable((unit) => { return unit.isTanker() }) &&
<div className="flex content-center justify-between">
<span className="font-normal dark:text-white"> Act as tanker </span>
<OlToggle toggled={selectedUnitsData.isActiveTanker} onClick={() => {
selectedUnits.forEach((unit) => {
unit.setAdvancedOptions(!selectedUnitsData.isActiveTanker, unit.getIsActiveAWACS(), unit.getTACAN(), unit.getRadio(), unit.getGeneralSettings());
setSelectedUnitsData({
...selectedUnitsData,
isActiveTanker: !selectedUnitsData.isActiveTanker
})
})
}} />
</div>
}
{
getApp()?.getUnitsManager()?.getSelectedUnitsVariable((unit) => { return unit.isAWACS() }) &&
<div className="flex content-center justify-between">
<span className="font-normal dark:text-white"> Act as AWACS </span>
<OlToggle toggled={selectedUnitsData.isActiveAWACS} onClick={() => {
selectedUnits.forEach((unit) => {
unit.setAdvancedOptions(unit.getIsActiveTanker(), !selectedUnitsData.isActiveAWACS, unit.getTACAN(), unit.getRadio(), unit.getGeneralSettings());
setSelectedUnitsData({
...selectedUnitsData,
isActiveAWACS: !selectedUnitsData.isActiveAWACS
})
})
}} />
</div>
}
{
selectedCategories.every((category) => { return ['GroundUnit', 'NavyUnit'].includes(category) }) && <> <div className="flex flex-col gap-2">
<span className="font-normal dark:text-white">Shots scatter</span>
<OlButtonGroup>
{
[olButtonsScatter1, olButtonsScatter2, olButtonsScatter3].map((icon, idx) => {
return <OlButtonGroupItem
onClick={() => {
selectedUnits.forEach((unit) => {
unit.setShotsScatter(idx);
setSelectedUnitsData({
...selectedUnitsData,
shotsScatter: idx
})
})
}}
active={selectedUnitsData.shotsScatter === idx}
icon={icon} />
})
}
</OlButtonGroup>
</div>
<div className="flex flex-col gap-2">
<span className="font-normal dark:text-white">Shots intensity</span>
<OlButtonGroup>
{
[olButtonsIntensity1, olButtonsIntensity2, olButtonsIntensity3].map((icon, idx) => {
return <OlButtonGroupItem
onClick={() => {
selectedUnits.forEach((unit) => {
unit.setShotsIntensity(idx);
setSelectedUnitsData({
...selectedUnitsData,
shotsIntensity: idx
})
})
}}
active={selectedUnitsData.shotsIntensity === idx}
icon={icon} />
})
}
</OlButtonGroup>
</div>
<div className="flex content-center justify-between">
<span className="font-normal dark:text-white"> Operate as </span>
<OlCoalitionToggle coalition={selectedUnitsData.operateAs as Coalition} onClick={() => {
selectedUnits.forEach((unit) => {
unit.setOperateAs(selectedUnitsData.operateAs === 'blue' ? 'red' : 'blue');
setSelectedUnitsData({
...selectedUnitsData,
operateAs: selectedUnitsData.operateAs === 'blue' ? 'red' : 'blue'
})
})
}} />
</div>
<div className="flex content-center justify-between">
<span className="font-normal dark:text-white"> Follow roads </span>
<OlToggle toggled={selectedUnitsData.followRoads} onClick={() => {
selectedUnits.forEach((unit) => {
unit.setFollowRoads(!selectedUnitsData.followRoads);
setSelectedUnitsData({
...selectedUnitsData,
followRoads: !selectedUnitsData.followRoads
})
})
}} />
</div>
<div className="flex content-center justify-between">
<span className="font-normal dark:text-white"> Enabled </span>
<OlToggle toggled={selectedUnitsData.onOff} onClick={() => {
selectedUnits.forEach((unit) => {
unit.setOnOff(!selectedUnitsData.onOff);
setSelectedUnitsData({
...selectedUnitsData,
onOff: !selectedUnitsData.onOff
})
})
}} />
</div>
</>
}
</div>
</Menu>
}