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 @@
-