Skip to content

Commit

Permalink
Chore: Reusable app window components (#64)
Browse files Browse the repository at this point in the history
  • Loading branch information
misterpotts authored Nov 23, 2022
1 parent 96df5ec commit 58c3e5e
Show file tree
Hide file tree
Showing 9 changed files with 676 additions and 358 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fabricate",
"version": "0.7.0",
"version": "0.7.1",
"description": "A system-agnostic, flexible crafting module for FoundryVT",
"main": "index.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/module.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "fabricate",
"title": "Fabricate",
"version": "0.7.0",
"version": "0.7.1",
"description": "A system-agnostic, flexible crafting module for FoundryVTT",
"authors": [
{
Expand Down
19 changes: 9 additions & 10 deletions src/scripts/interface/apps/CraftingSystemManagerApp.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import Properties from "../../Properties";
import {GameProvider} from "../../foundry/GameProvider";
import {EditCraftingSystemDetailDialog} from "./EditCraftingSystemDetailDialog";
import {EditComponentDialog} from "./EditComponentDialog";
import {EditEssenceDialog} from "./EditEssenceDialog";
import {CraftingSystem} from "../../system/CraftingSystem";
import {CraftingComponent} from "../../common/CraftingComponent";
import {Combination} from "../../common/Combination";
Expand All @@ -11,6 +8,9 @@ import {DefaultDocumentManager} from "../../foundry/DocumentManager";
import FabricateApplication from "../FabricateApplication";
import {Recipe} from "../../crafting/Recipe";
import {PartDictionary} from "../../system/PartDictionary";
import EditComponentDialogFactory from "./EditComponentDialog";
import EditEssenceDialogFactory from "./EditEssenceDialog";
import EditCraftingSystemDetailDialogFactory from "./EditCraftingSystemDetailDialog";

class CraftingSystemManagerApp extends FormApplication {

Expand Down Expand Up @@ -57,7 +57,7 @@ class CraftingSystemManagerApp extends FormApplication {
async getData(): Promise<any> {
const craftingSystems = await this.systemRegistry.getAllCraftingSystems();
if (!this._selectedSystem && craftingSystems.size > 0) {
[this._selectedSystem] = craftingSystems.values();
this._selectedSystem = Array.from(craftingSystems.values())[0];
}
await this._selectedSystem?.loadPartDictionary();
return {
Expand Down Expand Up @@ -139,13 +139,13 @@ class CraftingSystemManagerApp extends FormApplication {
private async handleUserAction(systemId: string, action: string, event: any) {
switch (action) {
case "editDetails":
new EditCraftingSystemDetailDialog(this._selectedSystem).render();
EditCraftingSystemDetailDialogFactory.make(this._selectedSystem).render();
break;
case "importCraftingSystem":
console.log(event);
break;
case "createCraftingSystem":
new EditCraftingSystemDetailDialog().render();
EditCraftingSystemDetailDialogFactory.make().render();
break;
case "toggleSystemEnabled":
const checked = event.target.checked;
Expand Down Expand Up @@ -179,8 +179,7 @@ class CraftingSystemManagerApp extends FormApplication {
if (!componentToEdit) {
throw new Error(`Cannot edit component. Component with ID "${componentIdToEdit}" not found. `);
}
new EditComponentDialog(componentToEdit, this._selectedSystem)
.render();
EditComponentDialogFactory.make(componentToEdit, this._selectedSystem).render();
break;
case "createComponent":
try {
Expand All @@ -207,15 +206,15 @@ class CraftingSystemManagerApp extends FormApplication {
}
break;
case "createEssence":
new EditEssenceDialog(this._selectedSystem).render();
EditEssenceDialogFactory.make(this._selectedSystem).render();
break;
case "editEssence":
const essenceIdToEdit = event?.target?.dataset?.essenceId;
const essenceToEdit = this._selectedSystem.essences.find(essence => essence.id === essenceIdToEdit);
if (!essenceToEdit) {
throw new Error(`Essence with ID "${essenceIdToEdit}" does not exist.`);
}
new EditEssenceDialog(this._selectedSystem, essenceToEdit).render();
EditEssenceDialogFactory.make(this._selectedSystem, essenceToEdit).render();
break;
case "deleteEssence":
const essenceIdToDelete = event?.target?.dataset?.essenceId;
Expand Down
238 changes: 146 additions & 92 deletions src/scripts/interface/apps/EditComponentDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,118 +5,134 @@ import {CombinableString, CraftingComponent} from "../../common/CraftingComponen
import {Essence} from "../../common/Essence";
import {Combination, Unit} from "../../common/Combination";
import FabricateApplication from "../FabricateApplication";
import {ApplicationWindow, Click, DefaultClickHandler, StateManager} from "./core/Applications";

class EditComponentDialog extends FormApplication {
interface EditComponentDialogView {
system: {
id: string,
essences: Essence[],
components: CraftingComponent[]
},
component: {
id: string,
name: string,
imageUrl: string,
essences: { essence: Essence; amount: number }[],
salvage: { component: CraftingComponent; amount: number }[]
}
}

class EditComponentDialogModel {

private readonly _system: CraftingSystem;
private readonly _component: CraftingComponent;
private readonly _craftingSystem: CraftingSystem;

constructor(component: CraftingComponent, selectedSystem: CraftingSystem) {
super(null);
constructor({
system,
component
}: {
system: CraftingSystem,
component: CraftingComponent
}) {
this._system = system;
this._component = component;
this._craftingSystem = selectedSystem;
}

static get defaultOptions() {
const GAME = new GameProvider().globalGameObject();
return {
...super.defaultOptions,
title: GAME.i18n.localize(`${Properties.module.id}.EditComponentDialog.title`),
id: `${Properties.module.id}-component-manager`,
template: Properties.module.templates.ComponentManagerApp,
width: 500,
};
get system(): CraftingSystem {
return this._system;
}

get component(): CraftingComponent {
return this._component;
}

public incrementEssence(essenceId: string): EditComponentDialogModel {
const essenceDelta = new Unit(new CombinableString(essenceId), 1);
this._component.essences = this._component.essences.add(essenceDelta);
return this;
}

public decrementEssence(essenceId: string): EditComponentDialogModel {
const essenceDelta = new Unit(new CombinableString(essenceId), 1);
this._component.essences = this._component.essences.minus(essenceDelta);
return this;
}

public incrementSalvage(salvageId: string): EditComponentDialogModel {
const salvageDelta = new Unit(new CombinableString(salvageId), 1);
this._component.salvage = this._component.salvage.add(salvageDelta);
return this;
}

public decrementSalvage(salvageId: string): EditComponentDialogModel {
const salvageDelta = new Unit(new CombinableString(salvageId), 1);
this._component.salvage = this._component.salvage.minus(salvageDelta);
return this;
}

}

class ComponentStateManager implements StateManager<EditComponentDialogView, EditComponentDialogModel> {

private readonly _model: EditComponentDialogModel;

constructor({
component,
system
}: {
component: CraftingComponent,
system: CraftingSystem
}) {
this._model = new EditComponentDialogModel({
component,
system
});
}

getModelState(): EditComponentDialogModel {
return this._model;
}

protected _updateObject(_event: Event, _formData: object | undefined): Promise<unknown> {
console.log("Update object");
this.render();
return undefined;
}

render(force: boolean = true) {
super.render(force);
}

activateListeners(html: JQuery) {
super.activateListeners(html);
const rootElement = html[0];
rootElement.addEventListener("click", this._onClick.bind(this));
}

async _onClick(event: any) {
const dataKeys = ["action", "componentId", "essenceId", "salvageId"];
const data = new Map(dataKeys.map(key => [key, this.getClosestElementDataForKey(key, event)]));
const shiftPressed: boolean = event.shiftKey;
return this.handleUserAction(data, shiftPressed);
}

getClosestElementDataForKey(key: string, event: any): string {
let element = event?.target;
let value = element?.dataset[key];
while (element && !value) {
value = element?.dataset[key];
element = element.parentElement;
}
return value;
};

async handleUserAction(data: Map<string, string>, shiftPressed: boolean) {
switch (data.get("action")) {
case "editComponentEssence":
const essenceId = data.get("essenceId");
const essenceDelta = new Unit(new CombinableString(essenceId), 1);
let essenceResult: Combination<CombinableString>;
if (shiftPressed) {
essenceResult = this._component.essences.minus(essenceDelta);
} else {
essenceResult = this._component.essences.add(essenceDelta);
}
this._component.essences = essenceResult;
await FabricateApplication.systemRegistry.saveCraftingSystem(this._craftingSystem);
await this.render(true);
break;
case "editComponentSalvage":
const salvageId = data.get("salvageId");
const salvageDelta = new Unit(new CombinableString(salvageId), 1);
let salvageResult: Combination<CombinableString>;
if (shiftPressed) {
salvageResult = this._component.salvage.minus(salvageDelta);
} else {
salvageResult = this._component.salvage.add(salvageDelta);
}
this._component.salvage = salvageResult;
await FabricateApplication.systemRegistry.saveCraftingSystem(this._craftingSystem);
await this.render(true);
break;
default:
return;
}
}

async getData(): Promise<any> {
getViewData(): EditComponentDialogView {
return {
system: {
id: this._craftingSystem.id,
essences: this._craftingSystem.essences,
components: this._craftingSystem.components.filter(component => component.id !== this._component.id)
id: this.system.id,
essences: this.system.essences,
components: this.system.components.filter(component => component.id !== this.component.id)
},
component: {
id: this._component.id,
name: this._component.name,
imageUrl: this._component.imageUrl,
essences: this.prepareEssenceData(this._craftingSystem.essences, this._component.essences),
salvage: this.prepareSalvageData(this._component.id, this._craftingSystem.components, this._component.salvage)
id: this.component.id,
name: this.component.name,
imageUrl: this.component.imageUrl,
essences: this.prepareEssenceData(this.system.essences, this.component.essences),
salvage: this.prepareSalvageData(this.component.id, this.system.components, this.component.salvage)
}
};
}

async load(): Promise<EditComponentDialogModel> {
return this.getModelState();
}

async save(model: EditComponentDialogModel): Promise<EditComponentDialogModel> {
await FabricateApplication.systemRegistry.saveCraftingSystem(model.system);
return this.getModelState();
}

get component(): CraftingComponent {
return this._model.component;
}

get system(): CraftingSystem {
return this._model.system;
}

private prepareEssenceData(all: Essence[], includedAmounts: Combination<CombinableString>): { essence: Essence; amount: number }[] {
return all.map(essence => {
return {
essence,
amount: includedAmounts.amountFor(new CombinableString(essence.id))
}});
}});
}

private prepareSalvageData(thisComponentId: string, all: CraftingComponent[], includedAmounts: Combination<CombinableString>): { component: CraftingComponent; amount: number }[] {
Expand All @@ -125,8 +141,46 @@ class EditComponentDialog extends FormApplication {
return {
component,
amount: includedAmounts.amountFor(new CombinableString(component.id))
}});
}});
}

}

class EditComponentDialogFactory {

make(component: CraftingComponent, system: CraftingSystem): ApplicationWindow<EditComponentDialogView, EditComponentDialogModel> {
return new ApplicationWindow<EditComponentDialogView, EditComponentDialogModel>({
clickHandler: new DefaultClickHandler({
dataKeys: ["componentId", "essenceId", "salvageId"],
actions: new Map([
["editComponentEssence", async (click: Click, currentState: EditComponentDialogModel) => {
const essenceId = click.data.get("essenceId");
if (click.keys.shift) {
return currentState.decrementEssence(essenceId);
} else {
return currentState.incrementEssence(essenceId);
}
}],
["editComponentSalvage", async (click: Click, currentState: EditComponentDialogModel) => {
const salvageId = click.data.get("salvageId");
if (click.keys.shift) {
return currentState.decrementSalvage(salvageId);
} else {
return currentState.incrementSalvage(salvageId);
}
}]
])
}),
stateManager: new ComponentStateManager({component, system}),
options: {
title: new GameProvider().globalGameObject().i18n.localize(`${Properties.module.id}.EditComponentDialog.title`),
id: `${Properties.module.id}-component-manager`,
template: Properties.module.templates.ComponentManagerApp,
width: 500,
}
});
}

}

export { EditComponentDialog }
export default new EditComponentDialogFactory();
Loading

0 comments on commit 58c3e5e

Please sign in to comment.