mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
More work on unit control panel
This commit is contained in:
23
frontend/react/src/ui/components/olbuttongroup.tsx
Normal file
23
frontend/react/src/ui/components/olbuttongroup.tsx
Normal 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>
|
||||
}
|
||||
@@ -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>
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
151
frontend/react/src/ui/components/olicons.tsx
Normal file
151
frontend/react/src/ui/components/olicons.tsx
Normal file
File diff suppressed because one or more lines are too long
@@ -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>
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
16
frontend/react/src/ui/components/oltoggle.tsx
Normal file
16
frontend/react/src/ui/components/oltoggle.tsx
Normal 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>
|
||||
}
|
||||
@@ -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={() => {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
Reference in New Issue
Block a user