diff --git a/client/plugins/databasemanager/src/airuniteditor.ts b/client/plugins/databasemanager/src/airuniteditor.ts index fee6fd57..1e99af3b 100644 --- a/client/plugins/databasemanager/src/airuniteditor.ts +++ b/client/plugins/databasemanager/src/airuniteditor.ts @@ -1,41 +1,75 @@ import { LoadoutBlueprint, UnitBlueprint } from "interfaces"; import { UnitEditor } from "./uniteditor"; import { LoadoutEditor } from "./loadouteditor"; +import { addDropdownInput, addLoadoutsScroll, addNewElementInput, addStringInput } from "./utils"; export class AirUnitEditor extends UnitEditor { + #blueprint: UnitBlueprint | null = null; #loadoutEditor: LoadoutEditor | null = null; - constructor(scrollDiv: HTMLElement, contentDiv1: HTMLElement, contentDiv2: HTMLElement) { - super(scrollDiv, contentDiv1, contentDiv2); - this.#loadoutEditor = new LoadoutEditor(this.contentDiv2); - } - - setContent(blueprint: UnitBlueprint) { - this.contentDiv1.replaceChildren(); + constructor(contentDiv1: HTMLElement, contentDiv2: HTMLElement, contentDiv3: HTMLElement) { + super(contentDiv1, contentDiv2, contentDiv3); + this.#loadoutEditor = new LoadoutEditor(this.contentDiv3); + this.contentDiv2.addEventListener("refresh", () => { + if (this.#blueprint !== null) + this.setBlueprint(this.#blueprint); + }); - this.addStringInput("Name", blueprint.name); - this.addStringInput("Label", blueprint.label); - this.addStringInput("Short label", blueprint.shortLabel); - this.addDropdownInput("Coalition", blueprint.coalition, ["", "blue", "red"]); - this.addDropdownInput("Era", blueprint.era, ["WW2", "Early Cold War", "Mid Cold War", "Late Cold War", "Modern"]); - this.addStringInput("Filename", blueprint.filename?? ""); - this.addStringInput("Cost", String(blueprint.cost)?? "", "number"); - - this.addLoadoutList(blueprint.loadouts?? []); + this.contentDiv3.addEventListener("refresh", () => { + if (this.#blueprint !== null) + this.setBlueprint(this.#blueprint); + this.#loadoutEditor?.show(); + }); } - addLoadoutList(loadouts: LoadoutBlueprint[]) { - var loadoutsEl = document.createElement("div"); - loadoutsEl.classList.add("dc-scroll-container", "dc-loadout-container") - loadouts.forEach((loadout: LoadoutBlueprint) => { - var div = document.createElement("div"); - loadoutsEl.appendChild(div); - div.textContent = loadout.name; - div.onclick = () => { + setBlueprint(blueprint: UnitBlueprint) { + this.#blueprint = blueprint; + + if (this.#blueprint !== null) { + this.contentDiv2.replaceChildren(); + + addStringInput(this.contentDiv2, "Name", blueprint.name, "text", (value: string) => {blueprint.name = value; }, true); + addStringInput(this.contentDiv2, "Label", blueprint.label, "text", (value: string) => {blueprint.label = value; }); + addStringInput(this.contentDiv2, "Short label", blueprint.shortLabel, "text", (value: string) => {blueprint.shortLabel = value; }); + addDropdownInput(this.contentDiv2, "Coalition", blueprint.coalition, ["", "blue", "red"],); + addDropdownInput(this.contentDiv2, "Era", blueprint.era, ["WW2", "Early Cold War", "Mid Cold War", "Late Cold War", "Modern"]); + addStringInput(this.contentDiv2, "Filename", blueprint.filename?? "", "text", (value: string) => {blueprint.filename = value; }); + addStringInput(this.contentDiv2, "Cost", String(blueprint.cost)?? "", "number", (value: string) => {blueprint.cost = parseFloat(value); }); + addLoadoutsScroll(this.contentDiv2, blueprint.loadouts?? [], (loadout: LoadoutBlueprint) => { this.#loadoutEditor?.setLoadout(loadout); this.#loadoutEditor?.show(); - }; - }); - this.contentDiv1.appendChild(loadoutsEl); + }); + addNewElementInput(this.contentDiv2, (ev: MouseEvent, input: HTMLInputElement) => { this.addLoadout(input.value); }); + + this.#loadoutEditor?.hide(); + } + } + + addBlueprint(key: string) { + if (this.database != null) { + this.database.blueprints[key] = { + name: key, + coalition: "", + label: "", + shortLabel: "", + era: "", + loadouts: [] + } + this.show(); + this.setBlueprint(this.database.blueprints[key]); + } + } + + addLoadout(loadoutName: string) { + if (loadoutName && this.#blueprint !== null) { + this.#blueprint.loadouts?.push({ + name: loadoutName, + code: "", + fuel: 1, + items: [], + roles: [] + }) + this.setBlueprint(this.#blueprint); + } } } diff --git a/client/plugins/databasemanager/src/databasemanagerplugin.ts b/client/plugins/databasemanager/src/databasemanagerplugin.ts index ba62e7e9..872f6a5e 100644 --- a/client/plugins/databasemanager/src/databasemanagerplugin.ts +++ b/client/plugins/databasemanager/src/databasemanagerplugin.ts @@ -6,11 +6,13 @@ export class DatabaseManagerPlugin implements OlympusPlugin { #app: OlympusApp | null = null; #element: HTMLElement; - #scrollDiv: HTMLElement; + #container: HTMLElement; #contentDiv1: HTMLElement; #contentDiv2: HTMLElement; + #contentDiv3: HTMLElement; #aircraftEditor: AirUnitEditor; + #helicopterEditor: AirUnitEditor; constructor() { this.#element = document.createElement("div"); @@ -19,19 +21,46 @@ export class DatabaseManagerPlugin implements OlympusPlugin { this.#element.classList.add("ol-dialog"); document.body.appendChild(this.#element); - this.#scrollDiv = document.createElement("div"); - this.#scrollDiv.classList.add("dc-scroll-container"); - this.#element.appendChild(this.#scrollDiv); + let buttonContainer = document.createElement("div"); + + let button1 = document.createElement("button"); + button1.textContent = "Aircraft"; + button1.onclick = () => this.#aircraftEditor.show(); + buttonContainer.appendChild(button1); + + let button2 = document.createElement("button"); + button2.textContent = "Helicopter"; + button2.onclick = () => this.#helicopterEditor.show(); + buttonContainer.appendChild(button2); + + let button3 = document.createElement("button"); + button3.textContent = "Ground Unit"; + buttonContainer.appendChild(button3); + + let button4 = document.createElement("button"); + button4.textContent = "Navy Unit"; + buttonContainer.appendChild(button4); + + this.#element.appendChild(buttonContainer); + + this.#container = document.createElement("div"); + this.#container.classList.add("dc-container"); + this.#element.appendChild(this.#container) this.#contentDiv1 = document.createElement("div"); this.#contentDiv1.classList.add("dc-content-container"); - this.#element.appendChild(this.#contentDiv1); + this.#container.appendChild(this.#contentDiv1); this.#contentDiv2 = document.createElement("div"); this.#contentDiv2.classList.add("dc-content-container"); - this.#element.appendChild(this.#contentDiv2); + this.#container.appendChild(this.#contentDiv2); - this.#aircraftEditor = new AirUnitEditor(this.#scrollDiv, this.#contentDiv1, this.#contentDiv2); + this.#contentDiv3 = document.createElement("div"); + this.#contentDiv3.classList.add("dc-content-container"); + this.#container.appendChild(this.#contentDiv3); + + this.#aircraftEditor = new AirUnitEditor(this.#contentDiv1, this.#contentDiv2, this.#contentDiv3); + this.#helicopterEditor = new AirUnitEditor(this.#contentDiv1, this.#contentDiv2, this.#contentDiv3); } getName() { @@ -44,7 +73,11 @@ export class DatabaseManagerPlugin implements OlympusPlugin { var aircraftDatabase = this.#app?.getAircraftDatabase(); if (aircraftDatabase != null) { this.#aircraftEditor.setDatabase(aircraftDatabase); - this.#aircraftEditor.show(); + } + + var helicopterDatabase = this.#app?.getHelicopterDatabase(); + if (helicopterDatabase != null) { + this.#helicopterEditor.setDatabase(helicopterDatabase); } return true; diff --git a/client/plugins/databasemanager/src/loadouteditor.ts b/client/plugins/databasemanager/src/loadouteditor.ts index 9fea8af6..8f062fea 100644 --- a/client/plugins/databasemanager/src/loadouteditor.ts +++ b/client/plugins/databasemanager/src/loadouteditor.ts @@ -1,4 +1,5 @@ import { LoadoutBlueprint, LoadoutItemBlueprint } from "interfaces"; +import { addLoadoutItemsEditor, addStringInput } from "./utils"; export class LoadoutEditor { #contentDiv: HTMLElement; @@ -6,6 +7,7 @@ export class LoadoutEditor { constructor(contentDiv: HTMLElement) { this.#contentDiv = contentDiv; + this.#contentDiv.addEventListener("refresh", () => { this.show(); }) } setLoadout(loadout: LoadoutBlueprint) { @@ -16,30 +18,14 @@ export class LoadoutEditor { this.#contentDiv.replaceChildren(); if (this.#loadout) { - this.addStringInput("Name", this.#loadout.name); - this.addStringInput("Code", this.#loadout.code); - - var itemsEl = document.createElement("div"); - itemsEl.classList.add("dc-scroll-container", "dc-items-container"); - this.#loadout.items.forEach((item: LoadoutItemBlueprint) => { - var div = document.createElement("div"); - itemsEl.appendChild(div); - div.textContent = item.name; - }) - this.#contentDiv.appendChild(itemsEl); + var laodout = this.#loadout; + addStringInput(this.#contentDiv, "Name", laodout.name, "text", (value: string) => {laodout.name = value; this.#contentDiv.dispatchEvent(new Event("refresh"));}); + addStringInput(this.#contentDiv, "Code", laodout.code, "text", (value: string) => {laodout.code = value; }); + addLoadoutItemsEditor(this.#contentDiv, this.#loadout); } } - addStringInput(key: string, value: string, type?: string) { - var dt = document.createElement("dt"); - var dd = document.createElement("dd"); - dt.innerText = key; - var input = document.createElement("input"); - input.value = value; - input.textContent = value; - input.type = type?? "text"; - dd.appendChild(input); - this.#contentDiv.appendChild(dt); - this.#contentDiv.appendChild(dd); + hide() { + this.#contentDiv.replaceChildren(); } } \ No newline at end of file diff --git a/client/plugins/databasemanager/src/uniteditor.ts b/client/plugins/databasemanager/src/uniteditor.ts index 04fd7c6f..c117688f 100644 --- a/client/plugins/databasemanager/src/uniteditor.ts +++ b/client/plugins/databasemanager/src/uniteditor.ts @@ -1,65 +1,47 @@ import { LoadoutBlueprint, UnitBlueprint } from "interfaces"; import { UnitDatabase } from "unit/databases/unitdatabase"; +import { addBlueprintsScroll, addNewElementInput } from "./utils"; export abstract class UnitEditor { - database: UnitDatabase | null = null; - scrollDiv: HTMLElement; + database: {blueprints: {[key: string]: UnitBlueprint}} | null = null; contentDiv1: HTMLElement; contentDiv2: HTMLElement; + contentDiv3: HTMLElement; - constructor(scrollDiv: HTMLElement, contentDiv1: HTMLElement, contentDiv2: HTMLElement) { - this.scrollDiv = scrollDiv; + constructor(contentDiv1: HTMLElement, contentDiv2: HTMLElement, contentDiv3: HTMLElement) { this.contentDiv1 = contentDiv1; this.contentDiv2 = contentDiv2; + this.contentDiv3 = contentDiv3; + this.contentDiv1.addEventListener("refresh", () => { this.show(); }) } - setDatabase(database: any) { - this.database = database; + setDatabase(database: UnitDatabase) { + this.database = JSON.parse(JSON.stringify(database)); } show() { - if (this.database !== null) { - var blueprints: {[key: string]: UnitBlueprint} = this.database.getBlueprints(); + this.contentDiv1.replaceChildren(); + this.contentDiv2.replaceChildren(); + this.contentDiv3.replaceChildren(); - for (let key in blueprints) { - var div = document.createElement("div"); - this.scrollDiv.appendChild(div); - div.textContent = key; - div.onclick = () => this.setContent(blueprints[key]); - } + if (this.database != null) { + addBlueprintsScroll(this.contentDiv1, this.database, (key: string) => { + if (this.database != null) + this.setBlueprint(this.database.blueprints[key]) + }) + addNewElementInput(this.contentDiv1, (ev: MouseEvent, input: HTMLInputElement) => { + if (input.value != "") + this.addBlueprint((input).value); + }); } } - addStringInput(key: string, value: string, type?: string) { - var dt = document.createElement("dt"); - var dd = document.createElement("dd"); - dt.innerText = key; - var input = document.createElement("input"); - input.value = value; - input.textContent = value; - input.type = type?? "text"; - dd.appendChild(input); - this.contentDiv1.appendChild(dt); - this.contentDiv1.appendChild(dd); + hide() { + this.contentDiv1.replaceChildren(); + this.contentDiv2.replaceChildren(); + this.contentDiv3.replaceChildren(); } - addDropdownInput(key: string, value: string, options: string[]) { - var dt = document.createElement("dt"); - var dd = document.createElement("dd"); - dt.innerText = key; - var select = document.createElement("select"); - options.forEach((option: string) => { - var el = document.createElement("option"); - el.value = option; - el.innerText = option; - select.appendChild(el); - }); - select.value = value; - dd.appendChild(select); - this.contentDiv1.appendChild(dt); - this.contentDiv1.appendChild(dd); - } - - abstract setContent(blueprint: UnitBlueprint): void; - + abstract setBlueprint(blueprint: UnitBlueprint): void; + abstract addBlueprint(key: string): void; } \ No newline at end of file diff --git a/client/plugins/databasemanager/src/utils.ts b/client/plugins/databasemanager/src/utils.ts new file mode 100644 index 00000000..7213eac0 --- /dev/null +++ b/client/plugins/databasemanager/src/utils.ts @@ -0,0 +1,161 @@ +import { LoadoutBlueprint, LoadoutItemBlueprint, UnitBlueprint } from "interfaces"; + +export function addStringInput(div: HTMLElement, key: string, value: string, type: string, callback: CallableFunction, disabled?: boolean) { + var row = document.createElement("div"); + var dt = document.createElement("dt"); + var dd = document.createElement("dd"); + dt.innerText = key; + var input = document.createElement("input"); + input.value = value; + input.textContent = value; + input.type = type?? "text"; + input.disabled = disabled?? false; + input.onchange = () => callback(input.value); + dd.appendChild(input); + row.appendChild(dt); + row.appendChild(dd); + row.classList.add("input-row"); + div.appendChild(row); +} + +export function addDropdownInput(div: HTMLElement, key: string, value: string, options: string[]) { + var row = document.createElement("div"); + var dt = document.createElement("dt"); + var dd = document.createElement("dd"); + dt.innerText = key; + var select = document.createElement("select"); + options.forEach((option: string) => { + var el = document.createElement("option"); + el.value = option; + el.innerText = option; + select.appendChild(el); + }); + select.value = value; + dd.appendChild(select); + row.appendChild(dt); + row.appendChild(dd); + row.classList.add("input-row"); + div.appendChild(row); +} + +export function addLoadoutItemsEditor(div: HTMLElement, loadout: LoadoutBlueprint) { + var itemsEl = document.createElement("div"); + itemsEl.classList.add("dc-scroll-container", "dc-items-container"); + loadout.items.forEach((item: LoadoutItemBlueprint, index: number) => { + var rowDiv = document.createElement("div"); + + var nameLabel = document.createElement("label"); + nameLabel.innerText = "Name" + rowDiv.appendChild(nameLabel); + + var nameInput = document.createElement("input"); + rowDiv.appendChild(nameInput); + nameInput.textContent = item.name; + nameInput.value = item.name + nameInput.onchange = () => { loadout.items[index].name = nameInput.value; } + + var quantityLabel = document.createElement("label"); + quantityLabel.innerText = "Quantity" + rowDiv.appendChild(quantityLabel); + + var quantityInput = document.createElement("input"); + rowDiv.appendChild(quantityInput); + quantityInput.textContent = String(item.quantity); + quantityInput.value = String(item.quantity); + quantityInput.type = "number"; + quantityInput.step = "1"; + quantityInput.onchange = () => { loadout.items[index].quantity = parseInt(quantityInput.value); } + + var button = document.createElement("button"); + button.innerText = "X"; + button.onclick = () => { + loadout.items.splice(index, 1); + div.dispatchEvent(new Event("refresh")); + } + rowDiv.appendChild(button); + + itemsEl.appendChild(rowDiv); + }) + div.appendChild(itemsEl); + + var inputDiv = document.createElement("div"); + inputDiv.classList.add("dc-new-item-input"); + var button = document.createElement("button"); + button.innerText = "Add"; + inputDiv.appendChild(button); + div.appendChild(inputDiv); + + button.addEventListener("click", (ev: MouseEvent) => { + loadout?.items.push({ + name: "", + quantity: 1 + }) + div.dispatchEvent(new Event("refresh")); + }); +} + +export function addNewElementInput(div: HTMLElement, callback: CallableFunction) { + var inputDiv = document.createElement("div"); + inputDiv.classList.add("dc-new-element-input"); + var input = document.createElement("input"); + inputDiv.appendChild(input); + var button = document.createElement("button"); + button.innerText = "Add"; + button.addEventListener("click", (ev: MouseEvent) => callback(ev, input)); + inputDiv.appendChild(button); + div.appendChild(inputDiv); +} + +export function addBlueprintsScroll(div: HTMLElement, database: {blueprints: {[key: string]: UnitBlueprint}}, callback: CallableFunction) { + var scrollDiv = document.createElement("div"); + scrollDiv.classList.add("dc-scroll-container"); + if (database !== null) { + var blueprints: {[key: string]: UnitBlueprint} = database.blueprints; + + for (let key in blueprints) { + var rowDiv = document.createElement("div"); + scrollDiv.appendChild(rowDiv); + + var text = document.createElement("label"); + text.textContent = key; + text.onclick = () => callback(key); + rowDiv.appendChild(text); + + var button = document.createElement("button"); + button.innerText = "X"; + button.onclick = () => { + delete blueprints[key]; + div.dispatchEvent(new Event("refresh")); + } + rowDiv.appendChild(button); + } + } + div.appendChild(scrollDiv); +} + +export function addLoadoutsScroll(div: HTMLElement, loadouts: LoadoutBlueprint[], callback: CallableFunction) { + var loadoutsEl = document.createElement("div"); + loadoutsEl.classList.add("dc-scroll-container", "dc-loadout-container") + + loadouts.forEach((loadout: LoadoutBlueprint, index: number) => { + var rowDiv = document.createElement("div"); + loadoutsEl.appendChild(rowDiv); + + var text = document.createElement("label"); + text.textContent = loadout.name; + text.onclick = () => { callback(loadout) }; + rowDiv.appendChild(text); + + if (loadout.name !== "Empty loadout") { + var button = document.createElement("button"); + button.innerText = "X"; + button.onclick = () => { + loadouts.splice(index, 1); + div.dispatchEvent(new Event("refresh")); + } + rowDiv.appendChild(button); + } + }); + + div.appendChild(loadoutsEl); +} \ No newline at end of file diff --git a/client/plugins/databasemanager/style.css b/client/plugins/databasemanager/style.css index 893fdea0..37e692ad 100644 --- a/client/plugins/databasemanager/style.css +++ b/client/plugins/databasemanager/style.css @@ -1,17 +1,53 @@ #database-control-panel { display: flex; - flex-direction: row; + flex-direction: column; width: 80%; + height: 80%; + padding: 10px; +} + +#database-control-panel * { + font-size: 13; + font-weight: bold; +} + +#database-control-panel>div:first-child { + display: flex; + column-gap: 2px; + align-items: center; +} + +.dc-container { + display: flex; + flex-direction: row; + width: 100%; + height: 100%; + padding: 10px; +} + +.dc-container>div { + min-width: 200px; + width: fit-content; + margin: 10px; +} + +.dc-container>div:nth-child(3) { + flex: 1; } .dc-scroll-container { - overflow-y: scroll; + display: flex; + flex-direction: column; + overflow-y: auto; max-height: 600px; - margin: 10px; color: black; font-weight: bold; } +.dc-scroll-container>* { + padding: 2px; +} + .dc-scroll-container>div:nth-child(even) { background-color: gainsboro; } @@ -20,26 +56,103 @@ background-color: white; } -.dc-scroll-container>div:hover{ +.dc-scroll-container>div *:nth-child(1) { + height: 100%; + width: 100%; +} + +.dc-scroll-container>div *:nth-child(1):hover{ background-color: var(--secondary-blue-text); color: white; cursor: pointer; } -.dc-content-container { - margin: 10px; +.dc-scroll-container>div { + display: flex; + align-items: center; + justify-content: space-between; } -.dc-content-container>dd>input { +.dc-scroll-container>div>button { + height: 20px; + width: 20px; + padding: 0px; +} + +.dc-content-container { + margin: 10px; + display: flex; + flex-direction: column; + row-gap: 5px; +} + +.input-row { + width: 100%; + display: flex; + flex-direction: row; +} + +.input-row>dt { + width: 150px; +} + +.input-row>dd { + width: 100%; +} + +.input-row>dd>* { width: 100%; - font-weight: bold; } .dc-loadout-container { - max-height: 200px; - width: 300px; + max-height: 100%; + width: 400px; } .dc-items-container { - max-height: 200px; + max-height: 100%; + height: fit-content; } + +.dc-items-container>div { + display: flex; + align-items: center; + column-gap: 2px; +} + +.dc-items-container>div>label { + width: 80px !important; +} + +.dc-items-container div>input:nth-of-type(1) { + width: 100%; +} + +.dc-items-container div>input:nth-of-type(2) { + width: 40px; +} + +.dc-new-element-input { + display: flex; + flex-direction: row; + column-gap: 2px; + width: 100%; + align-items: center; +} + +.dc-new-element-input>input { + width: 100%; +} + +.dc-new-element-input>button { + width: 60px; +} + +.dc-new-item-input { + display: flex; + justify-content: end; +} + +.dc-new-item-input>button { + width: 60px; +} \ No newline at end of file