FEAT: add enritch on inventory-item description

FEAT: add extensible behavior on inventory-item-content
FEAT: add fade effect on item-img to roll-itmg
This commit is contained in:
Joaquin Pereyra 2025-07-17 19:22:15 -03:00
parent aee8753f63
commit 5ac67a8714
6 changed files with 274 additions and 134 deletions

View file

@ -84,6 +84,7 @@ export default function DHApplicationMixin(Base) {
useItem: DHSheetV2.#useItem, useItem: DHSheetV2.#useItem,
useAction: DHSheetV2.#useAction, useAction: DHSheetV2.#useAction,
toggleEffect: DHSheetV2.#toggleEffect, toggleEffect: DHSheetV2.#toggleEffect,
toggleExtended: DHSheetV2.#toggleExtended,
}, },
contextMenus: [{ contextMenus: [{
handler: DHSheetV2.#getEffectContextOptions, handler: DHSheetV2.#getEffectContextOptions,
@ -124,6 +125,27 @@ export default function DHApplicationMixin(Base) {
this._createTagifyElements(this.options.tagifyConfigs); this._createTagifyElements(this.options.tagifyConfigs);
} }
/* -------------------------------------------- */
/* Sync Parts */
/* -------------------------------------------- */
/**@inheritdoc */
_syncPartState(partId, newElement, priorElement, state) {
super._syncPartState(partId, newElement, priorElement, state);
for (const el of priorElement.querySelectorAll(".extensible.extended")) {
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);
}
}
}
/* -------------------------------------------- */ /* -------------------------------------------- */
/* Tags */ /* Tags */
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -354,6 +376,37 @@ export default function DHApplicationMixin(Base) {
return context; return context;
} }
/* -------------------------------------------- */
/* Prepare Descriptions */
/* -------------------------------------------- */
/**
* 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<void>}
*/
async #prepareInventoryDescription(extensibleElement, descriptionElement) {
const parent = extensibleElement.closest('[data-item-uuid], [data-action-id]');
const { actionId, itemUuid } = parent?.dataset || {};
if (!actionId && !itemUuid) return;
const doc = itemUuid
? getDocFromElement(extensibleElement)
: this.document.system.attack?.id === actionId
? this.document.system.attack
: this.document.system.actions?.find(a => a.id === actionId);
if (!doc) return;
const description = 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
});
}
/* -------------------------------------------- */ /* -------------------------------------------- */
/* Application Clicks Actions */ /* Application Clicks Actions */
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -526,6 +579,20 @@ export default function DHApplicationMixin(Base) {
return CONFIG.ux.ContextMenu.triggerContextMenu(event); return CONFIG.ux.ContextMenu.triggerContextMenu(event);
} }
/**
* Toggle the 'extended' class on the .extensible element inside inventory-item-content
* @type {ApplicationClickAction}
* @this {DHSheetV2}
*/
static async #toggleExtended(_, target) {
const container = target.closest('.inventory-item');
const extensible = container?.querySelector('.extensible');
const t = extensible?.classList.toggle('extended');
const descriptionElement = extensible?.querySelector('.invetory-description');
if (t && !!descriptionElement) this.#prepareInventoryDescription(extensible, descriptionElement);
}
} }
return DHSheetV2; return DHSheetV2;

View file

@ -12,87 +12,151 @@
} }
} }
.appTheme({}, {
.inventory-item-header .img-portait .roll-img {
filter: invert(1);
}
});
.application.daggerheart.dh-style { .application.daggerheart.dh-style {
.inventory-item { .inventory-item {
display: grid;
grid-template-columns: 40px 1fr 60px;
gap: 10px;
width: 100%; width: 100%;
.item-img { &:hover {
height: 40px; .inventory-item-header .img-portait {
width: 40px; .roll-img {
border-radius: 3px; opacity: 1;
border: none;
cursor: pointer;
object-fit: cover;
&.actor-img {
border-radius: 50%;
}
}
.item-label-wrapper {
display: grid;
grid-template-columns: 1fr 60px;
}
.item-label {
font-family: @font-body;
align-self: center;
&.fullWidth {
grid-column: span 2;
}
.item-name {
font-size: 14px;
}
.item-tags,
.item-labels {
display: flex;
gap: 10px;
.tag {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 3px 5px;
font-size: 12px;
background: light-dark(@dark-15, @beige-15);
border: 1px solid light-dark(@dark, @beige);
border-radius: 3px;
} }
.item-img {
.label { opacity: 0;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
font-size: 12px;
gap: 4px;
} }
} }
} }
.controls { .inventory-item-header {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: end; gap: 10px;
gap: 8px; cursor: pointer;
a { .img-portait {
text-align: center; flex: 0 0 40px;
height: 40px;
cursor: pointer;
position: relative;
&.unequipped { .item-img,
opacity: 0.4; .roll-img {
position: absolute;
transition: opacity 300ms ease-in;
}
.roll-img {
opacity: 0;
}
}
.item-label {
flex: 1;
font-family: @font-body;
align-self: center;
.item-name {
font-size: 14px;
}
.item-tags,
.item-labels {
display: flex;
gap: 10px;
.tag,
.label {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
font-size: 12px;
}
.tag {
padding: 3px 5px;
background: light-dark(@dark-15, @beige-15);
border: 1px solid light-dark(@dark, @beige);
border-radius: 3px;
}
.label {
gap: 4px;
}
}
}
.item-resource {
flex: 0 0 60px;
display: flex;
align-items: center;
gap: 4px;
}
.controls {
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: end;
gap: 8px;
}
}
.inventory-item-content {
&.extensible {
max-height: 0;
overflow: hidden;
transition: max-height 2s linear;
&.extended {
max-height: 1000px; // Reasonable large value
//maybe need the redo this.... idk, i'm just a little wittle girl, atte: JP.
}
}
.item-resources {
display: flex;
align-items: center;
gap: 4px;
.item-dice-resource {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 26px;
label,
i {
position: absolute;
z-index: 2;
cursor: pointer;
}
label {
color: light-dark(white, black);
filter: drop-shadow(0 0 1px light-dark(@dark-blue, @golden));
font-size: 18px;
}
img {
filter: brightness(0) saturate(100%) invert(97%) sepia(7%) saturate(580%) hue-rotate(332deg)
brightness(96%) contrast(95%);
}
i {
text-shadow: 0 0 3px white;
filter: drop-shadow(0 1px white);
color: black;
font-size: 26px;
}
} }
} }
} }
} }
.card-item { .card-item {
position: relative; position: relative;
height: 120px; height: 120px;
@ -212,6 +276,7 @@
input { input {
flex: 1; flex: 1;
text-align: center;
} }
.item-dice-resource { .item-dice-resource {

View file

@ -85,6 +85,7 @@
hideDescription=true hideDescription=true
hideTooltip=true hideTooltip=true
hideResources=true hideResources=true
noExtensible=true
}} }}
</ul> </ul>
</div> </div>

View file

@ -89,6 +89,7 @@
hideTags=true hideTags=true
hideDescription=true hideDescription=true
hideResources=true hideResources=true
noExtensible=true
}} }}
{{/if}} {{/if}}
{{/each}} {{/each}}
@ -108,6 +109,7 @@
hideTags=true hideTags=true
hideDescription=true hideDescription=true
hideResources=true hideResources=true
noExtensible=true
}} }}
{{/each}} {{/each}}

View file

@ -1,4 +1,4 @@
<li class="card-item" data-item-id="{{item.id}}"> <li class="card-item" data-item-uuid="{{item.uuid}}">
<img src="{{item.img}}" data-action="useItem" class="card-img" /> <img src="{{item.img}}" data-action="useItem" class="card-img" />
<div class="card-label"> <div class="card-label">
<div <div

View file

@ -5,6 +5,7 @@ Parameters:
- type {string} : The type of items in the list - type {string} : The type of items in the list
- isActor {boolean} : Passed through to inventory-item partials. - isActor {boolean} : Passed through to inventory-item partials.
- categoryAdversary {string} : Category adversary id. - categoryAdversary {string} : Category adversary id.
- noExtensible {boolean} : If true, the inventory-item-content would be collapsable/extendible else it always be showed
- hideLabels {boolean} : If true, hide label-tags else show label-tags. - hideLabels {boolean} : If true, hide label-tags else show label-tags.
- hideTags {boolean} : If true, hide simple-tags else show simple-tags. - hideTags {boolean} : If true, hide simple-tags else show simple-tags.
- hideTooltip {boolean} : If true, disables the tooltip on the item image. - hideTooltip {boolean} : If true, disables the tooltip on the item image.
@ -16,12 +17,15 @@ Parameters:
<li class="inventory-item" {{#if (eq type 'action' )}}data-action-id="{{item.id}}" {{/if}} <li class="inventory-item" {{#if (eq type 'action' )}}data-action-id="{{item.id}}" {{/if}}
data-item-uuid="{{item.uuid}}" data-type="{{type}}" draggable="true"> data-item-uuid="{{item.uuid}}" data-type="{{type}}" draggable="true">
{{!-- Image --}} <div class="inventory-item-header" {{#unless noExtensible}}data-action="toggleExtended" {{/unless}}>
<img src="{{item.img}}" class="item-img {{#if isActor}}actor-img{{/if}}" {{!-- Image --}}
data-action='{{ifThen (hasProperty item "use") "useItem" (ifThen (hasProperty item "toChat") "toChat" "editDoc") }}' <div class="img-portait">
{{#unless hideTooltip}}data-tooltip="#item#{{item.uuid}}" {{/unless}} /> <img src="{{item.img}}" class="item-img {{#if isActor}}actor-img{{/if}}"
data-action='{{ifThen (hasProperty item "use") "useItem" (ifThen (hasProperty item "toChat") "toChat" "editDoc") }}'
{{#unless hideTooltip}}data-tooltip="#item#{{item.uuid}}" {{/unless}} />
<img class="roll-img" src="systems/daggerheart/assets/icons/dice/default/d20.svg" alt="">
</div>
<div class="item-label-wrapper">
{{!-- Name & Tags --}} {{!-- Name & Tags --}}
<div class="item-label {{#if hideResources}}fullWidth{{/if}}"> <div class="item-label {{#if hideResources}}fullWidth{{/if}}">
@ -155,6 +159,8 @@ Parameters:
{{/if}} {{/if}}
{{!-- Action Block End --}} {{!-- Action Block End --}}
</div> </div>
{{!-- Simple Resource --}}
{{#if (and (not hideResources) (eq item.system.resource.type 'simple'))}} {{#if (and (not hideResources) (eq item.system.resource.type 'simple'))}}
{{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}}
{{/if}} {{/if}}
@ -163,72 +169,71 @@ Parameters:
<input type="number" class="inventory-item-quantity" value="{{item.system.quantity}}" step="1" /> <input type="number" class="inventory-item-quantity" value="{{item.system.quantity}}" step="1" />
</div> </div>
{{/if}} {{/if}}
</div>
{{!-- Controls --}} {{!-- Controls --}}
{{#unless hideControls}} {{#unless hideControls}}
<div class="controls"> <div class="controls">
{{#if isActor}} {{#if isActor}}
<a data-action="editDoc" data-tooltip="DAGGERHEART.UI.Tooltip.openActorWorld"> <a data-action="editDoc" data-tooltip="DAGGERHEART.UI.Tooltip.openActorWorld">
<i class="fa-solid fa-globe"></i> <i class="fa-solid fa-globe"></i>
</a> </a>
{{#if (eq type 'adversary')}} {{#if (eq type 'adversary')}}
<a data-action='deleteAdversary' data-category="{{categoryAdversary}}" data-tooltip="CONTROLS.CommonDelete"> <a data-action='deleteAdversary' data-category="{{categoryAdversary}}" data-tooltip="CONTROLS.CommonDelete">
<i class='fas fa-trash'></i> <i class='fas fa-trash'></i>
</a> </a>
{{/if}}
{{else}}
{{#if (eq type 'weapon')}}
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem"
data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.system.equipped 'unequip' 'equip' }}">
<i class="fa-solid fa-hands"></i>
</a>
{{else if (eq type 'armor')}}
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem"
data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.system.equipped 'unequip' 'equip' }}">
<i class="fa-solid fa-shield"></i>
</a>
{{else if (eq type 'domainCard')}}
<a data-action="toggleVault"
data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.system.inVault 'sendToLoadout' 'sendToVault' }}">
<i class="fa-solid {{ifThen item.system.inVault 'fa-arrow-up' 'fa-arrow-down'}}"></i>
</a>
{{else if (eq type 'effect')}}
<a data-action="toggleEffect"
data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.disabled 'enableEffect' 'disableEffect' }}">
<i class="{{ifThen item.disabled 'fa-regular fa-lightbulb' 'fa-solid fa-lightbulb'}}"></i>
</a>
{{/if}}
{{#if (hasProperty item "toChat")}}
<a data-action="toChat" data-tooltip="DAGGERHEART.UI.Tooltip.sendToChat">
<i class="fa-regular fa-message"></i>
</a>
{{/if}}
<a data-action="triggerContextMenu" data-tooltip="DAGGERHEART.UI.Tooltip.moreOptions">
<i class="fa-solid fa-ellipsis-vertical"></i>
</a>
{{/if}}
</div>
{{/unless}}
</div>
<div class="inventory-item-content{{#unless noExtensible}} extensible{{/unless}}">
{{!-- Description --}}
{{#unless hideDescription}}
<div class="invetory-description"></div>
{{/unless}}
{{!-- Dice Resource --}}
{{#if (and (not hideResources) (eq item.system.resource.type 'diceValue'))}}
{{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}}
{{/if}} {{/if}}
{{else}} {{!-- Actions Buttons --}}
{{#if (eq type 'weapon')}} {{#if (and showActions (eq item.type 'feature'))}}
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem" <div class="item-buttons">
data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.system.equipped 'unequip' 'equip' }}"> {{#each item.system.actions as | action |}}
<i class="fa-solid fa-hands"></i> <button type="button" data-action="useAction" data-action-id="{{action.id}}">
</a> {{action.name}}
{{else if (eq type 'armor')}} </button>
<a class="{{#unless item.system.equipped}}unequipped{{/unless}}" data-action="toggleEquipItem" {{/each}}
data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.system.equipped 'unequip' 'equip' }}"> </div>
<i class="fa-solid fa-shield"></i>
</a>
{{else if (eq type 'domainCard')}}
<a data-action="toggleVault"
data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.system.inVault 'sendToLoadout' 'sendToVault' }}">
<i class="fa-solid {{ifThen item.system.inVault 'fa-arrow-up' 'fa-arrow-down'}}"></i>
</a>
{{else if (eq type 'effect')}}
<a data-action="toggleEffect"
data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.disabled 'enableEffect' 'disableEffect' }}">
<i class="{{ifThen item.disabled 'fa-regular fa-lightbulb' 'fa-solid fa-lightbulb'}}"></i>
</a>
{{/if}}
{{#if (hasProperty item "toChat")}}
<a data-action="toChat" data-tooltip="DAGGERHEART.UI.Tooltip.sendToChat">
<i class="fa-regular fa-message"></i>
</a>
{{/if}}
<a data-action="triggerContextMenu" data-tooltip="DAGGERHEART.UI.Tooltip.moreOptions">
<i class="fa-solid fa-ellipsis-vertical"></i>
</a>
{{/if}} {{/if}}
</div> </div>
{{else}}
<span></span>
{{/unless}}
{{!-- Description --}}
{{#unless hideDescription}}
<div class="item-description">
{{{item.system.description}}}
</div>
{{/unless}}
{{#if (and (not hideResources) (eq item.system.resource.type 'diceValue'))}}
{{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}}
{{/if}}
{{#if (and showActions (eq item.type 'feature'))}}
<div class="item-buttons">
{{#each item.system.actions as | action |}}
<button type="button" data-action="useAction" data-action-id="{{action.id}}">
{{action.name}}
</button>
{{/each}}
</div>
{{/if}}
</li> </li>