diff --git a/daggerheart.mjs b/daggerheart.mjs index 81b404d5..82c56dff 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -124,7 +124,7 @@ Hooks.once('init', () => { CONFIG.Token.rulerClass = placeables.DhTokenRuler; CONFIG.ui.resources = applications.ui.DhFearTracker; - CONFIG.ux.ContextMenu = applications.ux.ContextMenu; + CONFIG.ux.ContextMenu = applications.ux.DHContextMenu; CONFIG.ux.TooltipManager = documents.DhTooltipManager; game.socket.on(`system.${SYSTEM.id}`, socketRegistration.handleSocketEvent); diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 182a3539..2c8ad561 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -50,13 +50,13 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo element.addEventListener('mouseenter', this.hoverAdvantage) ); html.querySelectorAll('.advantage').forEach(element => - element.addEventListener('click', event => this.selectAdvantage.bind(this)(event, data.message)) + element.addEventListener('click', event => this.selectAdvantage.call(this, event, data.message)) ); html.querySelectorAll('.ability-use-button').forEach(element => - element.addEventListener('click', event => this.abilityUseButton.bind(this)(event, data.message)) + element.addEventListener('click', event => this.abilityUseButton.call(this, event, data.message)) ); html.querySelectorAll('.action-use-button').forEach(element => - element.addEventListener('click', event => this.actionUseButton.bind(this)(event, data.message)) + element.addEventListener('click', event => this.actionUseButton.call(this, event, data.message)) ); }; @@ -156,8 +156,8 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo getTargetList = (event, message) => { const targetSelection = event.target - .closest('.message-content') - .querySelector('.button-target-selection.target-selected'), + .closest('.message-content') + .querySelector('.button-target-selection.target-selected'), isHit = Boolean(targetSelection.dataset.targetHit); return { isHit, @@ -229,24 +229,54 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo } }; - onToggleTargets = async event => { + /** + * Toggle visibility of target containers. + * @param {MouseEvent} event + */ + onToggleTargets(event) { event.stopPropagation(); - $($(event.currentTarget).parent()).find('.target-container').toggleClass('hidden'); - }; + event.currentTarget.parentElement + ?.querySelectorAll('.target-container') + .forEach(el => el.classList.toggle('hidden')); + } - hoverAdvantage = event => { - $(event.currentTarget).siblings('.advantage').toggleClass('unused'); - }; + /** + * Highlight advantage icons on hover. + * @param {MouseEvent} event + */ + hoverAdvantage(event) { + const parent = event.currentTarget.parentElement; + if (!parent) return; - selectAdvantage = async (event, message) => { + parent.querySelectorAll('.advantage').forEach(el => { + if (el !== event.currentTarget) { + el.classList.toggle('unused'); + } + }); + } + + + /** + * Handle selecting an advantage and disable further selection. + * @param {MouseEvent} event + * @param {object} message + */ + async selectAdvantage(event, message) { event.stopPropagation(); const updateMessage = game.messages.get(message._id); - await updateMessage.update({ system: { advantageSelected: event.currentTarget.id === 'hope' ? 1 : 2 } }); + await updateMessage?.update({ + system: { advantageSelected: event.currentTarget.id === 'hope' ? 1 : 2 } + }); + + const parent = event.currentTarget.parentElement; + if (!parent) return; + + parent.querySelectorAll('.advantage').forEach(el => { + el.replaceWith(el.cloneNode(true)); + }); + } - $(event.currentTarget).siblings('.advantage').off('click'); - $(event.currentTarget).off('click'); - }; abilityUseButton = async (event, message) => { event.stopPropagation(); diff --git a/module/applications/ux/_module.mjs b/module/applications/ux/_module.mjs index 5ec0eecf..ead918fa 100644 --- a/module/applications/ux/_module.mjs +++ b/module/applications/ux/_module.mjs @@ -1,2 +1,2 @@ export { default as FilterMenu } from './filter-menu.mjs'; -export { default as ContextMenu } from './contextMenu.mjs'; +export { default as DHContextMenu } from './contextMenu.mjs'; diff --git a/module/applications/ux/contextMenu.mjs b/module/applications/ux/contextMenu.mjs index f7658d42..a913c450 100644 --- a/module/applications/ux/contextMenu.mjs +++ b/module/applications/ux/contextMenu.mjs @@ -1,4 +1,63 @@ -export default class DhContextMenu extends foundry.applications.ux.ContextMenu.implementation { +/** + * @typedef ContextMenuEntry + * @property {string} name The context menu label. Can be localized. + * @property {string} [icon] A string containing an HTML icon element for the menu item. + * @property {string} [classes] Additional CSS classes to apply to this menu item. + * @property {string} [group] An identifier for a group this entry belongs to. + * @property {ContextMenuJQueryCallback} callback The function to call when the menu item is clicked. + * @property {ContextMenuCondition|boolean} [condition] A function to call or boolean value to determine if this entry + * appears in the menu. + */ + +/** + * @callback ContextMenuCondition + * @param {jQuery|HTMLElement} html The element of the context menu entry. + * @returns {boolean} Whether the entry should be rendered in the context menu. + */ + +/** + * @callback ContextMenuCallback + * @param {HTMLElement} target The element that the context menu has been triggered for. + * @returns {unknown} + */ + +/** + * @callback ContextMenuJQueryCallback + * @param {HTMLElement|jQuery} target The element that the context menu has been triggered for. Will + * either be a jQuery object or an HTMLElement instance, depending + * on how the ContextMenu was configured. + * @returns {unknown} + */ + +/** + * @typedef ContextMenuOptions + * @property {string} [eventName="contextmenu"] Optionally override the triggering event which can spawn the menu. If + * the menu is using fixed positioning, this event must be a MouseEvent. + * @property {ContextMenuCallback} [onOpen] A function to call when the context menu is opened. + * @property {ContextMenuCallback} [onClose] A function to call when the context menu is closed. + * @property {boolean} [fixed=false] If true, the context menu is given a fixed position rather than being + * injected into the target. + * @property {boolean} [jQuery=true] If true, callbacks will be passed jQuery objects instead of HTMLElement + * instances. + */ + +/** + * @typedef ContextMenuRenderOptions + * @property {Event} [event] The event that triggered the context menu opening. + * @property {boolean} [animate=true] Animate the context menu opening. + */ + +/** + * A subclass of ContextMenu. + * @extends {foundry.applications.ux.ContextMenu} + */ +export default class DHContextMenu extends foundry.applications.ux.ContextMenu { + /** + * @param {HTMLElement|jQuery} container - The HTML element that contains the context menu targets. + * @param {string} selector - A CSS selector which activates the context menu. + * @param {ContextMenuEntry[]} menuItems - An Array of entries to display in the menu + * @param {ContextMenuOptions} [options] - Additional options to configure the context menu. + */ constructor(container, selector, menuItems, options) { super(container, selector, menuItems, options); @@ -6,12 +65,21 @@ export default class DhContextMenu extends foundry.applications.ux.ContextMenu.i this.#jQuery = options.jQuery; } + /** + * Whether to pass jQuery objects or HTMLElement instances to callback. + * @type {boolean} + */ #jQuery; + /**@inheritdoc */ activateListeners(menu) { menu.addEventListener('click', this.#onClickItem.bind(this)); } + /** + * Handle click events on context menu items. + * @param {PointerEvent} event The click event + */ #onClickItem(event) { event.preventDefault(); event.stopPropagation(); @@ -22,6 +90,12 @@ export default class DhContextMenu extends foundry.applications.ux.ContextMenu.i this.close(); } + /* -------------------------------------------- */ + + /** + * Trigger a context menu event in response to a normal click on a additional options button. + * @param {PointerEvent} event + */ static triggerContextMenu(event) { event.preventDefault(); event.stopPropagation(); diff --git a/module/applications/ux/filter-menu.mjs b/module/applications/ux/filter-menu.mjs index ff2fb036..93d8bbf0 100644 --- a/module/applications/ux/filter-menu.mjs +++ b/module/applications/ux/filter-menu.mjs @@ -197,10 +197,12 @@ export default class FilterMenu extends foundry.applications.ux.ContextMenu { } })); + const sort = arr => game.i18n.sortObjects(arr, 'name'); + return [ - ...game.i18n.sortObjects(typesFilters, 'name'), - ...game.i18n.sortObjects(burdenFilter, 'name'), - ...game.i18n.sortObjects(damageTypeFilter, 'name') + ...sort(typesFilters, 'name'), + ...sort(burdenFilter, 'name'), + ...sort(damageTypeFilter, 'name') ]; } diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index c90202fb..7bef3e02 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -1,4 +1,4 @@ -// import { actionsTypes } from '../action/_module.mjs'; +import { actionsTypes } from '../action/_module.mjs'; /** * Describes metadata about the item data model type @@ -55,24 +55,28 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { return data; } + /**@inheritdoc */ async _preCreate(data, options, user) { + // Skip if no initial action is required or actions already exist if (!this.constructor.metadata.hasInitialAction || !foundry.utils.isEmpty(this.actions)) return; - const actionType = { - weapon: 'attack' - }[this.constructor.metadata.type], - cls = game.system.api.models.actionsTypes[actionType], - // cls = actionsTypes.attack, - action = new cls( - { - _id: foundry.utils.randomID(), - type: actionType, - name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType].name), - ...cls.getSourceConfig(this.parent) - }, - { - parent: this.parent - } - ); + + const metadataType = this.constructor.metadata.type; + const actionType = { weapon: "attack" }[metadataType]; + const ActionClass = actionsTypes[actionType]; + + const action = new ActionClass( + { + _id: foundry.utils.randomID(), + type: actionType, + name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType].name), + ...ActionClass.getSourceConfig(this.parent) + }, + { + parent: this.parent + } + ); + this.updateSource({ actions: [action] }); } + } diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs index 6200b690..99c1ec84 100644 --- a/module/helpers/handlebarsHelper.mjs +++ b/module/helpers/handlebarsHelper.mjs @@ -7,49 +7,11 @@ export default class RegisterHandlebarsHelpers { join: this.join, add: this.add, subtract: this.subtract, - objectSelector: this.objectSelector, includes: this.includes, - debug: this.debug, - signedNumber: this.signedNumber, - length: this.length, - switch: this.switch, case: this.case, - eq: this.eq, - ne: this.ne, - lt: this.lt, - gt: this.gt, - lte: this.lte, - gte: this.gte, - and: this.and, - or: this.or }); } - static eq(v1, v2) { - return v1 === v2; - } - static ne(v1, v2) { - return v1 !== v2; - } - static lt(v1, v2) { - return v1 < v2; - } - static gt(v1, v2) { - return v1 > v2; - } - static lte(v1, v2) { - return v1 <= v2; - } - static gte(v1, v2) { - return v1 >= v2; - } - static and() { - return Array.prototype.every.call(arguments, Boolean); - } - static or() { - return Array.prototype.slice.call(arguments, 0, -1).some(Boolean); - } - static times(nr, block) { var accum = ''; for (var i = 0; i < nr; ++i) accum += block.fn(i); @@ -72,59 +34,11 @@ export default class RegisterHandlebarsHelpers { return (Number.isNaN(aNum) ? 0 : aNum) - (Number.isNaN(bNum) ? 0 : bNum); } - static objectSelector(options) { - let { title, values, titleFontSize, ids, style } = options.hash; - - const titleLength = getWidthOfText(title, titleFontSize, true, true); - const margins = 12; - - const buttons = options.fn(); - const nrButtons = Math.max($(buttons).length - 1, 1); - const iconWidth = 26; - - const texts = values - .reduce((acc, x, index) => { - if (x) { - acc.push( - `${x}` - ); - } - - return acc; - }, []) - .join(' '); - - const html = `
-
- ${title} -
- ${texts} -
- ${buttons} -
-
- `; - - return new Handlebars.SafeString(html); - } static includes(list, item) { return list.includes(item); } - static signedNumber(number) { - return number >= 0 ? `+${number}` : number; - } - - static length(obj) { - return Object.keys(obj).length; - } - - static switch(value, options) { - this.switch_value = value; - this.switch_break = false; - return options.fn(this); - } static case(value, options) { if (value == this.switch_value) { @@ -132,9 +46,4 @@ export default class RegisterHandlebarsHelpers { return options.fn(this); } } - - static debug(a) { - console.log(a); - return a; - } } diff --git a/templates/characterCreation/tabs/setup.hbs b/templates/characterCreation/tabs/setup.hbs index 4d6e638c..6dd04345 100644 --- a/templates/characterCreation/tabs/setup.hbs +++ b/templates/characterCreation/tabs/setup.hbs @@ -74,7 +74,7 @@ {{#each experience.values as |experience id|}}
-
{{signedNumber this.value}}
+
{{numberFormat this.value sign=true}}
{{/each}} diff --git a/templates/levelup/tabs/selections.hbs b/templates/levelup/tabs/selections.hbs index d397fa80..9d1d00b8 100644 --- a/templates/levelup/tabs/selections.hbs +++ b/templates/levelup/tabs/selections.hbs @@ -12,7 +12,7 @@
-
{{signedNumber this.modifier}}
+
{{numberFormat this.modifier sign=true}}
{{#if this.name}}{{/if}} diff --git a/templates/levelup/tabs/summary.hbs b/templates/levelup/tabs/summary.hbs index 4ed4438f..dc7e2a47 100644 --- a/templates/levelup/tabs/summary.hbs +++ b/templates/levelup/tabs/summary.hbs @@ -48,7 +48,7 @@
{{localize "DAGGERHEART.Application.LevelUp.summary.newExperiences"}}
{{#each this.achievements.experiences.values}} -
{{this.name}} {{signedNumber this.modifier}}
+
{{this.name}} {{numberFormat this.modifier sign=true}}
{{/each}}
@@ -125,7 +125,7 @@
{{localize "DAGGERHEART.Application.LevelUp.summary.experienceIncreases"}}
{{#each this.advancements.experiences}} -
{{this.name}} {{signedNumber this.modifier}}
+
{{this.name}} {{numberFormat this.modifier sign=true}}
{{/each}}
diff --git a/templates/sheets/actors/companion/tempMain.hbs b/templates/sheets/actors/companion/tempMain.hbs index 810d48db..f1f315ed 100644 --- a/templates/sheets/actors/companion/tempMain.hbs +++ b/templates/sheets/actors/companion/tempMain.hbs @@ -17,7 +17,7 @@ {{#each source.system.experiences as |experience key|}}
-
{{signedNumber experience.value}}
+
{{numberFormat experience.value sign=true}}
{{/each}}