mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Multiple bugfixes
This commit is contained in:
@@ -199,7 +199,7 @@ export class AudioManager {
|
|||||||
|
|
||||||
addRadio() {
|
addRadio() {
|
||||||
console.log("Adding new radio");
|
console.log("Adding new radio");
|
||||||
if (!this.#running) {
|
if (!this.#running || this.#sources[0] === undefined) {
|
||||||
console.log("Audio manager not started, aborting...");
|
console.log("Audio manager not started, aborting...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export class RadioSink extends AudioSink {
|
|||||||
#frequency = 251000000;
|
#frequency = 251000000;
|
||||||
#modulation = 0;
|
#modulation = 0;
|
||||||
#ptt = false;
|
#ptt = false;
|
||||||
#tuned = false;
|
#tuned = true;
|
||||||
#volume = 0.5;
|
#volume = 0.5;
|
||||||
#receiving = false;
|
#receiving = false;
|
||||||
#clearReceivingTimeout: number;
|
#clearReceivingTimeout: number;
|
||||||
|
|||||||
@@ -529,8 +529,8 @@ export namespace ContextActions {
|
|||||||
executeImmediately: true,
|
executeImmediately: true,
|
||||||
type: ContextActionType.DELETE,
|
type: ContextActionType.DELETE,
|
||||||
code: "Delete",
|
code: "Delete",
|
||||||
ctrlKey: true,
|
ctrlKey: false,
|
||||||
shiftKey: false,
|
shiftKey: true,
|
||||||
altKey: false,
|
altKey: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -562,7 +562,7 @@ export namespace ContextActions {
|
|||||||
export const FOLLOW = new ContextAction(
|
export const FOLLOW = new ContextAction(
|
||||||
"follow",
|
"follow",
|
||||||
"Follow unit",
|
"Follow unit",
|
||||||
"Click on a unit to follow it in formation",
|
"Right-click on a unit to follow it in formation",
|
||||||
olButtonsContextFollow,
|
olButtonsContextFollow,
|
||||||
ContextActionTarget.UNIT,
|
ContextActionTarget.UNIT,
|
||||||
(units: Unit[], targetUnit: Unit | null, _) => {
|
(units: Unit[], targetUnit: Unit | null, _) => {
|
||||||
@@ -580,7 +580,7 @@ export namespace ContextActions {
|
|||||||
export const BOMB = new ContextAction(
|
export const BOMB = new ContextAction(
|
||||||
"bomb",
|
"bomb",
|
||||||
"Precision bomb location",
|
"Precision bomb location",
|
||||||
"Click on a point to execute a precision bombing attack",
|
"Right-click on a point to execute a precision bombing attack",
|
||||||
faLocationCrosshairs,
|
faLocationCrosshairs,
|
||||||
ContextActionTarget.POINT,
|
ContextActionTarget.POINT,
|
||||||
(units: Unit[], _, targetPosition: LatLng | null) => {
|
(units: Unit[], _, targetPosition: LatLng | null) => {
|
||||||
@@ -595,7 +595,7 @@ export namespace ContextActions {
|
|||||||
export const CARPET_BOMB = new ContextAction(
|
export const CARPET_BOMB = new ContextAction(
|
||||||
"carpet-bomb",
|
"carpet-bomb",
|
||||||
"Carpet bomb location",
|
"Carpet bomb location",
|
||||||
"Click on a point to execute a carpet bombing attack",
|
"Right-click on a point to execute a carpet bombing attack",
|
||||||
faXmarksLines,
|
faXmarksLines,
|
||||||
ContextActionTarget.POINT,
|
ContextActionTarget.POINT,
|
||||||
(units: Unit[], _, targetPosition: LatLng | null) => {
|
(units: Unit[], _, targetPosition: LatLng | null) => {
|
||||||
@@ -610,7 +610,7 @@ export namespace ContextActions {
|
|||||||
export const LAND = new ContextAction(
|
export const LAND = new ContextAction(
|
||||||
"land",
|
"land",
|
||||||
"Land",
|
"Land",
|
||||||
"Click on a point to land at the nearest airbase",
|
"Right-click on a point to land at the nearest airbase",
|
||||||
faPlaneArrival,
|
faPlaneArrival,
|
||||||
ContextActionTarget.POINT,
|
ContextActionTarget.POINT,
|
||||||
(units: Unit[], _, targetPosition: LatLng | null) => {
|
(units: Unit[], _, targetPosition: LatLng | null) => {
|
||||||
@@ -622,7 +622,7 @@ export namespace ContextActions {
|
|||||||
export const LAND_AT_POINT = new ContextAction(
|
export const LAND_AT_POINT = new ContextAction(
|
||||||
"land-at-point",
|
"land-at-point",
|
||||||
"Land at location",
|
"Land at location",
|
||||||
"Click on a point to land there",
|
"Right-click on a point to land there",
|
||||||
olButtonsContextLandAtPoint,
|
olButtonsContextLandAtPoint,
|
||||||
ContextActionTarget.POINT,
|
ContextActionTarget.POINT,
|
||||||
(units: Unit[], _, targetPosition: LatLng | null) => {
|
(units: Unit[], _, targetPosition: LatLng | null) => {
|
||||||
@@ -649,7 +649,7 @@ export namespace ContextActions {
|
|||||||
export const ATTACK = new ContextAction(
|
export const ATTACK = new ContextAction(
|
||||||
"attack",
|
"attack",
|
||||||
"Attack unit",
|
"Attack unit",
|
||||||
"Click on a unit to attack it",
|
"Right-click on a unit to attack it",
|
||||||
olButtonsContextAttack,
|
olButtonsContextAttack,
|
||||||
ContextActionTarget.UNIT,
|
ContextActionTarget.UNIT,
|
||||||
(units: Unit[], targetUnit: Unit | null, _) => {
|
(units: Unit[], targetUnit: Unit | null, _) => {
|
||||||
@@ -661,7 +661,7 @@ export namespace ContextActions {
|
|||||||
export const FIRE_AT_AREA = new ContextAction(
|
export const FIRE_AT_AREA = new ContextAction(
|
||||||
"fire-at-area",
|
"fire-at-area",
|
||||||
"Fire at area",
|
"Fire at area",
|
||||||
"Click on a point to precisely fire at it (if possible)",
|
"Right-click on a point to precisely fire at it (if possible)",
|
||||||
faLocationCrosshairs,
|
faLocationCrosshairs,
|
||||||
ContextActionTarget.POINT,
|
ContextActionTarget.POINT,
|
||||||
(units: Unit[], _, targetPosition: LatLng | null) => {
|
(units: Unit[], _, targetPosition: LatLng | null) => {
|
||||||
|
|||||||
@@ -137,6 +137,19 @@ export class BindShortcutRequestEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ModalEvent {
|
||||||
|
static on(callback: (modal: boolean) => void) {
|
||||||
|
document.addEventListener(this.name, (ev: CustomEventInit) => {
|
||||||
|
callback(ev.detail.modal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static dispatch(modal: boolean) {
|
||||||
|
document.dispatchEvent(new CustomEvent(this.name, { detail: { modal } }));
|
||||||
|
console.log(`Event ${this.name} dispatched`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/************** Map events ***************/
|
/************** Map events ***************/
|
||||||
export class HiddenTypesChangedEvent {
|
export class HiddenTypesChangedEvent {
|
||||||
static on(callback: (hiddenTypes: MapHiddenTypes) => void) {
|
static on(callback: (hiddenTypes: MapHiddenTypes) => void) {
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import { DomEvent } from "leaflet";
|
|||||||
import { LatLngBounds } from "leaflet";
|
import { LatLngBounds } from "leaflet";
|
||||||
import { Bounds } from "leaflet";
|
import { Bounds } from "leaflet";
|
||||||
import { SELECT_TOLERANCE_PX } from "../constants/constants";
|
import { SELECT_TOLERANCE_PX } from "../constants/constants";
|
||||||
|
import { Map } from "./map";
|
||||||
|
|
||||||
export var BoxSelect = Handler.extend({
|
export var BoxSelect = Handler.extend({
|
||||||
initialize: function (map) {
|
initialize: function (map: Map) {
|
||||||
this._map = map;
|
this._map = map;
|
||||||
this._container = map.getContainer();
|
this._container = map.getContainer();
|
||||||
this._pane = map.getPanes().overlayPane;
|
this._pane = map.getPanes().overlayPane;
|
||||||
@@ -45,13 +46,12 @@ export var BoxSelect = Handler.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onMouseDown: function (e: any) {
|
_onMouseDown: function (e: any) {
|
||||||
if (e.which == 1 && e.button == 0) {
|
if (this._map.getEnableSelection() && e.button == 0) {
|
||||||
// Clear the deferred resetState if it hasn't executed yet, otherwise it
|
// Clear the deferred resetState if it hasn't executed yet, otherwise it
|
||||||
// will interrupt the interaction and orphan a box element in the container.
|
// will interrupt the interaction and orphan a box element in the container.
|
||||||
this._clearDeferredResetState();
|
this._clearDeferredResetState();
|
||||||
this._resetState();
|
this._resetState();
|
||||||
|
|
||||||
DomUtil.disableTextSelection();
|
|
||||||
DomUtil.disableImageDrag();
|
DomUtil.disableImageDrag();
|
||||||
this._map.dragging.disable();
|
this._map.dragging.disable();
|
||||||
|
|
||||||
@@ -65,10 +65,8 @@ export var BoxSelect = Handler.extend({
|
|||||||
contextmenu: DomEvent.stop,
|
contextmenu: DomEvent.stop,
|
||||||
touchmove: this._onMouseMove,
|
touchmove: this._onMouseMove,
|
||||||
touchend: this._onMouseUp,
|
touchend: this._onMouseUp,
|
||||||
touchstart: this._onKeyDown,
|
|
||||||
mousemove: this._onMouseMove,
|
mousemove: this._onMouseMove,
|
||||||
mouseup: this._onMouseUp,
|
mouseup: this._onMouseUp
|
||||||
keydown: this._onKeyDown,
|
|
||||||
},
|
},
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
@@ -77,6 +75,24 @@ export var BoxSelect = Handler.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onMouseUp: function (e: any) {
|
||||||
|
if (e.button !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._finish();
|
||||||
|
|
||||||
|
if (!this._moved) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Postpone to next JS tick so internal click event handling
|
||||||
|
// still see it as "moved".
|
||||||
|
window.setTimeout(Util.bind(this._resetState, this), 0);
|
||||||
|
var bounds = new LatLngBounds(this._map.containerPointToLatLng(this._startPoint), this._map.containerPointToLatLng(this._point));
|
||||||
|
|
||||||
|
this._map.fire("selectionend", { selectionBounds: bounds });
|
||||||
|
},
|
||||||
|
|
||||||
_onMouseMove: function (e: any) {
|
_onMouseMove: function (e: any) {
|
||||||
if (e.type === "touchmove") this._point = this._map.mouseEventToContainerPoint(e.touches[0]);
|
if (e.type === "touchmove") this._point = this._map.mouseEventToContainerPoint(e.touches[0]);
|
||||||
else this._point = this._map.mouseEventToContainerPoint(e);
|
else this._point = this._map.mouseEventToContainerPoint(e);
|
||||||
@@ -110,7 +126,6 @@ export var BoxSelect = Handler.extend({
|
|||||||
DomUtil.removeClass(this._container, "leaflet-crosshair");
|
DomUtil.removeClass(this._container, "leaflet-crosshair");
|
||||||
}
|
}
|
||||||
|
|
||||||
DomUtil.enableTextSelection();
|
|
||||||
DomUtil.enableImageDrag();
|
DomUtil.enableImageDrag();
|
||||||
this._map.dragging.enable();
|
this._map.dragging.enable();
|
||||||
|
|
||||||
@@ -121,38 +136,10 @@ export var BoxSelect = Handler.extend({
|
|||||||
contextmenu: DomEvent.stop,
|
contextmenu: DomEvent.stop,
|
||||||
touchmove: this._onMouseMove,
|
touchmove: this._onMouseMove,
|
||||||
touchend: this._onMouseUp,
|
touchend: this._onMouseUp,
|
||||||
touchstart: this._onKeyDown,
|
|
||||||
mousemove: this._onMouseMove,
|
mousemove: this._onMouseMove,
|
||||||
mouseup: this._onMouseUp,
|
mouseup: this._onMouseUp,
|
||||||
keydown: this._onKeyDown,
|
|
||||||
},
|
},
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMouseUp: function (e: any) {
|
|
||||||
if (e.which !== 1 && e.button !== 0 && e.type !== "touchend") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._finish();
|
|
||||||
|
|
||||||
if (!this._moved) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Postpone to next JS tick so internal click event handling
|
|
||||||
// still see it as "moved".
|
|
||||||
window.setTimeout(Util.bind(this._resetState, this), 0);
|
|
||||||
var bounds = new LatLngBounds(this._map.containerPointToLatLng(this._startPoint), this._map.containerPointToLatLng(this._point));
|
|
||||||
|
|
||||||
this._map.fire("selectionend", { selectionBounds: bounds });
|
|
||||||
},
|
|
||||||
|
|
||||||
_onKeyDown: function (e: any) {
|
|
||||||
if (e.keyCode === 27) {
|
|
||||||
this._finish();
|
|
||||||
this._clearDeferredResetState();
|
|
||||||
this._resetState();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ import { ExplosionMarker } from "./markers/explosionmarker";
|
|||||||
import { TextMarker } from "./markers/textmarker";
|
import { TextMarker } from "./markers/textmarker";
|
||||||
import { TargetMarker } from "./markers/targetmarker";
|
import { TargetMarker } from "./markers/targetmarker";
|
||||||
import {
|
import {
|
||||||
AirbaseSelectedEvent,
|
|
||||||
AppStateChangedEvent,
|
AppStateChangedEvent,
|
||||||
CoalitionAreaSelectedEvent,
|
CoalitionAreaSelectedEvent,
|
||||||
ConfigLoadedEvent,
|
ConfigLoadedEvent,
|
||||||
@@ -114,6 +113,7 @@ export class Map extends L.Map {
|
|||||||
#lastMouseCoordinates: L.LatLng = new L.LatLng(0, 0);
|
#lastMouseCoordinates: L.LatLng = new L.LatLng(0, 0);
|
||||||
#previousZoom: number = 0;
|
#previousZoom: number = 0;
|
||||||
#keepRelativePositions: boolean = false;
|
#keepRelativePositions: boolean = false;
|
||||||
|
#enableSelection: boolean = false;
|
||||||
|
|
||||||
/* Camera control plugin */
|
/* Camera control plugin */
|
||||||
#slaveDCSCamera: boolean = false;
|
#slaveDCSCamera: boolean = false;
|
||||||
@@ -360,6 +360,14 @@ export class Map extends L.Map {
|
|||||||
shiftKey: false,
|
shiftKey: false,
|
||||||
ctrlKey: false,
|
ctrlKey: false,
|
||||||
})
|
})
|
||||||
|
.addShortcut("toggleEnableSelection", {
|
||||||
|
label: "Toggle box selection",
|
||||||
|
keyUpCallback: () => this.setEnableSelection(false),
|
||||||
|
keyDownCallback: () => this.setEnableSelection(true),
|
||||||
|
code: "ShiftLeft",
|
||||||
|
altKey: false,
|
||||||
|
ctrlKey: false,
|
||||||
|
})
|
||||||
.addShortcut("increaseCameraZoom", {
|
.addShortcut("increaseCameraZoom", {
|
||||||
label: "Increase camera zoom",
|
label: "Increase camera zoom",
|
||||||
keyUpCallback: () => this.increaseCameraZoom(),
|
keyUpCallback: () => this.increaseCameraZoom(),
|
||||||
@@ -725,6 +733,14 @@ export class Map extends L.Map {
|
|||||||
return this.#keepRelativePositions;
|
return this.#keepRelativePositions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setEnableSelection(enableSelection: boolean) {
|
||||||
|
this.#enableSelection = enableSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEnableSelection() {
|
||||||
|
return this.#enableSelection;
|
||||||
|
}
|
||||||
|
|
||||||
increaseCameraZoom() {
|
increaseCameraZoom() {
|
||||||
//const slider = document.querySelector(`label[title="${DCS_LINK_RATIO}"] input`);
|
//const slider = document.querySelector(`label[title="${DCS_LINK_RATIO}"] input`);
|
||||||
//if (slider instanceof HTMLInputElement) {
|
//if (slider instanceof HTMLInputElement) {
|
||||||
@@ -769,7 +785,6 @@ export class Map extends L.Map {
|
|||||||
this.#currentEffectMarker = null;
|
this.#currentEffectMarker = null;
|
||||||
if (state !== OlympusState.UNIT_CONTROL) getApp().getUnitsManager().deselectAllUnits();
|
if (state !== OlympusState.UNIT_CONTROL) getApp().getUnitsManager().deselectAllUnits();
|
||||||
if (state !== OlympusState.DRAW || (state === OlympusState.DRAW && subState !== DrawSubState.EDIT)) this.deselectAllCoalitionAreas();
|
if (state !== OlympusState.DRAW || (state === OlympusState.DRAW && subState !== DrawSubState.EDIT)) this.deselectAllCoalitionAreas();
|
||||||
AirbaseSelectedEvent.dispatch(null);
|
|
||||||
|
|
||||||
/* Operations to perform when entering a state */
|
/* Operations to perform when entering a state */
|
||||||
if (state === OlympusState.IDLE) {
|
if (state === OlympusState.IDLE) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DivIcon } from "leaflet";
|
import { DivIcon, DomEvent } from "leaflet";
|
||||||
import { CustomMarker } from "../map/markers/custommarker";
|
import { CustomMarker } from "../map/markers/custommarker";
|
||||||
import { SVGInjector } from "@tanem/svg-injector";
|
import { SVGInjector } from "@tanem/svg-injector";
|
||||||
import { AirbaseChartData, AirbaseOptions } from "../interfaces";
|
import { AirbaseChartData, AirbaseOptions } from "../interfaces";
|
||||||
@@ -29,20 +29,28 @@ export class Airbase extends CustomMarker {
|
|||||||
|
|
||||||
AppStateChangedEvent.on((state, subState) => {
|
AppStateChangedEvent.on((state, subState) => {
|
||||||
const element = this.getElement();
|
const element = this.getElement();
|
||||||
if (element)
|
if (element) element.style.pointerEvents = state === OlympusState.IDLE || state === OlympusState.AIRBASE ? "all" : "none";
|
||||||
element.style.pointerEvents = (state === OlympusState.IDLE || state === OlympusState.AIRBASE)? "all": "none";
|
});
|
||||||
})
|
|
||||||
|
|
||||||
AirbaseSelectedEvent.on((airbase) => {
|
AirbaseSelectedEvent.on((airbase) => {
|
||||||
this.#selected = airbase == this;
|
this.#selected = (airbase === this);
|
||||||
if (this.getElement()?.querySelector(".airbase-icon"))
|
if (this.getElement()?.querySelector(".airbase-icon"))
|
||||||
(this.getElement()?.querySelector(".airbase-icon") as HTMLElement).dataset.selected = `${this.#selected}`;
|
(this.getElement()?.querySelector(".airbase-icon") as HTMLElement).dataset.selected = `${this.#selected}`;
|
||||||
})
|
});
|
||||||
|
|
||||||
this.addEventListener("click", (ev) => {
|
this.addEventListener("mousedown", (ev) => {
|
||||||
if (getApp().getState() === OlympusState.IDLE || getApp().getState() === OlympusState.AIRBASE) {
|
if (getApp().getState() === OlympusState.IDLE || getApp().getState() === OlympusState.AIRBASE) {
|
||||||
getApp().setState(OlympusState.AIRBASE)
|
DomEvent.stop(ev);
|
||||||
AirbaseSelectedEvent.dispatch(this)
|
ev.originalEvent.stopImmediatePropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addEventListener("mouseup", (ev) => {
|
||||||
|
if (getApp().getState() === OlympusState.IDLE || getApp().getState() === OlympusState.AIRBASE) {
|
||||||
|
DomEvent.stop(ev);
|
||||||
|
ev.originalEvent.stopImmediatePropagation();
|
||||||
|
getApp().setState(OlympusState.AIRBASE);
|
||||||
|
AirbaseSelectedEvent.dispatch(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -110,4 +118,8 @@ export class Airbase extends CustomMarker {
|
|||||||
getImg() {
|
getImg() {
|
||||||
return this.#img;
|
return this.#img;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSelected() {
|
||||||
|
return this.#selected;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import { BLUE_COMMANDER, GAME_MASTER, NONE, RED_COMMANDER } from "../constants/c
|
|||||||
import { AirbasesData, BullseyesData, CommandModeOptions, DateAndTime, MissionData } from "../interfaces";
|
import { AirbasesData, BullseyesData, CommandModeOptions, DateAndTime, MissionData } from "../interfaces";
|
||||||
import { Coalition } from "../types/types";
|
import { Coalition } from "../types/types";
|
||||||
import { Carrier } from "./carrier";
|
import { Carrier } from "./carrier";
|
||||||
import { NavyUnit } from "../unit/unit";
|
import { AirbaseSelectedEvent, AppStateChangedEvent, BullseyesDataChanged, CommandModeOptionsChangedEvent, InfoPopupEvent } from "../events";
|
||||||
import { BullseyesDataChanged, CommandModeOptionsChangedEvent, InfoPopupEvent } from "../events";
|
|
||||||
|
|
||||||
/** The MissionManager */
|
/** The MissionManager */
|
||||||
export class MissionManager {
|
export class MissionManager {
|
||||||
@@ -34,7 +33,12 @@ export class MissionManager {
|
|||||||
#spentSpawnPoint: number = 0;
|
#spentSpawnPoint: number = 0;
|
||||||
#coalitions: { red: string[]; blue: string[] } = { red: [], blue: [] };
|
#coalitions: { red: string[]; blue: string[] } = { red: [], blue: [] };
|
||||||
|
|
||||||
constructor() {}
|
constructor() {
|
||||||
|
AppStateChangedEvent.on((state, subState) => {
|
||||||
|
if (this.getSelectedAirbase() !== null) AirbaseSelectedEvent.dispatch(null);
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/** Update location of bullseyes
|
/** Update location of bullseyes
|
||||||
*
|
*
|
||||||
@@ -209,6 +213,14 @@ export class MissionManager {
|
|||||||
return this.#frameRate;
|
return this.#frameRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSelectedAirbase() {
|
||||||
|
const airbase = Object.values(this.#airbases).find((airbase: Airbase | Carrier) => {
|
||||||
|
return airbase.getSelected();
|
||||||
|
});
|
||||||
|
|
||||||
|
return airbase ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
#setcommandModeOptions(commandModeOptions: CommandModeOptions) {
|
#setcommandModeOptions(commandModeOptions: CommandModeOptions) {
|
||||||
/* Refresh all the data if we have exited the NONE state */
|
/* Refresh all the data if we have exited the NONE state */
|
||||||
var requestRefresh = false;
|
var requestRefresh = false;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { AppStateChangedEvent, ShortcutChangedEvent, ShortcutsChangedEvent } from "../events";
|
import { OlympusState } from "../constants/constants";
|
||||||
|
import { AppStateChangedEvent, ModalEvent, ShortcutChangedEvent, ShortcutsChangedEvent } from "../events";
|
||||||
import { ShortcutOptions } from "../interfaces";
|
import { ShortcutOptions } from "../interfaces";
|
||||||
import { keyEventWasInInput } from "../other/utils";
|
import { keyEventWasInInput } from "../other/utils";
|
||||||
|
|
||||||
@@ -6,34 +7,40 @@ export class Shortcut {
|
|||||||
#id: string;
|
#id: string;
|
||||||
#options: ShortcutOptions;
|
#options: ShortcutOptions;
|
||||||
#keydown: boolean = false;
|
#keydown: boolean = false;
|
||||||
|
#modal: boolean = false;
|
||||||
|
|
||||||
constructor(id, options: ShortcutOptions) {
|
constructor(id, options: ShortcutOptions) {
|
||||||
this.#id = id;
|
this.#id = id;
|
||||||
this.#options = options;
|
this.#options = options;
|
||||||
|
|
||||||
AppStateChangedEvent.on(() => (this.#keydown = false));
|
AppStateChangedEvent.on((state, subState) => (this.#keydown = false));
|
||||||
|
ModalEvent.on((modal) => (this.#modal = modal))
|
||||||
|
|
||||||
/* On keyup, it is enough to check the code only, not the entire combination */
|
/* On keyup, it is enough to check the code only, not the entire combination */
|
||||||
document.addEventListener("keyup", (ev: any) => {
|
document.addEventListener("keyup", (ev: any) => {
|
||||||
if (this.#keydown && options.code === ev.code) {
|
if (this.#modal) return;
|
||||||
|
if (this.#keydown && this.getOptions().code === ev.code) {
|
||||||
|
console.log(`Keydown up for shortcut ${this.#id}`)
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
options.keyUpCallback(ev);
|
|
||||||
this.#keydown = false;
|
this.#keydown = false;
|
||||||
|
this.getOptions().keyUpCallback(ev);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/* On keydown, check exactly if the requested key combination is being pressed */
|
/* On keydown, check exactly if the requested key combination is being pressed */
|
||||||
document.addEventListener("keydown", (ev: any) => {
|
document.addEventListener("keydown", (ev: any) => {
|
||||||
|
if (this.#modal) return;
|
||||||
if (
|
if (
|
||||||
!(this.#keydown || keyEventWasInInput(ev) || options.code !== ev.code) &&
|
!(this.#keydown || keyEventWasInInput(ev) || this.getOptions().code !== ev.code) &&
|
||||||
(options.altKey === undefined || ev.altKey === (options.altKey ?? ev.code.indexOf("Alt") >= 0)) &&
|
(this.getOptions().altKey === undefined || ev.altKey === (this.getOptions().altKey ?? ev.code.indexOf("Alt") >= 0)) &&
|
||||||
(options.ctrlKey === undefined || ev.ctrlKey === (options.ctrlKey ?? ev.code.indexOf("Control") >= 0)) &&
|
(this.getOptions().ctrlKey === undefined || ev.ctrlKey === (this.getOptions().ctrlKey ?? ev.code.indexOf("Control") >= 0)) &&
|
||||||
(options.shiftKey === undefined || ev.shiftKey === (options.shiftKey ?? ev.code.indexOf("Shift") >= 0))
|
(this.getOptions().shiftKey === undefined || ev.shiftKey === (this.getOptions().shiftKey ?? ev.code.indexOf("Shift") >= 0))
|
||||||
) {
|
) {
|
||||||
|
console.log(`Keydown event for shortcut ${this.#id}`)
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.#keydown = true;
|
this.#keydown = true;
|
||||||
|
const keyDownCallback = this.getOptions().keyDownCallback
|
||||||
if (options.keyDownCallback) options.keyDownCallback(ev); /* Key down event is optional */
|
if (keyDownCallback) keyDownCallback(ev); /* Key down event is optional */
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -43,7 +50,7 @@ export class Shortcut {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setOptions(options: ShortcutOptions) {
|
setOptions(options: ShortcutOptions) {
|
||||||
this.#options = { ...options };
|
this.#options = { ...this.#options, ...options };
|
||||||
}
|
}
|
||||||
|
|
||||||
getId() {
|
getId() {
|
||||||
@@ -54,14 +61,17 @@ export class Shortcut {
|
|||||||
let actions: string[] = [];
|
let actions: string[] = [];
|
||||||
if (this.getOptions().shiftKey) actions.push("Shift");
|
if (this.getOptions().shiftKey) actions.push("Shift");
|
||||||
if (this.getOptions().altKey) actions.push("Alt");
|
if (this.getOptions().altKey) actions.push("Alt");
|
||||||
if (this.getOptions().ctrlKey) actions.push("Ctrl")
|
if (this.getOptions().ctrlKey) actions.push("Ctrl");
|
||||||
actions.push(this.getOptions().code.replace("Key", "")
|
actions.push(
|
||||||
.replace("ControlLeft", "Left Ctrl")
|
this.getOptions()
|
||||||
.replace("AltLeft", "Left Alt")
|
.code.replace("Key", "")
|
||||||
.replace("ShiftLeft", "Left Shift")
|
.replace("ControlLeft", "Left Ctrl")
|
||||||
.replace("ControlRight", "Right Ctrl")
|
.replace("AltLeft", "Left Alt")
|
||||||
.replace("AltRight", "Right Alt")
|
.replace("ShiftLeft", "Left Shift")
|
||||||
.replace("ShiftRight", "Right Shift"))
|
.replace("ControlRight", "Right Ctrl")
|
||||||
return actions
|
.replace("AltRight", "Right Alt")
|
||||||
|
.replace("ShiftRight", "Right Shift")
|
||||||
|
);
|
||||||
|
return actions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ShortcutChangedEvent, ShortcutsChangedEvent } from "../events";
|
import { ShortcutsChangedEvent } from "../events";
|
||||||
import { ShortcutOptions } from "../interfaces";
|
import { ShortcutOptions } from "../interfaces";
|
||||||
import { Shortcut } from "./shortcut";
|
import { Shortcut } from "./shortcut";
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,28 @@
|
|||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
|
import { ModalEvent } from "../../../events";
|
||||||
|
|
||||||
|
export function Modal(props: { open: boolean; children?: JSX.Element | JSX.Element[]; className?: string }) {
|
||||||
|
useEffect(() => {
|
||||||
|
ModalEvent.dispatch(props.open);
|
||||||
|
}, [props.open]);
|
||||||
|
|
||||||
export function Modal(props: { grayout?: boolean; children?: JSX.Element | JSX.Element[]; className?: string }) {
|
|
||||||
return (
|
return (
|
||||||
<div
|
<>
|
||||||
className={`
|
{props.open && (
|
||||||
${props.className}
|
<>
|
||||||
fixed left-[50%] top-[50%] z-40 translate-x-[-50%] translate-y-[-50%]
|
<div className={`fixed left-0 top-0 z-30 h-full w-full bg-[#111111]/95`}></div>
|
||||||
rounded-xl border-[1px] border-solid border-gray-700 drop-shadow-md
|
<div
|
||||||
`}
|
className={`
|
||||||
>
|
${props.className}
|
||||||
{props.children}
|
fixed left-[50%] top-[50%] z-40 translate-x-[-50%]
|
||||||
</div>
|
translate-y-[-50%] rounded-xl border-[1px] border-solid
|
||||||
|
border-gray-700 drop-shadow-md
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { getApp } from "../../olympusapp";
|
|||||||
import { OlympusState } from "../../constants/constants";
|
import { OlympusState } from "../../constants/constants";
|
||||||
import { Shortcut } from "../../shortcut/shortcut";
|
import { Shortcut } from "../../shortcut/shortcut";
|
||||||
import { BindShortcutRequestEvent, ShortcutsChangedEvent } from "../../events";
|
import { BindShortcutRequestEvent, ShortcutsChangedEvent } from "../../events";
|
||||||
import { OlToggle } from "../components/oltoggle";
|
|
||||||
|
|
||||||
export function KeybindModal(props: { open: boolean }) {
|
export function KeybindModal(props: { open: boolean }) {
|
||||||
const [shortcuts, setShortcuts] = useState({} as { [key: string]: Shortcut });
|
const [shortcuts, setShortcuts] = useState({} as { [key: string]: Shortcut });
|
||||||
@@ -23,180 +22,139 @@ export function KeybindModal(props: { open: boolean }) {
|
|||||||
document.addEventListener("keydown", (ev) => {
|
document.addEventListener("keydown", (ev) => {
|
||||||
if (ev.code) {
|
if (ev.code) {
|
||||||
setCode(ev.code);
|
setCode(ev.code);
|
||||||
|
if (ev.code.indexOf("Control") < 0) setCtrlKey(ev.ctrlKey);
|
||||||
|
else setCtrlKey(false);
|
||||||
|
if (ev.code.indexOf("Shift") < 0) setShiftKey(ev.shiftKey);
|
||||||
|
else setShiftKey(false);
|
||||||
|
if (ev.code.indexOf("Alt") < 0) setAltKey(ev.altKey);
|
||||||
|
else setAltKey(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCode(shortcut?.getOptions().code ?? null)
|
setCode(shortcut?.getOptions().code ?? null);
|
||||||
setShiftKey(shortcut?.getOptions().shiftKey)
|
setShiftKey(shortcut?.getOptions().shiftKey);
|
||||||
setAltKey(shortcut?.getOptions().altKey)
|
setAltKey(shortcut?.getOptions().altKey);
|
||||||
setCtrlKey(shortcut?.getOptions().ctrlKey)
|
setCtrlKey(shortcut?.getOptions().ctrlKey);
|
||||||
}, [shortcut])
|
}, [shortcut]);
|
||||||
|
|
||||||
let available: null | boolean = code ? true : null;
|
let available: null | boolean = code ? true : null;
|
||||||
let inUseShortcut: null | Shortcut = null;
|
let inUseShortcuts: Shortcut[] = [];
|
||||||
for (let id in shortcuts) {
|
for (let id in shortcuts) {
|
||||||
if (id !== shortcut?.getId() &&
|
if (
|
||||||
|
id !== shortcut?.getId() &&
|
||||||
code === shortcuts[id].getOptions().code &&
|
code === shortcuts[id].getOptions().code &&
|
||||||
((shiftKey === undefined && shortcuts[id].getOptions().shiftKey !== undefined) ||
|
((shiftKey === undefined && shortcuts[id].getOptions().shiftKey !== undefined) ||
|
||||||
(shiftKey !== undefined && shortcuts[id].getOptions().shiftKey === undefined) ||
|
(shiftKey !== undefined && shortcuts[id].getOptions().shiftKey === undefined) ||
|
||||||
shiftKey === shortcuts[id].getOptions().shiftKey) && (
|
shiftKey === shortcuts[id].getOptions().shiftKey) &&
|
||||||
(altKey === undefined && shortcuts[id].getOptions().altKey !== undefined) ||
|
((altKey === undefined && shortcuts[id].getOptions().altKey !== undefined) ||
|
||||||
(altKey !== undefined && shortcuts[id].getOptions().altKey === undefined) ||
|
(altKey !== undefined && shortcuts[id].getOptions().altKey === undefined) ||
|
||||||
altKey === shortcuts[id].getOptions().altKey) && (
|
altKey === shortcuts[id].getOptions().altKey) &&
|
||||||
(ctrlKey === undefined && shortcuts[id].getOptions().ctrlKey !== undefined) ||
|
((ctrlKey === undefined && shortcuts[id].getOptions().ctrlKey !== undefined) ||
|
||||||
(ctrlKey !== undefined && shortcuts[id].getOptions().ctrlKey === undefined) ||
|
(ctrlKey !== undefined && shortcuts[id].getOptions().ctrlKey === undefined) ||
|
||||||
ctrlKey === shortcuts[id].getOptions().ctrlKey)
|
ctrlKey === shortcuts[id].getOptions().ctrlKey)
|
||||||
) {
|
) {
|
||||||
available = false;
|
available = false;
|
||||||
inUseShortcut = shortcuts[id];
|
inUseShortcuts.push(shortcuts[id]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Modal
|
||||||
{props.open && (
|
open={props.open}
|
||||||
<>
|
className={`
|
||||||
<Modal
|
inline-flex h-fit w-[600px] overflow-y-auto scroll-smooth bg-white p-10
|
||||||
|
dark:bg-olympus-800
|
||||||
|
max-md:h-full max-md:max-h-full max-md:w-full max-md:rounded-none
|
||||||
|
max-md:border-none
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<div className="flex h-full w-full flex-col gap-4">
|
||||||
|
<div className={`flex flex-col items-start gap-2`}>
|
||||||
|
<span
|
||||||
className={`
|
className={`
|
||||||
inline-flex h-fit w-[600px] overflow-y-auto scroll-smooth bg-white
|
text-gray-800 text-md
|
||||||
p-10
|
dark:text-white
|
||||||
dark:bg-olympus-800
|
|
||||||
max-md:h-full max-md:max-h-full max-md:w-full max-md:rounded-none
|
|
||||||
max-md:border-none
|
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<div className="flex h-full w-full flex-col gap-4">
|
{shortcut?.getOptions().label}
|
||||||
<div className={`flex flex-col items-start gap-2`}>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`
|
className={`
|
||||||
text-gray-800 text-md
|
text-gray-800 text-md
|
||||||
dark:text-white
|
dark:text-gray-500
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{shortcut?.getOptions().label}
|
Press the key you want to bind to this event
|
||||||
</span>
|
</span>
|
||||||
<span
|
</div>
|
||||||
className={`
|
<div className="w-full text-center text-white">
|
||||||
text-gray-800 text-md
|
{ctrlKey && "Ctrl + "}
|
||||||
dark:text-gray-500
|
{altKey && "Alt + "}
|
||||||
`}
|
{shiftKey && "Shift + "}
|
||||||
>
|
{code}
|
||||||
Press the key you want to bind to this event
|
</div>
|
||||||
</span>
|
<div className="text-white">
|
||||||
</div>
|
{available === true && <div className="text-green-600">Keybind is free!</div>}
|
||||||
<div className="w-full text-center text-white">{code}</div>
|
{available === false && (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<OlToggle
|
Keybind is already in use: <div className={`
|
||||||
onClick={() => {
|
flex flex-wrap gap-2 font-bold text-orange-600
|
||||||
if (shiftKey === false) setShiftKey(undefined);
|
`}>{inUseShortcuts.map((shortcut) => <span>{shortcut.getOptions().label}</span>)}</div>
|
||||||
else if (shiftKey === undefined) setShiftKey(true);
|
|
||||||
else setShiftKey(false);
|
|
||||||
}}
|
|
||||||
toggled={shiftKey}
|
|
||||||
></OlToggle>
|
|
||||||
<div className="text-white">
|
|
||||||
{shiftKey === true && "Shift key must be pressed"}
|
|
||||||
{shiftKey === undefined && "Shift key can be anything"}
|
|
||||||
{shiftKey === false && "Shift key must NOT be pressed"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<OlToggle
|
|
||||||
onClick={() => {
|
|
||||||
if (altKey === false) setAltKey(undefined);
|
|
||||||
else if (altKey === undefined) setAltKey(true);
|
|
||||||
else setAltKey(false);
|
|
||||||
}}
|
|
||||||
toggled={altKey}
|
|
||||||
></OlToggle>
|
|
||||||
<div className="text-white">
|
|
||||||
{altKey === true && "Alt key must be pressed"}
|
|
||||||
{altKey === undefined && "Alt key can be anything"}
|
|
||||||
{altKey === false && "Alt key must NOT be pressed"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<OlToggle
|
|
||||||
onClick={() => {
|
|
||||||
if (ctrlKey === false) setCtrlKey(undefined);
|
|
||||||
else if (ctrlKey === undefined) setCtrlKey(true);
|
|
||||||
else setCtrlKey(false);
|
|
||||||
}}
|
|
||||||
toggled={ctrlKey}
|
|
||||||
></OlToggle>
|
|
||||||
<div className="text-white">
|
|
||||||
{ctrlKey === true && "Ctrl key must be pressed"}
|
|
||||||
{ctrlKey === undefined && "Ctrl key can be anything"}
|
|
||||||
{ctrlKey === false && "Ctrl key must NOT be pressed"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div className="text-white">
|
|
||||||
{available === true && <div className="text-green-600">Keybind is free!</div>}
|
|
||||||
{available === false && (
|
|
||||||
<div>
|
|
||||||
Keybind is already in use: <span className={`
|
|
||||||
font-bold text-red-600
|
|
||||||
`}>{inUseShortcut?.getOptions().label}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end">
|
|
||||||
{available && shortcut && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => {
|
|
||||||
if (shortcut && code) {
|
|
||||||
let options = shortcut.getOptions();
|
|
||||||
options.code = code;
|
|
||||||
options.altKey = altKey;
|
|
||||||
options.shiftKey = shiftKey;
|
|
||||||
options.ctrlKey = ctrlKey;
|
|
||||||
getApp().getShortcutManager().setShortcutOption(shortcut.getId(), options);
|
|
||||||
|
|
||||||
getApp().setState(OlympusState.OPTIONS);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className={`
|
|
||||||
mb-2 me-2 ml-auto flex content-center items-center gap-2
|
|
||||||
rounded-sm bg-blue-700 px-5 py-2.5 text-sm font-medium
|
|
||||||
text-white
|
|
||||||
dark:bg-blue-600 dark:hover:bg-blue-700
|
|
||||||
dark:focus:ring-blue-800
|
|
||||||
focus:outline-none focus:ring-4 focus:ring-blue-300
|
|
||||||
hover:bg-blue-800
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
Continue
|
|
||||||
<FontAwesomeIcon icon={faArrowRight} />
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => getApp().setState(OlympusState.OPTIONS)}
|
|
||||||
className={`
|
|
||||||
mb-2 me-2 flex content-center items-center gap-2 rounded-sm
|
|
||||||
border-[1px] bg-blue-700 px-5 py-2.5 text-sm font-medium
|
|
||||||
text-white
|
|
||||||
dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400
|
|
||||||
dark:hover:bg-gray-700 dark:focus:ring-blue-800
|
|
||||||
focus:outline-none focus:ring-4 focus:ring-blue-300
|
|
||||||
hover:bg-blue-800
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
Back
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-gray-500">A key combination can be assigned to multiple actions, and all bound actions will fire</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
)}
|
||||||
<div className={`fixed left-0 top-0 z-30 h-full w-full bg-[#111111]/95`}></div>
|
</div>
|
||||||
</>
|
|
||||||
)}
|
<div className="flex justify-end">
|
||||||
</>
|
{shortcut && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
if (shortcut && code) {
|
||||||
|
let options = shortcut.getOptions();
|
||||||
|
options.code = code;
|
||||||
|
options.altKey = altKey;
|
||||||
|
options.shiftKey = shiftKey;
|
||||||
|
options.ctrlKey = ctrlKey;
|
||||||
|
getApp().getShortcutManager().setShortcutOption(shortcut.getId(), options);
|
||||||
|
|
||||||
|
getApp().setState(OlympusState.OPTIONS);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={`
|
||||||
|
mb-2 me-2 ml-auto flex content-center items-center gap-2
|
||||||
|
rounded-sm bg-blue-700 px-5 py-2.5 text-sm font-medium
|
||||||
|
text-white
|
||||||
|
dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
|
||||||
|
focus:outline-none focus:ring-4 focus:ring-blue-300
|
||||||
|
hover:bg-blue-800
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
<FontAwesomeIcon icon={faArrowRight} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => getApp().setState(OlympusState.OPTIONS)}
|
||||||
|
className={`
|
||||||
|
mb-2 me-2 flex content-center items-center gap-2 rounded-sm
|
||||||
|
border-[1px] bg-blue-700 px-5 py-2.5 text-sm font-medium
|
||||||
|
text-white
|
||||||
|
dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400
|
||||||
|
dark:hover:bg-gray-700 dark:focus:ring-blue-800
|
||||||
|
focus:outline-none focus:ring-4 focus:ring-blue-300
|
||||||
|
hover:bg-blue-800
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,25 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Modal } from "./components/modal";
|
import { Modal } from "./components/modal";
|
||||||
import { Card } from "./components/card";
|
import { Card } from "./components/card";
|
||||||
import { ErrorCallout } from "../../ui/components/olcallout";
|
import { ErrorCallout } from "../components/olcallout";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faArrowRight, faCheckCircle, faExternalLink } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowRight, faCheckCircle, faExternalLink } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { getApp, VERSION } from "../../olympusapp";
|
import { getApp, VERSION } from "../../olympusapp";
|
||||||
import { sha256 } from "js-sha256";
|
import { sha256 } from "js-sha256";
|
||||||
import { BLUE_COMMANDER, GAME_MASTER, OlympusState, RED_COMMANDER } from "../../constants/constants";
|
import { BLUE_COMMANDER, GAME_MASTER, OlympusState, RED_COMMANDER } from "../../constants/constants";
|
||||||
import { FaTrash, FaXmark } from "react-icons/fa6";
|
|
||||||
|
|
||||||
export function LoginModal(props: {}) {
|
export function LoginModal(props: { open: boolean }) {
|
||||||
// TODO: add warning if not in secure context and some features are disabled
|
// TODO: add warning if not in secure context and some features are disabled
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [profileName, setProfileName] = useState("Game Master");
|
const [profileName, setProfileName] = useState("");
|
||||||
const [checkingPassword, setCheckingPassword] = useState(false);
|
const [checkingPassword, setCheckingPassword] = useState(false);
|
||||||
const [loginError, setLoginError] = useState(false);
|
const [loginError, setLoginError] = useState(false);
|
||||||
const [commandMode, setCommandMode] = useState(null as null | string);
|
const [commandMode, setCommandMode] = useState(null as null | string);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
/* Set the profile name */
|
/* Set the profile name */
|
||||||
getApp().setProfile(profileName);
|
if (profileName !== "") getApp().setProfile(profileName);
|
||||||
}, [profileName])
|
}, [profileName]);
|
||||||
|
|
||||||
function checkPassword(password: string) {
|
function checkPassword(password: string) {
|
||||||
setCheckingPassword(true);
|
setCheckingPassword(true);
|
||||||
@@ -51,14 +50,14 @@ export function LoginModal(props: {}) {
|
|||||||
getApp().setState(OlympusState.IDLE);
|
getApp().setState(OlympusState.IDLE);
|
||||||
|
|
||||||
/* If no profile exists already with that name, create it from scratch from the defaults */
|
/* If no profile exists already with that name, create it from scratch from the defaults */
|
||||||
if (getApp().getProfile() === null)
|
if (getApp().getProfile() === null) getApp().saveProfile();
|
||||||
getApp().saveProfile();
|
|
||||||
/* Load the profile */
|
/* Load the profile */
|
||||||
getApp().loadProfile();
|
getApp().loadProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
open={props.open}
|
||||||
className={`
|
className={`
|
||||||
inline-flex h-[75%] max-h-[570px] w-[80%] max-w-[1100px] overflow-y-auto
|
inline-flex h-[75%] max-h-[570px] w-[80%] max-w-[1100px] overflow-y-auto
|
||||||
scroll-smooth bg-white
|
scroll-smooth bg-white
|
||||||
@@ -239,9 +238,7 @@ export function LoginModal(props: {}) {
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-400">
|
<div className="text-xs text-gray-400">The profile name you choose determines the saved key binds, groups and options you see.</div>
|
||||||
The profile name you choose determines the saved key binds, groups and options you see.
|
|
||||||
</div>
|
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { Modal } from "./components/modal";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
import { faArrowRight } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FaLock } from "react-icons/fa6";
|
|
||||||
import { getApp } from "../../olympusapp";
|
|
||||||
import { OlympusState } from "../../constants/constants";
|
|
||||||
|
|
||||||
export function ProtectionPrompt(props: { open: boolean }) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{props.open && (
|
|
||||||
<>
|
|
||||||
<Modal
|
|
||||||
className={`
|
|
||||||
inline-flex h-fit w-[600px] overflow-y-auto scroll-smooth bg-white
|
|
||||||
p-10
|
|
||||||
dark:bg-olympus-800
|
|
||||||
max-md:h-full max-md:max-h-full max-md:w-full max-md:rounded-none
|
|
||||||
max-md:border-none
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<div className="flex h-full w-full flex-col gap-12">
|
|
||||||
<div className={`flex flex-col items-start gap-2`}>
|
|
||||||
<span
|
|
||||||
className={`
|
|
||||||
text-gray-800 text-md
|
|
||||||
dark:text-white
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
Your selection contains protected units, are you sure you want to continue?
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className={`
|
|
||||||
text-gray-800 text-md
|
|
||||||
dark:text-gray-500
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
Pressing "Continue" will cause all DCS controlled units in the current selection to abort their mission and start following Olympus commands
|
|
||||||
only.
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className={`
|
|
||||||
text-gray-800 text-md
|
|
||||||
dark:text-gray-500
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
If you are trying to delete a human player unit, they will be killed and de-slotted. Be careful!
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className={`
|
|
||||||
text-gray-800 text-md
|
|
||||||
dark:text-gray-500
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
To disable this warning, press on the{" "}
|
|
||||||
<span
|
|
||||||
className={`
|
|
||||||
inline-block translate-y-3 rounded-full border-[1px]
|
|
||||||
border-gray-900 bg-red-500 p-2 text-olympus-900
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
<FaLock />
|
|
||||||
</span>{" "}
|
|
||||||
button
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => {
|
|
||||||
getApp().getUnitsManager().executeProtectionCallback();
|
|
||||||
getApp().setState(OlympusState.UNIT_CONTROL);
|
|
||||||
}}
|
|
||||||
className={`
|
|
||||||
mb-2 me-2 ml-auto flex content-center items-center gap-2
|
|
||||||
rounded-sm bg-blue-700 px-5 py-2.5 text-sm font-medium
|
|
||||||
text-white
|
|
||||||
dark:bg-blue-600 dark:hover:bg-blue-700
|
|
||||||
dark:focus:ring-blue-800
|
|
||||||
focus:outline-none focus:ring-4 focus:ring-blue-300
|
|
||||||
hover:bg-blue-800
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
Continue
|
|
||||||
<FontAwesomeIcon className={`my-auto`} icon={faArrowRight} />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => getApp().setState(OlympusState.UNIT_CONTROL)}
|
|
||||||
className={`
|
|
||||||
mb-2 me-2 flex content-center items-center gap-2 rounded-sm
|
|
||||||
border-[1px] bg-blue-700 px-5 py-2.5 text-sm font-medium
|
|
||||||
text-white
|
|
||||||
dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400
|
|
||||||
dark:hover:bg-gray-700 dark:focus:ring-blue-800
|
|
||||||
focus:outline-none focus:ring-4 focus:ring-blue-300
|
|
||||||
hover:bg-blue-800
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
Back
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
<div className={`fixed left-0 top-0 z-30 h-full w-full bg-[#111111]/95`}></div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
101
frontend/react/src/ui/modals/protectionpromptmodal.tsx
Normal file
101
frontend/react/src/ui/modals/protectionpromptmodal.tsx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Modal } from "./components/modal";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faArrowRight } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FaLock } from "react-icons/fa6";
|
||||||
|
import { getApp } from "../../olympusapp";
|
||||||
|
import { OlympusState } from "../../constants/constants";
|
||||||
|
|
||||||
|
export function ProtectionPromptModal(props: { open: boolean }) {
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
open={props.open}
|
||||||
|
className={`
|
||||||
|
inline-flex h-fit w-[600px] overflow-y-auto scroll-smooth bg-white p-10
|
||||||
|
dark:bg-olympus-800
|
||||||
|
max-md:h-full max-md:max-h-full max-md:w-full max-md:rounded-none
|
||||||
|
max-md:border-none
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<div className="flex h-full w-full flex-col gap-12">
|
||||||
|
<div className={`flex flex-col items-start gap-2`}>
|
||||||
|
<span
|
||||||
|
className={`
|
||||||
|
text-gray-800 text-md
|
||||||
|
dark:text-white
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
Your selection contains protected units, are you sure you want to continue?
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`
|
||||||
|
text-gray-800 text-md
|
||||||
|
dark:text-gray-500
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
Pressing "Continue" will cause all DCS controlled units in the current selection to abort their mission and start following Olympus commands only.
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`
|
||||||
|
text-gray-800 text-md
|
||||||
|
dark:text-gray-500
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
If you are trying to delete a human player unit, they will be killed and de-slotted. Be careful!
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`
|
||||||
|
text-gray-800 text-md
|
||||||
|
dark:text-gray-500
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
To disable this warning, press on the{" "}
|
||||||
|
<span
|
||||||
|
className={`
|
||||||
|
inline-block translate-y-3 rounded-full border-[1px]
|
||||||
|
border-gray-900 bg-red-500 p-2 text-olympus-900
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<FaLock />
|
||||||
|
</span>{" "}
|
||||||
|
button
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
getApp().getUnitsManager().executeProtectionCallback();
|
||||||
|
getApp().setState(OlympusState.UNIT_CONTROL);
|
||||||
|
}}
|
||||||
|
className={`
|
||||||
|
mb-2 me-2 ml-auto flex content-center items-center gap-2
|
||||||
|
rounded-sm bg-blue-700 px-5 py-2.5 text-sm font-medium text-white
|
||||||
|
dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
|
||||||
|
focus:outline-none focus:ring-4 focus:ring-blue-300
|
||||||
|
hover:bg-blue-800
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
<FontAwesomeIcon className={`my-auto`} icon={faArrowRight} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => getApp().setState(OlympusState.UNIT_CONTROL)}
|
||||||
|
className={`
|
||||||
|
mb-2 me-2 flex content-center items-center gap-2 rounded-sm
|
||||||
|
border-[1px] bg-blue-700 px-5 py-2.5 text-sm font-medium
|
||||||
|
text-white
|
||||||
|
dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400
|
||||||
|
dark:hover:bg-gray-700 dark:focus:ring-blue-800
|
||||||
|
focus:outline-none focus:ring-4 focus:ring-blue-300
|
||||||
|
hover:bg-blue-800
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -114,7 +114,7 @@ export function AirbaseMenu(props: { open: boolean; onClose: () => void; childre
|
|||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{airbase?.getChartData().runways.map((runway, idx) => {
|
{airbase?.getChartData().runways.map((runway, idx) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<div key={idx}>
|
||||||
{Object.keys(runway.headings[0]).map((runwayName) => {
|
{Object.keys(runway.headings[0]).map((runwayName) => {
|
||||||
return (
|
return (
|
||||||
<div key={`${idx}-${runwayName}`} className={`
|
<div key={`${idx}-${runwayName}`} className={`
|
||||||
@@ -137,7 +137,7 @@ export function AirbaseMenu(props: { open: boolean; onClose: () => void; childre
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children?
|
|||||||
const [audioManagerEnabled, setAudioManagerEnabled] = useState(false);
|
const [audioManagerEnabled, setAudioManagerEnabled] = useState(false);
|
||||||
const [activeSource, setActiveSource] = useState(null as AudioSource | null);
|
const [activeSource, setActiveSource] = useState(null as AudioSource | null);
|
||||||
const [count, setCount] = useState(0);
|
const [count, setCount] = useState(0);
|
||||||
const [shortcuts, setShortcuts] = useState({})
|
const [shortcuts, setShortcuts] = useState({});
|
||||||
|
|
||||||
/* Preallocate 128 references for the source and sink panels. If the number of references changes, React will give an error */
|
/* Preallocate 128 references for the source and sink panels. If the number of references changes, React will give an error */
|
||||||
const sourceRefs = Array(128)
|
const sourceRefs = Array(128)
|
||||||
@@ -251,20 +251,19 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children?
|
|||||||
right: (end as HTMLDivElement).offsetLeft + (end as HTMLDivElement).clientWidth,
|
right: (end as HTMLDivElement).offsetLeft + (end as HTMLDivElement).clientWidth,
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<div
|
||||||
<div
|
key={idx}
|
||||||
className={`
|
className={`
|
||||||
absolute rounded-br-md rounded-tr-md border-2 border-l-0
|
absolute rounded-br-md rounded-tr-md border-2 border-l-0
|
||||||
`}
|
`}
|
||||||
style={{
|
style={{
|
||||||
top: `${(startRect.bottom + startRect.top) / 2}px`,
|
top: `${(startRect.bottom + startRect.top) / 2}px`,
|
||||||
left: `${startRect.right}px`,
|
left: `${startRect.right}px`,
|
||||||
height: `${endRect.top - startRect.top + (endRect.height - startRect.height) / 2}px`,
|
height: `${endRect.top - startRect.top + (endRect.height - startRect.height) / 2}px`,
|
||||||
width: `${(lineCounters[idx] - 1) * lineDistance + 30}px`,
|
width: `${(lineCounters[idx] - 1) * lineDistance + 30}px`,
|
||||||
borderColor: lineColors[idx],
|
borderColor: lineColors[idx],
|
||||||
}}
|
}}
|
||||||
></div>
|
/>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -281,29 +280,26 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children?
|
|||||||
right: (div as HTMLDivElement).offsetLeft + (div as HTMLDivElement).clientWidth,
|
right: (div as HTMLDivElement).offsetLeft + (div as HTMLDivElement).clientWidth,
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<div
|
||||||
<div>
|
key={idx}
|
||||||
<div
|
data-active={activeSource === sources[idx]}
|
||||||
data-active={activeSource === sources[idx]}
|
className={`
|
||||||
className={`
|
absolute translate-y-[-50%] cursor-pointer rounded-full
|
||||||
absolute translate-y-[-50%] cursor-pointer rounded-full
|
bg-blue-600 p-1 text-xs text-white
|
||||||
bg-blue-600 p-1 text-xs text-white
|
data-[active='true']:bg-white
|
||||||
data-[active='true']:bg-white
|
data-[active='true']:text-blue-600
|
||||||
data-[active='true']:text-blue-600
|
hover:bg-blue-800
|
||||||
hover:bg-blue-800
|
`}
|
||||||
`}
|
style={{
|
||||||
style={{
|
top: `${(divRect.bottom + divRect.top) / 2}px`,
|
||||||
top: `${(divRect.bottom + divRect.top) / 2}px`,
|
left: `${divRect.right - 10}px`,
|
||||||
left: `${divRect.right - 10}px`,
|
}}
|
||||||
}}
|
onClick={() => {
|
||||||
onClick={() => {
|
activeSource !== sources[idx] ? setActiveSource(sources[idx]) : setActiveSource(null);
|
||||||
activeSource !== sources[idx] ? setActiveSource(sources[idx]) : setActiveSource(null);
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<FaPlus></FaPlus>
|
||||||
<FaPlus></FaPlus>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
@@ -318,28 +314,25 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children?
|
|||||||
right: (div as HTMLDivElement).offsetLeft + (div as HTMLDivElement).clientWidth,
|
right: (div as HTMLDivElement).offsetLeft + (div as HTMLDivElement).clientWidth,
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<div
|
||||||
<div>
|
key={idx}
|
||||||
<div
|
className={`
|
||||||
className={`
|
absolute translate-y-[-50%] cursor-pointer rounded-full
|
||||||
absolute translate-y-[-50%] cursor-pointer
|
bg-blue-600 p-1 text-xs text-white
|
||||||
rounded-full bg-blue-600 p-1 text-xs text-white
|
hover:bg-blue-800
|
||||||
hover:bg-blue-800
|
`}
|
||||||
`}
|
style={{
|
||||||
style={{
|
top: `${(divRect.bottom + divRect.top) / 2}px`,
|
||||||
top: `${(divRect.bottom + divRect.top) / 2}px`,
|
left: `${divRect.right - 10}px`,
|
||||||
left: `${divRect.right - 10}px`,
|
}}
|
||||||
}}
|
onClick={() => {
|
||||||
onClick={() => {
|
if (activeSource.getConnectedTo().includes(sinks[idx])) activeSource.disconnect(sinks[idx]);
|
||||||
if (activeSource.getConnectedTo().includes(sinks[idx])) activeSource.disconnect(sinks[idx]);
|
else activeSource.connect(sinks[idx]);
|
||||||
else activeSource.connect(sinks[idx]);
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{" "}
|
||||||
{" "}
|
{activeSource.getConnectedTo().includes(sinks[idx]) ? <FaMinus></FaMinus> : <FaPlus></FaPlus>}
|
||||||
{activeSource.getConnectedTo().includes(sinks[idx]) ? <FaMinus></FaMinus> : <FaPlus></FaPlus>}
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -46,21 +46,14 @@ export function ControlsPanel(props: {}) {
|
|||||||
text: "Select unit",
|
text: "Select unit",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
actions: [touch ? faHandPointer : "LMB", "Drag"],
|
actions: ["Shift", "LMB", "Drag"],
|
||||||
text: "Box selection",
|
text: "Box selection",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
actions: [touch ? faHandPointer : "Wheel", "Drag"],
|
actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||||
text: "Move map",
|
text: "Move map",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
if (!touch) {
|
|
||||||
controls.push({
|
|
||||||
actions: ["Shift", "LMB", "Drag"],
|
|
||||||
|
|
||||||
text: "Box selection",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appState === OlympusState.IDLE) {
|
if (appState === OlympusState.IDLE) {
|
||||||
controls = baseControls;
|
controls = baseControls;
|
||||||
|
|||||||
@@ -38,20 +38,12 @@ export function GameMasterMenu(props: { open: boolean; onClose: () => void; chil
|
|||||||
GAME MASTER
|
GAME MASTER
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{commandModeOptions.commandMode === BLUE_COMMANDER && (
|
{commandModeOptions.commandMode === BLUE_COMMANDER && <div className={`
|
||||||
<div
|
w-full rounded-md bg-blue-600 p-2 text-center font-bold
|
||||||
className={`w-full rounded-md bg-blue-600 p-2 text-center font-bold`}
|
`}>BLUE COMMANDER</div>}
|
||||||
>
|
{commandModeOptions.commandMode === RED_COMMANDER && <div className={`
|
||||||
BLUE COMMANDER
|
w-full rounded-md bg-red-700 p-2 text-center font-bold
|
||||||
</div>
|
`}>RED COMMANDER</div>}
|
||||||
)}
|
|
||||||
{commandModeOptions.commandMode === RED_COMMANDER && (
|
|
||||||
<div
|
|
||||||
className={`w-full rounded-md bg-red-700 p-2 text-center font-bold`}
|
|
||||||
>
|
|
||||||
RED COMMANDER
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{serverStatus.elapsedTime > currentSetupTime && (
|
{serverStatus.elapsedTime > currentSetupTime && (
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
@@ -125,6 +117,7 @@ export function GameMasterMenu(props: { open: boolean; onClose: () => void; chil
|
|||||||
.map((era) => {
|
.map((era) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
key={era}
|
||||||
className={`
|
className={`
|
||||||
group flex flex-row rounded-md justify-content
|
group flex flex-row rounded-md justify-content
|
||||||
cursor-pointer gap-4 p-2
|
cursor-pointer gap-4 p-2
|
||||||
@@ -274,9 +267,12 @@ export function GameMasterMenu(props: { open: boolean; onClose: () => void; chil
|
|||||||
group flex flex-row rounded-md justify-content gap-4 px-4 py-2
|
group flex flex-row rounded-md justify-content gap-4 px-4 py-2
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<span className="mr-auto">Elapsed time (seconds)</span> <span className={`
|
<span className="mr-auto">Elapsed time (seconds)</span>{" "}
|
||||||
w-32 text-center
|
<span
|
||||||
`}>{serverStatus.elapsedTime?.toFixed()}</span>
|
className={`w-32 text-center`}
|
||||||
|
>
|
||||||
|
{serverStatus.elapsedTime?.toFixed()}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{commandModeOptions.commandMode === GAME_MASTER && (
|
{commandModeOptions.commandMode === GAME_MASTER && (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ export function Header() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<OlLabelToggle
|
<OlLabelToggle
|
||||||
toggled={mapOptions.cameraPluginMode === "live"}
|
toggled={mapOptions.cameraPluginMode === "map"}
|
||||||
leftLabel={"Live"}
|
leftLabel={"Live"}
|
||||||
rightLabel={"Map"}
|
rightLabel={"Map"}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -15,12 +15,11 @@ export function InfoBar(props: {}) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={`absolute left-[50%] top-16`}>
|
||||||
className={`absolute left-[50%] top-16`}
|
|
||||||
>
|
|
||||||
{messages.slice(Math.max(0, messages.length - 4), Math.max(0, messages.length)).map((message, idx) => {
|
{messages.slice(Math.max(0, messages.length - 4), Math.max(0, messages.length)).map((message, idx) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
key={idx}
|
||||||
className={`
|
className={`
|
||||||
absolute w-fit translate-x-[-50%] gap-2 text-nowrap rounded-full
|
absolute w-fit translate-x-[-50%] gap-2 text-nowrap rounded-full
|
||||||
bg-olympus-800/90 px-4 py-2 text-center text-sm text-white
|
bg-olympus-800/90 px-4 py-2 text-center text-sm text-white
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
|
|||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<OlAccordion
|
<OlAccordion
|
||||||
onClick={() => setOpenAccordion(openAccordion === Accordion.NONE ? Accordion.BINDINGS : Accordion.NONE)}
|
onClick={() => setOpenAccordion(openAccordion !== Accordion.BINDINGS ? Accordion.BINDINGS : Accordion.NONE)}
|
||||||
open={openAccordion === Accordion.BINDINGS}
|
open={openAccordion === Accordion.BINDINGS}
|
||||||
title="Key bindings"
|
title="Key bindings"
|
||||||
>
|
>
|
||||||
@@ -53,9 +53,10 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
|
|||||||
.map(([id, shortcut]) => {
|
.map(([id, shortcut]) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
key={id}
|
||||||
className={`
|
className={`
|
||||||
group relative mr-2 flex cursor-pointer select-none
|
group relative mr-2 flex cursor-pointer select-none
|
||||||
items-center justify-between rounded-sm px-2 py-2 text-sm
|
items-center justify-between rounded-sm px-2 py-2
|
||||||
dark:text-gray-300 dark:hover:bg-olympus-500
|
dark:text-gray-300 dark:hover:bg-olympus-500
|
||||||
`}
|
`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -65,39 +66,9 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
|
|||||||
>
|
>
|
||||||
<span>{shortcut.getOptions().label}</span>
|
<span>{shortcut.getOptions().label}</span>
|
||||||
<span className="flex gap-1">
|
<span className="flex gap-1">
|
||||||
{shortcut.getOptions().altKey ? (
|
{shortcut.getOptions().ctrlKey && "Ctrl + "}
|
||||||
<div className="flex gap-1">
|
{shortcut.getOptions().altKey && "Alt + "}
|
||||||
<div className={`text-green-500`}>Alt</div> +{" "}
|
{shortcut.getOptions().shiftKey && "Shift + "}
|
||||||
</div>
|
|
||||||
) : shortcut.getOptions().altKey === false ? (
|
|
||||||
<div className={`flex gap-1`}>
|
|
||||||
<div className={`text-red-500`}>Alt</div> +{" "}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
""
|
|
||||||
)}
|
|
||||||
{shortcut.getOptions().ctrlKey ? (
|
|
||||||
<div className="flex gap-1">
|
|
||||||
<div className={`text-green-500`}>Shift</div> +{" "}
|
|
||||||
</div>
|
|
||||||
) : shortcut.getOptions().ctrlKey === false ? (
|
|
||||||
<div className={`flex gap-1`}>
|
|
||||||
<div className={`text-red-500`}>Shift</div> +{" "}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
""
|
|
||||||
)}
|
|
||||||
{shortcut.getOptions().shiftKey ? (
|
|
||||||
<div className="flex gap-1">
|
|
||||||
<div className={`text-green-500`}>Ctrl</div> +{" "}
|
|
||||||
</div>
|
|
||||||
) : shortcut.getOptions().shiftKey === false ? (
|
|
||||||
<div className={`flex gap-1`}>
|
|
||||||
<div className={`text-red-500`}>Ctrl</div> +{" "}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
""
|
|
||||||
)}
|
|
||||||
{shortcut.getOptions().code}
|
{shortcut.getOptions().code}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -107,7 +78,7 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
|
|||||||
</OlAccordion>
|
</OlAccordion>
|
||||||
|
|
||||||
<OlAccordion
|
<OlAccordion
|
||||||
onClick={() => setOpenAccordion(openAccordion === Accordion.NONE ? Accordion.MAP_OPTIONS : Accordion.NONE)}
|
onClick={() => setOpenAccordion(openAccordion !== Accordion.MAP_OPTIONS ? Accordion.MAP_OPTIONS : Accordion.NONE)}
|
||||||
open={openAccordion === Accordion.MAP_OPTIONS}
|
open={openAccordion === Accordion.MAP_OPTIONS}
|
||||||
title="Map options"
|
title="Map options"
|
||||||
>
|
>
|
||||||
@@ -158,7 +129,7 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
|
|||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
group flex flex-row gap-4 rounded-md justify-content
|
group flex flex-row gap-4 rounded-md justify-content
|
||||||
cursor-pointer p-2 text-sm
|
cursor-pointer p-2
|
||||||
dark:hover:bg-olympus-400
|
dark:hover:bg-olympus-400
|
||||||
`}
|
`}
|
||||||
onClick={() => getApp().getMap().setOption("hideUnitsShortRangeRings", !mapOptions.hideUnitsShortRangeRings)}
|
onClick={() => getApp().getMap().setOption("hideUnitsShortRangeRings", !mapOptions.hideUnitsShortRangeRings)}
|
||||||
@@ -166,11 +137,11 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
|
|||||||
<OlCheckbox checked={mapOptions.hideUnitsShortRangeRings} onChange={() => {}}></OlCheckbox>
|
<OlCheckbox checked={mapOptions.hideUnitsShortRangeRings} onChange={() => {}}></OlCheckbox>
|
||||||
<span>Hide Short range Rings</span>
|
<span>Hide Short range Rings</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
group flex flex-row gap-4 rounded-md justify-content
|
group flex flex-row gap-4 rounded-md justify-content
|
||||||
cursor-pointer p-2 text-sm
|
cursor-pointer p-2
|
||||||
dark:hover:bg-olympus-400
|
dark:hover:bg-olympus-400
|
||||||
`}
|
`}
|
||||||
onClick={() => getApp().getMap().setOption("hideGroupMembers", !mapOptions.hideGroupMembers)}
|
onClick={() => getApp().getMap().setOption("hideGroupMembers", !mapOptions.hideGroupMembers)}
|
||||||
@@ -181,7 +152,7 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
|
|||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
group flex flex-row gap-4 rounded-md justify-content
|
group flex flex-row gap-4 rounded-md justify-content
|
||||||
cursor-pointer p-2 text-sm
|
cursor-pointer p-2
|
||||||
dark:hover:bg-olympus-400
|
dark:hover:bg-olympus-400
|
||||||
`}
|
`}
|
||||||
onClick={() => getApp().getMap().setOption("showMinimap", !mapOptions.showMinimap)}
|
onClick={() => getApp().getMap().setOption("showMinimap", !mapOptions.showMinimap)}
|
||||||
@@ -192,7 +163,7 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre
|
|||||||
</OlAccordion>
|
</OlAccordion>
|
||||||
|
|
||||||
<OlAccordion
|
<OlAccordion
|
||||||
onClick={() => setOpenAccordion(openAccordion === Accordion.NONE ? Accordion.CAMERA_PLUGIN : Accordion.NONE)}
|
onClick={() => setOpenAccordion(openAccordion !== Accordion.CAMERA_PLUGIN ? Accordion.CAMERA_PLUGIN : Accordion.NONE)}
|
||||||
open={openAccordion === Accordion.CAMERA_PLUGIN}
|
open={openAccordion === Accordion.CAMERA_PLUGIN}
|
||||||
title="Camera plugin options"
|
title="Camera plugin options"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export function RadiosSummaryPanel(props: {}) {
|
|||||||
.map((radioSink, idx) => {
|
.map((radioSink, idx) => {
|
||||||
return (
|
return (
|
||||||
<OlStateButton
|
<OlStateButton
|
||||||
|
key={idx}
|
||||||
checked={radioSink.getPtt()}
|
checked={radioSink.getPtt()}
|
||||||
onClick={() => {}}
|
onClick={() => {}}
|
||||||
onMouseDown={() => {
|
onMouseDown={() => {
|
||||||
@@ -41,28 +42,23 @@ export function RadiosSummaryPanel(props: {}) {
|
|||||||
radioSink.setPtt(false);
|
radioSink.setPtt(false);
|
||||||
}}
|
}}
|
||||||
tooltip="Click to talk, lights up when receiving"
|
tooltip="Click to talk, lights up when receiving"
|
||||||
buttonColor={
|
buttonColor={radioSink.getReceiving() ? "white" : null}
|
||||||
radioSink.getReceiving()
|
|
||||||
? "white"
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<span className={`font-bold text-gray-200`}>{idx + 1}</span>
|
<span className={`font-bold text-gray-200`}>{idx + 1}</span>
|
||||||
</OlStateButton>
|
</OlStateButton>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{audioSinks.filter((audioSinks) => audioSinks instanceof UnitSink).length > 0 && (
|
{audioSinks.filter((audioSinks) => audioSinks instanceof UnitSink).length > 0 && <FaJetFighter className={`
|
||||||
<FaJetFighter
|
text-xl
|
||||||
className={`text-xl`}
|
`} />}
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{audioSinks.filter((audioSinks) => audioSinks instanceof UnitSink).length > 0 &&
|
{audioSinks.filter((audioSinks) => audioSinks instanceof UnitSink).length > 0 &&
|
||||||
audioSinks
|
audioSinks
|
||||||
.filter((audioSinks) => audioSinks instanceof UnitSink)
|
.filter((audioSinks) => audioSinks instanceof UnitSink)
|
||||||
.map((unitSink, idx) => {
|
.map((unitSink, idx) => {
|
||||||
return (
|
return (
|
||||||
<OlStateButton
|
<OlStateButton
|
||||||
|
key={idx}
|
||||||
checked={unitSink.getPtt()}
|
checked={unitSink.getPtt()}
|
||||||
onClick={() => {}}
|
onClick={() => {}}
|
||||||
onMouseDown={() => {
|
onMouseDown={() => {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { faStar } from "@fortawesome/free-solid-svg-icons";
|
|||||||
import { OlStringInput } from "../components/olstringinput";
|
import { OlStringInput } from "../components/olstringinput";
|
||||||
import { countryCodes } from "../data/codes";
|
import { countryCodes } from "../data/codes";
|
||||||
import { OlAccordion } from "../components/olaccordion";
|
import { OlAccordion } from "../components/olaccordion";
|
||||||
|
import { AppStateChangedEvent } from "../../events";
|
||||||
|
|
||||||
export function UnitSpawnMenu(props: {
|
export function UnitSpawnMenu(props: {
|
||||||
starredSpawns: { [key: string]: SpawnRequestTable };
|
starredSpawns: { [key: string]: SpawnRequestTable };
|
||||||
@@ -32,6 +33,7 @@ export function UnitSpawnMenu(props: {
|
|||||||
const altitudeStep = altitudeIncrements[props.blueprint.category];
|
const altitudeStep = altitudeIncrements[props.blueprint.category];
|
||||||
|
|
||||||
/* State initialization */
|
/* State initialization */
|
||||||
|
const [appState, setAppState] = useState(OlympusState.NOT_INITIALIZED);
|
||||||
const [spawnCoalition, setSpawnCoalition] = useState("blue" as Coalition);
|
const [spawnCoalition, setSpawnCoalition] = useState("blue" as Coalition);
|
||||||
const [spawnNumber, setSpawnNumber] = useState(1);
|
const [spawnNumber, setSpawnNumber] = useState(1);
|
||||||
const [spawnRole, setSpawnRole] = useState("");
|
const [spawnRole, setSpawnRole] = useState("");
|
||||||
@@ -46,9 +48,14 @@ export function UnitSpawnMenu(props: {
|
|||||||
const [key, setKey] = useState("");
|
const [key, setKey] = useState("");
|
||||||
const [spawnRequestTable, setSpawnRequestTable] = useState(null as null | SpawnRequestTable);
|
const [spawnRequestTable, setSpawnRequestTable] = useState(null as null | SpawnRequestTable);
|
||||||
|
|
||||||
/* When the menu is opened show the unit preview on the map as a cursor */
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!props.airbase && spawnRequestTable) {
|
setAppState(getApp()?.getState())
|
||||||
|
AppStateChangedEvent.on((state, subState) => setAppState(state));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/* When the menu is opened show the unit preview on the map as a cursor */
|
||||||
|
const setSpawnRequestTableCallback = useCallback(() => {
|
||||||
|
if (!props.airbase && spawnRequestTable && appState === OlympusState.SPAWN) {
|
||||||
/* Refresh the unique key identified */
|
/* Refresh the unique key identified */
|
||||||
const newKey = hash(JSON.stringify(spawnRequestTable));
|
const newKey = hash(JSON.stringify(spawnRequestTable));
|
||||||
setKey(newKey);
|
setKey(newKey);
|
||||||
@@ -56,7 +63,9 @@ export function UnitSpawnMenu(props: {
|
|||||||
getApp()?.getMap()?.setSpawnRequestTable(spawnRequestTable);
|
getApp()?.getMap()?.setSpawnRequestTable(spawnRequestTable);
|
||||||
getApp().setState(OlympusState.SPAWN, SpawnSubState.SPAWN_UNIT);
|
getApp().setState(OlympusState.SPAWN, SpawnSubState.SPAWN_UNIT);
|
||||||
}
|
}
|
||||||
}, [spawnRequestTable]);
|
}, [spawnRequestTable, appState]);
|
||||||
|
|
||||||
|
useEffect(setSpawnRequestTableCallback, [spawnRequestTable]);
|
||||||
|
|
||||||
/* Callback and effect to update the quick access name of the starredSpawn */
|
/* Callback and effect to update the quick access name of the starredSpawn */
|
||||||
const updateStarredSpawnQuickAccessNameS = useCallback(() => {
|
const updateStarredSpawnQuickAccessNameS = useCallback(() => {
|
||||||
@@ -231,6 +240,7 @@ export function UnitSpawnMenu(props: {
|
|||||||
{roles.map((role) => {
|
{roles.map((role) => {
|
||||||
return (
|
return (
|
||||||
<OlDropdownItem
|
<OlDropdownItem
|
||||||
|
key={role}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSpawnRole(role);
|
setSpawnRole(role);
|
||||||
setSpawnLoadout("");
|
setSpawnLoadout("");
|
||||||
@@ -256,6 +266,7 @@ export function UnitSpawnMenu(props: {
|
|||||||
{loadouts.map((loadout) => {
|
{loadouts.map((loadout) => {
|
||||||
return (
|
return (
|
||||||
<OlDropdownItem
|
<OlDropdownItem
|
||||||
|
key={loadout.name}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSpawnLoadout(loadout.name);
|
setSpawnLoadout(loadout.name);
|
||||||
}}
|
}}
|
||||||
@@ -285,10 +296,9 @@ export function UnitSpawnMenu(props: {
|
|||||||
>
|
>
|
||||||
Livery
|
Livery
|
||||||
</span>
|
</span>
|
||||||
<OlDropdown
|
<OlDropdown label={props.blueprint.liveries ? (props.blueprint.liveries[spawnLiveryID]?.name ?? "Default") : "No livery"} className={`
|
||||||
label={props.blueprint.liveries ? (props.blueprint.liveries[spawnLiveryID]?.name ?? "Default") : "No livery"}
|
w-64
|
||||||
className={`w-64`}
|
`}>
|
||||||
>
|
|
||||||
{props.blueprint.liveries &&
|
{props.blueprint.liveries &&
|
||||||
Object.keys(props.blueprint.liveries)
|
Object.keys(props.blueprint.liveries)
|
||||||
.sort((ida, idb) => {
|
.sort((ida, idb) => {
|
||||||
@@ -303,6 +313,7 @@ export function UnitSpawnMenu(props: {
|
|||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<OlDropdownItem
|
<OlDropdownItem
|
||||||
|
key={id}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSpawnLiveryID(id);
|
setSpawnLiveryID(id);
|
||||||
}}
|
}}
|
||||||
@@ -315,9 +326,10 @@ export function UnitSpawnMenu(props: {
|
|||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{props.blueprint.liveries && props.blueprint.liveries[id].countries.length == 1 && (
|
{props.blueprint.liveries && props.blueprint.liveries[id].countries.length == 1 && (
|
||||||
<img src={`images/countries/${country?.flagCode.toLowerCase()}.svg`} className={`
|
<img
|
||||||
h-6
|
src={`images/countries/${country?.flagCode.toLowerCase()}.svg`}
|
||||||
`} />
|
className={`h-6`}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="my-auto truncate">
|
<div className="my-auto truncate">
|
||||||
@@ -348,6 +360,7 @@ export function UnitSpawnMenu(props: {
|
|||||||
{["Average", "Good", "High", "Excellent"].map((skill) => {
|
{["Average", "Good", "High", "Excellent"].map((skill) => {
|
||||||
return (
|
return (
|
||||||
<OlDropdownItem
|
<OlDropdownItem
|
||||||
|
key={skill}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSpawnSkill(skill);
|
setSpawnSkill(skill);
|
||||||
}}
|
}}
|
||||||
@@ -383,7 +396,7 @@ export function UnitSpawnMenu(props: {
|
|||||||
>
|
>
|
||||||
{spawnLoadout.items.map((item) => {
|
{spawnLoadout.items.map((item) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex content-center gap-2">
|
<div className="flex content-center gap-2" key={item.name}>
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
my-auto w-6 min-w-6 rounded-full py-0.5 text-center
|
my-auto w-6 min-w-6 rounded-full py-0.5 text-center
|
||||||
@@ -421,7 +434,13 @@ export function UnitSpawnMenu(props: {
|
|||||||
if (spawnRequestTable)
|
if (spawnRequestTable)
|
||||||
getApp()
|
getApp()
|
||||||
.getUnitsManager()
|
.getUnitsManager()
|
||||||
.spawnUnits(spawnRequestTable.category, Array(spawnRequestTable.amount).fill(spawnRequestTable.unit), spawnRequestTable.coalition, false, props.airbase?.getName());
|
.spawnUnits(
|
||||||
|
spawnRequestTable.category,
|
||||||
|
Array(spawnRequestTable.amount).fill(spawnRequestTable.unit),
|
||||||
|
spawnRequestTable.coalition,
|
||||||
|
false,
|
||||||
|
props.airbase?.getName()
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Spawn
|
Spawn
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { OptionsMenu } from "./panels/optionsmenu";
|
|||||||
import { MapHiddenTypes, MapOptions } from "../types/types";
|
import { MapHiddenTypes, MapOptions } from "../types/types";
|
||||||
import { NO_SUBSTATE, OlympusState, OlympusSubState, OptionsSubstate, SpawnSubState, UnitControlSubState } from "../constants/constants";
|
import { NO_SUBSTATE, OlympusState, OlympusSubState, OptionsSubstate, SpawnSubState, UnitControlSubState } from "../constants/constants";
|
||||||
import { getApp, setupApp } from "../olympusapp";
|
import { getApp, setupApp } from "../olympusapp";
|
||||||
import { LoginModal } from "./modals/login";
|
import { LoginModal } from "./modals/loginmodal";
|
||||||
|
|
||||||
import { MiniMapPanel } from "./panels/minimappanel";
|
import { MiniMapPanel } from "./panels/minimappanel";
|
||||||
import { UnitControlBar } from "./panels/unitcontrolbar";
|
import { UnitControlBar } from "./panels/unitcontrolbar";
|
||||||
@@ -20,7 +20,7 @@ import { MapContextMenu } from "./contextmenus/mapcontextmenu";
|
|||||||
import { AirbaseMenu } from "./panels/airbasemenu";
|
import { AirbaseMenu } from "./panels/airbasemenu";
|
||||||
import { AudioMenu } from "./panels/audiomenu";
|
import { AudioMenu } from "./panels/audiomenu";
|
||||||
import { FormationMenu } from "./panels/formationmenu";
|
import { FormationMenu } from "./panels/formationmenu";
|
||||||
import { ProtectionPrompt } from "./modals/protectionprompt";
|
import { ProtectionPromptModal } from "./modals/protectionpromptmodal";
|
||||||
import { KeybindModal } from "./modals/keybindmodal";
|
import { KeybindModal } from "./modals/keybindmodal";
|
||||||
import { UnitExplosionMenu } from "./panels/unitexplosionmenu";
|
import { UnitExplosionMenu } from "./panels/unitexplosionmenu";
|
||||||
import { JTACMenu } from "./panels/jtacmenu";
|
import { JTACMenu } from "./panels/jtacmenu";
|
||||||
@@ -65,16 +65,8 @@ export function UI() {
|
|||||||
>
|
>
|
||||||
<Header />
|
<Header />
|
||||||
<div className="flex h-full w-full flex-row-reverse">
|
<div className="flex h-full w-full flex-row-reverse">
|
||||||
{appState === OlympusState.LOGIN && (
|
<LoginModal open={appState === OlympusState.LOGIN} />
|
||||||
<>
|
<ProtectionPromptModal open={appState === OlympusState.UNIT_CONTROL && appSubState == UnitControlSubState.PROTECTION} />
|
||||||
<div className={`
|
|
||||||
fixed left-0 top-0 z-30 h-full w-full bg-[#111111]/95
|
|
||||||
`}></div>
|
|
||||||
<LoginModal />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<ProtectionPrompt open={appState === OlympusState.UNIT_CONTROL && appSubState == UnitControlSubState.PROTECTION} />
|
|
||||||
<KeybindModal open={appState === OlympusState.OPTIONS && appSubState === OptionsSubstate.KEYBIND} />
|
<KeybindModal open={appState === OlympusState.OPTIONS && appSubState === OptionsSubstate.KEYBIND} />
|
||||||
|
|
||||||
<div id="map-container" className="z-0 h-full w-screen" />
|
<div id="map-container" className="z-0 h-full w-screen" />
|
||||||
|
|||||||
Reference in New Issue
Block a user