From 071942b632b4e451ed4713785b8a4ebc1671cd10 Mon Sep 17 00:00:00 2001 From: PeekabooSteam Date: Tue, 4 Apr 2023 23:38:08 +0100 Subject: [PATCH] Slightly nicer PoC. --- client/public/stylesheets/atc.css | 6 +-- client/public/stylesheets/layout.css | 2 + client/public/stylesheets/olympus.css | 41 +++++++---------- client/src/atc/atcboard.ts | 64 ++++++++++++++++++++++++--- client/src/atc/board/flight.ts | 38 +++++++++------- client/src/controls/dropdown.ts | 9 ++++ client/views/atc.ejs | 2 +- client/views/navbar.ejs | 9 ++++ 8 files changed, 122 insertions(+), 49 deletions(-) diff --git a/client/public/stylesheets/atc.css b/client/public/stylesheets/atc.css index 9c779eb3..7eb22afb 100644 --- a/client/public/stylesheets/atc.css +++ b/client/public/stylesheets/atc.css @@ -12,7 +12,7 @@ column-gap: 4px; display:flex; flex-flow: row nowrap; - padding:4px; + row-gap:4px; } .ol-strip-board-strip[data-flight-status="checkedin"] { @@ -20,7 +20,7 @@ } .ol-strip-board-strip[data-flight-status="readytotaxi"] { - background-color: #ffff002A; + background-color: #ffff0063; } .ol-strip-board-strip[data-flight-status="clearedtotaxi"] { @@ -46,7 +46,7 @@ padding: 4px; text-overflow: ellipsis; white-space: nowrap; - width:70px; + width:80px; } .ol-strip-board-strip input[type="text"] { diff --git a/client/public/stylesheets/layout.css b/client/public/stylesheets/layout.css index 4c713e0e..fe45ecaf 100644 --- a/client/public/stylesheets/layout.css +++ b/client/public/stylesheets/layout.css @@ -7,6 +7,8 @@ } #primary-toolbar { + align-items: center; + display:flex; position: absolute; left: 10px; top: 10px; diff --git a/client/public/stylesheets/olympus.css b/client/public/stylesheets/olympus.css index a273c0ef..dd94619a 100644 --- a/client/public/stylesheets/olympus.css +++ b/client/public/stylesheets/olympus.css @@ -346,6 +346,11 @@ nav.ol-panel> :last-child { row-gap: 4px; } +.ol-group-header { + text-align: center; + width: 100%; +} + .ol-panel .ol-group.wrap { flex-wrap: wrap; } @@ -545,30 +550,6 @@ nav.ol-panel> :last-child { width: 28px; } -#unit-selection #unit-identification [data-object|="unit"] .unit-short-label { - font-size: 12px; -} - -#unit-selection #unit-identification #unit-name { - background-color: transparent; - border: none; - color: white; - font-size: 16px; - font-weight: var(--font-weight-bolder); - outline: none; - overflow: hidden; - white-space: nowrap; - width: 150px; -} - -#edit-unit-name { - background-image: url("/images/buttons/edit.svg"); - background-repeat: no-repeat; - height: 14px; - margin-left: 10px; - width: 15px; -} - #unit-visibility-control { align-items: center; } @@ -619,6 +600,18 @@ body[data-hide-navyunit] #unit-visibility-control-navyunit { background-image: var(--visibility-control-navyunit-hidden-url); } +#atc-navbar-control { + align-items: center; + display:flex; + flex-direction: column; +} + +#atc-navbar-control button { + background:#ffffff20; + border-radius: var( --border-radius-sm ); + padding:4px; +} + .toggle { --width: 40px; diff --git a/client/src/atc/atcboard.ts b/client/src/atc/atcboard.ts index 50766884..0ab9f52d 100644 --- a/client/src/atc/atcboard.ts +++ b/client/src/atc/atcboard.ts @@ -1,8 +1,12 @@ +import { Draggable } from "leaflet"; +import { Dropdown } from "../controls/dropdown"; import { zeroAppend } from "../other/utils"; import { ATC } from "./atc"; -export interface ATCTemplateInterface { - "template": string +export interface StripBoardStripInterface { + "id": string, + "element": HTMLElement, + "dropdowns": {[key:string]: Dropdown} } export abstract class ATCBoard { @@ -14,6 +18,9 @@ export abstract class ATCBoard { #boardElement:HTMLElement; #clockElement:HTMLElement; #stripBoardElement:HTMLElement; + + // Content + #strips:{[key:string]: StripBoardStripInterface} = {}; // Update timing #updateInterval:number|undefined = undefined; @@ -27,6 +34,18 @@ export abstract class ATCBoard { this.#stripBoardElement = this.getBoardElement().querySelector( ".ol-strip-board-strips" ); this.#clockElement = this.getBoardElement().querySelector( ".ol-strip-board-clock" ); + if ( this.#boardElement.classList.contains( "ol-draggable" ) ) { + + let options:any = {}; + + let handle = this.#boardElement.querySelector( ".handle" ); + + if ( handle instanceof HTMLElement ) { + options.handle = handle; + } + + } + setInterval( () => { this.updateClock(); @@ -35,17 +54,30 @@ export abstract class ATCBoard { } + addStrip( strip:StripBoardStripInterface ) { + this.#strips[ strip.id ] = strip; + } + + calculateTimeToGo( fromTimestamp:number, toTimestamp:number ) { let timestamp = ( toTimestamp - fromTimestamp ) / 1000; - const hours = zeroAppend( Math.floor( timestamp / 3600 ), 2 ); + const hasElapsed = ( timestamp < 0 ) ? true : false; + + if ( hasElapsed ) { + timestamp = -( timestamp ); + } + + const hours = ( timestamp < 3600 ) ? "00" : zeroAppend( Math.floor( timestamp / 3600 ), 2 ); const rMinutes = timestamp % 3600; - const minutes = zeroAppend( Math.floor( rMinutes / 60 ), 2 ); + const minutes = ( timestamp < 60 ) ? "00" : zeroAppend( Math.floor( rMinutes / 60 ), 2 ); const seconds = zeroAppend( Math.floor( rMinutes % 60 ), 2 ); return { + "elapsedMarker": ( hasElapsed ) ? "+" : "-", + "hasElapsed": hasElapsed, "hours": hours, "minutes": minutes, "seconds": seconds, @@ -56,6 +88,16 @@ export abstract class ATCBoard { } + deleteStrip( id:string ) { + + if ( this.#strips.hasOwnProperty( id ) ) { + this.#strips[ id ].element.remove(); + delete this.#strips[ id ]; + } + + } + + getATC() { return this.#atc; } @@ -71,6 +113,16 @@ export abstract class ATCBoard { } + getStrips() { + return this.#strips; + } + + + getStrip( id:string ) { + return this.#strips[ id ] || false; + } + + getTemplate( key:string ) { return this.#templates[ key ] || false; @@ -115,7 +167,9 @@ export abstract class ATCBoard { timeToGo( timestamp:number ) { - return ( timestamp === -1 ) ? "-" : this.calculateTimeToGo( this.getATC().getMissionDateTime().getTime(), timestamp ).time; + const timeData = this.calculateTimeToGo( this.getATC().getMissionDateTime().getTime(), timestamp ); + + return ( timestamp === -1 ) ? "-" : timeData.elapsedMarker + timeData.time; } diff --git a/client/src/atc/board/flight.ts b/client/src/atc/board/flight.ts index a348f38c..b9830fc8 100644 --- a/client/src/atc/board/flight.ts +++ b/client/src/atc/board/flight.ts @@ -1,7 +1,7 @@ import { getMissionData } from "../.."; import { Dropdown } from "../../controls/dropdown"; import { ATC } from "../atc"; -import { ATCBoard } from "../atcboard"; +import { ATCBoard, StripBoardStripInterface } from "../atcboard"; export class ATCBoardFlight extends ATCBoard { @@ -83,7 +83,7 @@ export class ATCBoardFlight extends ATCBoard { flights.forEach( flight => { - let strip = stripBoard.querySelector( `[data-flight-id="${flight.id}"]`); + let strip = this.getStrip( flight.id ); if ( !strip ) { @@ -104,15 +104,20 @@ export class ATCBoardFlight extends ATCBoard { stripBoard.insertAdjacentHTML( "beforeend", template ); - strip = stripBoard.lastElementChild; - strip.querySelectorAll( ".ol-select" ).forEach( select => { + strip = { + "id": flight.id, + "element": stripBoard.lastElementChild, + "dropdowns": {} + }; + + strip.element.querySelectorAll( ".ol-select" ).forEach( select => { switch( select.getAttribute( "data-point" ) ) { case "status": - new Dropdown( select.id, ( value:string, ev:MouseEvent ) => { + strip.dropdowns.status = new Dropdown( select.id, ( value:string, ev:MouseEvent ) => { fetch( '/api/atc/flight/' + flight.id, { method: 'PATCH', @@ -135,10 +140,7 @@ export class ATCBoardFlight extends ATCBoard { }); - - - - strip.querySelectorAll( `input[type="text"]` ).forEach( input => { + strip.element.querySelectorAll( `input[type="text"]` ).forEach( input => { if ( input instanceof HTMLInputElement ) { @@ -190,22 +192,26 @@ export class ATCBoardFlight extends ATCBoard { }); + this.addStrip( strip ); + } else { - // TODO: change status dropdown if status is different - strip.setAttribute( "data-flight-status", flight.status ); + if ( flight.status !== strip.element.getAttribute( "data-flight-status" ) ) { + strip.element.setAttribute( "data-flight-status", flight.status ); + strip.dropdowns.status.selectText( flight.status ); + } - strip.querySelectorAll( `input[name="takeoffTime"]:not(:focus)` ).forEach( el => { + strip.element.querySelectorAll( `input[name="takeoffTime"]:not(:focus)` ).forEach( el => { if ( el instanceof HTMLInputElement ) { el.value = this.timestampToLocaleTime( flight.takeoffTime ); el.dataset.previousValue = el.value; } }); - strip.querySelectorAll( `[data-point="timeToGo"]` ).forEach( el => { + strip.element.querySelectorAll( `[data-point="timeToGo"]` ).forEach( el => { if ( flight.takeoffTime > 0 && this.calculateTimeToGo( missionTime, flight.takeoffTime ).totalSeconds <= 120 ) { - strip?.setAttribute( "data-time-warning", "level-1" ); + strip.element.setAttribute( "data-time-warning", "level-1" ); } if ( el instanceof HTMLElement ) { @@ -217,12 +223,12 @@ export class ATCBoardFlight extends ATCBoard { } - strip.toggleAttribute( "data-updating", false ); + strip.element.toggleAttribute( "data-updating", false ); }); stripBoard.querySelectorAll( `[data-updating]` ).forEach( strip => { - strip.remove(); + this.deleteStrip( strip.getAttribute( "data-flight-id" ) || "" ); }); } diff --git a/client/src/controls/dropdown.ts b/client/src/controls/dropdown.ts index 04f656d8..ad1a5d93 100644 --- a/client/src/controls/dropdown.ts +++ b/client/src/controls/dropdown.ts @@ -54,6 +54,15 @@ export class Dropdown { })); } + selectText( text:string ) { + + const index = [].slice.call( this.#options.children ).findIndex( ( opt:Element ) => opt.querySelector( "button" )?.innerText === text ); + if ( index > -1 ) { + this.selectValue( index ); + } + + } + selectValue(idx: number) { if (idx < this.#optionsList.length) diff --git a/client/views/atc.ejs b/client/views/atc.ejs index 7f94557a..062f5738 100644 --- a/client/views/atc.ejs +++ b/client/views/atc.ejs @@ -1,4 +1,4 @@ -
+
diff --git a/client/views/navbar.ejs b/client/views/navbar.ejs index fd0960d1..9a385f22 100644 --- a/client/views/navbar.ejs +++ b/client/views/navbar.ejs @@ -50,4 +50,13 @@
+ + +
+
ATC
+
+ + +
+
\ No newline at end of file