174 character context menu (#189)

* Set up the contextMenues properly
* Fixed double item generation on drop
This commit is contained in:
WBHarry 2025-06-28 10:05:53 +02:00 committed by GitHub
parent 30ab18d683
commit b4fff7b9e6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 197 additions and 62 deletions

View file

@ -1099,6 +1099,17 @@
"biography": "Biography", "biography": "Biography",
"effects": "Effects" "effects": "Effects"
}, },
"ContextMenu": {
"UseItem": "Use",
"Equip": "Equip",
"Unequip": "Unequip",
"Edit": "Edit",
"Delete": "Delete",
"ToLoadout": "Send to Loadout",
"ToVault": "Send to Vault",
"Consume": "Consume Item",
"SendToChat": "Send To Chat"
},
"Armor": { "Armor": {
"Title": "Active Armor" "Title": "Active Armor"
}, },

View file

@ -43,10 +43,11 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
makeDeathMove: this.makeDeathMove, makeDeathMove: this.makeDeathMove,
itemQuantityDecrease: (_, button) => this.setItemQuantity(button, -1), itemQuantityDecrease: (_, button) => this.setItemQuantity(button, -1),
itemQuantityIncrease: (_, button) => this.setItemQuantity(button, 1), itemQuantityIncrease: (_, button) => this.setItemQuantity(button, 1),
useAbility: this.useAbility, toChat: this.toChat,
useAdvancementCard: this.useAdvancementCard, useAdvancementCard: this.useAdvancementCard,
useAdvancementAbility: this.useAdvancementAbility, useAdvancementAbility: this.useAdvancementAbility,
toggleEquipItem: this.toggleEquipItem, toggleEquipItem: this.toggleEquipItem,
toggleVault: this.toggleVault,
levelManagement: this.levelManagement, levelManagement: this.levelManagement,
editImage: this._onEditImage editImage: this._onEditImage
}, },
@ -58,11 +59,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
submitOnChange: true, submitOnChange: true,
closeOnSubmit: false closeOnSubmit: false
}, },
dragDrop: [ dragDrop: []
{ dragSelector: null, dropSelector: '.weapon-section' },
{ dragSelector: null, dropSelector: '.armor-section' },
{ dragSelector: '.item-list .item', dropSelector: null }
]
}; };
static PARTS = { static PARTS = {
@ -214,6 +211,109 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
return { primary: primaryTabs, secondary: secondaryTabs }; return { primary: primaryTabs, secondary: secondaryTabs };
} }
async _onFirstRender(context, options) {
await super._onFirstRender(context, options);
this._createContextMenues();
}
_createContextMenues() {
const allOptions = {
useItem: {
name: 'DAGGERHEART.Sheets.PC.ContextMenu.UseItem',
icon: '<i class="fa-solid fa-burst"></i>',
callback: this.constructor.useItem.bind(this)
},
equip: {
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Equip',
icon: '<i class="fa-solid fa-hands"></i>',
condition: el => {
const item = foundry.utils.fromUuidSync(el.dataset.uuid);
return !item.system.equipped;
},
callback: this.constructor.toggleEquipItem.bind(this)
},
unequip: {
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Unequip',
icon: '<i class="fa-solid fa-hands"></i>',
condition: el => {
const item = foundry.utils.fromUuidSync(el.dataset.uuid);
return item.system.equipped;
},
callback: this.constructor.toggleEquipItem.bind(this)
},
edit: {
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Edit',
icon: '<i class="fa-solid fa-pen-to-square"></i>',
callback: this.constructor.viewObject.bind(this)
},
delete: {
name: 'DAGGERHEART.Sheets.PC.ContextMenu.Delete',
icon: '<i class="fa-solid fa-trash"></i>',
callback: this.constructor.deleteItem.bind(this)
},
sendToLoadout: {
name: 'DAGGERHEART.Sheets.PC.ContextMenu.ToLoadout',
icon: '<i class="fa-solid fa-arrow-up"></i>',
condition: el => {
const item = foundry.utils.fromUuidSync(el.dataset.uuid);
return item.system.inVault;
},
callback: this.constructor.toggleVault.bind(this)
},
sendToVault: {
name: 'DAGGERHEART.Sheets.PC.ContextMenu.ToVault',
icon: '<i class="fa-solid fa-arrow-down"></i>',
condition: el => {
const item = foundry.utils.fromUuidSync(el.dataset.uuid);
return !item.system.inVault;
},
callback: this.constructor.toggleVault.bind(this)
},
sendToChat: {
name: 'DAGGERHEART.Sheets.PC.ContextMenu.SendToChat',
icon: '<i class="fa-regular fa-message"></i>',
callback: this.constructor.toChat.bind(this)
}
};
const getMenuOptions = type => () => {
let menuItems = ['class', 'subclass'].includes(type) ? [] : [allOptions.useItem];
switch (type) {
case 'weapon':
case 'armor':
menuItems.push(...[allOptions.equip, allOptions.unequip]);
break;
case 'domainCard':
menuItems.push(...[allOptions.sendToLoadout, allOptions.sendToVault]);
break;
}
menuItems.push(...[allOptions.sendToChat, allOptions.edit, allOptions.delete]);
return menuItems;
};
const menuConfigs = [
'armor',
'weapon',
'miscellaneous',
'consumable',
'domainCard',
'miscellaneous',
'ancestry',
'community',
'class',
'subclass'
];
menuConfigs.forEach(type => {
this._createContextMenu(getMenuOptions(type), `.${type}-context-menu`, {
eventName: 'click',
parentClassHooks: false,
fixed: true
});
});
}
_attachPartListeners(partId, htmlElement, options) { _attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options); super._attachPartListeners(partId, htmlElement, options);
@ -544,20 +644,14 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
(await game.packs.get('daggerheart.communities'))?.render(true); (await game.packs.get('daggerheart.communities'))?.render(true);
} }
static useItem(event) { static useItem(element, button) {
const uuid = event.target.closest('[data-item-id]').dataset.itemId, const id = (button ?? element).closest('a').id,
item = this.document.items.find(i => i.uuid === uuid); item = this.document.items.get(id);
item.use(event); item.use({});
} }
static async viewObject(_, button) { static async viewObject(element, button) {
const object = await fromUuid(button.dataset.value); const object = await fromUuid((button ?? element).dataset.uuid);
if (!object) return;
const tab = button.dataset.tab;
if (tab && object.sheet._tabs) object.sheet._tabs[0].active = tab;
if (object.sheet.editMode) object.sheet.editMode = false;
object.sheet.render(true); object.sheet.render(true);
} }
@ -620,8 +714,8 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
this.render(); this.render();
} }
static async deleteItem(_, button) { static async deleteItem(element, button) {
const item = await fromUuid($(button).closest('[data-item-id]')[0].dataset.itemId); const item = await fromUuid((button ?? element).closest('a').dataset.uuid);
await item.delete(); await item.delete();
} }
@ -655,35 +749,29 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
cls.create(msg.toObject()); cls.create(msg.toObject());
} }
static async useAbility(_, button) { static async toChat(element, button) {
const item = await fromUuid(button.dataset.feature); if (button?.dataset?.type === 'experience') {
const type = button.dataset.type; const experience = this.document.system.experiences[button.dataset.uuid];
const cls = getDocumentClass('ChatMessage');
const systemData = {
name: game.i18n.localize('DAGGERHEART.General.Experience.Single'),
description: `${experience.description} ${experience.total < 0 ? experience.total : `+${experience.total}`}`
};
const msg = new cls({
type: 'abilityUse',
user: game.user.id,
system: systemData,
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/chat/ability-use.hbs',
systemData
)
});
const cls = getDocumentClass('ChatMessage'); cls.create(msg.toObject());
const systemData = { } else {
title: const item = await fromUuid((button ?? element).dataset.uuid);
type === 'ancestry' item.toChat(this.document.id);
? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.AncestryTitle') }
: type === 'community'
? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.CommunityTitle')
: game.i18n.localize('DAGGERHEART.Chat.FoundationCard.SubclassFeatureTitle'),
origin: this.document.id,
img: item.img,
name: item.name,
description: item.system.description,
actions: []
};
const msg = new cls({
type: 'abilityUse',
user: game.user.id,
system: systemData,
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/chat/ability-use.hbs',
systemData
)
});
cls.create(msg.toObject());
} }
static async useAdvancementCard(_, button) { static async useAdvancementCard(_, button) {
@ -738,8 +826,9 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
cls.create(msg.toObject()); cls.create(msg.toObject());
} }
static async toggleEquipItem(_, button) { static async toggleEquipItem(element, button) {
const item = this.document.items.get(button.id); const id = (button ?? element).closest('a').id;
const item = this.document.items.get(id);
if (item.system.equipped) { if (item.system.equipped) {
await item.update({ 'system.equipped': false }); await item.update({ 'system.equipped': false });
return; return;
@ -763,6 +852,12 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
this.render(); this.render();
} }
static async toggleVault(element, button) {
const id = (button ?? element).closest('a').id;
const item = this.document.items.get(id);
await item.update({ 'system.inVault': !item.system.inVault });
}
async _onDragStart(_, event) { async _onDragStart(_, event) {
super._onDragStart(event); super._onDragStart(event);
} }

View file

@ -141,4 +141,32 @@ export default class DhpItem extends Item {
// Display Item Card in chat // Display Item Card in chat
return response; return response;
} }
async toChat(origin) {
const cls = getDocumentClass('ChatMessage');
const systemData = {
title:
this.type === 'ancestry'
? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.AncestryTitle')
: this.type === 'community'
? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.CommunityTitle')
: game.i18n.localize('DAGGERHEART.Chat.FoundationCard.SubclassFeatureTitle'),
origin: origin,
img: this.img,
name: this.name,
description: this.system.description,
actions: []
};
const msg = new cls({
type: 'abilityUse',
user: game.user.id,
system: systemData,
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/chat/ability-use.hbs',
systemData
)
});
cls.create(msg.toObject());
}
} }

View file

@ -283,6 +283,7 @@
h2 { h2 {
width: 100%; width: 100%;
text-align: center; text-align: center;
margin: 0;
} }
} }

View file

@ -1598,6 +1598,7 @@
.daggerheart.chat.domain-card .domain-card-title h2 { .daggerheart.chat.domain-card .domain-card-title h2 {
width: 100%; width: 100%;
text-align: center; text-align: center;
margin: 0;
} }
.daggerheart.chat.domain-card .ability-card-footer { .daggerheart.chat.domain-card .ability-card-footer {
display: flex; display: flex;

View file

@ -1,6 +1,5 @@
<div class="daggerheart chat domain-card"> <div class="daggerheart chat domain-card">
<div class="domain-card-title"> <div class="domain-card-title">
<div>{{title}}</div>
<h2>{{name}}</h2> <h2>{{name}}</h2>
</div> </div>
<img src="{{img}}" /> <img src="{{img}}" />

View file

@ -23,6 +23,6 @@
<div class="items-section"> <div class="items-section">
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.Sheets.PC.Tabs.Loadout') type='domainCard' isGlassy=true cardView='list'}} {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.Sheets.PC.Tabs.Loadout') type='domainCard' isGlassy=true cardView='list'}}
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.Sheets.PC.Tabs.Vault') type='domainCard' isGlassy=true cardView='list'}} {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.Sheets.PC.Tabs.Vault') type='domainCard' isVault=true isGlassy=true cardView='list'}}
</div> </div>
</section> </section>

View file

@ -111,7 +111,7 @@
</div> </div>
<input name="{{concat "system.experiences." id ".description"}}" data-experience={{id}} value="{{experience.description}}" type="text" /> <input name="{{concat "system.experiences." id ".description"}}" data-experience={{id}} value="{{experience.description}}" type="text" />
<div class="controls"> <div class="controls">
<a><i class="fa-regular fa-message"></i></a> <a data-action="toChat" data-type="experience" data-uuid="{{id}}"><i class="fa-regular fa-message"></i></a>
</div> </div>
</div> </div>
{{/each}} {{/each}}

View file

@ -1,5 +1,5 @@
<li class="card-item"> <li class="card-item">
<img src="{{item.img}}" data-action="viewObject" data-value="{{item.uuid}}" class="card-img" /> <img src="{{item.img}}" data-action="viewObject" data-uuid="{{item.uuid}}" class="card-img" />
<div class="card-label"> <div class="card-label">
<div class="controls"> <div class="controls">
{{#if (eq type 'weapon')}} {{#if (eq type 'weapon')}}
@ -24,8 +24,8 @@
{{/unless}} {{/unless}}
{{/if}} {{/if}}
<a data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToChat'}}"><i class="fa-regular fa-message"></i></a> <a data-action="toChat" data-uuid="{{item.uuid}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToChat'}}"><i class="fa-regular fa-message"></i></a>
<a data-tooltip="{{localize 'DAGGERHEART.Tooltip.moreOptions'}}"><i class="fa-solid fa-ellipsis-vertical"></i></a> <a class="{{concat type "-context-menu"}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.moreOptions'}}"><i class="fa-solid fa-ellipsis-vertical"></i></a>
</div> </div>
<div class="card-name">{{item.name}}</div> <div class="card-name">{{item.name}}</div>
</div> </div>

View file

@ -3,7 +3,7 @@
<ul class="items-list"> <ul class="items-list">
{{#each document.items as |item|}} {{#each document.items as |item|}}
{{#if (eq item.type ../type)}} {{#if (eq item.type ../type)}}
{{#unless (or (eq ../type 'ancestry') (eq item.type 'class') (eq item.type 'subclass'))}} {{#unless (or (eq ../type 'ancestry') (eq item.type 'class') (eq item.type 'subclass') (and (eq ../type 'domainCard') (or (and item.system.inVault (not ../isVault)) (and (not item.system.inVault) ../isVault))))}}
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=item type=../type}} {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=item type=../type}}
{{/unless}} {{/unless}}
{{/if}} {{/if}}

View file

@ -1,5 +1,5 @@
<li class="inventory-item"> <li class="inventory-item">
<img src="{{item.img}}" data-action="viewObject" data-value="{{item.uuid}}" class="item-img" /> <img src="{{item.img}}" data-action="viewObject" data-uuid="{{item.uuid}}" class="item-img" />
<div class="item-label"> <div class="item-label">
<div class="item-name">{{item.name}}</div> <div class="item-name">{{item.name}}</div>
{{#if (eq type 'weapon')}} {{#if (eq type 'weapon')}}
@ -114,17 +114,17 @@
{{/if}} {{/if}}
{{#if (eq type 'domainCard')}} {{#if (eq type 'domainCard')}}
{{#unless item.system.inVault}} {{#unless item.system.inVault}}
<a data-action="sendToVault" data-domain="{{card.uuid}}" id="{{item.id}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToVault'}}"> <a data-action="toggleVault" id="{{item.id}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToVault'}}">
<i class="fa-solid fa-arrow-down"></i> <i class="fa-solid fa-arrow-down"></i>
</a> </a>
{{else}} {{else}}
<a data-action="sendToLoadout" data-domain="{{card.uuid}}" id="{{item.id}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToLoadout'}}"> <a data-action="toggleVault" id="{{item.id}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToLoadout'}}">
<i class="fa-solid fa-arrow-up"></i> <i class="fa-solid fa-arrow-up"></i>
</a> </a>
{{/unless}} {{/unless}}
{{/if}} {{/if}}
<a data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToChat'}}"><i class="fa-regular fa-message"></i></a> <a data-action="toChat" data-uuid="{{item.uuid}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.sendToChat'}}"><i class="fa-regular fa-message"></i></a>
<a data-tooltip="{{localize 'DAGGERHEART.Tooltip.moreOptions'}}"><i class="fa-solid fa-ellipsis-vertical"></i></a> <a class="{{concat type '-context-menu'}}" data-type="{{type}}" data-uuid="{{item.uuid}}" id="{{item.id}}" data-tooltip="{{localize 'DAGGERHEART.Tooltip.moreOptions'}}"><i class="fa-solid fa-ellipsis-vertical"></i></a>
</div> </div>
</li> </li>