diff --git a/lang/en.json b/lang/en.json index e8f56d1be9..e0064e9fae 100644 --- a/lang/en.json +++ b/lang/en.json @@ -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" }, @@ -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", diff --git a/module/applications/item/item-sheet-2.mjs b/module/applications/item/item-sheet-2.mjs index a3963759f9..6c78fc2d5a 100644 --- a/module/applications/item/item-sheet-2.mjs +++ b/module/applications/item/item-sheet-2.mjs @@ -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) }); diff --git a/module/applications/item/item-sheet.mjs b/module/applications/item/item-sheet.mjs index 1f472b2336..a05011274b 100644 --- a/module/applications/item/item-sheet.mjs +++ b/module/applications/item/item-sheet.mjs @@ -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: "", - condition, - callback: li => this._onAdvancementAction(li[0], "edit") - }, - { - name: "DND5E.AdvancementControlDuplicate", - icon: "", - 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: "", - condition, - callback: li => this._onAdvancementAction(li[0], "delete") - } - ]; } /* -------------------------------------------- */ diff --git a/module/documents/advancement/advancement.mjs b/module/documents/advancement/advancement.mjs index 66d04f5dd4..e3e63f4d2a 100644 --- a/module/documents/advancement/advancement.mjs +++ b/module/documents/advancement/advancement.mjs @@ -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: "", + callback: () => this.sheet?.render(true) + }, + { + name: "DND5E.ADVANCEMENT.Action.Duplicate", + icon: "", + 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: "", + callback: () => this.deleteDialog() + } + ]; + + return [{ + name: "DND5E.ADVANCEMENT.Action.View", + icon: "", + 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; + } } diff --git a/module/documents/item.mjs b/module/documents/item.mjs index d1820f3d5b..9c4ae1a2c9 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -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; }); } diff --git a/module/documents/mixins/pseudo-document.mjs b/module/documents/mixins/pseudo-document.mjs index 5a5c4492ff..3065e47c31 100644 --- a/module/documents/mixins/pseudo-document.mjs +++ b/module/documents/mixins/pseudo-document.mjs @@ -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); } diff --git a/templates/advancement/item-grant-config-items.hbs b/templates/advancement/item-grant-config-items.hbs index b19f3b106a..01019736b6 100644 --- a/templates/advancement/item-grant-config-items.hbs +++ b/templates/advancement/item-grant-config-items.hbs @@ -16,10 +16,12 @@
+ {{#if @root.editable}} + {{/if}}
{{/each}} diff --git a/templates/items/parts/item-advancement.hbs b/templates/items/parts/item-advancement.hbs index c295b16f65..cd279de85e 100644 --- a/templates/items/parts/item-advancement.hbs +++ b/templates/items/parts/item-advancement.hbs @@ -19,7 +19,7 @@ {{#if advancementEditable}}
- +
@@ -73,12 +73,12 @@ {{/if}} {{#if @root.advancementEditable}}
- + - +
diff --git a/templates/items/parts/item-advancement2.hbs b/templates/items/parts/item-advancement2.hbs index 6926dfc101..b04f38cd12 100644 --- a/templates/items/parts/item-advancement2.hbs +++ b/templates/items/parts/item-advancement2.hbs @@ -78,23 +78,23 @@ {{#if @root.editable}} {{!-- Editing --}} - + data-tooltip="DND5E.ADVANCEMENT.Action.Edit" + aria-label="{{ localize "DND5E.DND5E.ADVANCEMENT.Action.Edit" }}"> + {{!-- Deleting --}} - + data-tooltip="DND5E.ADVANCEMENT.Action.Delete" + aria-label="{{ localize "DND5E.ADVANCEMENT.Action.Delete" }}"> + {{/if}} {{!-- Context Menu --}} - +