Fix Layout
This commit is contained in:
@@ -18,7 +18,8 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
classes: ["configurable-reactions", "standard-form"],
|
||||
window: {
|
||||
title: "CONFIGURABLE_REACTIONS.App.Title",
|
||||
icon: "fa-solid fa-bolt"
|
||||
icon: "fa-solid fa-bolt",
|
||||
resizable: true
|
||||
},
|
||||
position: {
|
||||
width: 1040,
|
||||
@@ -33,6 +34,7 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
removeAction: ConfigurableReactionsConfigApp.#onRemoveAction,
|
||||
saveReactionBasics: ConfigurableReactionsConfigApp.#onSaveReactionBasics,
|
||||
clearTriggerSpell: ConfigurableReactionsConfigApp.#onClearTriggerSpell,
|
||||
removeDamageType: ConfigurableReactionsConfigApp.#onRemoveDamageType,
|
||||
clearActionSpell: ConfigurableReactionsConfigApp.#onClearActionSpell,
|
||||
removeActionStatus: ConfigurableReactionsConfigApp.#onRemoveActionStatus
|
||||
}
|
||||
@@ -67,7 +69,12 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
const activeTrigger = triggerPalette.find(t => t.type === selectedReaction?.trigger?.type) ?? null;
|
||||
const triggerSpellName = selectedReaction?.trigger?.spell?.itemName || selectedReaction?.trigger?.spell?.itemUuid || "";
|
||||
const activeTriggerAcceptsSpell = isSpellTrigger(selectedReaction?.trigger?.type);
|
||||
const activeTriggerIsDamage = selectedReaction?.trigger?.type === TRIGGER_TYPES.DAMAGE_RECEIVED;
|
||||
const statusEffects = getStatusEffectOptions();
|
||||
const damageTypes = getDamageTypeOptions();
|
||||
const selectedDamageTypeIds = Array.from(new Set(selectedReaction?.conditions?.damage?.types ?? []));
|
||||
const selectedDamageTypes = selectedDamageTypeIds.map(typeId => resolveDamageType(typeId, damageTypes));
|
||||
const availableDamageTypes = damageTypes.filter(type => !selectedDamageTypeIds.includes(type.id));
|
||||
const visualActions = (selectedReaction?.actions ?? []).map((action, index) => {
|
||||
const paletteEntry = actionPalette.find(p => p.type === action.type);
|
||||
const selectedStatusIds = Array.from(new Set(action.statuses ?? []));
|
||||
@@ -101,9 +108,12 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
actionPalette,
|
||||
activeTrigger,
|
||||
activeTriggerAcceptsSpell,
|
||||
activeTriggerIsDamage,
|
||||
triggerSpellName,
|
||||
visualActions,
|
||||
statusEffects
|
||||
statusEffects,
|
||||
selectedDamageTypes,
|
||||
availableDamageTypes
|
||||
};
|
||||
}
|
||||
|
||||
@@ -115,7 +125,10 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
this.render({ force: true });
|
||||
});
|
||||
|
||||
this.#activateJsonEditor();
|
||||
this.#activateBasicsSync();
|
||||
this.#activateStatusSelectors();
|
||||
this.#activateDamageTypeSelector();
|
||||
this.#activateDragAndDrop();
|
||||
}
|
||||
|
||||
@@ -130,6 +143,41 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
});
|
||||
}
|
||||
}
|
||||
#activateJsonEditor() {
|
||||
const textarea = this.element.querySelector("[name='reactionJson']");
|
||||
if (!textarea) return;
|
||||
|
||||
textarea.addEventListener("blur", async () => {
|
||||
if (document.activeElement === textarea) return;
|
||||
await this.#saveReactionJsonFromTextarea({ render: true });
|
||||
});
|
||||
}
|
||||
|
||||
#activateBasicsSync() {
|
||||
const nameInput = this.element.querySelector("[name='reactionName']");
|
||||
const enabledInput = this.element.querySelector("[name='reactionEnabled']");
|
||||
|
||||
nameInput?.addEventListener("input", () => this.#syncJsonFromDesignerForm());
|
||||
nameInput?.addEventListener("change", async () => this.#saveBasicsFromDesignerForm());
|
||||
nameInput?.addEventListener("blur", async () => this.#saveBasicsFromDesignerForm());
|
||||
|
||||
enabledInput?.addEventListener("change", async () => {
|
||||
this.#syncJsonFromDesignerForm();
|
||||
await this.#saveBasicsFromDesignerForm();
|
||||
});
|
||||
}
|
||||
|
||||
#activateDamageTypeSelector() {
|
||||
const select = this.element.querySelector("[data-cr-damage-type-select]");
|
||||
if (!select) return;
|
||||
|
||||
select.addEventListener("change", async event => {
|
||||
const damageType = event.currentTarget.value;
|
||||
if (!damageType) return;
|
||||
await this.#addDamageType(damageType);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#activateDragAndDrop() {
|
||||
for (const draggable of this.element.querySelectorAll("[data-cr-drag]")) {
|
||||
@@ -208,29 +256,7 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
}
|
||||
|
||||
static async #onSaveReactionJson(event, target) {
|
||||
const textarea = this.element.querySelector("[name='reactionJson']");
|
||||
if (!textarea) return;
|
||||
|
||||
let reaction;
|
||||
try {
|
||||
reaction = JSON.parse(textarea.value);
|
||||
} catch (error) {
|
||||
ui.notifications.error(game.i18n.localize("CONFIGURABLE_REACTIONS.Errors.InvalidJson"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reaction.id) reaction.id = foundry.utils.randomID();
|
||||
if (!reaction.name) reaction.name = game.i18n.localize("CONFIGURABLE_REACTIONS.Reactions.NewReaction");
|
||||
reaction.actions ??= [];
|
||||
|
||||
const reactions = foundry.utils.deepClone(game.settings.get(MODULE_ID, SETTINGS.REACTIONS) ?? []);
|
||||
const index = reactions.findIndex(r => r.id === reaction.id);
|
||||
if (index >= 0) reactions[index] = reaction;
|
||||
else reactions.push(reaction);
|
||||
|
||||
await game.settings.set(MODULE_ID, SETTINGS.REACTIONS, reactions);
|
||||
this._selectedReactionId = reaction.id;
|
||||
this.render({ force: true });
|
||||
await this.#saveReactionJsonFromTextarea({ render: true });
|
||||
}
|
||||
|
||||
static async #onDeleteReaction(event, target) {
|
||||
@@ -308,16 +334,21 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
});
|
||||
}
|
||||
|
||||
static async #onSaveReactionBasics(event, target) {
|
||||
const name = this.element.querySelector("[name='reactionName']")?.value?.trim();
|
||||
const enabled = this.element.querySelector("[name='reactionEnabled']")?.checked === true;
|
||||
static async #onRemoveDamageType(event, target) {
|
||||
const damageType = target.dataset.damageType;
|
||||
if (!damageType) return;
|
||||
|
||||
await this.#mutateSelectedReaction(reaction => {
|
||||
if (name) reaction.name = name;
|
||||
reaction.enabled = enabled;
|
||||
reaction.conditions ??= {};
|
||||
reaction.conditions.damage ??= {};
|
||||
reaction.conditions.damage.types = (reaction.conditions.damage.types ?? []).filter(type => type !== damageType);
|
||||
});
|
||||
}
|
||||
|
||||
static async #onSaveReactionBasics(event, target) {
|
||||
await this.#saveBasicsFromDesignerForm();
|
||||
}
|
||||
|
||||
async #setTrigger(triggerType) {
|
||||
await this.#mutateSelectedReaction(reaction => {
|
||||
reaction.trigger ??= {};
|
||||
@@ -361,12 +392,77 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
});
|
||||
}
|
||||
|
||||
async #addDamageType(damageType) {
|
||||
await this.#mutateSelectedReaction(reaction => {
|
||||
reaction.conditions ??= {};
|
||||
reaction.conditions.damage ??= { enabled: true, amountMode: "damageOnly", types: [], typeMode: "any", minAmount: 1 };
|
||||
reaction.conditions.damage.types ??= [];
|
||||
if (!reaction.conditions.damage.types.includes(damageType)) reaction.conditions.damage.types.push(damageType);
|
||||
});
|
||||
}
|
||||
|
||||
async #saveReactionJsonFromTextarea({ render = true } = {}) {
|
||||
const textarea = this.element.querySelector("[name='reactionJson']");
|
||||
if (!textarea) return false;
|
||||
|
||||
let reaction;
|
||||
try {
|
||||
reaction = JSON.parse(textarea.value);
|
||||
} catch (error) {
|
||||
ui.notifications.error(game.i18n.localize("CONFIGURABLE_REACTIONS.Errors.InvalidJson"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!reaction.id) reaction.id = foundry.utils.randomID();
|
||||
if (!reaction.name) reaction.name = game.i18n.localize("CONFIGURABLE_REACTIONS.Reactions.NewReaction");
|
||||
reaction.actions ??= [];
|
||||
|
||||
const reactions = foundry.utils.deepClone(game.settings.get(MODULE_ID, SETTINGS.REACTIONS) ?? []);
|
||||
const index = reactions.findIndex(r => r.id === reaction.id);
|
||||
if (index >= 0) reactions[index] = reaction;
|
||||
else reactions.push(reaction);
|
||||
|
||||
await game.settings.set(MODULE_ID, SETTINGS.REACTIONS, reactions);
|
||||
this._selectedReactionId = reaction.id;
|
||||
if (render) this.render({ force: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
async #saveBasicsFromDesignerForm() {
|
||||
const name = this.element.querySelector("[name='reactionName']")?.value?.trim();
|
||||
const enabled = this.element.querySelector("[name='reactionEnabled']")?.checked === true;
|
||||
|
||||
await this.#mutateSelectedReaction(reaction => {
|
||||
if (name) reaction.name = name;
|
||||
reaction.enabled = enabled;
|
||||
});
|
||||
}
|
||||
|
||||
#syncJsonFromDesignerForm() {
|
||||
const reactions = foundry.utils.deepClone(game.settings.get(MODULE_ID, SETTINGS.REACTIONS) ?? []);
|
||||
const reaction = reactions.find(r => r.id === this._selectedReactionId);
|
||||
if (!reaction) return;
|
||||
|
||||
const name = this.element.querySelector("[name='reactionName']")?.value?.trim();
|
||||
const enabled = this.element.querySelector("[name='reactionEnabled']")?.checked === true;
|
||||
if (name) reaction.name = name;
|
||||
reaction.enabled = enabled;
|
||||
this.#updateJsonTextarea(reaction);
|
||||
}
|
||||
|
||||
#updateJsonTextarea(reaction) {
|
||||
const textarea = this.element.querySelector("[name='reactionJson']");
|
||||
if (!textarea || document.activeElement === textarea) return;
|
||||
textarea.value = JSON.stringify(reaction, null, 2);
|
||||
}
|
||||
|
||||
async #mutateSelectedReaction(mutator) {
|
||||
const reactions = foundry.utils.deepClone(game.settings.get(MODULE_ID, SETTINGS.REACTIONS) ?? []);
|
||||
const reaction = reactions.find(r => r.id === this._selectedReactionId);
|
||||
if (!reaction) return;
|
||||
|
||||
mutator(reaction);
|
||||
this.#updateJsonTextarea(reaction);
|
||||
await game.settings.set(MODULE_ID, SETTINGS.REACTIONS, reactions);
|
||||
this.render({ force: true });
|
||||
}
|
||||
@@ -495,6 +591,25 @@ function resolveStatusEffect(statusId, statusEffects = getStatusEffectOptions())
|
||||
};
|
||||
}
|
||||
|
||||
function getDamageTypeOptions() {
|
||||
const damageTypes = CONFIG.DND5E?.damageTypes ?? {};
|
||||
const entries = damageTypes instanceof Map
|
||||
? Array.from(damageTypes.entries())
|
||||
: Object.entries(damageTypes);
|
||||
|
||||
return entries.map(([id, value]) => ({
|
||||
id,
|
||||
label: game.i18n.localize(value?.label ?? value ?? id)
|
||||
})).sort((a, b) => a.label.localeCompare(b.label, game.i18n.lang));
|
||||
}
|
||||
|
||||
function resolveDamageType(typeId, damageTypes = getDamageTypeOptions()) {
|
||||
return damageTypes.find(type => type.id === typeId) ?? {
|
||||
id: typeId,
|
||||
label: typeId
|
||||
};
|
||||
}
|
||||
|
||||
function summarizeAction(action, statusEffects = getStatusEffectOptions()) {
|
||||
switch (action.type) {
|
||||
case ACTION_TYPES.TELEPORT:
|
||||
|
||||
Reference in New Issue
Block a user