From 7eee469bed41ab69032b5a4aa88fa0fe230ddbf8 Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Wed, 12 Apr 2023 17:21:36 +0200 Subject: [PATCH] More work on advanced settings dialog --- client/public/stylesheets/layout.css | 135 ++++++++++---------- client/public/stylesheets/olympus.css | 1 + client/src/@types/unit.d.ts | 1 + client/src/controls/dropdown.ts | 82 +++++++------ client/src/panels/unitcontrolpanel.ts | 44 ++++++- client/src/server/server.ts | 22 ++-- client/src/units/unit.ts | 11 +- client/src/units/unitsmanager.ts | 18 --- client/views/dialogs.ejs | 170 +------------------------- client/views/unitcontrolpanel.ejs | 137 ++++++++++++++++++++- 10 files changed, 315 insertions(+), 306 deletions(-) diff --git a/client/public/stylesheets/layout.css b/client/public/stylesheets/layout.css index afa76477..0fc20782 100644 --- a/client/public/stylesheets/layout.css +++ b/client/public/stylesheets/layout.css @@ -1,4 +1,3 @@ - /* Page style */ #map-container { height: 100%; @@ -14,32 +13,30 @@ } #olympus-toolbar-summary { - background-image: url( "/images/icon-round.png" ); + background-image: url("/images/icon-round.png"); background-position: 25px 20px; background-repeat: no-repeat; - background-size:36px 36px; + background-size: 36px 36px; display: flex; flex-direction: column; text-indent: 44px; } - - dl.ol-data-grid { align-items: center; - display:flex; + display: flex; flex-direction: row; flex-wrap: wrap; - margin:0; + margin: 0; row-gap: 4px; } dl.ol-data-grid dt { - width:60%; + width: 60%; } dl.ol-data-grid dd { - width:40%; + width: 40%; } @@ -48,24 +45,23 @@ dl.ol-data-grid dt.icon { } dl.ol-data-grid dt.icon::before { - content: url( /images/icons/speed.svg ); - display:inline-block; - filter:invert(100%); + content: url(/images/icons/speed.svg ); + display: inline-block; + filter: invert(100%); width: 20px; - translate:-20px 2px; + translate: -20px 2px; } dl.ol-data-grid dt.icon-speed::before { - content: url( /images/icons/speed.svg ); + content: url(/images/icons/speed.svg ); } dl.ol-data-grid dt.icon-altitude::before { - content: url( /images/icons/altitude.svg ); + content: url(/images/icons/altitude.svg ); } - dl.ol-data-grid dd { display: flex; justify-content: flex-end; @@ -73,56 +69,50 @@ dl.ol-data-grid dd { } .br-info::after { - content: attr( data-bearing ) '\00B0 / ' attr( data-distance ) attr( data-distance-units ); + content: attr(data-bearing) '\00B0 / ' attr(data-distance) attr(data-distance-units); } .br-info[data-message]::after { - content: attr( data-message ); + content: attr(data-message); } .coordinates::after { - content: attr( data-dd ) "\00b0 " attr( data-mm ) "'" attr( data-ss ) "." attr( data-sss ) '"' attr( data-label ); + content: attr(data-dd) "\00b0 " attr(data-mm) "'" attr(data-ss) "." attr(data-sss) '"' attr(data-label); } - - - .ol-button-box { column-gap: 6px; - display:flex; + display: flex; flex-direction: row; flex-wrap: wrap; - margin:5px 0; + margin: 5px 0; row-gap: 5px; } .ol-button-box button { - background-repeat: no-repeat;; - border:1px solid var( --accent-light-blue ); - color: var( --accent-light-blue ); + background-repeat: no-repeat; + ; + border: 1px solid var(--accent-light-blue); + color: var(--accent-light-blue); } - - - - .ol-dialog { align-self: center; - background-color: var( --background-slate-blue ); - color:white; + background-color: var(--background-slate-blue); + color: white; justify-self: center; position: absolute; - z-index:1000; + z-index: 1000; } .ol-panel.ol-dialog { - padding:20px; + padding: 20px; } .ol-dialog-close { cursor: pointer; - font-size:16px; - font-weight: var( --font-weight-bolder ); + font-size: 16px; + font-weight: var(--font-weight-bolder); position: absolute; right: 25px; top: 25px; @@ -133,12 +123,12 @@ dl.ol-data-grid dd { } .ol-dialog-header { - border-bottom:1px solid var( --background-grey ); - padding-bottom:10px; + border-bottom: 1px solid var(--background-grey); + padding-bottom: 10px; } .ol-dialog-footer { - border-top: 1px solid var( --background-grey ); + border-top: 1px solid var(--background-grey); padding-top: 15px; display: flex; row-gap: 10px; @@ -157,46 +147,67 @@ dl.ol-data-grid dd { } .ol-checkbox input[type="checkbox"] { - appearance:none; + appearance: none; background-color: transparent; - border:none; - margin:0; + border: none; + margin: 0; } .ol-checkbox input[type="checkbox"]::before { - align-self:center; - background-image: url( "/images/icons/square-check-solid.svg" ); + align-self: center; + background-image: url("/images/icons/square-check-solid.svg"); background-repeat: no-repeat; content: ""; - filter: invert( 100% ); - display:flex; - height:16px; - margin-right:10px; - width:16px; + filter: invert(100%); + display: flex; + height: 16px; + margin-right: 10px; + width: 16px; } - .ol-checkbox input[type="checkbox"]:checked::before { - background-image: url( "/images/icons/square-regular.svg" ); + background-image: url("/images/icons/square-regular.svg"); } +.ol-text-input input { + height: 40px; + border-radius: 5px; + color: var(--background-offwhite); + background-color: var(--background-grey); + border: 1px solid var(--background-grey); + border-radius: var(--border-radius-sm); + text-align: center; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); +} + +input[type=number] { + -moz-appearance: textfield; + appearance: textfield; + margin: 0; +} + +input[type=number]::-webkit-inner-spin-button, +input[type=number]::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} [class|="ol-button"] { align-items: center; background-repeat: no-repeat; - display:flex; + display: flex; font-weight: normal; - padding:8px 10px; + padding: 8px 10px; white-space: nowrap; } [class|="ol-button"]::before { - margin-right:8px; + margin-right: 8px; } .ol-button-close { background: transparent; - border:1px solid white; + border: 1px solid white; } .ol-button-close::before { @@ -213,16 +224,16 @@ dl.ol-data-grid dd { } .ol-button-settings { - background-color: var( --background-slate-blue ); + background-color: var(--background-slate-blue); } .ol-button-settings::before { - background-image: url( "/images/icons/gears-solid.svg" ); - background-position:0 50%; - background-size:24px 24px; + background-image: url("/images/icons/gears-solid.svg"); + background-position: 0 50%; + background-size: 24px 24px; content: ""; - display:flex; - filter: invert( 100% ); + display: flex; + filter: invert(100%); height: 24px; width: 24px; } \ No newline at end of file diff --git a/client/public/stylesheets/olympus.css b/client/public/stylesheets/olympus.css index 3e506469..a0cccbec 100644 --- a/client/public/stylesheets/olympus.css +++ b/client/public/stylesheets/olympus.css @@ -348,6 +348,7 @@ nav.ol-panel> :last-child { flex-direction: row; flex-wrap: nowrap; row-gap: 4px; + align-items: center; } .ol-panel .ol-group.wrap { diff --git a/client/src/@types/unit.d.ts b/client/src/@types/unit.d.ts index fca60a3b..9dc67839 100644 --- a/client/src/@types/unit.d.ts +++ b/client/src/@types/unit.d.ts @@ -46,6 +46,7 @@ interface TaskData { radioOn: boolean; TACANOn: boolean; radioFrequency: number; + radioCallsign: number; TACANChannel: number; TACANXY: string; TACANCallsign: string; diff --git a/client/src/controls/dropdown.ts b/client/src/controls/dropdown.ts index 2870db8c..7e59d00b 100644 --- a/client/src/controls/dropdown.ts +++ b/client/src/controls/dropdown.ts @@ -5,7 +5,8 @@ export class Dropdown { #callback: CallableFunction; #defaultValue: string; #optionsList: string[] = []; - + #index: number = 0; + constructor(ID: string, callback: CallableFunction, options: string[] | null = null) { this.#element = document.getElementById(ID); @@ -13,69 +14,39 @@ export class Dropdown { this.#value = this.#element.querySelector(".ol-select-value"); this.#defaultValue = this.#value.innerText; this.#callback = callback; - + if (options != null) { this.setOptions(options); } this.#value.addEventListener( "click", ev => { - this.#element.classList.toggle( "is-open" ); this.#clip(); - }); this.#element.addEventListener("mouseleave", ev => { this.#close(); }); - } - #clip() { - - const options = this.#options; - const bounds = options.getBoundingClientRect(); - - this.#element.dataset.position = ( bounds.bottom > window.innerHeight ) ? "top" : ""; - - } - - - #close() { - this.#element.classList.remove( "is-open" ); - this.#element.dataset.position = ""; - } - - - #open() { - this.#element.classList.add( "is-open" ); - } - - - #toggle() { - - if ( this.#element.classList.contains( "is-open" ) ) { - this.#close(); - } else { - this.#open(); - } - - } - - setOptions(optionsList: string[]) { this.#optionsList = optionsList; - this.#options.replaceChildren(...optionsList.map((option: string) => { + this.#options.replaceChildren(...optionsList.map((option: string, idx: number) => { var div = document.createElement("div"); var button = document.createElement("button"); button.textContent = option; div.appendChild(button); + + if (option === this.#defaultValue) + this.#index = idx; + button.addEventListener("click", (e: MouseEvent) => { e.stopPropagation(); this.#value.innerText = option; this.#close(); - this.#callback( option, e ); + this.#callback(option, e); + this.#index = idx; }); return div; })); @@ -87,6 +58,8 @@ export class Dropdown { { var option = this.#optionsList[idx]; this.#value.innerText = option; + this.#index = idx; + this.#close(); this.#callback(option); } } @@ -95,4 +68,35 @@ export class Dropdown { this.#options.replaceChildren(); this.#value.innerText = this.#defaultValue; } + + getValue() { + return this.#value.innerText; + } + + getIndex() { + return this.#index; + } + + #clip() { + const options = this.#options; + const bounds = options.getBoundingClientRect(); + this.#element.dataset.position = ( bounds.bottom > window.innerHeight ) ? "top" : ""; + } + + #close() { + this.#element.classList.remove( "is-open" ); + this.#element.dataset.position = ""; + } + + #open() { + this.#element.classList.add( "is-open" ); + } + + #toggle() { + if ( this.#element.classList.contains( "is-open" ) ) { + this.#close(); + } else { + this.#open(); + } + } } \ No newline at end of file diff --git a/client/src/panels/unitcontrolpanel.ts b/client/src/panels/unitcontrolpanel.ts index f3c28d63..8c71b386 100644 --- a/client/src/panels/unitcontrolpanel.ts +++ b/client/src/panels/unitcontrolpanel.ts @@ -1,4 +1,5 @@ import { getUnitsManager } from ".."; +import { Dropdown } from "../controls/dropdown"; import { Slider } from "../controls/slider"; import { dataPointMap } from "../other/utils"; import { aircraftDatabase } from "../units/aircraftdatabase"; @@ -20,6 +21,9 @@ const altitudeIncrements: { [key: string]: number } = { Aircraft: 2500, Helicopt export class UnitControlPanel extends Panel { #altitudeSlider: Slider; #airspeedSlider: Slider; + #TACANXYDropdown: Dropdown; + #radioDecimalsDropdown: Dropdown; + #radioCallsignDropdown: Dropdown; #expectedAltitude: number = -1; #expectedSpeed: number = -1; #optionButtons: { [key: string]: HTMLButtonElement[] } = {} @@ -39,6 +43,13 @@ export class UnitControlPanel extends Panel { getUnitsManager().selectedUnitsSetSpeed(value / 1.94384) }); + /* 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", () => {}); + /* Option buttons */ this.#optionButtons["ROE"] = ROEs.map((option: string, index: number) => { var button = document.createElement("button"); @@ -64,6 +75,11 @@ export class UnitControlPanel extends Panel { document.addEventListener("unitUpdated", (e: CustomEvent) => { if (e.detail.getSelected()) this.update() }); document.addEventListener("unitsSelection", (e: CustomEvent) => { this.show(); this.update() }); document.addEventListener("clearSelection", () => { this.hide() }); + document.addEventListener("applyAdvancedSettings", () => {this.#applyAdvancedSettings();}) + document.addEventListener("showAdvancedSettings", () => { + this.#updateAdvancedSettingsDialog(getUnitsManager().getSelectedUnits()); + this.#advancedSettingsDialog.classList.remove("hide"); + }) this.hide(); } @@ -97,7 +113,6 @@ export class UnitControlPanel extends Panel { var units = getUnitsManager().getSelectedUnits(); if (this.getElement() != null && units.length > 0) { this.#showFlightControlSliders(units); - this.#updateAdvancedSettingsDialog(units); this.getElement().querySelector("#selected-units-container")?.replaceChildren(...units.map((unit: Unit, index: number) => { let database: UnitDatabase | null; @@ -195,10 +210,15 @@ export class UnitControlPanel extends Panel { (this.#advancedSettingsDialog.querySelector("#unit-name")).innerText = unit.getBaseData().unitName; if (getUnitsManager().getSelectedUnits().length == 1){ + this.#radioCallsignDropdown.setOptions(["Enfield", "Springfield", "Uzi", "Colt", "Dodge", "Ford", "Chevy", "Pontiac"]); + this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign); + var roles = aircraftDatabase.getByName(unit.getBaseData().name)?.loadouts.map((loadout) => {return loadout.roles}) if (roles != undefined && Array.prototype.concat.apply([], roles)?.includes("Tanker")){ this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.querySelector("input")?.setAttribute('checked', String(unit.getTaskData().isTanker)); this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.classList.remove("hide"); + this.#radioCallsignDropdown.setOptions(["Texaco", "Arco", "Shell"]); + this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign); } else { this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.classList.add("hide"); @@ -207,10 +227,32 @@ export class UnitControlPanel extends Panel { if (roles != undefined && Array.prototype.concat.apply([], roles)?.includes("AWACS")){ this.#advancedSettingsDialog.querySelector("#AWACS-checkbox")?.querySelector("input")?.setAttribute('checked', String(unit.getTaskData().isAWACS)); this.#advancedSettingsDialog.querySelector("#AWACS-checkbox")?.classList.remove("hide"); + this.#radioCallsignDropdown.setOptions(["Overlord", "Magic", "Wizard", "Focus", "Darkstar"]); + this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign); } else { this.#advancedSettingsDialog.querySelector("#AWACS-checkbox")?.classList.add("hide"); } } } } + + #applyAdvancedSettings() + { + this.#advancedSettingsDialog.classList.add("hide"); + + const isTanker = this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.querySelector("input")?.checked; + const isAWACS= this.#advancedSettingsDialog.querySelector("#AWACS-checkbox")?.querySelector("input")?.checked; + const TACANChannel = Number(this.#advancedSettingsDialog.querySelector("#TACAN-channel")?.querySelector("input")?.value); + const TACANXY = this.#TACANXYDropdown.getValue(); + const TACANCallsign = this.#advancedSettingsDialog.querySelector("#tacan-callsign")?.querySelector("input")?.value + const radioMHz = Number(this.#advancedSettingsDialog.querySelector("#radio-mhz")?.querySelector("input")?.value); + const radioDecimals = this.#radioDecimalsDropdown.getValue(); + const radioCallsign = this.#radioCallsignDropdown.getIndex(); + + var radioFrequency = (radioMHz * 1000 + Number(radioDecimals.substring(1))) * 1000; + + var units = getUnitsManager().getSelectedUnits(); + if (units.length > 0) + units[0].setAdvancedOptions(isTanker, isAWACS, TACANChannel, TACANXY, TACANCallsign, radioFrequency, radioCallsign); + } } \ No newline at end of file diff --git a/client/src/server/server.ts b/client/src/server/server.ts index 4e4c4e02..b6ee81d2 100644 --- a/client/src/server/server.ts +++ b/client/src/server/server.ts @@ -183,14 +183,18 @@ export function refuel(ID: number) { POST(data, () => { }); } -export function setTanker(ID: number, state: boolean) { - var command = { "ID": ID, "state": state }; - var data = { "setIsTanker": command } - POST(data, () => { }); -} +export function setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolean, TACANChannel: number, TACANXY: string, TACANCallsign: string, radioFrequency: number, radioCallsign: number) +{ + var command = { "ID": ID, + "isTanker": isTanker, + "isAWACS": isAWACS, + "TACANChannel": TACANChannel, + "TACANXY": TACANXY, + "TACANCallsign": TACANCallsign, + "radioFrequency": radioFrequency, + "radioCallsign": radioCallsign + }; -export function setAWACS(ID: number, state: boolean) { - var command = { "ID": ID, "state": state }; - var data = { "setIsAWACS": command } + var data = { "setAdvancedOptions": command }; POST(data, () => { }); -} +} \ No newline at end of file diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index b58114b8..24f40250 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -1,7 +1,7 @@ import { Marker, LatLng, Polyline, Icon, DivIcon } from 'leaflet'; import { getMap, getUnitsManager } from '..'; import { rad2deg } from '../other/utils'; -import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, getUnits, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setTanker, setAWACS } from '../server/server'; +import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, getUnits, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setAdvacedOptions } from '../server/server'; import { aircraftDatabase } from './aircraftdatabase'; import { groundUnitsDatabase } from './groundunitsdatabase'; @@ -55,6 +55,7 @@ export class Unit extends Marker { radioOn: false, TACANOn: false, radioFrequency: 0, + radioCallsign: 0, TACANChannel: 0, TACANXY: "X", TACANCallsign: "", @@ -375,12 +376,8 @@ export class Unit extends Marker { refuel(this.ID); } - toggleTanker() { - setTanker(this.ID, !this.getTaskData().isTanker); - } - - toggleAWACS() { - setAWACS(this.ID, !this.getTaskData().isAWACS); + setAdvancedOptions(isTanker: boolean, isAWACS: boolean, TACANChannel: number, TACANXY: string, TACANcallsign: string, radioFrequency: number, radioCallsign: number) { + setAdvacedOptions(this.ID, isTanker, isAWACS, TACANChannel, TACANXY, TACANcallsign, radioFrequency, radioCallsign); } #onClick(e: any) { diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index 754705b6..4fc63a34 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -334,24 +334,6 @@ export class UnitsManager { } } - selectedUnitsToggleTanker() - { - var selectedUnits = this.getSelectedUnits(); - for (let idx in selectedUnits) - { - selectedUnits[idx].toggleTanker(); - } - } - - selectedUnitsToggleAWACS() - { - var selectedUnits = this.getSelectedUnits(); - for (let idx in selectedUnits) - { - selectedUnits[idx].toggleAWACS(); - } - } - copyUnits() { this.#copiedUnits = this.getSelectedUnits(); diff --git a/client/views/dialogs.ejs b/client/views/dialogs.ejs index 45615680..99d09c92 100644 --- a/client/views/dialogs.ejs +++ b/client/views/dialogs.ejs @@ -5,7 +5,7 @@

DCS Olympus

Dynamic Unit Command

-
Version v0.1.0
+
Version v0.1.2
- - - -
-
- -
-

Olympus 1-1

-
- -
-
- - -
- -
- -
- -
- -
- -
- -
-
-
40
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
- -
-
X
-
-
- -
-
- -
-
- -
-
- -
- -
- -
-
-
120
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
- -
-
.750
-
-
- -
-
- -
-
- -
-
- -
-
-
-
-
-
- - - -
\ No newline at end of file diff --git a/client/views/unitcontrolpanel.ejs b/client/views/unitcontrolpanel.ejs index 62f781dd..42c1b9e0 100644 --- a/client/views/unitcontrolpanel.ejs +++ b/client/views/unitcontrolpanel.ejs @@ -55,10 +55,145 @@
- +
+ + +
+
+ +
+

Olympus 1-1

+
+ +
+
+ + +
+ +
+ +
+ +
+ +
+
+ +
+ +
+ +
+ +
+
X
+
+
+
+ + + +
+ +
+
+ +
+
+ +
+ +
+ +
+ +
+
.000
+
+
+
+
+ +
+ + +
+
+
+
+
+ + + +
+ +
+ +
+
+
+ + + +
\ No newline at end of file