Support drag resort on features in actor setting sheets (#2023)

This commit is contained in:
Carlos Fernandez 2026-06-20 13:55:51 -04:00 committed by GitHub
parent 6f1da42735
commit 6b80a6243c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 52 additions and 88 deletions

View file

@ -97,32 +97,4 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del }); await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del });
} }
async _onDragStart(event) {
const featureItem = event.currentTarget.closest('.feature-item');
if (featureItem) {
const feature = this.actor.items.get(featureItem.id);
const featureData = { type: 'Item', uuid: feature.uuid, fromInternal: true };
event.dataTransfer.setData('text/plain', JSON.stringify(featureData));
event.dataTransfer.setDragImage(featureItem.querySelector('img'), 60, 0);
}
}
async _onDrop(event) {
event.stopPropagation();
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
const item = await fromUuid(data.uuid);
if (item?.type === 'feature') {
if (data.fromInternal && item.parent?.uuid === this.actor.uuid) {
return;
}
const itemData = item.toObject();
delete itemData._id;
await this.actor.createEmbeddedDocuments('Item', [itemData]);
}
}
} }

View file

@ -15,7 +15,7 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
dragDrop: [ dragDrop: [
{ dragSelector: null, dropSelector: '.category-container' }, { dragSelector: null, dropSelector: '.category-container' },
{ dragSelector: null, dropSelector: '.tab.features' }, { dragSelector: null, dropSelector: '.tab.features' },
{ dragSelector: '.feature-item', dropSelector: null } { dragSelector: '.feature-item, .inventory-item[data-type="adversary"]', dropSelector: null }
] ]
}; };
@ -110,33 +110,30 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
} }
async _onDragStart(event) { async _onDragStart(event) {
const featureItem = event.currentTarget.closest('.feature-item'); const element = event.currentTarget.closest('.inventory-item[data-type=adversary]');
if (element) {
if (featureItem) { const adversaryData = { type: 'Actor', uuid: element.dataset.itemUuid };
const feature = this.actor.items.get(featureItem.id); event.dataTransfer.setData('text/plain', JSON.stringify(adversaryData));
const featureData = { type: 'Item', uuid: feature.uuid, fromInternal: true }; event.dataTransfer.setDragImage(element, 60, 0);
event.dataTransfer.setData('text/plain', JSON.stringify(featureData)); } else {
event.dataTransfer.setDragImage(featureItem.querySelector('img'), 60, 0); return super._onDragStart(event);
} }
} }
async _onDrop(event) { async _onDrop(event) {
event.stopPropagation(); event.stopPropagation();
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
const item = await fromUuid(data.uuid); const doc = await fromUuid(data.uuid);
if (data.fromInternal && item?.parent?.uuid === this.actor.uuid) return; if (doc?.type === 'adversary' && event.target.closest('.category-container')) {
if (item.type === 'adversary' && event.target.closest('.category-container')) {
const target = event.target.closest('.category-container'); const target = event.target.closest('.category-container');
const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`; const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`;
const current = foundry.utils.getProperty(this.actor, path).map(x => x.uuid); const current = foundry.utils.getProperty(this.actor, path).map(x => x.uuid);
await this.actor.update({ if (!current.includes(doc.uuid)) {
[path]: [...current, item.uuid] await this.actor.update({ [path]: [...current, doc.uuid] });
});
this.render();
} else if (item.type === 'feature' && event.target.closest('.tab.features')) {
await this.actor.createEmbeddedDocuments('Item', [item]);
this.render();
} }
return;
}
return super._onDrop(event);
} }
} }

View file

@ -52,34 +52,4 @@ export default class DHNPCSettings extends DHBaseActorSettings {
return context; return context;
} }
/* -------------------------------------------- */
async _onDragStart(event) {
const featureItem = event.currentTarget.closest('.feature-item');
if (featureItem) {
const feature = this.actor.items.get(featureItem.id);
const featureData = { type: 'Item', uuid: feature.uuid, fromInternal: true };
event.dataTransfer.setData('text/plain', JSON.stringify(featureData));
event.dataTransfer.setDragImage(featureItem.querySelector('img'), 60, 0);
}
}
async _onDrop(event) {
event.stopPropagation();
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
const item = await fromUuid(data.uuid);
if (item?.type === 'feature') {
if (data.fromInternal && item.parent?.uuid === this.actor.uuid) {
return;
}
const itemData = item.toObject();
delete itemData._id;
await this.actor.createEmbeddedDocuments('Item', [itemData]);
}
}
} }

View file

@ -78,7 +78,6 @@ export default class DhpEnvironment extends DHBaseActorSheet {
switch (partId) { switch (partId) {
case 'header': case 'header':
await this._prepareHeaderContext(context, options); await this._prepareHeaderContext(context, options);
break; break;
case 'features': case 'features':
await this._prepareFeaturesContext(context, options); await this._prepareFeaturesContext(context, options);

View file

@ -1,13 +1,15 @@
import DHApplicationMixin from './application-mixin.mjs'; import DHApplicationMixin from './application-mixin.mjs';
const { DocumentSheetV2 } = foundry.applications.api; const { ActorSheetV2 } = foundry.applications.sheets;
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ /**
* @typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction
*/
/** /**
* Base settings sheet for Daggerheart actors. * Base settings sheet for Daggerheart actors.
* @extends {DHApplicationMixin<DocumentSheetV2>} * @extends {DHApplicationMixin<ActorSheetV2>}
*/ */
export default class DHBaseActorSettings extends DHApplicationMixin(DocumentSheetV2) { export default class DHBaseActorSettings extends DHApplicationMixin(ActorSheetV2) {
/**@inheritdoc */ /**@inheritdoc */
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
classes: ['dialog'], classes: ['dialog'],
@ -34,7 +36,7 @@ export default class DHBaseActorSettings extends DHApplicationMixin(DocumentShee
return options; return options;
} }
/**@returns {foundry.documents.Actor} */ /** @returns {foundry.documents.Actor} */
get actor() { get actor() {
return this.document; return this.document;
} }
@ -73,4 +75,29 @@ export default class DHBaseActorSettings extends DHApplicationMixin(DocumentShee
return context; return context;
} }
async _onDragStart(event) {
const featureItemEl = event.currentTarget.closest('.feature-item');
const feature = this.actor.items.get(featureItemEl?.dataset.itemId);
if (feature && event.target.closest('.tab.features')) {
const featureData = { ...feature.toDragData(), fromInternal: true };
event.dataTransfer.setData('text/plain', JSON.stringify(featureData));
event.dataTransfer.setDragImage(featureItemEl.querySelector('img'), 60, 0);
}
}
async _onDrop(event) {
event.stopPropagation();
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
const item = await fromUuid(data.uuid);
if (item?.type === 'feature') {
if (data.fromInternal && item.parent?.uuid === this.actor.uuid) {
return super._onDrop(event);
}
const itemData = item.toObject();
delete itemData._id;
await this.actor.createEmbeddedDocuments('Item', [itemData]);
}
}
} }

View file

@ -381,8 +381,7 @@ export default function DHApplicationMixin(Base) {
* @protected * @protected
*/ */
_onDrop(event) { _onDrop(event) {
// Fallback to super, but note that config sheets don't have this option // Potentially handle subclasses that dont descend from actor/item sheet.
// We still need this to avoid setting apps having issues
event.stopPropagation(); event.stopPropagation();
return super._onDrop?.(event); return super._onDrop?.(event);
} }

View file

@ -10,7 +10,7 @@
<legend>{{localize tabs.features.label}}</legend> <legend>{{localize tabs.features.label}}</legend>
<ul class="feature-list"> <ul class="feature-list">
{{#each @root.features as |feature|}} {{#each @root.features as |feature|}}
<li class="feature-item" id="{{feature.id}}" draggable="true"> <li class="feature-item" data-item-id="{{feature.id}}" draggable="true">
<img src="{{feature.img}}" alt=""> <img src="{{feature.img}}" alt="">
<div class="label"> <div class="label">
<span>{{feature.name}}</span> <span>{{feature.name}}</span>

View file

@ -10,7 +10,7 @@
<legend>{{localize tabs.features.label}}</legend> <legend>{{localize tabs.features.label}}</legend>
<ul class="feature-list"> <ul class="feature-list">
{{#each @root.features as |feature|}} {{#each @root.features as |feature|}}
<li class="feature-item" id="{{feature.id}}"> <li class="feature-item" data-item-id="{{feature.id}}">
<img src="{{feature.img}}" alt=""> <img src="{{feature.img}}" alt="">
<div class="label"> <div class="label">
<span>{{feature.name}}</span> <span>{{feature.name}}</span>

View file

@ -10,7 +10,7 @@
<legend>{{localize tabs.features.label}}</legend> <legend>{{localize tabs.features.label}}</legend>
<ul class="feature-list"> <ul class="feature-list">
{{#each @root.features as |feature|}} {{#each @root.features as |feature|}}
<li class="feature-item" id="{{feature.id}}" draggable="true"> <li class="feature-item" data-item-id="{{feature.id}}" draggable="true">
<img src="{{feature.img}}" alt=""> <img src="{{feature.img}}" alt="">
<div class="label"> <div class="label">
<span>{{feature.name}}</span> <span>{{feature.name}}</span>