From 99d0eab5bdaea6a431807def45353f55c777c6c9 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Sun, 21 Dec 2025 11:37:00 -0500 Subject: [PATCH] [PR] [Feature] Support drag dropping currencies to actor sheets (#1431) * Support drag dropping currencies to actor sheets * Adjust sizing and spacing * Restore ItemTransferDialog subclass for module use * Bigger is better --- module/applications/dialogs/itemTransfer.mjs | 62 ++++++++----------- module/applications/sheets/api/base-actor.mjs | 50 ++++++++++++--- styles/less/dialog/item-transfer/sheet.less | 43 ++++++++----- templates/dialogs/item-transfer.hbs | 31 +++++++--- .../sheets/actors/character/inventory.hbs | 6 +- templates/sheets/actors/party/inventory.hbs | 6 +- 6 files changed, 128 insertions(+), 70 deletions(-) diff --git a/module/applications/dialogs/itemTransfer.mjs b/module/applications/dialogs/itemTransfer.mjs index aba43d27..ad3cf103 100644 --- a/module/applications/dialogs/itemTransfer.mjs +++ b/module/applications/dialogs/itemTransfer.mjs @@ -1,69 +1,61 @@ const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; export default class ItemTransferDialog extends HandlebarsApplicationMixin(ApplicationV2) { - constructor(item) { + constructor(data) { super({}); - - this.item = item; - this.quantity = item.system.quantity; + this.data = data; } get title() { - return this.item.name; + return this.data.title; } static DEFAULT_OPTIONS = { tag: 'form', classes: ['daggerheart', 'dh-style', 'dialog', 'item-transfer'], - position: { width: 300, height: 'auto' }, + position: { width: 400, height: 'auto' }, window: { icon: 'fa-solid fa-hand-holding-hand' }, actions: { finish: ItemTransferDialog.#finish - }, - form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false } + } }; static PARTS = { - main: { template: 'systems/daggerheart/templates/dialogs/item-transfer.hbs' } + main: { template: 'systems/daggerheart/templates/dialogs/item-transfer.hbs', root: true } }; - _attachPartListeners(partId, htmlElement, options) { - super._attachPartListeners(partId, htmlElement, options); - - htmlElement.querySelector('.number-display').addEventListener('change', event => { - this.quantity = isNaN(event.target.value) ? this.quantity : Number(event.target.value); - this.render(); - }); - } - async _prepareContext(_options) { const context = await super._prepareContext(_options); - context.item = this.item; - context.quantity = this.quantity; - - return context; - } - - static async updateData(_event, _element, formData) { - const { quantity } = foundry.utils.expandObject(formData.object); - this.quantity = quantity; - this.render(); + return foundry.utils.mergeObject(context, this.data); } static async #finish() { - this.close({ submitted: true }); + this.selected = this.form.elements.quantity.valueAsNumber || null; + this.close(); } - close(options = {}) { - if (!options.submitted) this.quantity = null; + static #determineTransferOptions({ originActor, targetActor, item, currency }) { + originActor ??= item?.actor; + const homebrewKey = CONFIG.DH.SETTINGS.gameSettings.Homebrew; + const currencySetting = game.settings.get(CONFIG.DH.id, homebrewKey).currency?.[currency] ?? null; - super.close(); + return { + originActor, + targetActor, + itemImage: item?.img, + currencyIcon: currencySetting?.icon, + max: item?.system.quantity ?? originActor.system.gold[currency] ?? 0, + title: item?.name ?? currencySetting?.label + }; } - static async configure(item) { + static async configure(options) { return new Promise(resolve => { - const app = new this(item); - app.addEventListener('close', () => resolve(app.quantity), { once: true }); + const data = this.#determineTransferOptions(options); + if (data.max <= 1) return resolve(data.max); + + const app = new this(data); + app.addEventListener('close', () => resolve(app.selected), { once: true }); app.render({ force: true }); }); } diff --git a/module/applications/sheets/api/base-actor.mjs b/module/applications/sheets/api/base-actor.mjs index adb0d39a..5d054949 100644 --- a/module/applications/sheets/api/base-actor.mjs +++ b/module/applications/sheets/api/base-actor.mjs @@ -34,7 +34,10 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { } } ], - dragDrop: [{ dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null }] + dragDrop: [ + { dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null }, + { dragSelector: ".currency[data-currency] .drag-handle", dropSelector: null } + ] }; /* -------------------------------------------- */ @@ -254,14 +257,35 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { /* Application Drag/Drop */ /* -------------------------------------------- */ + async _onDrop(event) { + event.stopPropagation(); + const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); + if (data.type === 'Currency' && ['character', 'party'].includes(this.document.type)) { + const originActor = await foundry.utils.fromUuid(data.originActor); + if (!originActor || originActor.uuid === this.document.uuid) return; + const currency = data.currency; + const quantity = await game.system.api.applications.dialogs.ItemTransferDialog.configure({ + originActor, + targetActor: this.document, + currency + }); + if (quantity) { + originActor.update({ [`system.gold.${currency}`]: Math.max(0, originActor.system.gold[currency] - quantity) }); + this.document.update({ [`system.gold.${currency}`]: this.document.system.gold[currency] + quantity }); + } + return; + } + + return super._onDrop(event); + } + async _onDropItem(event, item) { const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); - const physicalActorTypes = ['character', 'party']; const originActor = item.actor; if ( item.actor?.uuid === this.document.uuid || !originActor || - !physicalActorTypes.includes(this.document.type) + !['character', 'party'].includes(this.document.type) ) { return super._onDropItem(event, item); } @@ -270,10 +294,10 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { if (item.system.metadata.isInventoryItem) { if (item.system.metadata.isQuantifiable) { const actorItem = originActor.items.get(data.originId); - const quantityTransfered = - actorItem.system.quantity === 1 - ? 1 - : await game.system.api.applications.dialogs.ItemTransferDialog.configure(item); + const quantityTransfered = await game.system.api.applications.dialogs.ItemTransferDialog.configure({ + item, + targetActor: this.document + }); if (quantityTransfered) { if (quantityTransfered === actorItem.system.quantity) { @@ -314,6 +338,16 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { * @param {DragEvent} event - The drag event */ async _onDragStart(event) { + // Handle drag/dropping currencies + const currencyEl = event.currentTarget.closest(".currency[data-currency]"); + if (currencyEl) { + const currency = currencyEl.dataset.currency; + const data = { type: 'Currency', currency, originActor: this.document.uuid }; + event.dataTransfer.setData('text/plain', JSON.stringify(data)); + return; + } + + // Handle drag/dropping attacks const attackItem = event.currentTarget.closest('.inventory-item[data-type="attack"]'); if (attackItem) { const attackData = { @@ -340,4 +374,4 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { super._onDragStart(event); } -} +} \ No newline at end of file diff --git a/styles/less/dialog/item-transfer/sheet.less b/styles/less/dialog/item-transfer/sheet.less index dd55fdb2..7807e479 100644 --- a/styles/less/dialog/item-transfer/sheet.less +++ b/styles/less/dialog/item-transfer/sheet.less @@ -1,20 +1,35 @@ .daggerheart.dh-style.dialog.item-transfer { - .item-transfer-container { - display: grid; - grid-template-columns: 1fr 42px; - gap: 4px; + .summary { + display: flex; + align-items: center; + justify-content: center; + gap: 3rem; + + .actor-img { + border-radius: 50%; + width: 5rem; + height: 5rem; + object-fit: cover; + object-position: top center; + } - .number-display { - text-align: center; + .granted-item { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: var(--font-size-32); + + img { + width: 2rem; + height: 2rem; + object-fit: contain; + border-radius: 3px; + } } } - - .item-sheet-footer { - padding-top: 8px; - display: flex; - - button { - flex: 1; - } + label { + flex: 0; + margin-right: 0.5rem; } } diff --git a/templates/dialogs/item-transfer.hbs b/templates/dialogs/item-transfer.hbs index 2b9fa19c..0e7df3dc 100644 --- a/templates/dialogs/item-transfer.hbs +++ b/templates/dialogs/item-transfer.hbs @@ -1,9 +1,26 @@ -
-
- - +
+
+ +
+ {{#if itemImage}} + + {{else}} + + {{/if}} + +
+
-
- -
+
+ +
+ +
+
+
+
+
\ No newline at end of file diff --git a/templates/sheets/actors/character/inventory.hbs b/templates/sheets/actors/character/inventory.hbs index f9dee872..a05fed35 100644 --- a/templates/sheets/actors/character/inventory.hbs +++ b/templates/sheets/actors/character/inventory.hbs @@ -14,10 +14,10 @@ {{#if this.inventory.hasCurrency}}
- {{#each this.inventory.currencies as | currency |}} + {{#each this.inventory.currencies as |currency key|}} {{#if currency.enabled}} -
- +
+ {{localize currency.label}} diff --git a/templates/sheets/actors/party/inventory.hbs b/templates/sheets/actors/party/inventory.hbs index 09f3ba62..92371b8d 100644 --- a/templates/sheets/actors/party/inventory.hbs +++ b/templates/sheets/actors/party/inventory.hbs @@ -17,10 +17,10 @@ {{#if inventory.hasCurrency}}
- {{#each this.inventory.currencies as | currency |}} + {{#each this.inventory.currencies as |currency key|}} {{#if currency.enabled}} -
- +
+ {{localize currency.label}}