From 8ef48ad9771cf2e655939e4d078f058394c77261 Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Tue, 28 Nov 2023 16:59:23 +0100 Subject: [PATCH] Implemented filename selector --- client/demo.js | 9 ++-- client/public/stylesheets/style/style.css | 36 +++++++++++++ .../olympus/images/icons/keyboard-solid.svg | 1 + client/src/constants/constants.ts | 2 +- client/src/unit/unitdatafile.ts | 26 +++++----- client/src/unit/unitdatafileexport.ts | 52 ++++++++++++------- client/src/unit/unitdatafileimport.ts | 40 +++++++------- client/views/other/dialogs.ejs | 6 ++- client/views/other/dialogs/importexport.ejs | 9 ++++ 9 files changed, 122 insertions(+), 59 deletions(-) create mode 100644 client/public/themes/olympus/images/icons/keyboard-solid.svg diff --git a/client/demo.js b/client/demo.js index 5db2660c..5d7dfc7c 100644 --- a/client/demo.js +++ b/client/demo.js @@ -53,7 +53,7 @@ class DemoDataGenerator { } - /* + // UNCOMMENT TO TEST ALL UNITS **************** var databases = Object.assign({}, aircraftDatabase, helicopterDatabase, groundUnitDatabase, navyUnitDatabase); @@ -70,6 +70,7 @@ class DemoDataGenerator { DEMO_UNIT_DATA[idx].groupName = `Group-${idx}`; DEMO_UNIT_DATA[idx].position.lat += latIdx / 5; DEMO_UNIT_DATA[idx].position.lng += lngIdx / 5; + DEMO_UNIT_DATA[idx].coalition = Math.floor(Math.random() * 3) latIdx += 1; if (latIdx === l) { @@ -89,9 +90,9 @@ class DemoDataGenerator { idx += 1; } } - */ - + + /* let idx = 1; DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData)); DEMO_UNIT_DATA[idx].name = "S_75M_Volhov"; @@ -152,7 +153,7 @@ class DemoDataGenerator { DEMO_UNIT_DATA[idx].position.lat += idx / 100; DEMO_UNIT_DATA[idx].category = "Aircraft"; DEMO_UNIT_DATA[idx].isLeader = true; - + */ this.startTime = Date.now(); } diff --git a/client/public/stylesheets/style/style.css b/client/public/stylesheets/style/style.css index 135e6f7b..82c47702 100644 --- a/client/public/stylesheets/style/style.css +++ b/client/public/stylesheets/style/style.css @@ -1635,4 +1635,40 @@ input[type=number]::-webkit-outer-spin-button { .file-import-export button.start-transfer { background-color: var(--secondary-blue-text); border-color: var(--secondary-blue-text); +} + +.file-import-export .export-filename-container { + display: flex; + column-gap: 15px; + width: 100%; + align-items: center; + padding: 10px 0px; + color: white; + font-size: 14px; +} + +.file-import-export .export-filename-container input { + width: 100%; + background-color: var(--background-grey); + appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + border-style: solid; + border: 1px solid var(--background-steel); + color: white; + font-size: 14px; + padding: 4px; +} + +.file-import-export .export-filename-container img { + height: 16px; + width: 16px; + filter: invert(100%); + margin-left: -31px; + transform: translateX(-15px); + pointer-events: none; +} + +.file-import-export .ol-dialog-footer button:first-of-type{ + margin-left: auto; } \ No newline at end of file diff --git a/client/public/themes/olympus/images/icons/keyboard-solid.svg b/client/public/themes/olympus/images/icons/keyboard-solid.svg new file mode 100644 index 00000000..8838d567 --- /dev/null +++ b/client/public/themes/olympus/images/icons/keyboard-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/constants/constants.ts b/client/src/constants/constants.ts index c131704c..518b9458 100644 --- a/client/src/constants/constants.ts +++ b/client/src/constants/constants.ts @@ -214,7 +214,7 @@ export const MAP_MARKER_CONTROLS: MapMarkerVisibilityControl[] = [{ export const IADSTypes = ["AAA", "MANPADS", "SAM Site", "Radar"]; export const IADSDensities: { [key: string]: number } = { "AAA": 0.8, "MANPADS": 0.3, "SAM Site": 0.1, "Radar": 0.05 }; - +export const GROUND_UNIT_AIR_DEFENCE_REGEX:RegExp = /(\b(AAA|SAM|MANPADS?|[mM]anpads?)|[sS]tinger\b)/; export const HIDE_GROUP_MEMBERS = "Hide group members when zoomed out"; export const SHOW_UNIT_LABELS = "Show unit labels (L)"; export const SHOW_UNITS_ENGAGEMENT_RINGS = "Show units threat range rings (Q)"; diff --git a/client/src/unit/unitdatafile.ts b/client/src/unit/unitdatafile.ts index 1994bc6d..5f3aad52 100644 --- a/client/src/unit/unitdatafile.ts +++ b/client/src/unit/unitdatafile.ts @@ -3,38 +3,38 @@ import { createCheckboxOption } from "../other/utils"; export abstract class UnitDataFile { - protected data:any; - protected dialog!:Dialog; + protected data: any; + protected dialog!: Dialog; - constructor() {} + constructor() { } buildCategoryCoalitionTable() { - + const categories = this.#getCategoriesFromData(); const coalitions = ["blue", "neutral", "red"]; - let headersHTML:string = ``; - let matrixHTML:string = ``; + let headersHTML: string = ``; + let matrixHTML: string = ``; - categories.forEach((category:string, index) => { + categories.forEach((category: string, index) => { matrixHTML += `${category}`; - coalitions.forEach((coalition:string) => { + coalitions.forEach((coalition: string) => { if (index === 0) - headersHTML += `${coalition[0].toUpperCase()+coalition.substring(1)}`; + headersHTML += `${coalition[0].toUpperCase() + coalition.substring(1)}`; const optionIsValid = this.data[category].hasOwnProperty(coalition); - let checkboxHTML = createCheckboxOption(`${category}:${coalition}`, category, optionIsValid, () => {}, { + let checkboxHTML = createCheckboxOption(`${category}:${coalition}`, category, optionIsValid, () => { }, { "disabled": !optionIsValid, "name": "category-coalition-selection", "readOnly": !optionIsValid }).outerHTML; if (optionIsValid) - checkboxHTML = checkboxHTML.replace( `"checkbox"`, `"checkbox" checked`); // inner and outerHTML screw default checked up + checkboxHTML = checkboxHTML.replace(`"checkbox"`, `"checkbox" checked`); // inner and outerHTML screw default checked up matrixHTML += `${checkboxHTML}`; - + }); matrixHTML += ""; }); @@ -43,7 +43,7 @@ export abstract class UnitDataFile { (table.tHead).innerHTML = ` ${headersHTML}`; (table.querySelector(`tbody`)).innerHTML = matrixHTML; - + } #getCategoriesFromData() { diff --git a/client/src/unit/unitdatafileexport.ts b/client/src/unit/unitdatafileexport.ts index 2085381f..7f90b715 100644 --- a/client/src/unit/unitdatafileexport.ts +++ b/client/src/unit/unitdatafileexport.ts @@ -6,16 +6,17 @@ import { UnitDataFile } from "./unitdatafile"; export class UnitDataFileExport extends UnitDataFile { - protected data!:any; - protected dialog:Dialog; - #element!:HTMLElement; + protected data!: any; + protected dialog: Dialog; + #element!: HTMLElement; + #filename: string = "export.json"; - constructor( elementId:string ) { + constructor(elementId: string) { super(); this.dialog = new Dialog(elementId); this.#element = this.dialog.getElement(); - this.#element.querySelector(".start-transfer")?.addEventListener("click", (ev:MouseEventInit) => { + this.#element.querySelector(".start-transfer")?.addEventListener("click", (ev: MouseEventInit) => { this.#doExport(); }); } @@ -23,12 +24,12 @@ export class UnitDataFileExport extends UnitDataFile { /** * Show the form to start the export journey */ - showForm(units:Unit[]) { - const data:any = {}; - const unitCanBeExported = (unit:Unit) => !["Aircraft", "Helicopter"].includes(unit.getCategory()); + showForm(units: Unit[]) { + const data: any = {}; + const unitCanBeExported = (unit: Unit) => !["Aircraft", "Helicopter"].includes(unit.getCategory()); - units.filter((unit:Unit) => unit.getAlive() && unitCanBeExported(unit)).forEach((unit:Unit) => { - const category = unit.getCategory(); + units.filter((unit: Unit) => unit.getAlive() && unitCanBeExported(unit)).forEach((unit: Unit) => { + const category = unit.getCategory(); const coalition = unit.getCoalition(); if (!data.hasOwnProperty(category)) { @@ -44,13 +45,22 @@ export class UnitDataFileExport extends UnitDataFile { this.data = data; this.buildCategoryCoalitionTable(); this.dialog.show(); + + const date = new Date(); + this.#filename = `olympus_${getApp().getMissionManager().getTheatre().replace(/[^\w]/gi, "").toLowerCase()}_${date.getFullYear()}${zeroAppend(date.getMonth() + 1, 2)}${zeroAppend(date.getDate(), 2)}_${zeroAppend(date.getHours(), 2)}${zeroAppend(date.getMinutes(), 2)}${zeroAppend(date.getSeconds(), 2)}.json`; + var input = this.#element.querySelector("#export-filename") as HTMLInputElement; + input.onchange = (ev: Event) => { + this.#filename = (ev.currentTarget as HTMLInputElement).value; + } + if (input) + input.value = this.#filename; } - + #doExport() { - let selectedUnits:Unit[] = []; - - this.#element.querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`).forEach((checkbox:HTMLInputElement) => { + let selectedUnits: Unit[] = []; + + this.#element.querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`).forEach((checkbox: HTMLInputElement) => { if (checkbox instanceof HTMLInputElement) { const [category, coalition] = checkbox.value.split(":"); // e.g. "category:coalition" selectedUnits = selectedUnits.concat(this.data[category][coalition]); @@ -63,7 +73,7 @@ export class UnitDataFileExport extends UnitDataFile { } var unitsToExport: { [key: string]: any } = {}; - selectedUnits.forEach((unit:Unit) => { + selectedUnits.forEach((unit: Unit) => { var data: any = unit.getData(); if (unit.getGroupName() in unitsToExport) unitsToExport[unit.getGroupName()].push(data); @@ -71,11 +81,15 @@ export class UnitDataFileExport extends UnitDataFile { unitsToExport[unit.getGroupName()] = [data]; }); - const date = new Date(); - const a = document.createElement("a"); + + const a = document.createElement("a"); const file = new Blob([JSON.stringify(unitsToExport)], { type: 'text/plain' }); - a.href = URL.createObjectURL(file); - a.download = `olympus_${getApp().getMissionManager().getTheatre().replace( /[^\w]/gi, "" ).toLowerCase()}_${date.getFullYear()}${zeroAppend(date.getMonth()+1, 2)}${zeroAppend(date.getDate(), 2)}_${zeroAppend(date.getHours(), 2)}${zeroAppend(date.getMinutes(), 2)}${zeroAppend(date.getSeconds(), 2)}.json`; + a.href = URL.createObjectURL(file); + + var filename = this.#filename; + if (!this.#filename.toLowerCase().endsWith(".json")) + filename += ".json"; + a.download = filename; a.click(); this.dialog.hide(); } diff --git a/client/src/unit/unitdatafileimport.ts b/client/src/unit/unitdatafileimport.ts index b883c150..549a5a9e 100644 --- a/client/src/unit/unitdatafileimport.ts +++ b/client/src/unit/unitdatafileimport.ts @@ -5,14 +5,14 @@ import { UnitDataFile } from "./unitdatafile"; export class UnitDataFileImport extends UnitDataFile { - protected data!:any; - protected dialog:Dialog; - #fileData!:{[key:string]: UnitData[]}; + protected data!: any; + protected dialog: Dialog; + #fileData!: { [key: string]: UnitData[] }; - constructor( elementId:string ) { + constructor(elementId: string) { super(); this.dialog = new Dialog(elementId); - this.dialog.getElement().querySelector(".start-transfer")?.addEventListener("click", (ev:MouseEventInit) => { + this.dialog.getElement().querySelector(".start-transfer")?.addEventListener("click", (ev: MouseEventInit) => { this.#doImport(); this.dialog.hide(); }); @@ -20,18 +20,18 @@ export class UnitDataFileImport extends UnitDataFile { #doImport() { - let selectedCategories:any = {}; + let selectedCategories: any = {}; const unitsManager = getApp().getUnitsManager(); - this.dialog.getElement().querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`).forEach((checkbox:HTMLInputElement) => { + this.dialog.getElement().querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`).forEach((checkbox: HTMLInputElement) => { if (checkbox instanceof HTMLInputElement) { - const [category, coalition] = checkbox.value.split(":"); // e.g. "category:coalition" - selectedCategories[category] = selectedCategories[category] || {}; + const [category, coalition] = checkbox.value.split(":"); // e.g. "category:coalition" + selectedCategories[category] = selectedCategories[category] || {}; selectedCategories[category][coalition] = true; } }); - for (const[groupName, groupData] of Object.entries(this.#fileData)) { + for (const [groupName, groupData] of Object.entries(this.#fileData)) { if (groupName === "" || groupData.length === 0 || !this.#unitGroupDataCanBeImported(groupData)) continue; @@ -39,10 +39,10 @@ export class UnitDataFileImport extends UnitDataFile { if (!selectedCategories.hasOwnProperty(category) || !selectedCategories[category].hasOwnProperty(coalition) - || selectedCategories[category][coalition] !== true ) + || selectedCategories[category][coalition] !== true) continue; - let unitsToSpawn = groupData.filter((unitData:UnitData) => this.#unitDataCanBeImported(unitData)).map((unitData:UnitData) => { + let unitsToSpawn = groupData.filter((unitData: UnitData) => this.#unitDataCanBeImported(unitData)).map((unitData: UnitData) => { return { unitType: unitData.name, location: unitData.position, liveryID: "" } }); @@ -63,13 +63,13 @@ export class UnitDataFileImport extends UnitDataFile { } #showForm() { - const data:any = {}; + const data: any = {}; - for (const [ group, units ] of Object.entries(this.#fileData) ) { + for (const [group, units] of Object.entries(this.#fileData)) { if (group === "" || units.length === 0) continue; - if (units.some((unit:UnitData) => !this.#unitDataCanBeImported(unit))) + if (units.some((unit: UnitData) => !this.#unitDataCanBeImported(unit))) continue; const category = units[0].category; @@ -78,7 +78,7 @@ export class UnitDataFileImport extends UnitDataFile { data[category] = {}; } - units.forEach((unit:UnitData) => { + units.forEach((unit: UnitData) => { if (!data[category].hasOwnProperty(unit.coalition)) data[category][unit.coalition] = []; @@ -125,14 +125,14 @@ export class UnitDataFileImport extends UnitDataFile { input.click(); } - #unitDataCanBeImported(unitData:UnitData) { + #unitDataCanBeImported(unitData: UnitData) { return unitData.alive && this.#unitGroupDataCanBeImported([unitData]); } - #unitGroupDataCanBeImported(unitGroupData:UnitData[]) { - return unitGroupData.every((unitData:UnitData) => { + #unitGroupDataCanBeImported(unitGroupData: UnitData[]) { + return unitGroupData.every((unitData: UnitData) => { return !["Aircraft", "Helicopter"].includes(unitData.category); - }) && unitGroupData.some((unitData:UnitData) => unitData.alive); + }) && unitGroupData.some((unitData: UnitData) => unitData.alive); } } \ No newline at end of file diff --git a/client/views/other/dialogs.ejs b/client/views/other/dialogs.ejs index e1e59b2f..4fa33d6b 100644 --- a/client/views/other/dialogs.ejs +++ b/client/views/other/dialogs.ejs @@ -5,13 +5,15 @@ "dialogId": "unit-export-dialog", "submitButtonText": "Export units to file", "textContent": "Select the unit categories you would like to export. Note: only ground and naval units can be exported at this time.", - "title": "Export" + "title": "Export", + "showFilenameInput": true }) %> <%- include('dialogs/importexport.ejs', { "dialogId": "unit-import-dialog", "submitButtonText": "Import units into mission", "textContent": "Select the unit categories you would like to import.", - "title": "Import" + "title": "Import", + "showFilenameInput": false }) %> <%- include('dialogs/slowdelete.ejs') %> <%- include('dialogs/splash.ejs') %> \ No newline at end of file diff --git a/client/views/other/dialogs/importexport.ejs b/client/views/other/dialogs/importexport.ejs index 6f10f716..3dfecc59 100644 --- a/client/views/other/dialogs/importexport.ejs +++ b/client/views/other/dialogs/importexport.ejs @@ -5,6 +5,15 @@

<%= textContent %>

+ + <% if (showFilenameInput) { %> +
+ + + +
+ <% } %> +