From 1a5311536a8d82f0d2d2f3b72498d02802900c59 Mon Sep 17 00:00:00 2001 From: Murilo Brito <91566541+moliloo@users.noreply.github.com> Date: Wed, 13 Aug 2025 09:40:11 -0300 Subject: [PATCH] [Feature] Descriptions enhancements (#887) * add style to hover items and add start setting to features be expanded by default * REFACTOR: now prepare description onRender and simply the other methods * add setting to extend description from items and add molilo contacts in system.json --------- Co-authored-by: Joaquin Pereyra --- lang/en.json | 8 +- .../sheets/api/application-mixin.mjs | 118 +++++++++++++----- module/data/item/domainCard.mjs | 1 + module/data/settings/Appearance.mjs | 16 +++ styles/less/global/inventory-item.less | 41 ++++++ system.json | 5 +- templates/settings/appearance-settings.hbs | 16 ++- .../global/partials/inventory-item-V2.hbs | 2 +- 8 files changed, 169 insertions(+), 38 deletions(-) diff --git a/lang/en.json b/lang/en.json index f473d98d..c971e084 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1939,6 +1939,7 @@ "itemResource": "Item Resource", "label": "Label", "level": "Level", + "levelShort": "Lv", "levelUp": "Level Up", "loadout": "Loadout", "max": "Max", @@ -2086,7 +2087,12 @@ "FIELDS": { "displayFear": { "label": "Fear Display" }, "dualityColorScheme": { "label": "Chat Style" }, - "showGenericStatusEffects": { "label": "Show Foundry Status Effects" } + "showGenericStatusEffects": { "label": "Show Foundry Status Effects" }, + "expandedTitle": "Auto-expand Descriptions", + "extendCharacterDescriptions": { "label": "Characters" }, + "extendAdversaryDescriptions": { "label": "Adversaries" }, + "extendEnvironmentDescriptions": { "label": "Environments" }, + "extendItemDescriptions": { "label": "Items" } }, "fearDisplay": { "token": "Tokens", diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 18a5ac91..83dc1581 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -2,6 +2,23 @@ const { HandlebarsApplicationMixin } = foundry.applications.api; import { getDocFromElement, getDocFromElementSync, tagifyElement } from '../../../helpers/utils.mjs'; import { ItemBrowser } from '../../ui/itemBrowser.mjs'; +const typeSettingsMap = { + character: 'extendCharacterDescriptions', + adversary: 'extendAdversaryDescriptions', + environment: 'extendEnvironmentDescriptions', + ancestry: 'extendItemDescriptions', + community: 'extendItemDescriptions', + class: 'extendItemDescriptions', + subclass: 'extendItemDescriptions', + feature: 'extendItemDescriptions', + domainCard: 'extendItemDescriptions', + loot: 'extendItemDescriptions', + consumable: 'extendItemDescriptions', + weapon: 'extendItemDescriptions', + armor: 'extendItemDescriptions', + beastform: 'extendItemDescriptions' +}; + /** * @typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ @@ -137,6 +154,8 @@ export default function DHApplicationMixin(Base) { docs.filter(doc => doc).forEach(doc => (doc.apps[this.id] = this)); if (!!this.options.contextMenus.length) this._createContextMenus(); + + this.#autoExtendDescriptions(context); } /** @inheritDoc */ @@ -149,6 +168,7 @@ export default function DHApplicationMixin(Base) { async _onRender(context, options) { await super._onRender(context, options); this._createTagifyElements(this.options.tagifyConfigs); + await this.#prepareInventoryDescription(context); } /* -------------------------------------------- */ @@ -162,13 +182,7 @@ export default function DHApplicationMixin(Base) { const { actionId, itemUuid } = el.parentElement.dataset; const selector = `${actionId ? `[data-action-id="${actionId}"]` : `[data-item-uuid="${itemUuid}"]`} .extensible`; const newExtensible = newElement.querySelector(selector); - - if (!newExtensible) continue; - newExtensible.classList.add('extended'); - const descriptionElement = newExtensible.querySelector('.invetory-description'); - if (descriptionElement) { - this.#prepareInventoryDescription(newExtensible, descriptionElement); - } + newExtensible?.classList.add('extended'); } } @@ -395,6 +409,7 @@ export default function DHApplicationMixin(Base) { context.source = this.document; context.fields = this.document.schema.fields; context.systemFields = this.document.system.schema.fields; + context.settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); return context; } @@ -404,32 +419,69 @@ export default function DHApplicationMixin(Base) { /** * Prepares and enriches an inventory item or action description for display. - * @param {HTMLElement} extensibleElement - The parent element containing the description. - * @param {HTMLElement} descriptionElement - The element where the enriched description will be rendered. * @returns {Promise} */ - async #prepareInventoryDescription(extensibleElement, descriptionElement) { - const parent = extensibleElement.closest('[data-item-uuid], [data-action-id]'); - const { actionId, itemUuid } = parent?.dataset || {}; - if (!actionId && !itemUuid) return; + async #prepareInventoryDescription(context) { + // Get all inventory item elements with a data-item-uuid attribute + const inventoryItems = this.element.querySelectorAll('.inventory-item[data-item-uuid]'); + for (const el of inventoryItems) { + // Get the doc uuid from the element + const { itemUuid } = el?.dataset || {}; + if (!itemUuid) continue; - const doc = itemUuid - ? await getDocFromElement(extensibleElement) - : this.document.system.attack?.id === actionId - ? this.document.system.attack - : this.document.system.actions?.get(actionId); - if (!doc) return; + //get doc by uuid + const doc = await fromUuid(itemUuid); - const description = game.i18n.localize(doc.system?.description ?? doc.description); - const isAction = !!actionId; - descriptionElement.innerHTML = await foundry.applications.ux.TextEditor.implementation.enrichHTML( - description, - { - relativeTo: isAction ? doc.parent : doc, - rollData: doc.getRollData?.(), - secrets: isAction ? doc.parent.isOwner : doc.isOwner + //get inventory-item description element + const descriptionElement = el.querySelector('.invetory-description'); + if (!doc || !descriptionElement) continue; + + // localize the description (idk if it's still necessary) + const description = game.i18n.localize(doc.system?.description ?? doc.description); + + // Enrich the description and attach it; + const isAction = doc.documentName === 'Action'; + descriptionElement.innerHTML = await foundry.applications.ux.TextEditor.implementation.enrichHTML( + description, + { + relativeTo: isAction ? doc.parent : doc, + rollData: doc.getRollData?.(), + secrets: isAction ? doc.parent.isOwner : doc.isOwner + } + ); + } + } + + /* -------------------------------------------- */ + /* Extend Descriptions by Settings */ + /* -------------------------------------------- */ + + /** + * Extend inventory description when enabled in settings. + * @returns {Promise} + */ + async #autoExtendDescriptions(context) { + const inventoryItems = this.element.querySelectorAll('.inventory-item[data-item-uuid]'); + for (const el of inventoryItems) { + // Get the doc uuid from the element + const { itemUuid } = el?.dataset || {}; + if (!itemUuid) continue; + + //get doc by uuid + const doc = await fromUuid(itemUuid); + + //check the type of the document + const actorType = + doc?.type === 'adversary' && context.document?.type === 'environment' + ? typeSettingsMap[doc?.type] + : doc.actor?.type; + + // If the actor type is defined and the setting is enabled, extend the description + if (typeSettingsMap[actorType]) { + const settingKey = typeSettingsMap[actorType]; + if (context.settings[settingKey]) this.#activeExtended(el); } - ); + } } /* -------------------------------------------- */ @@ -437,8 +489,6 @@ export default function DHApplicationMixin(Base) { /* -------------------------------------------- */ static async #addNewItem(event, target) { - const { type } = target.dataset; - const createChoice = await foundry.applications.api.DialogV2.wait({ classes: ['dh-style', 'two-big-buttons'], buttons: [ @@ -606,10 +656,12 @@ export default function DHApplicationMixin(Base) { static async #toggleExtended(_, target) { const container = target.closest('.inventory-item'); const extensible = container?.querySelector('.extensible'); - const t = extensible?.classList.toggle('extended'); + extensible?.classList.toggle('extended'); + } - const descriptionElement = extensible?.querySelector('.invetory-description'); - if (t && !!descriptionElement) await this.#prepareInventoryDescription(extensible, descriptionElement); + async #activeExtended(element) { + const extensible = element?.querySelector('.extensible'); + extensible?.classList.add('extended'); } } diff --git a/module/data/item/domainCard.mjs b/module/data/item/domainCard.mjs index 4cf986db..5c471ca1 100644 --- a/module/data/item/domainCard.mjs +++ b/module/data/item/domainCard.mjs @@ -77,6 +77,7 @@ export default class DHDomainCard extends BaseDataItem { const tags = [ game.i18n.localize(`DAGGERHEART.CONFIG.DomainCardTypes.${this.type}`), this.domainLabel, + `${game.i18n.localize('DAGGERHEART.GENERAL.levelShort')}: ${this.level}`, `${game.i18n.localize('DAGGERHEART.ITEMS.DomainCard.recallCost')}: ${this.recallCost}` ]; diff --git a/module/data/settings/Appearance.mjs b/module/data/settings/Appearance.mjs index e215f956..ac980346 100644 --- a/module/data/settings/Appearance.mjs +++ b/module/data/settings/Appearance.mjs @@ -55,6 +55,22 @@ export default class DhAppearance extends foundry.abstract.DataModel { showGenericStatusEffects: new fields.BooleanField({ initial: true, label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.showGenericStatusEffects.label' + }), + extendCharacterDescriptions: new fields.BooleanField({ + initial: false, + label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendCharacterDescriptions.label' + }), + extendAdversaryDescriptions: new fields.BooleanField({ + initial: false, + label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendAdversaryDescriptions.label' + }), + extendEnvironmentDescriptions: new fields.BooleanField({ + initial: false, + label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendEnvironmentDescriptions.label' + }), + extendItemDescriptions: new fields.BooleanField({ + initial: false, + label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendItemDescriptions.label' }) }; } diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index e79a88c7..c4239d27 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -35,6 +35,37 @@ } } } + + &:hover { + .inventory-item-header .item-label .item-name .expanded-icon { + margin-left: 10px; + display: inline-block; + } + &:has(.inventory-item-content.extensible) { + .inventory-item-header, + .inventory-item-content { + background: light-dark(@dark-blue-40, @golden-40); + } + } + &:has(.inventory-item-content.extended) { + .inventory-item-header .item-label .item-name .expanded-icon { + display: none; + } + } + } + + &:has(.inventory-item-content.extensible) { + .inventory-item-header { + border-radius: 5px 5px 0 0; + } + .inventory-item-content { + border-radius: 0 0 5px 5px; + } + } + + &:not(:has(.inventory-item-content.extensible)) .inventory-item-header { + border-radius: 5px; + } } .inventory-item-header { @@ -42,6 +73,7 @@ align-items: center; gap: 10px; cursor: pointer; + border-radius: 3px; .img-portait { flex: 0 0 40px; @@ -75,6 +107,10 @@ .item-name { font-size: 14px; + + .expanded-icon { + display: none; + } } .item-tags, @@ -118,6 +154,11 @@ justify-content: end; gap: 8px; + a { + width: 15px; + text-align: center; + } + .unequipped { opacity: 0.5; } diff --git a/system.json b/system.json index 1be9ada5..66a4cfdb 100644 --- a/system.json +++ b/system.json @@ -28,7 +28,10 @@ "name": "jacobwojoski" }, { - "name": "moliloo" + "name": "moliloo", + "url": "https://github.com/moliloo", + "email": "molilofl@gmail.com", + "discord": "molilo" }, { "name": "Mysteryusy" diff --git a/templates/settings/appearance-settings.hbs b/templates/settings/appearance-settings.hbs index ab30eefd..d4d41d62 100644 --- a/templates/settings/appearance-settings.hbs +++ b/templates/settings/appearance-settings.hbs @@ -2,8 +2,20 @@

{{localize 'DAGGERHEART.SETTINGS.Menu.appearance.name'}}

- {{formGroup settingFields.schema.fields.displayFear value=settingFields._source.displayFear localize=true}} - {{formGroup settingFields.schema.fields.showGenericStatusEffects value=settingFields._source.showGenericStatusEffects localize=true}} + +
+ {{localize 'DAGGERHEART.GENERAL.fear'}} + {{formGroup settingFields.schema.fields.displayFear value=settingFields._source.displayFear localize=true}} + {{formGroup settingFields.schema.fields.showGenericStatusEffects value=settingFields._source.showGenericStatusEffects localize=true}} +
+ +
+ {{localize 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandedTitle'}} + {{formGroup settingFields.schema.fields.extendCharacterDescriptions value=settingFields._source.extendCharacterDescriptions localize=true}} + {{formGroup settingFields.schema.fields.extendAdversaryDescriptions value=settingFields._source.extendAdversaryDescriptions localize=true}} + {{formGroup settingFields.schema.fields.extendEnvironmentDescriptions value=settingFields._source.extendEnvironmentDescriptions localize=true}} + {{formGroup settingFields.schema.fields.extendItemDescriptions value=settingFields._source.extendItemDescriptions localize=true}} +
{{#if showDiceSoNice}}
diff --git a/templates/sheets/global/partials/inventory-item-V2.hbs b/templates/sheets/global/partials/inventory-item-V2.hbs index 80aa6ec3..c5dca4f9 100644 --- a/templates/sheets/global/partials/inventory-item-V2.hbs +++ b/templates/sheets/global/partials/inventory-item-V2.hbs @@ -30,7 +30,7 @@ Parameters:
{{!-- Item Name --}} -
{{localize item.name}}
+
{{localize item.name}} {{#unless noExtensible}}{{/unless}}
{{!-- Tags Start --}} {{#with item}}