From b3e9c3fd9f13da00b3ff7e683806de0c01936c05 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 20 Mar 2026 19:46:30 -0400 Subject: [PATCH] Use ActiveEffect Config for settings as well (#1741) --- .../applications/sheets-configs/_module.mjs | 1 - .../sheets-configs/action-settings-config.mjs | 2 +- .../sheets-configs/activeEffectConfig.mjs | 26 ++ .../setting-active-effect-config.mjs | 223 ------------------ .../sheets-configs/setting-feature-config.mjs | 2 +- 5 files changed, 28 insertions(+), 226 deletions(-) delete mode 100644 module/applications/sheets-configs/setting-active-effect-config.mjs diff --git a/module/applications/sheets-configs/_module.mjs b/module/applications/sheets-configs/_module.mjs index d3fb3c39..4b83a042 100644 --- a/module/applications/sheets-configs/_module.mjs +++ b/module/applications/sheets-configs/_module.mjs @@ -3,7 +3,6 @@ export { default as ActionSettingsConfig } from './action-settings-config.mjs'; export { default as CharacterSettings } from './character-settings.mjs'; export { default as AdversarySettings } from './adversary-settings.mjs'; export { default as CompanionSettings } from './companion-settings.mjs'; -export { default as SettingActiveEffectConfig } from './setting-active-effect-config.mjs'; export { default as SettingFeatureConfig } from './setting-feature-config.mjs'; export { default as EnvironmentSettings } from './environment-settings.mjs'; export { default as ActiveEffectConfig } from './activeEffectConfig.mjs'; diff --git a/module/applications/sheets-configs/action-settings-config.mjs b/module/applications/sheets-configs/action-settings-config.mjs index 91b85802..9cb866bc 100644 --- a/module/applications/sheets-configs/action-settings-config.mjs +++ b/module/applications/sheets-configs/action-settings-config.mjs @@ -55,7 +55,7 @@ export default class DHActionSettingsConfig extends DHActionBaseConfig { static async editEffect(event) { const id = event.target.closest('[data-effect-id]')?.dataset?.effectId; - const updatedEffect = await game.system.api.applications.sheetConfigs.SettingActiveEffectConfig.configure( + const updatedEffect = await game.system.api.applications.sheetConfigs.ActiveEffectConfig.configureSetting( this.getEffectDetails(id) ); if (!updatedEffect) return; diff --git a/module/applications/sheets-configs/activeEffectConfig.mjs b/module/applications/sheets-configs/activeEffectConfig.mjs index 2bd7d5b9..6e3fb1bd 100644 --- a/module/applications/sheets-configs/activeEffectConfig.mjs +++ b/module/applications/sheets-configs/activeEffectConfig.mjs @@ -247,4 +247,30 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac return submitData; } + + /** @inheritDoc */ + _processSubmitData(event, form, submitData, options) { + if (this.options.isSetting) { + // Settings should update source instead + this.document.updateSource(submitData); + this.render(); + } else { + return super._processSubmitData(event, form, submitData, options); + } + } + + /** Creates an active effect config for a setting */ + static async configureSetting(effect, options = {}) { + const document = new CONFIG.ActiveEffect.documentClass({ ...foundry.utils.duplicate(effect), _id: effect.id }); + return new Promise(resolve => { + const app = new this({ document, ...options, isSetting: true }); + app.addEventListener('close', () => { + const newEffect = app.document.toObject(true); + newEffect.id = newEffect._id; + delete newEffect._id; + resolve(newEffect); + }, { once: true }); + app.render({ force: true }); + }); + } } diff --git a/module/applications/sheets-configs/setting-active-effect-config.mjs b/module/applications/sheets-configs/setting-active-effect-config.mjs deleted file mode 100644 index 12ac90d1..00000000 --- a/module/applications/sheets-configs/setting-active-effect-config.mjs +++ /dev/null @@ -1,223 +0,0 @@ -import autocomplete from 'autocompleter'; - -const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; - -export default class SettingActiveEffectConfig extends HandlebarsApplicationMixin(ApplicationV2) { - constructor(effect) { - super({}); - - this.effect = foundry.utils.deepClone(effect); - this.changeChoices = game.system.api.applications.sheetConfigs.ActiveEffectConfig.getChangeChoices(); - } - - static DEFAULT_OPTIONS = { - classes: ['daggerheart', 'sheet', 'dh-style', 'active-effect-config', 'standard-form'], - tag: 'form', - position: { - width: 560 - }, - form: { - submitOnChange: false, - closeOnSubmit: false, - handler: SettingActiveEffectConfig.#onSubmit - }, - actions: { - editImage: SettingActiveEffectConfig.#editImage, - addChange: SettingActiveEffectConfig.#addChange, - deleteChange: SettingActiveEffectConfig.#deleteChange - } - }; - - static PARTS = { - header: { template: 'systems/daggerheart/templates/sheets/activeEffect/header.hbs' }, - tabs: { template: 'templates/generic/tab-navigation.hbs' }, - details: { template: 'systems/daggerheart/templates/sheets/activeEffect/details.hbs', scrollable: [''] }, - settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' }, - changes: { - template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs', - scrollable: ['ol[data-changes]'] - }, - footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' } - }; - - static TABS = { - sheet: { - tabs: [ - { id: 'details', icon: 'fa-solid fa-book' }, - { id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' }, - { id: 'changes', icon: 'fa-solid fa-gears' } - ], - initial: 'details', - labelPrefix: 'EFFECT.TABS' - } - }; - - /**@inheritdoc */ - async _onFirstRender(context, options) { - await super._onFirstRender(context, options); - } - - async _prepareContext(_options) { - const context = await super._prepareContext(_options); - context.source = this.effect; - context.fields = game.system.api.documents.DhActiveEffect.schema.fields; - context.systemFields = game.system.api.data.activeEffects.BaseEffect._schema.fields; - - return context; - } - - _attachPartListeners(partId, htmlElement, options) { - super._attachPartListeners(partId, htmlElement, options); - const changeChoices = this.changeChoices; - - htmlElement.querySelectorAll('.effect-change-input').forEach(element => { - autocomplete({ - input: element, - fetch: function (text, update) { - if (!text) { - update(changeChoices); - } else { - text = text.toLowerCase(); - var suggestions = changeChoices.filter(n => n.label.toLowerCase().includes(text)); - update(suggestions); - } - }, - render: function (item, search) { - const label = game.i18n.localize(item.label); - const matchIndex = label.toLowerCase().indexOf(search); - - const beforeText = label.slice(0, matchIndex); - const matchText = label.slice(matchIndex, matchIndex + search.length); - const after = label.slice(matchIndex + search.length, label.length); - - const element = document.createElement('li'); - element.innerHTML = - `${beforeText}${matchText ? `${matchText}` : ''}${after}`.replaceAll( - ' ', - ' ' - ); - if (item.hint) { - element.dataset.tooltip = game.i18n.localize(item.hint); - } - - return element; - }, - renderGroup: function (label) { - const itemElement = document.createElement('div'); - itemElement.textContent = game.i18n.localize(label); - return itemElement; - }, - onSelect: function (item) { - element.value = `system.${item.value}`; - }, - click: e => e.fetch(), - customize: function (_input, _inputRect, container) { - container.style.zIndex = foundry.applications.api.ApplicationV2._maxZ; - }, - minLength: 0 - }); - }); - } - - async _preparePartContext(partId, context) { - if (partId in context.tabs) context.tab = context.tabs[partId]; - switch (partId) { - case 'details': - context.statuses = CONFIG.statusEffects.map(s => ({ value: s.id, label: game.i18n.localize(s.name) })); - context.isActorEffect = false; - context.isItemEffect = true; - const useGeneric = game.settings.get( - CONFIG.DH.id, - CONFIG.DH.SETTINGS.gameSettings.appearance - ).showGenericStatusEffects; - if (!useGeneric) { - context.statuses = [ - ...context.statuses, - Object.values(CONFIG.DH.GENERAL.conditions).map(status => ({ - value: status.id, - label: game.i18n.localize(status.name) - })) - ]; - } - break; - case 'changes': - context.modes = Object.entries(CONST.ACTIVE_EFFECT_MODES).reduce((modes, [key, value]) => { - modes[value] = game.i18n.localize(`EFFECT.MODE_${key}`); - return modes; - }, {}); - - context.priorities = ActiveEffectConfig.DEFAULT_PRIORITIES; - break; - } - - return context; - } - - static async #onSubmit(_event, _form, formData) { - this.data = foundry.utils.expandObject(formData.object); - this.close(); - } - - /** - * Edit a Document image. - * @this {DocumentSheetV2} - * @type {ApplicationClickAction} - */ - static async #editImage(_event, target) { - if (target.nodeName !== 'IMG') { - throw new Error('The editImage action is available only for IMG elements.'); - } - - const attr = target.dataset.edit; - const current = foundry.utils.getProperty(this.effect, attr); - const fp = new FilePicker.implementation({ - current, - type: 'image', - callback: path => (target.src = path), - position: { - top: this.position.top + 40, - left: this.position.left + 10 - } - }); - - await fp.browse(); - } - - /** - * Add a new change to the effect's changes array. - * @this {ActiveEffectConfig} - * @type {ApplicationClickAction} - */ - static async #addChange() { - const { changes, ...rest } = foundry.utils.expandObject(new FormDataExtended(this.form).object); - const updatedChanges = Object.values(changes ?? {}); - updatedChanges.push({}); - - this.effect = { ...rest, changes: updatedChanges }; - this.render(); - } - - /** - * Delete a change from the effect's changes array. - * @this {ActiveEffectConfig} - * @type {ApplicationClickAction} - */ - static async #deleteChange(event) { - const submitData = foundry.utils.expandObject(new FormDataExtended(this.form).object); - const updatedChanges = Object.values(submitData.changes); - const row = event.target.closest('li'); - const index = Number(row.dataset.index) || 0; - updatedChanges.splice(index, 1); - - this.effect = { ...submitData, changes: updatedChanges }; - this.render(); - } - - static async configure(effect, options = {}) { - return new Promise(resolve => { - const app = new this(effect, options); - app.addEventListener('close', () => resolve(app.data), { once: true }); - app.render({ force: true }); - }); - } -} diff --git a/module/applications/sheets-configs/setting-feature-config.mjs b/module/applications/sheets-configs/setting-feature-config.mjs index fb790f7f..f90bb52f 100644 --- a/module/applications/sheets-configs/setting-feature-config.mjs +++ b/module/applications/sheets-configs/setting-feature-config.mjs @@ -147,7 +147,7 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App const effectIndex = this.move.effects.findIndex(x => x.id === id); const effect = this.move.effects[effectIndex]; const updatedEffect = - await game.system.api.applications.sheetConfigs.SettingActiveEffectConfig.configure(effect); + await game.system.api.applications.sheetConfigs.ActiveEffectConfig.configureSetting(effect); if (!updatedEffect) return; await this.updateMove({