Skip to content

Commit

Permalink
[#2138] Rebuild RollConfigurationDialog using Application V2
Browse files Browse the repository at this point in the history
  • Loading branch information
arbron committed Jul 22, 2024
1 parent 4d12427 commit f1eaea5
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 87 deletions.
4 changes: 3 additions & 1 deletion lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1308,7 +1308,9 @@
"DND5E.RitualAbbr": "R",
"DND5E.Roll": "Roll",
"DND5E.RollConfiguration": {
"Title": "Configure Roll"
"Title": "Configure Roll",
"Configuration": "Configuration",
"Rolls": "Rolls"
},
"DND5E.RollExample": "e.g. 1d4",
"DND5E.RollMode": "Roll Mode",
Expand Down
1 change: 1 addition & 0 deletions module/applications/_module.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * as actor from "./actor/_module.mjs";
export * as advancement from "./advancement/_module.mjs";
export * as api from "./api/_module.mjs";
export * as combat from "./combat/_module.mjs";
export * as components from "./components/_module.mjs";
export * as dice from "./dice/_module.mjs";
Expand Down
1 change: 1 addition & 0 deletions module/applications/api/_module.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Application5e } from "./application.mjs";
22 changes: 22 additions & 0 deletions module/applications/api/application.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;

/**
* Base application from which all system applications should be based.
*/
export default class Application5e extends HandlebarsApplicationMixin(ApplicationV2) {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["dnd5e"]
};

/* -------------------------------------------- */
/* Rendering */
/* -------------------------------------------- */

/** @inheritDoc */
async _prepareContext(options) {
const context = await super._prepareContext(options);
context.CONFIG = CONFIG.DND5E;
return context;
}
}
199 changes: 137 additions & 62 deletions module/applications/dice/roll-configuration-dialog.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Application5e from "../api/application.mjs";

/**
* Dialog rendering options for a roll configuration dialog.
*
Expand All @@ -16,9 +18,9 @@
* @param {BasicRollMessageConfiguration} [message={}] Message configuration.
* @param {BasicRollConfigurationDialogOptions} [options={}] Dialog rendering options.
*/
export default class RollConfigurationDialog extends FormApplication {
export default class RollConfigurationDialog extends Application5e {
constructor(config={}, message={}, options={}) {
super(null, options);
super(options);

/**
* Roll configuration.
Expand All @@ -27,23 +29,51 @@ export default class RollConfigurationDialog extends FormApplication {
Object.defineProperty(this, "config", { value: config, writable: false });

this.message = message;
this.object = this._buildRolls(foundry.utils.deepClone(this.config));
this.rolls = this._buildRolls(foundry.utils.deepClone(this.config));
}

/* -------------------------------------------- */

/** @inheritDoc */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
template: "systems/dnd5e/templates/dice/roll-configuration-dialog.hbs",
/** @override */
static DEFAULT_OPTIONS = {
classes: ["roll-configuration"],
tag: "form",
window: {
title: "DND5E.RollConfiguration.Title",
classes: ["dnd5e", "dialog", "roll"],
width: 400,
submitOnChange: true,
closeOnSubmit: false,
jQuery: false,
rollType: CONFIG.Dice.BasicRoll
});
icon: "fa-solid fa-dice",
minimizable: false
},
form: {
handler: RollConfigurationDialog.#handleFormSubmission
},
position: {
width: 400
}
};

/* -------------------------------------------- */

/** @override */
static PARTS = {
formulas: {
template: "systems/dnd5e/templates/dice/roll-formulas.hbs"
},
configuration: {
template: "systems/dnd5e/templates/dice/roll-configuration.hbs"
},
buttons: {
template: "systems/dnd5e/templates/dice/roll-buttons.hbs"
}
};

/* -------------------------------------------- */

/**
* Roll type to use when constructing the rolls.
* @type {BasicRoll}
*/
static get rollType() {
return CONFIG.Dice.BasicRoll;
}

/* -------------------------------------------- */
Expand All @@ -62,41 +92,81 @@ export default class RollConfigurationDialog extends FormApplication {
* The rolls being configured.
* @type {BasicRoll[]}
*/
get rolls() {
return this.object;
}
rolls;

set rolls(rolls) {
this.object = rolls;
/* -------------------------------------------- */

/**
* Roll type to use when constructing the rolls.
* @type {BasicRoll}
*/
get rollType() {
return this.options.rollType ?? this.constructor.rollType;
}

/* -------------------------------------------- */
/* Rendering */
/* -------------------------------------------- */

/** @inheritDoc */
async _preparePartContext(partId, context, options) {
context = await super._preparePartContext(partId, context, options);
switch (partId) {
case "buttons":
return this._prepareButtonsContext(context, options);
case "configuration":
return this._prepareConfigurationContext(context, options);
case "formulas":
return this._prepareFormulasContext(context, options);
default:
return context;
}
}

/* -------------------------------------------- */

/**
* Buttons displayed in the configuration dialog.
* @returns {object}
* @protected
* Prepare the context for the buttons.
* @param {ApplicationRenderContext} context Shared context provided by _prepareContext.
* @param {HandlebarsRenderOptions} options Options which configure application rendering behavior.
* @returns {ApplicationRenderContext}
*/
_getButtons() {
return {
roll: { label: game.i18n.localize("DND5E.Roll") }
_prepareButtonsContext(context, options) {
context.buttons = {
roll: {
icon: '<i class="fa-solid fa-dice"></i>',
label: game.i18n.localize("DND5E.Roll")
}
};
return context;
}

/* -------------------------------------------- */

/** @inheritDoc */
getData(options={}) {
return foundry.utils.mergeObject({
CONFIG: CONFIG.DND5E,
rolls: this.rolls,
rollMode: this.message.rollMode ?? this.options.default?.rollMode,
rollModes: CONFIG.Dice.rollModes,
situational: this.rolls[0].data.situational,
buttons: this._getButtons()
}, super.getData(options));
/**
* Prepare the context for the roll configuration section.
* @param {ApplicationRenderContext} context Shared context provided by _prepareContext.
* @param {HandlebarsRenderOptions} options Options which configure application rendering behavior.
* @returns {ApplicationRenderContext}
*/
_prepareConfigurationContext(context, options) {
context.rollMode = this.message.rollMode ?? this.options.default?.rollMode;
context.rollModesOptions = CONFIG.Dice.rollModes;
return context;
}

/* -------------------------------------------- */

/**
* Prepare the context for the formulas list.
* @param {ApplicationRenderContext} context Shared context provided by _prepareContext.
* @param {HandlebarsRenderOptions} options Options which configure application rendering behavior.
* @returns {ApplicationRenderContext}
*/
_prepareFormulasContext(context, options) {
context.rolls = this.rolls;
context.situational = this.rolls[0].data.situational;
return context;
}

/* -------------------------------------------- */
Expand All @@ -106,12 +176,12 @@ export default class RollConfigurationDialog extends FormApplication {
/**
* Build a roll from the provided configuration objects.
* @param {BasicRollProcessConfiguration} config Roll configuration data.
* @param {object} [formData={}] Any data entered into the rolling prompt.
* @param {FormDataExtended} [formData] Any data entered into the rolling prompt.
* @returns {BasicRoll[]}
* @internal
*/
_buildRolls(config, formData={}) {
const RollType = this.options.rollType ?? CONFIG.Dice.BasicRoll;
_buildRolls(config, formData) {
const RollType = this.rollType;
return config.rolls?.map((config, index) => RollType.create(this._buildConfig(config, formData, index))) ?? [];
}

Expand All @@ -120,7 +190,7 @@ export default class RollConfigurationDialog extends FormApplication {
/**
* Prepare individual configuration object before building a roll.
* @param {BasicRollConfiguration} config Roll configuration data.
* @param {object} formData Any data entered into the rolling prompt.
* @param {FormDataExtended} [formData] Any data entered into the rolling prompt.
* @param {number} index Index of the roll within all rolls being prepared.
* @returns {BasicRollConfiguration}
* @protected
Expand All @@ -134,14 +204,14 @@ export default class RollConfigurationDialog extends FormApplication {
* @memberof hookEvents
* @param {RollConfigurationDialog} app Roll configuration dialog.
* @param {BasicRollConfiguration} config Roll configuration data.
* @param {object} formData Any data entered into the rolling prompt.
* @param {FormDataExtended} [formData] Any data entered into the rolling prompt.
* @param {number} index Index of the roll within all rolls being prepared.
*/
Hooks.callAll("dnd5e.buildRollConfig", this, config, formData, index);

if ( formData.situational && (config.situational !== false) ) {
if ( formData?.get("situational") && (config.situational !== false) ) {
config.parts.push("@situational");
config.data.situational = formData.situational;
config.data.situational = formData.get("situational");
}

return config;
Expand All @@ -164,37 +234,42 @@ export default class RollConfigurationDialog extends FormApplication {
* Rebuild rolls based on an updated config and re-render the dialog.
*/
rebuild() {
this._onSubmit(new Event("change"));
this._onChangeForm(this.options.form, new Event("change"));
}

/* -------------------------------------------- */
/* Event Handling */
/* -------------------------------------------- */

/** @inheritDoc */
async close(options={}) {
this.options.resolve?.([]);
return super.close(options);
/**
* Handle submission of the dialog using the form buttons.
* @param {Event|SubmitEvent} event The form submission event.
* @param {HTMLFormElement} form The submitted form.
* @param {FormDataExtended} formData Data from the dialog.
* @private
*/
static async #handleFormSubmission(event, form, formData) {
const rolls = this._finalizeRolls(event.submitter?.dataset?.action);
await this.close({ dnd5e: { rolls } });
}

/* -------------------------------------------- */
/* <><><><> <><><><> <><><><> <><><><> */

/** @inheritDoc */
_updateObject(event, formData) {
if ( formData.rollMode ) this.message.rollMode = formData.rollMode;

// If one of the buttons was clicked, finalize the roll, resolve the promise, and close
if ( event.type === "submit" ) {
const rolls = this._finalizeRolls(event.submitter?.dataset.action);
this.options.resolve?.(rolls);
this.close({ submit: false, force: true });
}
_onChangeForm(formConfig, event) {
super._onChangeForm(formConfig, event);

// Otherwise, re-build the in-memory rolls and re-render the dialog
else {
this.rolls = this._buildRolls(foundry.utils.deepClone(this.config), formData);
this.render();
}
const formData = new FormDataExtended(this.element);
if (formData.has("rollMode")) this.message.rollMode = formData.get("rollMode");
this.rolls = this._buildRolls(foundry.utils.deepClone(this.config), formData);
this.render({ parts: ["formulas"] });
}

/* <><><><> <><><><> <><><><> <><><><> */

/** @override */
_onClose(options = {}) {
this.options.resolve?.(options[game.system.id]?.rolls ?? []);
}

/* -------------------------------------------- */
Expand Down
5 changes: 5 additions & 0 deletions templates/dice/roll-buttons.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<nav class="dialog-buttons">
{{#each buttons}}
<button type="submit" data-action="{{ @key }}">{{{ icon }}} {{{ label }}}</button>
{{/each}}
</nav>
24 changes: 0 additions & 24 deletions templates/dice/roll-configuration-dialog.hbs

This file was deleted.

9 changes: 9 additions & 0 deletions templates/dice/roll-configuration.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<fieldset>
<legend>{{ localize "DND5E.RollConfiguration.Configuration" }}</legend>
<div class="form-group">
<label>{{ localize "DND5E.RollMode" }}</label>
<select name="rollMode">
{{ selectOptions rollModesOptions selected=rollMode localize=true }}
</select>
</div>
</fieldset>
14 changes: 14 additions & 0 deletions templates/dice/roll-formulas.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<fieldset class="roll-formulas">
<legend>{{ localize "DND5E.RollConfiguration.Rolls" }}</legend>
{{#each rolls}}
<div class="form-group">
<label>{{ localize "DND5E.Formula" }} {{#if type}}({{ type }}){{/if}}</label>
<input type="text" name="formula" value="{{ formula }}" disabled>
</div>
{{/each}}
<div class="form-group">
<label>{{ localize "DND5E.RollSituationalBonus" }}</label>
<input type="text" name="situational" value="{{ situational }}"
placeholder="{{ localize 'DND5E.RollExample' }}">
</div>
</fieldset>

0 comments on commit f1eaea5

Please sign in to comment.