diff --git a/client/public/stylesheets/layout/layout.css b/client/public/stylesheets/layout/layout.css
index 91926b4b..43bd9917 100644
--- a/client/public/stylesheets/layout/layout.css
+++ b/client/public/stylesheets/layout/layout.css
@@ -51,7 +51,7 @@
left: 10px;
position: absolute;
top: 80px;
- width: 240px;
+ width: 320px;
z-index: 1000;
}
diff --git a/client/public/stylesheets/olympus.css b/client/public/stylesheets/olympus.css
index a23ecfd6..b540f9d2 100644
--- a/client/public/stylesheets/olympus.css
+++ b/client/public/stylesheets/olympus.css
@@ -50,6 +50,7 @@ button {
cursor: pointer;
font-weight: var(--font-weight-bolder);
padding: 6px;
+ column-gap: 5px;
}
button:hover {
@@ -61,6 +62,13 @@ button[disabled="disabled"] {
cursor: not-allowed;
}
+button>svg:first-child,
+button>img:first-child {
+ position: relative;
+ aspect-ratio: initial;
+ height: 100%;
+}
+
form {
margin: 0;
padding: 0;
@@ -121,10 +129,10 @@ form>div {
}
.ol-panel hr {
- background-color: var(--secondary-light-grey);
+ background-color: var(--secondary-transparent-white);
border: none;
height: 1px;
- margin: 20px 0;
+ margin: 10px 0;
width: 100%;
}
@@ -356,6 +364,12 @@ h4 {
button.ol-button-warning {
border: 1px solid var(--primary-red);
color: var(--primary-red);
+ font-weight: bold;
+}
+
+button.ol-button-warning>svg:first-child {
+ stroke: var(--primary-red);
+ fill: var(--primary-red);
}
nav.ol-panel {
@@ -482,16 +496,16 @@ nav.ol-panel> :last-child {
flex-direction: column;
}
-.slider-container {
+.ol-slider-container {
width: 100%;
}
-.slider-container:not(:first-of-type) {
+.ol-slider-container:not(:first-of-type) {
margin-top: 10px;
width: 100%;
}
-.slider {
+.ol-slider {
-webkit-appearance: none;
appearance: none;
background: #d3d3d3;
@@ -505,34 +519,51 @@ nav.ol-panel> :last-child {
width: 100%;
}
-.slider:hover {
+.ol-slider:hover {
opacity: 1;
}
-.slider::-webkit-slider-thumb {
+.ol-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
- background: gray;
+ background: var(--background-grey);
border-radius: 999px;
cursor: pointer;
- height: 20px;
- width: 20px;
+ height: 25px;
+ width: 25px;
}
-.active .slider::-webkit-slider-thumb {
- background: #5ca7ff;
+.active .ol-slider::-webkit-slider-thumb {
+ background: radial-gradient(circle at center, var(--accent-light-blue), var(--accent-light-blue) 40%, color-mix(in srgb, var(--accent-light-blue), transparent 66%) 50%);
}
-.slider::-moz-range-thumb {
- background: gray;
+.ol-slider::-moz-range-thumb {
+ -moz-appearance: none;
+ border: 0px solid transparent;
+ background: var(--background-grey);
border-radius: 999px;
cursor: pointer;
- height: 20px;
- width: 20px;
+ height: 25px;
+ width: 25px;
}
-.active .slider::-moz-range-thumb {
- background: #5ca7ff;
+.active .ol-slider::-moz-range-thumb {
+ -moz-appearance: none;
+ background: radial-gradient(circle at center, var(--accent-light-blue), var(--accent-light-blue) 40%, color-mix(in srgb, var(--accent-light-blue), transparent 66%) 50%);
+}
+
+.ol-slider-min-max {
+ display: flex;
+ justify-content: space-between;
+ color: var(--secondary-light-grey);
+}
+
+.ol-slider-min-max::before {
+ content: attr(data-min-value);
+}
+
+.ol-slider-min-max::after {
+ content: attr(data-max-value);
}
.main-logo {
@@ -848,12 +879,8 @@ dl.ol-data-grid {
row-gap: 4px;
}
-dl.ol-data-grid dt {
- width: 60%;
-}
-
dl.ol-data-grid dd {
- width: 40%;
+ width: fit-content;
}
dl.ol-data-grid dt.icon {
@@ -1056,25 +1083,26 @@ input[type=number]::-webkit-outer-spin-button {
width: 24px;
}
-.toggle {
+.ol-switch {
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
-.toggle-input {
+.ol-switch-input {
display: none;
}
-.toggle-fill {
+.ol-switch-fill {
border-radius: 999px;
- height: 20px;
position: relative;
transition: background-color 0.2s;
+ height: var(--height);
+ width: var(--width);
}
-.toggle-fill::after {
+.ol-switch-fill::after {
aspect-ratio : 1 / 1;
background-clip: content-box;
background-color: #ffffff;
@@ -1082,7 +1110,29 @@ input[type=number]::-webkit-outer-spin-button {
box-sizing: border-box;
content: "";
height: 100%;
- padding: 2px;
+ padding: 3px;
position: absolute;
transition: transform 0.2s;
+ top: 0px;
+}
+
+.ol-switch-fill::before {
+ align-items: center;
+ box-sizing: border-box;
+ color: white;
+ display: flex;
+ font-size: 11px;
+ height: 100%;
+ padding: 0px 7px;
+ position: absolute;
+ transition: transform 0.2s;
+}
+
+.ol-switch[data-value="false"]>.ol-switch-fill::before {
+ transform: translateX(calc(var(--width) - 100%));
+
+}
+
+.ol-switch[data-value="true"]>.ol-switch-fill::after {
+ transform: translateX(calc(var(--width) - var(--height)));
}
\ No newline at end of file
diff --git a/client/public/stylesheets/other/contextmenus.css b/client/public/stylesheets/other/contextmenus.css
index ddd329bd..cc1dbd34 100644
--- a/client/public/stylesheets/other/contextmenus.css
+++ b/client/public/stylesheets/other/contextmenus.css
@@ -109,11 +109,11 @@
border-top-right-radius: var(--border-radius-sm);
}
-#context-menu-switch .toggle-fill {
+#context-menu-switch .ol-switch-fill {
width: 40;
}
-[data-active-coalition="blue"].toggle-fill,
+[data-active-coalition="blue"].ol-switch-fill,
[data-active-coalition="blue"].unit-spawn-button:hover,
[data-active-coalition="blue"].unit-spawn-button.is-open,
[data-active-coalition="blue"]#active-coalition-label,
@@ -122,7 +122,7 @@
background-color: var(--primary-blue)
}
-[data-active-coalition="red"].toggle-fill,
+[data-active-coalition="red"].ol-switch-fill,
[data-active-coalition="red"].unit-spawn-button:hover,
[data-active-coalition="red"].unit-spawn-button.is-open,
[data-active-coalition="red"]#active-coalition-label,
@@ -131,7 +131,7 @@
background-color: var(--primary-red)
}
-[data-active-coalition="neutral"].toggle-fill,
+[data-active-coalition="neutral"].ol-switch-fill,
[data-active-coalition="neutral"].unit-spawn-button:hover,
[data-active-coalition="neutral"].unit-spawn-button.is-open,
[data-active-coalition="neutral"]#active-coalition-label,
@@ -158,15 +158,15 @@
cursor: default;
}
-[data-active-coalition="blue"].toggle-fill::after {
+[data-active-coalition="blue"].ol-switch-fill::after {
transform: translateX(0%);
}
-[data-active-coalition="red"].toggle-fill::after {
+[data-active-coalition="red"].ol-switch-fill::after {
transform: translateX(100%);
}
-[data-active-coalition="neutral"].toggle-fill::after {
+[data-active-coalition="neutral"].ol-switch-fill::after {
transform: translateX(50%);
}
diff --git a/client/public/stylesheets/panels/unitcontrol.css b/client/public/stylesheets/panels/unitcontrol.css
index c94e266a..2b3ef0c7 100644
--- a/client/public/stylesheets/panels/unitcontrol.css
+++ b/client/public/stylesheets/panels/unitcontrol.css
@@ -2,6 +2,12 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
display: block !important;
}
+#unit-control-panel {
+ display: flex;
+ flex-direction: column;
+ row-gap: 10px;
+}
+
#unit-control-panel h3 {
margin-bottom: 8px;
}
@@ -24,42 +30,27 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
font-size: 11px;
height: 32px;
margin-right: 5px;
- padding: 8px 0;
position: relative;
width: calc(100% - 5px);
+ justify-content: space-between;
}
-#unit-control-panel #selected-units-container button::before {
- background-color: var(--primary-neutral);
+#unit-control-panel #selected-units-container button::after {
border-radius: 999px;
- content: attr(data-short-label);
+ color: var(--secondary-semitransparent-white);
+ content: attr(data-label);
font-size: 10px;
- margin: 2px 4px;
- max-width: 30px;
- min-width: 20px;
- overflow: hidden;
padding: 4px 6px;
- text-overflow: ellipsis;
- white-space: nowrap;
width: fit-content;
+ white-space: nowrap;
}
-#unit-control-panel #selected-units-container button:hover::before {
- background-color: black;
+#unit-control-panel #selected-units-container button:hover::after {
max-width: 100%;
text-overflow: unset;
}
-#unit-control-panel #selected-units-container button[data-coalition="blue"]::before {
- background-color: var(--accent-light-blue);
-}
-
-#unit-control-panel #selected-units-container button[data-coalition="red"]::before {
- background-color: var(--accent-light-red);
- color: var(--secondary-red-outline)
-}
-
-#unit-control-panel #selected-units-container button::after {
+#unit-control-panel #selected-units-container button::before {
border-radius: var(--border-radius-sm);
content: attr(data-callsign);
display: block;
@@ -76,40 +67,10 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
margin-bottom: 8px;
}
-#unit-control-panel #threat,
-#unit-control-panel #roe,
-#unit-control-panel #emissions-countermeasures {
- margin-top: 12px;
-}
-
#advanced-settings-dialog {
width: 400px;
}
-#advanced-settings-dialog:not([data-show-settings]) #general-settings {
- display: none;
-}
-
-#advanced-settings-dialog:not([data-show-tasking]) #tasking {
- display: none;
-}
-
-#advanced-settings-dialog:not([data-show-tanker]) #tanker-checkbox {
- display: none;
-}
-
-#advanced-settings-dialog:not([data-show-AWACS]) #AWACS-checkbox {
- display: none;
-}
-
-#advanced-settings-dialog:not([data-show-TACAN]) #TACAN-options {
- display: none;
-}
-
-#advanced-settings-dialog:not([data-show-radio]) #radio-options {
- display: none;
-}
-
#advanced-settings-dialog>.ol-dialog-content {
display: flex;
flex-direction: column;
@@ -149,60 +110,155 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
width: 49%;
}
-#altitude-type-switch>.toggle-fill {
- background-color: var(--background-offwhite);
- height: 15px;
- width: 40px;
+#flight-data .ol-slider {
+ margin: 20px 0px;
}
-#altitude-type-switch>.toggle-fill::before {
- display: flex;
- position: absolute;
- box-sizing: border-box;
- align-items: center;
- padding: 0px 5px;
- color: var(--background-steel);
- font-weight: bold;
- font-size: 10px;
- transition: transform 0.2s;
- height: 15px;
+.ol-slider-container dd {
+ column-gap: 5px;
}
-#altitude-type-switch[data-altitude-type="agl"]>.toggle-fill::before {
+#flight-data .ol-switch {
+ height: 20px;
+ width: 50px;
+}
+
+#flight-data .ol-switch-fill {
+ background-color: var(--accent-light-blue);
+}
+
+#flight-data .ol-switch-fill::after {
+ background-color: white;
+}
+
+#altitude-type-switch[data-value="true"]>.ol-switch-fill::before {
content: "AGL";
}
-#altitude-type-switch[data-altitude-type="asl"]>.toggle-fill::before {
+#altitude-type-switch[data-value="false"]>.ol-switch-fill::before {
content: "ASL";
- transform: translateX(calc(40px - 28px));
}
-#altitude-type-switch>.toggle-fill::after {
- position: absolute;
- display: block;
- background-color: var(--background-steel);
- width: 15px;
- top: 0px;
+#airspeed-type-switch[data-value="true"]>.ol-switch-fill::before {
+ content: "GS";
}
-#altitude-type-switch[data-altitude-type="asl"]>.toggle-fill::after {
- transform: translateX(0);
+#airspeed-type-switch[data-value="false"]>.ol-switch-fill::before {
+ content: "CAS";
}
-#altitude-type-switch[data-altitude-type="agl"]>.toggle-fill::after {
- transform: translateX(calc(40px - 15px));
+#unit-control-panel .ol-slider-value {
+ color: var(--accent-light-blue);
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: bold;
}
-#altitude-slider dt {
- width: 40%;
+#ai-on-off {
+ align-items: center;
+ display: grid;
+ grid-template-columns: 1.35fr 0.65fr ;
}
-#altitude-slider dd {
+#ai-on-off>*:nth-child(2) {
+ justify-self: end;
+}
+
+#ai-on-off>*:nth-child(3) {
+ color: var(--secondary-semitransparent-white);
+}
+
+#ai-on-off h4 {
+ margin: 0px;
+}
+
+#on-off-switch {
+ width: 60px;
+ height: 25px;
+}
+
+#on-off-switch>.ol-switch-fill {
+ background-color: var(--accent-light-blue);
+}
+
+#on-off-switch>.ol-switch-fill::after {
+ background-color: white;
+}
+
+#on-off-switch[data-value="true"]>.ol-switch-fill::before {
+ content: "ON";
+}
+
+#on-off-switch[data-value="false"]>.ol-switch-fill::before {
+ content: "OFF";
+}
+
+#advanced-settings-div {
+ display: flex;
column-gap: 5px;
- width: 60%;
}
-#altitude-slider dd>*:nth-child(2) {
- width: 40px;
- text-align: right;
-}
\ No newline at end of file
+#advanced-settings-div>*:nth-child(2) {
+ margin-left: auto;
+}
+
+#advanced-settings-div button {
+ height: 40px;
+}
+
+/* Element visibility control */
+#unit-control-panel:not([data-show-categories-tooltip]) #categories-tooltip {
+ display: none;
+}
+
+#unit-control-panel:not([data-show-airspeed-slider]) #airspeed-slider {
+ display: none;
+}
+
+#unit-control-panel:not([data-show-altitude-slider]) #altitude-slider {
+ display: none;
+}
+
+#unit-control-panel:not([data-show-roe]) #roe {
+ display: none;
+}
+
+#unit-control-panel:not([data-show-threat]) #threat {
+ display: none;
+}
+
+#unit-control-panel:not([data-show-emissions-countermeasures]) #emissions-countermeasures {
+ display: none;
+}
+
+#unit-control-panel:not([data-show-on-off]) #ai-on-off {
+ display: none;
+}
+
+#unit-control-panel:not([data-show-advanced-settings-button]) #advanced-settings-button {
+ display: none;
+}
+
+#advanced-settings-dialog:not([data-show-settings]) #general-settings {
+ display: none;
+}
+
+#advanced-settings-dialog:not([data-show-tasking]) #tasking {
+ display: none;
+}
+
+#advanced-settings-dialog:not([data-show-tanker]) #tanker-checkbox {
+ display: none;
+}
+
+#advanced-settings-dialog:not([data-show-AWACS]) #AWACS-checkbox {
+ display: none;
+}
+
+#advanced-settings-dialog:not([data-show-TACAN]) #TACAN-options {
+ display: none;
+}
+
+#advanced-settings-dialog:not([data-show-radio]) #radio-options {
+ display: none;
+}
diff --git a/client/public/themes/olympus/images/icons/bomb-solid.svg b/client/public/themes/olympus/images/icons/bomb-solid.svg
new file mode 100644
index 00000000..79718751
--- /dev/null
+++ b/client/public/themes/olympus/images/icons/bomb-solid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/client/public/themes/olympus/images/icons/explosion-solid.svg b/client/public/themes/olympus/images/icons/explosion-solid.svg
new file mode 100644
index 00000000..b6383531
--- /dev/null
+++ b/client/public/themes/olympus/images/icons/explosion-solid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/client/public/themes/olympus/images/icons/trash-can-regular.svg b/client/public/themes/olympus/images/icons/trash-can-regular.svg
new file mode 100644
index 00000000..011e1a5e
--- /dev/null
+++ b/client/public/themes/olympus/images/icons/trash-can-regular.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/client/public/themes/olympus/theme.css b/client/public/themes/olympus/theme.css
index a7b402af..2ce06a95 100644
--- a/client/public/themes/olympus/theme.css
+++ b/client/public/themes/olympus/theme.css
@@ -32,6 +32,8 @@
--secondary-dark-steel: #181e25;
--secondary-gunmetal-grey: #2f2f2f;
--secondary-light-grey: #797e83;
+ --secondary-semitransparent-white: #FFFFFFAA;
+ --secondary-transparent-white: #FFFFFF30;
--secondary-yellow: #ffd46893;
--background-hover: #f2f2f333;
diff --git a/client/src/controls/airbasecontextmenu.ts b/client/src/controls/airbasecontextmenu.ts
index bb5e7f29..5ee53166 100644
--- a/client/src/controls/airbasecontextmenu.ts
+++ b/client/src/controls/airbasecontextmenu.ts
@@ -24,7 +24,7 @@ export class AirbaseContextMenu extends ContextMenu {
this.setProperties(airbase.getProperties());
this.setParkings(airbase.getParkings());
this.setCoalition(airbase.getCoalition());
- this.enableLandButton(getUnitsManager().getSelectedUnitsType() === "Aircraft" && (getUnitsManager().getSelectedUnitsCoalition() === airbase.getCoalition() || airbase.getCoalition() === "neutral"))
+ this.enableLandButton(getUnitsManager().getSelectedUnitsTypes().length == 1 && getUnitsManager().getSelectedUnitsTypes()[0] === "Aircraft" && (getUnitsManager().getSelectedUnitsCoalition() === airbase.getCoalition() || airbase.getCoalition() === "neutral"))
}
setName(airbaseName: string) {
diff --git a/client/src/controls/control.ts b/client/src/controls/control.ts
new file mode 100644
index 00000000..2c111152
--- /dev/null
+++ b/client/src/controls/control.ts
@@ -0,0 +1,21 @@
+export class Control {
+ #container: HTMLElement | null;
+
+ constructor(ID: string) {
+ this.#container = document.getElementById(ID);
+ }
+
+ show() {
+ if (this.#container != null)
+ this.#container.classList.remove("hide");
+ }
+
+ hide() {
+ if (this.#container != null)
+ this.#container.classList.add("hide");
+ }
+
+ getContainer() {
+ return this.#container;
+ }
+}
\ No newline at end of file
diff --git a/client/src/controls/slider.ts b/client/src/controls/slider.ts
index e8070d2e..656a9cbb 100644
--- a/client/src/controls/slider.ts
+++ b/client/src/controls/slider.ts
@@ -1,50 +1,40 @@
-export class Slider {
- #container: HTMLElement | null;
+import { zeroPad } from "../other/utils";
+import { Control } from "./control";
+
+export class Slider extends Control {
#callback: CallableFunction;
#slider: HTMLInputElement | null = null;
#valueText: HTMLElement | null = null;
#minValue: number;
#maxValue: number;
#increment: number;
- #minValueDiv: HTMLElement | null = null;
- #maxValueDiv: HTMLElement | null = null;
+ #minMaxValueDiv: HTMLElement | null = null;
#unit: string;
- #display: string = "";
#dragged: boolean = false;
#value: number = 0;
constructor(ID: string, minValue: number, maxValue: number, unit: string, callback: CallableFunction) {
- this.#container = document.getElementById(ID);
+ super(ID);
this.#callback = callback;
this.#minValue = minValue;
this.#maxValue = maxValue;
this.#increment = 1;
this.#unit = unit;
- if (this.#container != null) {
- this.#display = this.#container.style.display;
- this.#slider = this.#container.querySelector("input");
- if (this.#slider != null) {
- this.#slider.addEventListener("input", (e: any) => this.#onInput());
- this.#slider.addEventListener("mousedown", (e: any) => this.#onStart());
- this.#slider.addEventListener("mouseup", (e: any) => this.#onFinalize());
- }
- this.#valueText = this.#container.querySelector("#value");
+ this.#slider = this.getContainer()?.querySelector("input") as HTMLInputElement;
+
+ if (this.#slider != null) {
+ this.#slider.addEventListener("input", (e: any) => this.#onInput());
+ this.#slider.addEventListener("mousedown", (e: any) => this.#onStart());
+ this.#slider.addEventListener("mouseup", (e: any) => this.#onFinalize());
}
- }
- show() {
- if (this.#container != null)
- this.#container.style.display = this.#display;
- }
-
- hide() {
- if (this.#container != null)
- this.#container.style.display = 'none';
+ this.#valueText = this.getContainer()?.querySelector(".ol-slider-value") as HTMLElement;
+ this.#minMaxValueDiv = this.getContainer()?.querySelector(".ol-slider-min-max") as HTMLElement;
}
setActive(newActive: boolean) {
- if (this.#container && !this.#dragged) {
- this.#container.classList.toggle("active", newActive);
+ if (!this.#dragged) {
+ this.getContainer()?.classList.toggle("active", newActive);
if (!newActive && this.#valueText != null)
this.#valueText.innerText = "Mixed values";
}
@@ -54,6 +44,10 @@ export class Slider {
this.#minValue = newMinValue;
this.#maxValue = newMaxValue;
this.#updateMax();
+ if (this.#minMaxValueDiv != null) {
+ this.#minMaxValueDiv.setAttribute('data-min-value', `${this.#minValue}${this.#unit}`);
+ this.#minMaxValueDiv.setAttribute('data-max-value', `${this.#maxValue}${this.#unit}`);
+ }
}
setIncrement(newIncrement: number) {
@@ -88,7 +82,16 @@ export class Slider {
#onValue() {
if (this.#valueText != null && this.#slider != null)
- this.#valueText.innerText = this.#minValue + Math.round(parseFloat(this.#slider.value) / parseFloat(this.#slider.max) * (this.#maxValue - this.#minValue)) + this.#unit
+ {
+ var value = this.#minValue + Math.round(parseFloat(this.#slider.value) / parseFloat(this.#slider.max) * (this.#maxValue - this.#minValue));
+ var strValue = String(value);
+ if (value > 1000)
+ strValue = String(Math.floor(value / 1000)) + "," + zeroPad(value - Math.floor(value / 1000) * 1000, 3);
+ this.#valueText.innerText = strValue + " " + this.#unit.toUpperCase();
+
+ var percentValue = parseFloat(this.#slider.value) / parseFloat(this.#slider.max) * 90 + 5;
+ this.#slider.style.background = 'linear-gradient(to right, var(--accent-light-blue) 5%, var(--accent-light-blue) ' + percentValue + '%, var(--background-grey) ' + percentValue + '%, var(--background-grey) 100%)'
+ }
this.setActive(true);
}
diff --git a/client/src/controls/switch.ts b/client/src/controls/switch.ts
new file mode 100644
index 00000000..b22ed0a5
--- /dev/null
+++ b/client/src/controls/switch.ts
@@ -0,0 +1,34 @@
+import { Control } from "./control";
+
+export class Switch extends Control {
+ #value: boolean = false;
+ constructor(ID: string, initialValue?: boolean) {
+ super(ID);
+ this.getContainer()?.addEventListener('click', (e) => this.#onToggle());
+ this.setValue(initialValue !== undefined? initialValue: true);
+
+ /* Add the toggle itself to the document */
+ const container = this.getContainer();
+ if (container != undefined){
+ const width = getComputedStyle(container).width;
+ const height = getComputedStyle(container).height;
+ var el = document.createElement("div");
+ el.classList.add("ol-switch-fill");
+ el.style.setProperty("--width", width? width: "0px");
+ el.style.setProperty("--height", height? height: "0px");
+ this.getContainer()?.appendChild(el);
+ }
+ }
+ setValue(value: boolean) {
+ this.#value = value;
+ this.getContainer()?.setAttribute("data-value", String(value));
+ }
+
+ getValue() {
+ return this.#value;
+ }
+
+ #onToggle() {
+ this.setValue(!this.getValue());
+ }
+}
\ No newline at end of file
diff --git a/client/src/index.ts b/client/src/index.ts
index c2f8a1b6..ac14f5c6 100644
--- a/client/src/index.ts
+++ b/client/src/index.ts
@@ -15,6 +15,7 @@ import { keyEventWasInInput } from "./other/utils";
import { Popup } from "./popups/popup";
import { Dropdown } from "./controls/dropdown";
import { HotgroupPanel } from "./panels/hotgrouppanel";
+import { SVGInjector } from "@tanem/svg-injector";
var map: Map;
@@ -194,6 +195,15 @@ function setupEvents() {
location.reload();
})
+ document.querySelectorAll("[inject-svg]").forEach((el: Element) => {
+ var img = el as HTMLImageElement;
+ var isLoaded = img.complete && img.naturalHeight !== 0;
+ if (isLoaded)
+ SVGInjector(img);
+ else
+ img.onload = () => SVGInjector(img);
+ })
+
}
export function getMap() {
diff --git a/client/src/panels/unitcontrolpanel.ts b/client/src/panels/unitcontrolpanel.ts
index 2c2a2b09..4981f4fc 100644
--- a/client/src/panels/unitcontrolpanel.ts
+++ b/client/src/panels/unitcontrolpanel.ts
@@ -7,6 +7,7 @@ import { groundUnitsDatabase } from "../units/groundunitsdatabase";
import { Aircraft, GroundUnit, Unit } from "../units/unit";
import { UnitDatabase } from "../units/unitdatabase";
import { Panel } from "./panel";
+import { Switch } from "../controls/switch";
const ROEs: string[] = ["Hold", "Return", "Designated", "Free"];
const reactionsToThreat: string[] = ["None", "Manoeuvre", "Passive", "Evade"];
@@ -25,7 +26,10 @@ const altitudeIncrements: { [key: string]: number } = { Aircraft: 500, Helicopte
export class UnitControlPanel extends Panel {
#altitudeSlider: Slider;
+ #altitudeTypeSwitch: Switch;
#airspeedSlider: Slider;
+ #airspeedTypeSwitch: Switch;
+ #onOffSwitch: Switch;
#TACANXYDropdown: Dropdown;
#radioDecimalsDropdown: Dropdown;
#radioCallsignDropdown: Dropdown;
@@ -40,22 +44,15 @@ export class UnitControlPanel extends Panel {
/* Unit control sliders */
this.#altitudeSlider = new Slider("altitude-slider", 0, 100, "ft", (value: number) => {
this.#expectedAltitude = value;
- getUnitsManager().selectedUnitsSetAltitude(value * 0.3048)
+ getUnitsManager().selectedUnitsSetAltitude(value * 0.3048);
});
+ this.#altitudeTypeSwitch = new Switch("altitude-type-switch");
this.#airspeedSlider = new Slider("airspeed-slider", 0, 100, "kts", (value: number) => {
this.#expectedSpeed = value;
- getUnitsManager().selectedUnitsSetSpeed(value / 1.94384)
+ getUnitsManager().selectedUnitsSetSpeed(value / 1.94384);
});
-
- this.getElement()?.querySelector("#altitude-type-switch")?.addEventListener('click', (e) => this.#onToggleAltitudeTypeSwitch(e));
-
- /* Advanced settings dropdowns */
- this.#TACANXYDropdown = new Dropdown("TACAN-XY", () => {});
- this.#TACANXYDropdown.setOptions(["X", "Y"]);
- this.#radioDecimalsDropdown = new Dropdown("radio-decimals", () => {});
- this.#radioDecimalsDropdown.setOptions([".000", ".250", ".500", ".750"]);
- this.#radioCallsignDropdown = new Dropdown("radio-callsign", () => {});
+ this.#airspeedTypeSwitch = new Switch("airspeed-type-switch");
/* Option buttons */
this.#optionButtons["ROE"] = ROEs.map((option: string, index: number) => {
@@ -74,8 +71,20 @@ export class UnitControlPanel extends Panel {
this.getElement().querySelector("#reaction-to-threat-buttons-container")?.append(...this.#optionButtons["reactionToThreat"]);
this.getElement().querySelector("#emissions-countermeasures-buttons-container")?.append(...this.#optionButtons["emissionsCountermeasures"]);
+ /* On off switch */
+ this.#onOffSwitch = new Switch("on-off-switch");
+
+ /* Advanced settings dialog */
this.#advancedSettingsDialog = document.querySelector("#advanced-settings-dialog");
+ /* Advanced settings dropdowns */
+ this.#TACANXYDropdown = new Dropdown("TACAN-XY", () => {});
+ this.#TACANXYDropdown.setOptions(["X", "Y"]);
+ this.#radioDecimalsDropdown = new Dropdown("radio-decimals", () => {});
+ this.#radioDecimalsDropdown.setOptions([".000", ".250", ".500", ".750"]);
+ this.#radioCallsignDropdown = new Dropdown("radio-callsign", () => {});
+
+ /* Events and timer */
window.setInterval(() => {this.update();}, 25);
document.addEventListener("unitsSelection", (e: CustomEvent) => { this.show(); this.addButtons();});
@@ -112,7 +121,7 @@ export class UnitControlPanel extends Panel {
var button = document.createElement("button");
var callsign = unit.getBaseData().unitName || "";
- button.setAttribute("data-short-label", database?.getByName(unit.getBaseData().name)?.shortLabel || unit.getBaseData().name);
+ button.setAttribute("data-label", unit.getBaseData().name);
button.setAttribute("data-callsign", callsign);
button.setAttribute("data-coalition", unit.getMissionData().coalition);
@@ -133,11 +142,53 @@ export class UnitControlPanel extends Panel {
update() {
if (this.getVisible()){
- var units = getUnitsManager().getSelectedUnits();
- this.getElement().querySelector("#advanced-settings-div")?.classList.toggle("hide", units.length != 1);
- if (this.getElement() != null && units.length > 0) {
- this.#showFlightControlSliders(units);
+ const element = this.getElement();
+ const units = getUnitsManager().getSelectedUnits();
+ const selectedUnitsTypes = getUnitsManager().getSelectedUnitsTypes();
+
+ if (element != null && units.length > 0) {
+ /* Toggle visibility of control elements */
+ element.toggleAttribute("data-show-categories-tooltip", selectedUnitsTypes.length > 1);
+ element.toggleAttribute("data-show-airspeed-slider", selectedUnitsTypes.length == 1);
+ element.toggleAttribute("data-show-altitude-slider", selectedUnitsTypes.length == 1 && (selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")));
+ element.toggleAttribute("data-show-roe", true);
+ element.toggleAttribute("data-show-threat", (selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")) && !(selectedUnitsTypes.includes("GroundUnit") || selectedUnitsTypes.includes("NavyUnit")));
+ element.toggleAttribute("data-show-emissions-countermeasures", (selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")) && !(selectedUnitsTypes.includes("GroundUnit") || selectedUnitsTypes.includes("NavyUnit")));
+ element.toggleAttribute("data-show-on-off", (selectedUnitsTypes.includes("GroundUnit") || selectedUnitsTypes.includes("NavyUnit")) && !(selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")));
+ element.toggleAttribute("data-show-advanced-settings-button", units.length == 1);
+
+ /* Flight controls */
+ var targetAltitude = getUnitsManager().getSelectedUnitsTargetAltitude();
+ var targetSpeed = getUnitsManager().getSelectedUnitsTargetSpeed();
+ if (selectedUnitsTypes.length == 1) {
+ this.#airspeedSlider.setMinMax(minSpeedValues[selectedUnitsTypes[0]], maxSpeedValues[selectedUnitsTypes[0]]);
+ this.#altitudeSlider.setMinMax(minAltitudeValues[selectedUnitsTypes[0]], maxAltitudeValues[selectedUnitsTypes[0]]);
+ this.#airspeedSlider.setIncrement(speedIncrements[selectedUnitsTypes[0]]);
+ this.#altitudeSlider.setIncrement(altitudeIncrements[selectedUnitsTypes[0]]);
+
+ this.#airspeedSlider.setActive(targetSpeed != undefined);
+ if (targetSpeed != undefined) {
+ targetSpeed *= 1.94384;
+ if (this.#updateCanSetSpeedSlider(targetSpeed)) {
+ this.#airspeedSlider.setValue(targetSpeed);
+ }
+ }
+
+ this.#altitudeSlider.setActive(targetAltitude != undefined);
+ if (targetAltitude != undefined) {
+ targetAltitude /= 0.3048;
+ if (this.#updateCanSetAltitudeSlider(targetAltitude)) {
+ this.#altitudeSlider.setValue(targetAltitude);
+ }
+ }
+ }
+ else {
+ this.#airspeedSlider.setActive(false);
+ this.#altitudeSlider.setActive(false);
+ }
+
+ /* Option buttons */
this.#optionButtons["ROE"].forEach((button: HTMLButtonElement) => {
button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().ROE === button.value))
});
@@ -170,54 +221,6 @@ export class UnitControlPanel extends Panel {
return false;
}
- #showFlightControlSliders(units: Unit[]) {
- if (getUnitsManager().getSelectedUnitsType() !== undefined)
- this.#airspeedSlider.show()
- else
- this.#airspeedSlider.hide();
-
- if (getUnitsManager().getSelectedUnitsType() === "Aircraft" || getUnitsManager().getSelectedUnitsType() === "Helicopter")
- this.#altitudeSlider.show()
- else
- this.#altitudeSlider.hide();
-
- this.getElement().querySelector(`#categories-tooltip`)?.classList.toggle("hide", getUnitsManager().getSelectedUnitsType() !== undefined);
-
- var unitsType = getUnitsManager().getSelectedUnitsType();
- var targetAltitude = getUnitsManager().getSelectedUnitsTargetAltitude();
- var targetSpeed = getUnitsManager().getSelectedUnitsTargetSpeed();
-
- if (unitsType != undefined) {
- if (["GroundUnit", "NavyUnit"].includes(unitsType))
- this.#altitudeSlider.hide()
-
- this.#airspeedSlider.setMinMax(minSpeedValues[unitsType], maxSpeedValues[unitsType]);
- this.#altitudeSlider.setMinMax(minAltitudeValues[unitsType], maxAltitudeValues[unitsType]);
- this.#airspeedSlider.setIncrement(speedIncrements[unitsType]);
- this.#altitudeSlider.setIncrement(altitudeIncrements[unitsType]);
-
- this.#airspeedSlider.setActive(targetSpeed != undefined);
- if (targetSpeed != undefined) {
- targetSpeed *= 1.94384;
- if (this.#updateCanSetSpeedSlider(targetSpeed)) {
- this.#airspeedSlider.setValue(targetSpeed);
- }
- }
-
- this.#altitudeSlider.setActive(targetAltitude != undefined);
- if (targetAltitude != undefined) {
- targetAltitude /= 0.3048;
- if (this.#updateCanSetAltitudeSlider(targetAltitude)) {
- this.#altitudeSlider.setValue(targetAltitude);
- }
- }
- }
- else {
- this.#airspeedSlider.setActive(false);
- this.#altitudeSlider.setActive(false);
- }
- }
-
#updateAdvancedSettingsDialog(units: Unit[])
{
if (units.length == 1)
@@ -356,10 +359,4 @@ export class UnitControlPanel extends Panel {
button.addEventListener("click", callback);
return button;
}
-
- #onToggleAltitudeTypeSwitch(e: any) {
- const altitudeType = this.getElement()?.querySelector("#altitude-type-switch")?.getAttribute("data-altitude-type");
- var newAltitudeType = altitudeType == "asl"? "agl": "asl";
- this.getElement()?.querySelector("#altitude-type-switch")?.setAttribute("data-altitude-type", newAltitudeType);
- }
}
\ No newline at end of file
diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts
index d54034a7..2170e53b 100644
--- a/client/src/units/unit.ts
+++ b/client/src/units/unit.ts
@@ -560,7 +560,7 @@ export class Unit extends CustomMarker {
if (getUnitsManager().getSelectedUnits().length > 0 && !(getUnitsManager().getSelectedUnits().length == 1 && (getUnitsManager().getSelectedUnits().includes(this)))) {
options["attack"] = {text: "Attack", tooltip: "Attack the unit using A/A or A/G weapons"};
- if (getUnitsManager().getSelectedUnitsType() === "Aircraft")
+ if (getUnitsManager().getSelectedUnitsTypes().length == 1 && getUnitsManager().getSelectedUnitsTypes()[0] === "Aircraft")
options["follow"] = {text: "Follow", tooltip: "Follow the unit at a user defined distance and position"};;
}
else if ((getUnitsManager().getSelectedUnits().length > 0 && (getUnitsManager().getSelectedUnits().includes(this))) || getUnitsManager().getSelectedUnits().length == 0) {
diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts
index 07f5ac9c..adbe4b71 100644
--- a/client/src/units/unitsmanager.ts
+++ b/client/src/units/unitsmanager.ts
@@ -145,13 +145,13 @@ export class UnitsManager {
this.getUnitsByHotgroup(hotgroup).forEach((unit: Unit) => unit.setSelected(true))
}
- getSelectedUnitsType() {
+ getSelectedUnitsTypes() {
if (this.getSelectedUnits().length == 0)
- return undefined;
+ return [];
return this.getSelectedUnits().map((unit: Unit) => {
return unit.constructor.name
- })?.reduce((a: any, b: any) => {
- return a == b ? a : undefined
+ })?.filter((value: any, index: any, array: string[]) => {
+ return array.indexOf(value) === index;
});
};
diff --git a/client/views/other/contextmenus.ejs b/client/views/other/contextmenus.ejs
index 30628b6c..4a535b9d 100644
--- a/client/views/other/contextmenus.ejs
+++ b/client/views/other/contextmenus.ejs
@@ -1,8 +1,8 @@