-
}
+/* Conveniency Component for dropdown elements */
export function OlDropdownItem(props) {
- return
{ })} className="px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white flex flex-row content-center gap-2">
+ return { })} className={(props.className ?? "") + " px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white flex flex-row content-center rounded-md select-none cursor-pointer"}>
{props.children}
-
+
}
\ No newline at end of file
diff --git a/frontend/react/src/ui/components/ollabeltoggle.tsx b/frontend/react/src/ui/components/ollabeltoggle.tsx
index 22a6a09c..f461e4fa 100644
--- a/frontend/react/src/ui/components/ollabeltoggle.tsx
+++ b/frontend/react/src/ui/components/ollabeltoggle.tsx
@@ -3,9 +3,9 @@ import React, { useState } from "react";
export function OlLabelToggle(props) {
var [toggled, setToggled] = useState(false);
- return
{setToggled(!toggled)}} className="relative flex flex-row contents-center justify-between w-32 h-10 dark:bg-gray-700 rounded-md py-1 px-1 select-none cursor-pointer">
-
- MSL
- AGL
-
+ return
{setToggled(!toggled)}} 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">
+
+ {props.leftLabel}
+ {props.rightLabel}
+
}
\ No newline at end of file
diff --git a/frontend/react/src/ui/components/olnumberinput.tsx b/frontend/react/src/ui/components/olnumberinput.tsx
index 4fd8fdfd..0ed2e4cb 100644
--- a/frontend/react/src/ui/components/olnumberinput.tsx
+++ b/frontend/react/src/ui/components/olnumberinput.tsx
@@ -3,13 +3,13 @@ import React, {useEffect, useId} from "react";
export function OlNumberInput(props) {
return
-
+
-
-
+
+
diff --git a/frontend/react/src/ui/components/olrangeslider.tsx b/frontend/react/src/ui/components/olrangeslider.tsx
index 88c22542..8e58ca4a 100644
--- a/frontend/react/src/ui/components/olrangeslider.tsx
+++ b/frontend/react/src/ui/components/olrangeslider.tsx
@@ -1,7 +1,19 @@
-import React, { useState } from "react";
+import React, { useEffect, useRef } from "react";
export function OlRangeSlider(props) {
+ var elementRef = useRef(null);
+
+ useEffect(() => {
+ if (elementRef.current) {
+ const sliderEl = elementRef.current as HTMLInputElement;
+ const tempSliderValue = Number(sliderEl.value);
+ const progress = (tempSliderValue / Number(sliderEl.max)) * 100;
+ sliderEl.style.background = `linear-gradient(to right, #3F83F8 ${progress}%, #4B5563 ${progress}%)`;
+ }
+ })
+
return { props.onChange(Number(ev.target?.value ?? props.value)) }}
value={props.value}
min={props.minValue ?? 0}
diff --git a/frontend/react/src/ui/components/olstatebutton.tsx b/frontend/react/src/ui/components/olstatebutton.tsx
index 8847dc02..817c8765 100644
--- a/frontend/react/src/ui/components/olstatebutton.tsx
+++ b/frontend/react/src/ui/components/olstatebutton.tsx
@@ -2,8 +2,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import React from "react"
export function OlStateButton(props) {
- const className = (props.className ?? '') + ` h-[40px] w-[40px] m-auto border border-gray-900 font-medium rounded-md text-sm ` +
- `dark:bg-transparent dark:data-[checked='true']:bg-white dark:text-white dark:data-[checked='true']:text-gray-900 dark:border-gray-600 `;
+ const className = (props.className ?? '') + ` h-[40px] w-[40px] flex-none font-medium rounded-md text-sm dark:bg-[#2A3949] dark:data-[checked='true']:bg-blue-500 dark:text-white dark:border-gray-600 `;
return
@@ -11,8 +10,7 @@ export function OlStateButton(props) {
}
export function OlRoundStateButton(props) {
- const className = (props.className ?? '') + ` h-[40px] w-[40px] m-auto border border-gray-900 font-medium rounded-full text-sm ` +
- `dark:bg-transparent dark:data-[checked='true']:bg-white dark:text-white dark:data-[checked='true']:text-gray-900 dark:border-gray-600 `;
+ const className = (props.className ?? '') + ` h-8 w-8 flex-none m-auto border border-gray-900 font-medium rounded-full text-sm dark:bg-[transparent] dark:data-[checked='true']:bg-white dark:text-white dark:data-[checked='true']:text-gray-900 dark:border-gray-600 `;
return
diff --git a/frontend/react/src/ui/components/olunitsummary.tsx b/frontend/react/src/ui/components/olunitsummary.tsx
index 552383e9..130c4620 100644
--- a/frontend/react/src/ui/components/olunitsummary.tsx
+++ b/frontend/react/src/ui/components/olunitsummary.tsx
@@ -2,7 +2,7 @@ import React from "react";
import { UnitBlueprint } from "../../interfaces";
export function OlUnitSummary(props: {blueprint: UnitBlueprint}) {
- return
+ return
{props.blueprint.label}
diff --git a/frontend/react/src/ui/panels/components/menu.tsx b/frontend/react/src/ui/panels/components/menu.tsx
index cbd075c7..0dc2d390 100644
--- a/frontend/react/src/ui/panels/components/menu.tsx
+++ b/frontend/react/src/ui/panels/components/menu.tsx
@@ -1,17 +1,17 @@
+import { faArrowLeft } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React from "react";
export function Menu(props) {
- return
-
- {props.title}
-
-
+ return
+
+ {props.showBackButton && {})} icon={faArrowLeft} className="mr-4 cursor-pointer dark:hover:bg-gray-600 p-2 rounded-md"/>} {props.title}
+
- Close menu
+
{props.children}
-
}
\ No newline at end of file
diff --git a/frontend/react/src/ui/panels/header.tsx b/frontend/react/src/ui/panels/header.tsx
index 144ba2d1..05155f98 100644
--- a/frontend/react/src/ui/panels/header.tsx
+++ b/frontend/react/src/ui/panels/header.tsx
@@ -1,30 +1,26 @@
-import React, { useEffect } from 'react'
-import { OlRoundStateButton } from '../components/olstatebutton';
-import { faPlus, faGamepad, faRuler, faPencil, faMap, faLock, faPerson, faBrain, faRobot, faJetFighter, faHelicopter, faShield, faTruck, faShip, faPlaneDeparture, faSkull, faShieldAlt, faGears } from '@fortawesome/free-solid-svg-icons';
-import { library } from '@fortawesome/fontawesome-svg-core'
+import React from 'react'
+import { OlRoundStateButton, OlStateButton } from '../components/olstatebutton';
+import { faLock, faPerson, faBrain, faRobot, faJetFighter, faHelicopter, faShield, faTruck, faShip, faPlaneDeparture, faSkull, faShieldAlt, faCamera } from '@fortawesome/free-solid-svg-icons';
import { EventsConsumer } from '../../eventscontext';
import { StateConsumer } from '../../statecontext';
-import { OlDropdownItem, OlElementDropdown, OlTextDropdown } from '../components/oldropdown';
-import { OlCheckbox } from '../components/olcheckbox';
-import { MAP_OPTIONS_DEFAULTS, MAP_OPTIONS_TOOLTIPS } from '../../constants/constants';
+import { OlDropdownItem, OlDropdown } from '../components/oldropdown';
+import { OlLabelToggle } from '../components/ollabeltoggle';
import { getApp } from '../../olympusapp';
-library.add(faPlus, faGamepad, faRuler, faPencil, faMap);
-
export function Header(props) {
return
{(appState) =>
{(events) =>
-
-
-
-
+
+
+
+
-
+
{
Object.entries({
'human': faPerson,'olympus': faBrain, 'dcs': faRobot
@@ -38,7 +34,7 @@ export function Header(props) {
})
}
-
+
{
Object.entries({
@@ -54,7 +50,7 @@ export function Header(props) {
})
}
-
+
getApp().getMap().setHiddenType( 'blue', !appState.mapHiddenTypes['blue'] )}
@@ -69,19 +65,12 @@ export function Header(props) {
checked={!appState.mapHiddenTypes['neutral']}
icon={faShield} className={"!text-gray-500"} />
-
-
- {Object.keys(MAP_OPTIONS_TOOLTIPS).map((key) => {
- return
- {
- getApp().getMap()?.setOption(key, ev.target.checked);
- }}/>
- { MAP_OPTIONS_TOOLTIPS[key] }
-
- })}
-
+
+
+
+ DCS Sat
+ DCS Alt
+
}
diff --git a/frontend/react/src/ui/panels/mainmenu.tsx b/frontend/react/src/ui/panels/mainmenu.tsx
index 98aa0de6..8f641a9d 100644
--- a/frontend/react/src/ui/panels/mainmenu.tsx
+++ b/frontend/react/src/ui/panels/mainmenu.tsx
@@ -7,13 +7,13 @@ import { faGithub } from "@fortawesome/free-brands-svg-icons";
export function MainMenu(props) {
return
-
+
Version {VERSION}
-
Overview
-
User guide
-
Database Manager
-
Export to file
-
Import from file
+
Overview
+
User guide
+
Database Manager
+
Export to file
+
Import from file
}
\ No newline at end of file
diff --git a/frontend/react/src/ui/panels/sidebar.tsx b/frontend/react/src/ui/panels/sidebar.tsx
index f9a56d39..f97f7116 100644
--- a/frontend/react/src/ui/panels/sidebar.tsx
+++ b/frontend/react/src/ui/panels/sidebar.tsx
@@ -1,24 +1,22 @@
import React from 'react'
import { OlStateButton } from '../components/olstatebutton';
-import { faPlus, faGamepad, faRuler, faPencil, faMap } from '@fortawesome/free-solid-svg-icons';
-import { library } from '@fortawesome/fontawesome-svg-core'
+import { faPlus, faGamepad, faRuler, faPencil, faListDots } from '@fortawesome/free-solid-svg-icons';
import { EventsConsumer } from '../../eventscontext';
import { StateConsumer } from '../../statecontext';
-library.add(faPlus, faGamepad, faRuler, faPencil, faMap);
-
export function SideBar(props) {
return
{(appState) =>
{(events) =>
-
-
-
-
-
-
+
diff --git a/frontend/react/src/ui/panels/spawnmenu.tsx b/frontend/react/src/ui/panels/spawnmenu.tsx
index a3f19d60..7baaa202 100644
--- a/frontend/react/src/ui/panels/spawnmenu.tsx
+++ b/frontend/react/src/ui/panels/spawnmenu.tsx
@@ -29,8 +29,13 @@ export function SpawnMenu(props) {
}
});
- return
- {!blueprint &&
+ return
setBlueprint(null)}
+ >
+ {!blueprint &&
diff --git a/frontend/react/src/ui/panels/unitcontrolmenu.tsx b/frontend/react/src/ui/panels/unitcontrolmenu.tsx
index feeaf4c6..7df5c9a3 100644
--- a/frontend/react/src/ui/panels/unitcontrolmenu.tsx
+++ b/frontend/react/src/ui/panels/unitcontrolmenu.tsx
@@ -1,13 +1,63 @@
-import React from "react";
+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";
library.add(faGamepad);
export function UnitControlMenu(props) {
+ var [open, setOpen] = useState(false);
+ var [selectedUnits, setSelectedUnits] = useState([] as Unit[]);
- return
+ document.addEventListener("unitsSelection", (ev: CustomEventInit) => {
+ setOpen(true);
+ setSelectedUnits(ev.detail as Unit[])
+ })
+
+ document.addEventListener("unitDeselection", (ev: CustomEventInit) => {
+
+ })
+
+ document.addEventListener("clearSelection", () => {
+ setOpen(false);
+ setSelectedUnits([])
+ })
+
+ var unitOccurences = {
+ blue: {},
+ red: {},
+ neutral: {}
+ }
+
+ selectedUnits.forEach((unit) => {
+ if (!(unit.getName() in unitOccurences[unit.getCoalition()]))
+ unitOccurences[unit.getCoalition()][unit.getName()] = 1;
+ else
+ unitOccurences[unit.getCoalition()][unit.getName()]++;
+ })
+
+ return
+
+ {
+ <>
+ {
+ ['blue', 'red', 'neutral'].map((coalition) => {
+ return Object.keys(unitOccurences[coalition]).map((name) => {
+ return
+
+ {name}
+
+
+ x{unitOccurences[coalition][name]}
+
+
+ })
+ })
+ }
+ >
+ }
+
}
\ No newline at end of file
diff --git a/frontend/react/src/ui/panels/unitspawnmenu.tsx b/frontend/react/src/ui/panels/unitspawnmenu.tsx
index cdbf689b..7a1e8e0a 100644
--- a/frontend/react/src/ui/panels/unitspawnmenu.tsx
+++ b/frontend/react/src/ui/panels/unitspawnmenu.tsx
@@ -1,28 +1,94 @@
-import React, {useState} from "react";
+import React, { useState } from "react";
import { OlUnitSummary } from "../components/olunitsummary";
import { OlCoalitionToggle } from "../components/olcoalitiontoggle";
import { OlNumberInput } from "../components/olnumberinput";
import { OlLabelToggle } from "../components/ollabeltoggle";
import { OlRangeSlider } from "../components/olrangeslider";
+import { OlDropdownItem, OlDropdown } from '../components/oldropdown';
+import { LoadoutBlueprint, UnitBlueprint } from "../../interfaces";
export function UnitSpawnMenu(props) {
+ var [spawnRole, setSpawnRole] = useState("");
+ var [spawnLoadoutName, setSpawnLoadout] = useState("");
var [spawnAltitude, setSpawnAltitude] = useState(1000);
+ /* Get a list of all the roles */
+ const roles: string[] = [];
+ (props.blueprint as UnitBlueprint).loadouts?.forEach((loadout) => {
+ loadout.roles.forEach((role) => {
+ !roles.includes(role) && roles.push(role);
+ })
+ })
+
+ /* Initialize the role */
+ spawnRole === "" && roles.length > 0 && setSpawnRole(roles[0]);
+
+ /* Get a list of all the loadouts */
+ const loadouts: LoadoutBlueprint[] = [];
+ (props.blueprint as UnitBlueprint).loadouts?.forEach((loadout) => {
+ loadout.roles.includes(spawnRole) && loadouts.push(loadout);
+ })
+
+ /* Initialize the loadout */
+ spawnLoadoutName === "" && loadouts.length > 0 && setSpawnLoadout(loadouts[0].name)
+
+ const spawnLoadout = props.blueprint.loadouts.find((loadout) => { return loadout.name === spawnLoadoutName; })
+
return
-
-
-
-
-
-
Altitude
-
{`${spawnAltitude} FT`}
+
+
+
+
+
+
+ Altitude
+ {`${spawnAltitude} FT`}
+
+
-
+
-
+
+
+ Role
+
+
+ {
+ roles.map((role) => {
+ return { setSpawnRole(role); setSpawnLoadout(""); }} className="w-full">
+ {role}
+
+ })
+ }
+
+
+
+ Weapons
+
+
+ {
+ loadouts.map((loadout) => {
+ return { setSpawnLoadout(loadout.name) }} className="w-full">
+
+ {loadout.name}
+
+
+ })
+ }
+
+
+
+
+ {spawnLoadout && spawnLoadout.items.map((item) => {
+ return
+
{item.quantity}
+
{item.name}
+
+ })}
+
}
diff --git a/frontend/react/src/ui/ui.css b/frontend/react/src/ui/ui.css
index bd60d577..0a78d8f7 100644
--- a/frontend/react/src/ui/ui.css
+++ b/frontend/react/src/ui/ui.css
@@ -26,4 +26,71 @@
/* IE and Edge */
scrollbar-width: none;
/* Firefox */
-}
\ No newline at end of file
+}
+
+input[type="range"] {
+ /* removing default appearance */
+ -webkit-appearance: none;
+ appearance: none;
+ /* creating a custom design */
+ width: 100%;
+ cursor: pointer;
+ outline: none;
+ border-radius: 15px;
+ /* overflow: hidden; remove this line*/
+
+ /* New additions */
+ height: 6px;
+ background: #4B5563;
+ }
+
+ /* Thumb: webkit */
+ input[type="range"]::-webkit-slider-thumb {
+ /* removing default appearance */
+ -webkit-appearance: none;
+ appearance: none;
+ /* creating a custom design */
+ height: 22px;
+ width: 22px;
+ background-color: #4B5563;
+ border-radius: 50%;
+ border: 1px solid #6B7280;
+
+ /* box-shadow: -407px 0 0 400px #4B5563; emove this line */
+ transition: .2s ease-in-out;
+ }
+
+ /* Thumb: Firefox */
+ input[type="range"]::-moz-range-thumb {
+ height: 15px;
+ width: 15px;
+ background-color: #4B5563;
+ border-radius: 50%;
+ border: none;
+ border: 1px solid #6B7280;
+
+ /* box-shadow: -407px 0 0 400px #4B5563; emove this line */
+ transition: .2s ease-in-out;
+ }
+
+ /* Hover, active & focus Thumb: Webkit */
+ input[type="range"]::-webkit-slider-thumb:hover {
+ box-shadow: 0 0 0 5px #3F83F822
+ }
+ input[type="range"]:active::-webkit-slider-thumb {
+ box-shadow: 0 0 0 7px #3F83F855
+ }
+ input[type="range"]:focus::-webkit-slider-thumb {
+ box-shadow: 0 0 0 7px #3F83F855
+ }
+
+ /* Hover, active & focus Thumb: Firefox */
+ input[type="range"]::-moz-range-thumb:hover {
+ box-shadow: 0 0 0 10px #3F83F822
+ }
+ input[type="range"]:active::-moz-range-thumb {
+ box-shadow: 0 0 0 13px #3F83F855
+ }
+ input[type="range"]:focus::-moz-range-thumb {
+ box-shadow: 0 0 0 13px #3F83F855
+ }
\ No newline at end of file
diff --git a/frontend/react/src/ui/ui.tsx b/frontend/react/src/ui/ui.tsx
index 2c6a993e..c144020e 100644
--- a/frontend/react/src/ui/ui.tsx
+++ b/frontend/react/src/ui/ui.tsx
@@ -12,7 +12,6 @@ import { SideBar } from './panels/sidebar';
import { MapHiddenTypes, MapOptions } from '../types/types'
import { MAP_HIDDEN_TYPES_DEFAULTS, MAP_OPTIONS_DEFAULTS } from '../constants/constants'
import { getApp } from '../olympusapp'
-import { Dropdown } from 'flowbite'
export type OlympusState = {
mainMenuVisible: boolean,
diff --git a/frontend/server/demo.js b/frontend/server/demo.js
index 90c3694f..741ab307 100644
--- a/frontend/server/demo.js
+++ b/frontend/server/demo.js
@@ -139,6 +139,7 @@ module.exports = function (configLocation) {
DEMO_UNIT_DATA[idx].position.lat += idx / 100;
DEMO_UNIT_DATA[idx].category = "Aircraft";
DEMO_UNIT_DATA[idx].isLeader = false;
+ DEMO_UNIT_DATA[idx].coalition = 1;
idx += 1;
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
diff --git a/frontend/server/public/stylesheets/panels/unitlist.css b/frontend/server/public/stylesheets/panels/unitlist.css
index 7f60bdda..b4077469 100644
--- a/frontend/server/public/stylesheets/panels/unitlist.css
+++ b/frontend/server/public/stylesheets/panels/unitlist.css
@@ -26,7 +26,6 @@
justify-content: space-between;
}
-
.unit-list-unit:nth-of-type(even) {
background:#ffffff10;
overflow:visible;