Skip to content

Commit

Permalink
[#4935] Show advancement context menu when locked
Browse files Browse the repository at this point in the history
Moves the advancement context menu configuration from the item
sheet into the `Advancement` class to mimic the activity setup.
In the process adds a "View Advancement" button for when the item
is not editable so the advancement sheet can still be viewed.
  • Loading branch information
arbron committed Jan 10, 2025
1 parent abc5593 commit e59dc99
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 70 deletions.
11 changes: 7 additions & 4 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,13 @@
"Type": "Only features with the \"feat\" type can be selected."
}
},
"Action": {
"Create": "Create Advancement",
"Delete": "Delete Advancement",
"Duplicate": "Duplicate Advancement",
"Edit": "Edit Advancement",
"View": "View Advancement"
},
"Config": {
"Details": "Advancement Details"
},
Expand Down Expand Up @@ -395,10 +402,6 @@
"DND5E.AdvancementConfigureDropAreaHint": "Drop Items here to add them to the pool from which a player can choose.",
"DND5E.AdvancementConfiguredComplete": "Fully Configured",
"DND5E.AdvancementConfiguredIncomplete": "Not Configured",
"DND5E.AdvancementControlCreate": "Create Advancement",
"DND5E.AdvancementControlDelete": "Delete Advancement",
"DND5E.AdvancementControlDuplicate": "Duplicate Advancement",
"DND5E.AdvancementControlEdit": "Edit Advancement",
"DND5E.AdvancementCustomIcon": "Custom Icon",
"DND5E.AdvancementCustomTitle": "Custom Title",
"DND5E.AdvancementDeleteConfirmationLabel": "Remove advancement changes",
Expand Down
3 changes: 3 additions & 0 deletions module/applications/item/item-sheet-2.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ export default class ItemSheet5e2 extends ItemSheetV2Mixin(ItemSheet5e) {
html.find("button.control-button").on("click", this._onSheetAction.bind(this));
}

new ContextMenu5e(html, ".advancement-item[data-id]", [], {
onOpen: target => dnd5e.documents.advancement.Advancement.onContextMenu(this.item, target)
});
new ContextMenu5e(html, ".activity[data-activity-id]", [], {
onOpen: target => dnd5e.documents.activity.UtilityActivity.onContextMenu(this.item, target)
});
Expand Down
47 changes: 0 additions & 47 deletions module/applications/item/item-sheet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -496,53 +496,6 @@ export default class ItemSheet5e extends ItemSheet {
}
}
html[0].querySelectorAll('[data-action="view"]').forEach(e => e.addEventListener("click", this._onView.bind(this)));

// Advancement context menu
const contextOptions = this._getAdvancementContextMenuOptions();
/**
* A hook event that fires when the context menu for the advancements list is constructed.
* @function dnd5e.getItemAdvancementContext
* @memberof hookEvents
* @param {jQuery} html The HTML element to which the context options are attached.
* @param {ContextMenuEntry[]} entryOptions The context menu entries.
*/
Hooks.call("dnd5e.getItemAdvancementContext", html, contextOptions);
if ( contextOptions ) new this.options.contextMenu(html, ".advancement-item", contextOptions);
}

/* -------------------------------------------- */

/**
* Get the set of ContextMenu options which should be applied for advancement entries.
* @returns {ContextMenuEntry[]} Context menu entries.
* @protected
*/
_getAdvancementContextMenuOptions() {
const condition = li => (this.advancementConfigurationMode || !this.isEmbedded) && this.isEditable;
return [
{
name: "DND5E.AdvancementControlEdit",
icon: "<i class='fas fa-edit fa-fw'></i>",
condition,
callback: li => this._onAdvancementAction(li[0], "edit")
},
{
name: "DND5E.AdvancementControlDuplicate",
icon: "<i class='fas fa-copy fa-fw'></i>",
condition: li => {
const id = li[0].closest(".advancement-item")?.dataset.id;
const advancement = this.item.advancement.byId[id];
return condition(li) && advancement?.constructor.availableForItem(this.item);
},
callback: li => this._onAdvancementAction(li[0], "duplicate")
},
{
name: "DND5E.AdvancementControlDelete",
icon: "<i class='fas fa-trash fa-fw' style='color: rgb(255, 65, 65);'></i>",
condition,
callback: li => this._onAdvancementAction(li[0], "delete")
}
];
}

/* -------------------------------------------- */
Expand Down
65 changes: 65 additions & 0 deletions module/documents/advancement/advancement.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -314,4 +314,69 @@ export default class Advancement extends PseudoDocumentMixin(BaseAdvancement) {
"flags.dnd5e.advancementOrigin": `${this.item.id}.${this.id}`
}, { keepId: true }).toObject();
}

/* -------------------------------------------- */
/* Event Listeners and Handlers */
/* -------------------------------------------- */

/**
* Construct context menu options for this Activity.
* @returns {ContextMenuEntry[]}
*/
getContextMenuOptions() {
if ( (this.item.advancementConfigurationMode || !this.item.isEmbedded)
&& this.item.isOwner && !this.item.compendium?.locked ) return [
{
name: "DND5E.ADVANCEMENT.Action.Edit",
icon: "<i class='fas fa-edit fa-fw'></i>",
callback: () => this.sheet?.render(true)
},
{
name: "DND5E.ADVANCEMENT.Action.Duplicate",
icon: "<i class='fas fa-copy fa-fw'></i>",
condition: li => this?.constructor.availableForItem(this.item),
callback: () => {
const createData = this.toObject();
delete createData._id;
this.item.createAdvancement(createData.type, createData, { renderSheet: false });
}
},
{
name: "DND5E.ADVANCEMENT.Action.Delete",
icon: "<i class='fas fa-trash fa-fw'></i>",
callback: () => this.deleteDialog()
}
];

return [{
name: "DND5E.ADVANCEMENT.Action.View",
icon: "<i class='fas fa-eye fa-fw'></i>",
callback: () => this.sheet?.render(true)
}];
}

/* -------------------------------------------- */

/**
* Handle context menu events on activities.
* @param {Item5e} item The Item the Activity belongs to.
* @param {HTMLElement} target The element the menu was triggered on.
*/
static onContextMenu(item, target) {
const { id } = target.closest("[data-id]")?.dataset ?? {};
const advancement = item.advancement?.byId[id];
if ( !advancement ) return;
const menuItems = advancement.getContextMenuOptions();

/**
* A hook even that fires when the context menu for an Advancement is opened.
* @function dnd5e.getItemAdvancementContext
* @memberof hookEvents
* @param {Advancement} advancement The Advancement.
* @param {HTMLElement} target The element that menu was triggered on.
* @param {ContextMenuEntry[]} menuItems The context menu entries.
*/
Hooks.callAll("dnd5e.getItemAdvancementContext", advancement, target, menuItems);
ui.context.menuItems = menuItems;
}
}
7 changes: 3 additions & 4 deletions module/documents/item.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1228,10 +1228,9 @@ export default class Item5e extends SystemDocumentMixin(Item) {
const advancementCollection = this.toObject().system.advancement;
advancementCollection.push(advancement.toObject());
if ( source ) return this.updateSource({"system.advancement": advancementCollection});
return this.update({"system.advancement": advancementCollection}).then(() => {
if ( !showConfig ) return this;
const config = new cls.metadata.apps.config(this.advancement.byId[advancement.id]);
return config.render(true);
return this.update({ "system.advancement": advancementCollection }).then(() => {
if ( showConfig ) return this.advancement.byId[advancement.id]?.sheet?.render(true);
return this;
});
}

Expand Down
9 changes: 6 additions & 3 deletions module/documents/mixins/pseudo-document.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,17 @@ export default function PseudoDocumentMixin(Base) {
/* -------------------------------------------- */

/**
* Lazily obtain a ApplicationV2 instance used to configure this PseudoDocument, or null if no sheet is available.
* @type {ApplicationV2|null}
* Lazily obtain a Application instance used to configure this PseudoDocument, or null if no sheet is available.
* @type {Application|ApplicationV2|null}
*/
get sheet() {
const cls = this.constructor.metadata.sheetClass ?? this.constructor.metadata.apps?.config;
if ( !cls ) return null;
if ( !this.constructor._sheets.has(this.uuid) ) {
this.constructor._sheets.set(this.uuid, new cls({ document: this }));
let sheet;
if ( cls.prototype instanceof Application ) sheet = new cls(this);
else sheet = new cls({ document: this });
this.constructor._sheets.set(this.uuid, sheet);
}
return this.constructor._sheets.get(this.uuid);
}
Expand Down
2 changes: 2 additions & 0 deletions templates/advancement/item-grant-config-items.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
<input type="hidden" name="configuration.items.{{ @index }}.uuid" value="{{ data.uuid }}">
</div>
<div class="item-controls flexrow">
{{#if @root.editable}}
<a class="item-control item-action" data-action="deleteItem" data-tooltip="DND5E.ItemDelete"
aria-label="{{ localize 'DND5E.ItemDelete' }}">
<i class="fas fa-trash" inert></i>
</a>
{{/if}}
</div>
</li>
{{/each}}
Expand Down
10 changes: 5 additions & 5 deletions templates/items/parts/item-advancement.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
</div>
{{#if advancementEditable}}
<div class="item-controls flexrow add-button">
<a class="item-control" data-action="add" data-tooltip="DND5E.AdvancementControlCreate">
<a class="item-control" data-action="add" data-tooltip="DND5E.ADVANCEMENT.Action.Create">
<i class="fas fa-plus"></i>
</a>
</div>
Expand Down Expand Up @@ -73,12 +73,12 @@
{{/if}}
{{#if @root.advancementEditable}}
<div class="item-controls flexrow">
<a class="item-control" data-action="edit" data-tooltip="DND5E.AdvancementControlEdit"
aria-label="{{ localize 'DND5E.AdvancementControlEdit' }}">
<a class="item-control" data-action="edit" data-tooltip="DND5E.ADVANCEMENT.Action.Edit"
aria-label="{{ localize 'DND5E.ADVANCEMENT.Action.Edit' }}">
<i class="fas fa-edit" aria-hidden="true"></i>
</a>
<a class="item-control" data-action="delete" data-tooltip="DND5E.AdvancementControlDelete"
aria-label="{{ localize 'DND5E.AdvancementControlDelete' }}">
<a class="item-control" data-action="delete" data-tooltip="DND5E.ADVANCEMENT.Action.Delete"
aria-label="{{ localize 'DND5E.ADVANCEMENT.Action.Delete' }}">
<i class="fas fa-trash" aria-hidden="true"></i>
</a>
</div>
Expand Down
14 changes: 7 additions & 7 deletions templates/items/parts/item-advancement2.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -78,23 +78,23 @@
{{#if @root.editable}}
{{!-- Editing --}}
<a class="item-control item-action" data-action="edit"
data-tooltip="DND5E.AdvancementControlEdit"
aria-label="{{ localize "DND5E.AdvancementControlEdit" }}">
<i class="fas fa-pen-to-square"></i>
data-tooltip="DND5E.ADVANCEMENT.Action.Edit"
aria-label="{{ localize "DND5E.DND5E.ADVANCEMENT.Action.Edit" }}">
<i class="fas fa-pen-to-square" inert></i>
</a>

{{!-- Deleting --}}
<a class="item-control item-action" data-action="delete"
data-tooltip="DND5E.AdvancementControlDelete"
aria-label="{{ localize "DND5E.AdvancementControlDelete" }}">
<i class="fas fa-trash"></i>
data-tooltip="DND5E.ADVANCEMENT.Action.Delete"
aria-label="{{ localize "DND5E.ADVANCEMENT.Action.Delete" }}">
<i class="fas fa-trash" inert></i>
</a>
{{/if}}

{{!-- Context Menu --}}
<a class="item-control interface-only" data-context-menu
aria-label="{{ localize "DND5E.AdditionalControls" }}">
<i class="fas fa-ellipsis-vertical"></i>
<i class="fas fa-ellipsis-vertical" inert></i>
</a>

</div>
Expand Down

0 comments on commit e59dc99

Please sign in to comment.