diff --git a/lang/en.json b/lang/en.json index 5b05f58a..ce8f0295 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1071,6 +1071,13 @@ "Default": "Default Ownership" }, "Sheets": { + "TABS": { + "features": "Features", + "effects": "Effects", + "settings": "Settings", + "actions": "Actions", + "description": "Description" + }, "PC": { "Name": "Name", "Pronouns": "Pronouns", diff --git a/module/applications/_module.mjs b/module/applications/_module.mjs index b1a1d59e..85a7d7ed 100644 --- a/module/applications/_module.mjs +++ b/module/applications/_module.mjs @@ -13,3 +13,5 @@ export { default as DhpArmor } from './sheets/items/armor.mjs'; export { default as DhpChatMessage } from './chatMessage.mjs'; export { default as DhpEnvironment } from './sheets/environment.mjs'; export { default as DhActiveEffectConfig } from './sheets/activeEffectConfig.mjs'; + +export * as api from './sheets/api/_modules.mjs'; diff --git a/module/applications/sheets/api/_modules.mjs b/module/applications/sheets/api/_modules.mjs new file mode 100644 index 00000000..4316da2a --- /dev/null +++ b/module/applications/sheets/api/_modules.mjs @@ -0,0 +1,3 @@ +export { default as DHApplicationMixin } from './application-mixin.mjs'; +export { default as DHBaseItemSheet } from './base-item.mjs'; +export { default as DHHeritageSheet } from './heritage-sheet.mjs'; diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs new file mode 100644 index 00000000..2dcfff6e --- /dev/null +++ b/module/applications/sheets/api/application-mixin.mjs @@ -0,0 +1,204 @@ +const { HandlebarsApplicationMixin } = foundry.applications.api; +import { tagifyElement } from '../../../helpers/utils.mjs'; + +/** + * @typedef {object} DragDropConfig + * @property {string} [dragSelector] - A CSS selector that identifies draggable elements. + * @property {string} [dropSelector] - A CSS selector that identifies drop targets. + * + * @typedef {Object} TagOption + * @property {string} label + * @property {string} [src] + * + * @typedef {object} TagifyConfig + * @property {String} selector - The CSS selector for get the element to transform into a tag input + * @property {Record | (() => Record)} options - Available tag options as key-value pairs + * @property {TagChangeCallback} callback - Callback function triggered when tags change + * @property {TagifyOptions} [tagifyOptions={}] - Additional configuration for Tagify + * + * @callback TagChangeCallback + * @param {Array<{value: string, name: string, src?: string}>} selectedOptions - Current selected tags + * @param {{option: string, removed: boolean}} change - What changed (added/removed tag) + * @param {HTMLElement} inputElement - Original input element + * + * + * @typedef {Object} TagifyOptions + * @property {number} [maxTags] - Maximum number of allowed tags + * + * @typedef {import("@client/applications/api/handlebars-application.mjs").HandlebarsRenderOptions} HandlebarsRenderOptions + * @typedef {foundry.applications.types.ApplicationConfiguration & HandlebarsRenderOptions & { dragDrop?: DragDropConfig[], tagifyConfigs?: TagifyConfig[] }} DHSheetV2Configuration + */ + +/** + * @template {Constructor} BaseDocumentSheet + * @param {BaseDocumentSheet} Base - The base class to extend. + * @returns {BaseDocumentSheet} + */ +export default function DHApplicationMixin(Base) { + class DHSheetV2 extends HandlebarsApplicationMixin(Base) { + /** + * @param {DHSheetV2Configuration} [options={}] + */ + constructor(options = {}) { + super(options); + /** + * @type {foundry.applications.ux.DragDrop[]} + * @private + */ + this._dragDrop = this._createDragDropHandlers(); + } + + /** + * The default options for the sheet. + * @type {DHSheetV2Configuration} + */ + static DEFAULT_OPTIONS = { + classes: ['daggerheart', 'sheet', 'dh-style'], + position: { + width: 480, + height: 'auto' + }, + actions: { + addEffect: DHSheetV2.#addEffect, + editEffect: DHSheetV2.#editEffect, + removeEffect: DHSheetV2.#removeEffect + }, + dragDrop: [], + tagifyConfigs: [] + }; + + /* -------------------------------------------- */ + + /**@inheritdoc */ + _attachPartListeners(partId, htmlElement, options) { + super._attachPartListeners(partId, htmlElement, options); + this._dragDrop.forEach(d => d.bind(htmlElement)); + } + + /**@inheritdoc */ + async _onRender(context, options) { + await super._onRender(context, options); + this._createTagifyElements(this.options.tagifyConfigs); + } + + /** + * Creates Tagify elements from configuration objects + * @param {TagifyConfig[]} tagConfigs - Array of Tagify configuration objects + * @throws {TypeError} If tagConfigs is not an array + * @throws {Error} If required properties are missing in config objects + * @param {TagifyConfig[]} tagConfigs + */ + _createTagifyElements(tagConfigs) { + if (!Array.isArray(tagConfigs)) throw new TypeError('tagConfigs must be an array'); + + tagConfigs.forEach(config => { + try { + const { selector, options, callback, tagifyOptions = {} } = config; + + // Validate required fields + if (!selector || !options || !callback) { + throw new Error('Invalid TagifyConfig - missing required properties', config); + } + + // Find target element + const element = this.element.querySelector(selector); + if (!element) { + throw new Error(`Element not found with selector: ${selector}`); + } + // Resolve dynamic options if function provided + const resolvedOptions = typeof options === 'function' ? options.call(this) : options; + + // Initialize Tagify + tagifyElement(element, resolvedOptions, callback.bind(this), tagifyOptions); + } catch (error) { + console.error('Error initializing Tagify:', error); + } + }); + } + + /* -------------------------------------------- */ + /* Drag and Drop */ + /* -------------------------------------------- */ + + /** + * Creates drag-drop handlers from the configured options. + * @returns {foundry.applications.ux.DragDrop[]} + * @private + */ + _createDragDropHandlers() { + return this.options.dragDrop.map(d => { + d.callbacks = { + drop: this._onDrop.bind(this) + }; + return new foundry.applications.ux.DragDrop.implementation(d); + }); + } + + /** + * Handle drop event. + * @param {DragEvent} event + * @protected + */ + _onDrop(event) { } + + /* -------------------------------------------- */ + /* Prepare Context */ + /* -------------------------------------------- */ + + /** + * Prepare the template context. + * @param {object} options + * @param {string} [objectPath='document'] + * @returns {Promise} + * @inheritdoc + */ + async _prepareContext(options, objectPath = 'document') { + const context = await super._prepareContext(options); + context.config = CONFIG.daggerheart; + context.source = this[objectPath]; + context.fields = this[objectPath].schema.fields; + context.systemFields = this[objectPath].system ? this[objectPath].system.schema.fields : {}; + return context; + } + + /* -------------------------------------------- */ + /* Application Clicks Actions */ + /* -------------------------------------------- */ + + /** + * Renders an ActiveEffect's sheet sheet. + * @param {PointerEvent} event - The originating click event + * @param {HTMLElement} button - The capturing HTML element which defines the [data-action="removeAction"] + */ + static async #addEffect() { + const cls = foundry.documents.ActiveEffect; + await cls.create( + { + name: game.i18n.format('DOCUMENT.New', { type: game.i18n.localize(cls.metadata.label) }) + }, + { parent: this.document } + ); + } + + /** + * Renders an ActiveEffect's sheet sheet. + * @param {PointerEvent} event - The originating click event + * @param {HTMLElement} button - The capturing HTML element which defines the [data-action="removeAction"] + */ + static async #editEffect(_event, button) { + const effect = this.document.effects.get(button.dataset.effect); + effect.sheet.render({ force: true }); + } + + /** + * Delete an ActiveEffect from the item. + * @param {PointerEvent} _event - The originating click event + * @param {HTMLElement} button - The capturing HTML element which defines the [data-action="removeAction"] + */ + static async #removeEffect(_event, button) { + await this.document.effects.get(button.dataset.effect).delete(); + } + } + + return DHSheetV2; +} diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs new file mode 100644 index 00000000..5528f8fc --- /dev/null +++ b/module/applications/sheets/api/base-item.mjs @@ -0,0 +1,148 @@ +import DHApplicationMixin from './application-mixin.mjs'; +import { actionsTypes } from '../../../data/_module.mjs'; +import DHActionConfig from '../../config/Action.mjs'; + +const { ItemSheetV2 } = foundry.applications.sheets; + +/** + * A base item sheet extending {@link ItemSheetV2} via {@link DHApplicationMixin} + * @extends ItemSheetV2 + * @mixes DHSheetV2 + */ +export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { + /** @inheritDoc */ + static DEFAULT_OPTIONS = { + classes: ['item'], + position: { width: 600 }, + form: { + submitOnChange: true + }, + actions: { + addAction: DHBaseItemSheet.#addAction, + editAction: DHBaseItemSheet.#editAction, + removeAction: DHBaseItemSheet.#removeAction + } + }; + + /* -------------------------------------------- */ + + /** @inheritdoc */ + static TABS = { + primary: { + tabs: [{ id: 'description' }, { id: 'actions' }, { id: 'settings' }], + initial: 'description', + labelPrefix: 'DAGGERHEART.Sheets.TABS' + } + }; + + /* -------------------------------------------- */ + /* Prepare Context */ + /* -------------------------------------------- */ + + /**@inheritdoc */ + async _preparePartContext(partId, context) { + await super._preparePartContext(partId, context); + const { TextEditor } = foundry.applications.ux; + + switch (partId) { + case 'description': + const value = foundry.utils.getProperty(this.document, "system.description") ?? ""; + context.enrichedDescription = await TextEditor.enrichHTML(value, { + relativeTo: this.item, + rollData: this.item.getRollData(), + secrets: this.item.isOwner + }) + break; + } + + return context; + } + + + /* -------------------------------------------- */ + /* Application Clicks Actions */ + /* -------------------------------------------- */ + + /** + * Render a dialog prompting the user to select an action type. + * + * @returns {Promise} An object containing the selected action type. + */ + static async selectActionType() { + const content = await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/views/actionType.hbs', + { types: SYSTEM.ACTIONS.actionTypes } + ), + title = 'Select Action Type', //useless var + type = 'form', + data = {}; //useless var + //TODO: use DialogV2 + return Dialog.prompt({ + title, + label: title, + content, + type, //this prop is useless + callback: html => { + const form = html[0].querySelector('form'), + fd = new foundry.applications.ux.FormDataExtended(form); + foundry.utils.mergeObject(data, fd.object, { inplace: true }); + // if (!data.name?.trim()) data.name = game.i18n.localize(SYSTEM.ACTIONS.actionTypes[data.type].name); + return data; + }, + rejectClose: false + }); + } + + /** + * Add a new action to the item, prompting the user for its type. + * @param {PointerEvent} _event - The originating click event + * @param {HTMLElement} _button - The capturing HTML element which defines the [data-action="addAction"] + */ + static async #addAction(_event, _button) { + const actionType = await DHBaseItemSheet.selectActionType(); + try { + const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack, + action = new cls( + { + _id: foundry.utils.randomID(), + type: actionType.type, + name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name), + ...cls.getSourceConfig(this.document) + }, + { + parent: this.document + } + ); + await this.document.update({ 'system.actions': [...this.document.system.actions, action] }); + await new DHActionConfig(this.document.system.actions[this.document.system.actions.length - 1]).render({ + force: true + }); + } catch (error) { + console.log(error); + } + } + + /** + * Edit an existing action on the item + * @param {PointerEvent} _event - The originating click event + * @param {HTMLElement} button - The capturing HTML element which defines the [data-action="editAction"] + */ + static async #editAction(_event, button) { + const action = this.document.system.actions[button.dataset.index]; + await new DHActionConfig(action).render({ force: true }); + } + + /** + * Remove an action from the item. + * @param {PointerEvent} event - The originating click event + * @param {HTMLElement} button - The capturing HTML element which defines the [data-action="removeAction"] + */ + static async #removeAction(event, button) { + event.stopPropagation(); + await this.document.update({ + 'system.actions': this.document.system.actions.filter( + (_, index) => index !== Number.parseInt(button.dataset.index) + ) + }); + } +} diff --git a/module/applications/sheets/api/heritage-sheet.mjs b/module/applications/sheets/api/heritage-sheet.mjs new file mode 100644 index 00000000..4938f5b0 --- /dev/null +++ b/module/applications/sheets/api/heritage-sheet.mjs @@ -0,0 +1,31 @@ +import DHBaseItemSheet from './base-item.mjs'; + +export default class DHHeritageSheet extends DHBaseItemSheet { + /**@inheritdoc */ + static DEFAULT_OPTIONS = { + position: { width: 450, height: 700 } + }; + + /**@override */ + static PARTS = { + tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, + description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' }, + actions: { + template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs', + scrollable: ['.actions'] + }, + effects: { + template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs', + scrollable: ['.effects'] + } + }; + + /** @override*/ + static TABS = { + primary: { + tabs: [{ id: 'description' }, { id: 'actions' }, { id: 'effects' }], + initial: 'description', + labelPrefix: 'DAGGERHEART.Sheets.TABS' + } + }; +} diff --git a/module/applications/sheets/item.mjs b/module/applications/sheets/item.mjs deleted file mode 100644 index 1b8c416b..00000000 --- a/module/applications/sheets/item.mjs +++ /dev/null @@ -1,132 +0,0 @@ -import DhpApplicationMixin from './daggerheart-sheet.mjs'; -import DHActionConfig from '../config/Action.mjs'; -import { actionsTypes } from '../../data/_module.mjs'; - -export default function DHItemMixin(Base) { - return class DHItemSheetV2 extends DhpApplicationMixin(Base) { - constructor(options = {}) { - super(options); - } - - static DEFAULT_OPTIONS = { - tag: 'form', - classes: ['daggerheart', 'sheet', 'item', 'dh-style'], - position: { width: 600 }, - form: { - handler: this.updateForm, - submitOnChange: true, - closeOnSubmit: false - }, - actions: { - addAction: this.addAction, - editAction: this.editAction, - removeAction: this.removeAction - } - }; - - static TABS = { - description: { - active: true, - cssClass: '', - group: 'primary', - id: 'description', - icon: null, - label: 'DAGGERHEART.Sheets.Feature.Tabs.Description' - }, - actions: { - active: false, - cssClass: '', - group: 'primary', - id: 'actions', - icon: null, - label: 'DAGGERHEART.Sheets.Feature.Tabs.Actions' - }, - settings: { - active: false, - cssClass: '', - group: 'primary', - id: 'settings', - icon: null, - label: 'DAGGERHEART.Sheets.Feature.Tabs.Settings' - } - }; - - async _prepareContext(_options) { - const context = await super._prepareContext(_options); - context.document = this.document; - context.config = CONFIG.daggerheart; - context.tabs = super._getTabs(this.constructor.TABS); - - return context; - } - - static async updateForm(event, _, formData) { - await this.document.update(formData.object); - this.render(); - } - - static async selectActionType() { - const content = await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/views/actionType.hbs', - { types: SYSTEM.ACTIONS.actionTypes } - ), - title = 'Select Action Type', - type = 'form', - data = {}; - return Dialog.prompt({ - title, - label: title, - content, - type, - callback: html => { - const form = html[0].querySelector('form'), - fd = new foundry.applications.ux.FormDataExtended(form); - foundry.utils.mergeObject(data, fd.object, { inplace: true }); - // if (!data.name?.trim()) data.name = game.i18n.localize(SYSTEM.ACTIONS.actionTypes[data.type].name); - return data; - }, - rejectClose: false - }); - } - - static async addAction() { - const actionType = await DHItemSheetV2.selectActionType(), - actionIndexes = this.document.system.actions.map(x => x._id.split('-')[2]).sort((a, b) => a - b); - try { - const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack, - action = new cls( - { - // id: `${this.document.id}-Action-${actionIndexes.length > 0 ? actionIndexes[0] + 1 : 1}` - _id: foundry.utils.randomID(), - type: actionType.type, - name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name), - ...cls.getSourceConfig(this.document) - }, - { - parent: this.document - } - ); - await this.document.update({ 'system.actions': [...this.document.system.actions, action] }); - await new DHActionConfig(this.document.system.actions[this.document.system.actions.length - 1]).render( - true - ); - } catch (error) { - console.log(error); - } - } - - static async editAction(_, button) { - const action = this.document.system.actions[button.dataset.index]; - await new DHActionConfig(action).render(true); - } - - static async removeAction(event, button) { - event.stopPropagation(); - await this.document.update({ - 'system.actions': this.document.system.actions.filter( - (_, index) => index !== Number.parseInt(button.dataset.index) - ) - }); - } - }; -} diff --git a/module/applications/sheets/items/ancestry.mjs b/module/applications/sheets/items/ancestry.mjs index 3d20df7a..3636ea62 100644 --- a/module/applications/sheets/items/ancestry.mjs +++ b/module/applications/sheets/items/ancestry.mjs @@ -1,11 +1,12 @@ -import DHHeritageSheetV2 from './heritage.mjs'; +import DHHeritageSheet from '../api/heritage-sheet.mjs'; -const { ItemSheetV2 } = foundry.applications.sheets; -export default class AncestrySheet extends DHHeritageSheetV2(ItemSheetV2) { +export default class AncestrySheet extends DHHeritageSheet { + /**@inheritdoc */ static DEFAULT_OPTIONS = { classes: ['ancestry'] }; + /**@inheritdoc */ static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/items/ancestry/header.hbs' }, ...super.PARTS diff --git a/module/applications/sheets/items/armor.mjs b/module/applications/sheets/items/armor.mjs index 32c482ba..4e53d8fa 100644 --- a/module/applications/sheets/items/armor.mjs +++ b/module/applications/sheets/items/armor.mjs @@ -1,14 +1,20 @@ -import { armorFeatures } from '../../../config/itemConfig.mjs'; -import { tagifyElement } from '../../../helpers/utils.mjs'; -import DHItemSheetV2 from '../item.mjs'; +import DHBaseItemSheet from '../api/base-item.mjs'; -const { ItemSheetV2 } = foundry.applications.sheets; -export default class ArmorSheet extends DHItemSheetV2(ItemSheetV2) { +export default class ArmorSheet extends DHBaseItemSheet { + /**@inheritdoc */ static DEFAULT_OPTIONS = { classes: ['armor'], - dragDrop: [{ dragSelector: null, dropSelector: null }] + dragDrop: [{ dragSelector: null, dropSelector: null }], + tagifyConfigs: [ + { + selector: '.features-input', + options: () => CONFIG.daggerheart.ITEM.armorFeatures, + callback: ArmorSheet.#onFeatureSelect + } + ] }; + /**@override */ static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/items/armor/header.hbs' }, tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, @@ -23,8 +29,9 @@ export default class ArmorSheet extends DHItemSheetV2(ItemSheetV2) { } }; + /**@inheritdoc */ async _preparePartContext(partId, context) { - super._preparePartContext(partId, context); + await super._preparePartContext(partId, context); switch (partId) { case 'settings': @@ -35,15 +42,11 @@ export default class ArmorSheet extends DHItemSheetV2(ItemSheetV2) { return context; } - _attachPartListeners(partId, htmlElement, options) { - super._attachPartListeners(partId, htmlElement, options); - - const featureInput = htmlElement.querySelector('.features-input'); - tagifyElement(featureInput, armorFeatures, this.onFeatureSelect.bind(this)); - } - - async onFeatureSelect(features) { - await this.document.update({ 'system.features': features.map(x => ({ value: x.value })) }); - this.render(true); + /** + * Callback function used by `tagifyElement`. + * @param {Array} selectedOptions - The currently selected tag objects. + */ + static async #onFeatureSelect(selectedOptions) { + await this.document.update({ 'system.features': selectedOptions.map(x => ({ value: x.value })) }); } } diff --git a/module/applications/sheets/items/class.mjs b/module/applications/sheets/items/class.mjs index a4659598..efe556fa 100644 --- a/module/applications/sheets/items/class.mjs +++ b/module/applications/sheets/items/class.mjs @@ -1,33 +1,29 @@ +import DHBaseItemSheet from '../api/base-item.mjs'; import { actionsTypes } from '../../../data/_module.mjs'; -import { tagifyElement } from '../../../helpers/utils.mjs'; import DHActionConfig from '../../config/Action.mjs'; -import DaggerheartSheet from '../daggerheart-sheet.mjs'; -const { ItemSheetV2 } = foundry.applications.sheets; const { TextEditor } = foundry.applications.ux; -export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) { +export default class ClassSheet extends DHBaseItemSheet { + /**@inheritdoc */ static DEFAULT_OPTIONS = { - tag: 'form', - classes: ['daggerheart', 'sheet', 'item', 'dh-style', 'class'], + classes: ['class'], position: { width: 700 }, actions: { - removeSubclass: this.removeSubclass, - viewSubclass: this.viewSubclass, + removeItemFromCollection: ClassSheet.#removeItemFromCollection, + removeSuggestedItem: ClassSheet.#removeSuggestedItem, + viewDoc: ClassSheet.#viewDoc, addFeature: this.addFeature, editFeature: this.editFeature, - deleteFeature: this.deleteFeature, - removeItem: this.removeItem, - viewItem: this.viewItem, - removePrimaryWeapon: this.removePrimaryWeapon, - removeSecondaryWeapon: this.removeSecondaryWeapon, - removeArmor: this.removeArmor - }, - form: { - handler: this.updateForm, - submitOnChange: true, - closeOnSubmit: false + deleteFeature: this.deleteFeature }, + tagifyConfigs: [ + { + selector: '.domain-input', + options: () => CONFIG.daggerheart.DOMAIN.domains, + callback: ClassSheet.#onDomainSelect + } + ], dragDrop: [ { dragSelector: '.suggested-item', dropSelector: null }, { dragSelector: null, dropSelector: '.take-section' }, @@ -40,9 +36,11 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) { ] }; + /**@override */ static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/items/class/header.hbs' }, tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, + description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' }, features: { template: 'systems/daggerheart/templates/sheets/items/class/features.hbs', scrollable: ['.features'] @@ -53,170 +51,33 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) { } }; + /** @inheritdoc */ static TABS = { - features: { - active: true, - cssClass: '', - group: 'primary', - id: 'features', - icon: null, - label: 'DAGGERHEART.Sheets.Class.Tabs.Features' - }, - settings: { - active: false, - cssClass: '', - group: 'primary', - id: 'settings', - icon: null, - label: 'DAGGERHEART.Sheets.Class.Tabs.settings' + primary: { + tabs: [{ id: 'description' }, { id: 'features' }, { id: 'settings' }], + initial: 'description', + labelPrefix: 'DAGGERHEART.Sheets.TABS' } }; - _attachPartListeners(partId, htmlElement, options) { - super._attachPartListeners(partId, htmlElement, options); - - const domainInput = htmlElement.querySelector('.domain-input'); - tagifyElement(domainInput, SYSTEM.DOMAIN.domains, this.onDomainSelect.bind(this)); - } - + /**@inheritdoc */ async _prepareContext(_options) { const context = await super._prepareContext(_options); - context.document = this.document; - context.tabs = super._getTabs(this.constructor.TABS); context.domains = this.document.system.domains; - return context; } - static async updateForm(event, _, formData) { - await this.document.update(formData.object); - this.render(); + /* -------------------------------------------- */ + + /** + * Callback function used by `tagifyElement`. + * @param {Array} selectedOptions - The currently selected tag objects. + */ + static async #onDomainSelect(selectedOptions) { + await this.document.update({ 'system.domains': selectedOptions.map(x => x.value) }); } - onAddTag(e) { - if (e.detail.index === 2) { - ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.ClassCanOnlyHaveTwoDomains')); - } - } - - async onDomainSelect(domains) { - await this.document.update({ 'system.domains': domains.map(x => x.value) }); - this.render(true); - } - - static async removeSubclass(_, button) { - await this.document.update({ - 'system.subclasses': this.document.system.subclasses.filter(x => x.uuid !== button.dataset.subclass) - }); - } - - static async viewSubclass(_, button) { - const subclass = await fromUuid(button.dataset.subclass); - subclass.sheet.render(true); - } - - static async deleteFeature(_, button) { - await this.document.update({ - 'system.features': this.document.system.features.map(x => x.uuid).filter(x => x !== button.dataset.feature) - }); - } - - static async editFeature(_, button) { - const feature = await fromUuid(button.dataset.feature); - feature.sheet.render(true); - } - - static async removeItem(event, button) { - event.stopPropagation(); - const type = button.dataset.type; - const path = `system.inventory.${type}`; - await this.document.update({ - [path]: this.document.system.inventory[type].filter(x => x.uuid !== button.dataset.item) - }); - } - - static async viewItem(_, button) { - const item = await fromUuid(button.dataset.item); - item.sheet.render(true); - } - - static async removePrimaryWeapon(event) { - event.stopPropagation(); - await this.document.update({ 'system.characterGuide.suggestedPrimaryWeapon': null }, { diff: false }); - } - - static async removeSecondaryWeapon(event) { - event.stopPropagation(); - await this.document.update({ 'system.characterGuide.suggestedSecondaryWeapon': null }, { diff: false }); - } - - static async removeArmor(event) { - event.stopPropagation(); - await this.document.update({ 'system.characterGuide.suggestedArmor': null }, { diff: false }); - } - - async selectActionType() { - const content = await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/views/actionType.hbs', - { types: SYSTEM.ACTIONS.actionTypes } - ), - title = 'Select Action Type', - type = 'form', - data = {}; - return Dialog.prompt({ - title, - label: title, - content, - type, - callback: html => { - const form = html[0].querySelector('form'), - fd = new foundry.applications.ux.FormDataExtended(form); - foundry.utils.mergeObject(data, fd.object, { inplace: true }); - - return data; - }, - rejectClose: false - }); - } - - getActionPath(type) { - return type === 'hope' ? 'hopeFeatures' : 'classFeatures'; - } - - static async addFeature(_, target) { - const actionPath = this.getActionPath(target.dataset.type); - const actionType = await this.selectActionType(); - const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack, - action = new cls( - { - _id: foundry.utils.randomID(), - systemPath: actionPath, - type: actionType.type, - name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name), - ...cls.getSourceConfig(this.document) - }, - { - parent: this.document - } - ); - await this.document.update({ [`system.${actionPath}`]: [...this.document.system[actionPath], action] }); - } - - static async editFeature(_, target) { - const action = this.document.system[this.getActionPath(target.dataset.type)].find( - x => x._id === target.dataset.feature - ); - await new DHActionConfig(action).render(true); - } - - static async deleteFeature(_, target) { - const actionPath = this.getActionPath(target.dataset.type); - await this.document.update({ - [`system.${actionPath}`]: this.document.system[actionPath].filter( - action => action._id !== target.dataset.feature - ) - }); - } + /* -------------------------------------------- */ async _onDrop(event) { const data = TextEditor.getDragEventData(event); @@ -272,4 +133,107 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) { } } } + + /* -------------------------------------------- */ + /* Application Clicks Actions */ + /* -------------------------------------------- */ + + /** + * Removes an item from an class collection by UUID. + * @param {PointerEvent} event - The originating click event + * @param {HTMLElement} element - The capturing HTML element which defines the [data-action="removeItemFromCollection"] + */ + static async #removeItemFromCollection(_event, element) { + const { uuid, target } = element.dataset; + const prop = foundry.utils.getProperty(this.document.system, target); + await this.document.update({ [target]: prop.filter(i => i.uuid !== uuid) }); + } + + /** + * Removes an suggested item from the class. + * @param {PointerEvent} _event - The originating click event + * @param {HTMLElement} element - The capturing HTML element which defines the [data-action="removeSuggestedItem"] + */ + static async #removeSuggestedItem(_event, element) { + const { target } = element.dataset; + await this.document.update({ [`system.characterGuide.${target}`]: null }); + } + + /** + * Open the sheet of a item by UUID. + * @param {PointerEvent} _event - + * @param {HTMLElement} button + */ + static async #viewDoc(_event, button) { + const doc = await fromUuid(button.dataset.uuid); + doc.sheet.render({ force: true }); + } + + //TODO: redo this + async selectActionType() { + const content = await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/views/actionType.hbs', + { types: SYSTEM.ACTIONS.actionTypes } + ), + title = 'Select Action Type', + type = 'form', + data = {}; + return Dialog.prompt({ + title, + label: title, + content, + type, + callback: html => { + const form = html[0].querySelector('form'), + fd = new foundry.applications.ux.FormDataExtended(form); + foundry.utils.mergeObject(data, fd.object, { inplace: true }); + + return data; + }, + rejectClose: false + }); + } + + //TODO: redo this + getActionPath(type) { + return type === 'hope' ? 'hopeFeatures' : 'classFeatures'; + } + + //TODO: redo this + static async addFeature(_, target) { + const actionPath = this.getActionPath(target.dataset.type); + const actionType = await this.selectActionType(); + const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack, + action = new cls( + { + _id: foundry.utils.randomID(), + systemPath: actionPath, + type: actionType.type, + name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name), + ...cls.getSourceConfig(this.document) + }, + { + parent: this.document + } + ); + await this.document.update({ [`system.${actionPath}`]: [...this.document.system[actionPath], action] }); + } + + //TODO: redo this + static async editFeature(_, target) { + const action = this.document.system[this.getActionPath(target.dataset.type)].find( + x => x._id === target.dataset.feature + ); + await new DHActionConfig(action).render(true); + } + + //TODO: redo this + static async deleteFeature(_, target) { + const actionPath = this.getActionPath(target.dataset.type); + await this.document.update({ + [`system.${actionPath}`]: this.document.system[actionPath].filter( + action => action._id !== target.dataset.feature + ) + }); + } } diff --git a/module/applications/sheets/items/community.mjs b/module/applications/sheets/items/community.mjs index 890ff605..92c3731e 100644 --- a/module/applications/sheets/items/community.mjs +++ b/module/applications/sheets/items/community.mjs @@ -1,11 +1,12 @@ -import DHHeritageSheetV2 from './heritage.mjs'; +import DHHeritageSheet from '../api/heritage-sheet.mjs'; -const { ItemSheetV2 } = foundry.applications.sheets; -export default class CommunitySheet extends DHHeritageSheetV2(ItemSheetV2) { +export default class CommunitySheet extends DHHeritageSheet { + /**@inheritdoc */ static DEFAULT_OPTIONS = { classes: ['community'] }; + /**@inheritdoc */ static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/items/community/header.hbs' }, ...super.PARTS diff --git a/module/applications/sheets/items/consumable.mjs b/module/applications/sheets/items/consumable.mjs index 815c6b9b..f6fb1ba5 100644 --- a/module/applications/sheets/items/consumable.mjs +++ b/module/applications/sheets/items/consumable.mjs @@ -1,12 +1,13 @@ -import DHItemSheetV2 from '../item.mjs'; +import DHBaseItemSheet from '../api/base-item.mjs'; -const { ItemSheetV2 } = foundry.applications.sheets; -export default class ConsumableSheet extends DHItemSheetV2(ItemSheetV2) { +export default class ConsumableSheet extends DHBaseItemSheet { + /**@inheritdoc */ static DEFAULT_OPTIONS = { classes: ['consumable'], position: { width: 550 } }; + /**@override */ static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/items/consumable/header.hbs' }, tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, diff --git a/module/applications/sheets/items/domainCard.mjs b/module/applications/sheets/items/domainCard.mjs index dd951be5..d7a70741 100644 --- a/module/applications/sheets/items/domainCard.mjs +++ b/module/applications/sheets/items/domainCard.mjs @@ -1,59 +1,37 @@ -import DHItemSheetV2 from '../item.mjs'; +import DHBaseItemSheet from '../api/base-item.mjs'; -const { ItemSheetV2 } = foundry.applications.sheets; -export default class DomainCardSheet extends DHItemSheetV2(ItemSheetV2) { +export default class DomainCardSheet extends DHBaseItemSheet { + /**@inheritdoc */ static DEFAULT_OPTIONS = { classes: ['domain-card'], - position: { width: 450, height: 700 }, - actions: { - addEffect: this.addEffect, - editEffect: this.editEffect, - removeEffect: this.removeEffect + position: { width: 450, height: 700 } + }; + + /** @override */ + static TABS = { + primary: { + tabs: [{ id: 'description' }, { id: 'actions' }, { id: 'settings' }, { id: 'effects' }], + initial: 'description', + labelPrefix: 'DAGGERHEART.Sheets.TABS' } }; + /**@override */ static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/items/domainCard/header.hbs' }, tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' }, - settings: { - template: 'systems/daggerheart/templates/sheets/items/domainCard/settings.hbs', - scrollable: ['.settings'] - }, actions: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs', scrollable: ['.actions'] }, + settings: { + template: 'systems/daggerheart/templates/sheets/items/domainCard/settings.hbs', + scrollable: ['.settings'] + }, effects: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs', scrollable: ['.effects'] } }; - - static TABS = { - ...super.TABS, - effects: { - active: false, - cssClass: '', - group: 'primary', - id: 'effects', - icon: null, - label: 'DAGGERHEART.Sheets.Feature.Tabs.Effects' - } - }; - - static async addEffect() { - await this.document.createEmbeddedDocuments('ActiveEffect', [ - { name: game.i18n.localize('DAGGERHEART.Feature.NewEffect') } - ]); - } - - static async editEffect(_, target) { - const effect = this.document.effects.get(target.dataset.effect); - effect.sheet.render(true); - } - - static async removeEffect(_, target) { - await this.document.effects.get(target.dataset.effect).delete(); - } } diff --git a/module/applications/sheets/items/feature.mjs b/module/applications/sheets/items/feature.mjs index 40fa9b02..655e4542 100644 --- a/module/applications/sheets/items/feature.mjs +++ b/module/applications/sheets/items/feature.mjs @@ -1,17 +1,11 @@ -import DHItemSheetV2 from '../item.mjs'; - -const { ItemSheetV2 } = foundry.applications.sheets; -export default class FeatureSheet extends DHItemSheetV2(ItemSheetV2) { - constructor(options = {}) { - super(options); - - this.selectedEffectType = null; - } +import DHBaseItemSheet from '../api/base-item.mjs'; +export default class FeatureSheet extends DHBaseItemSheet { + /** @inheritDoc */ static DEFAULT_OPTIONS = { id: 'daggerheart-feature', classes: ['feature'], - position: { width: 600, height: 600 }, + position: { height: 600 }, window: { resizable: true }, actions: { addEffect: this.addEffect, @@ -19,6 +13,7 @@ export default class FeatureSheet extends DHItemSheetV2(ItemSheetV2) { } }; + /**@override */ static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/items/feature/header.hbs' }, tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, @@ -37,58 +32,86 @@ export default class FeatureSheet extends DHItemSheetV2(ItemSheetV2) { } }; + /** + * Internally tracks the selected effect type from the select. + * @type {String} + * @private + */ + _selectedEffectType; + + /**@override */ static TABS = { - ...super.TABS, - effects: { - active: false, - cssClass: '', - group: 'primary', - id: 'effects', - icon: null, - label: 'DAGGERHEART.Sheets.Feature.Tabs.Effects' + primary: { + tabs: [{ id: 'description' }, { id: 'actions' }, { id: 'settings' }, { id: 'effects' }], + initial: 'description', + labelPrefix: 'DAGGERHEART.Sheets.TABS' } }; + /* -------------------------------------------- */ + + /**@inheritdoc*/ _attachPartListeners(partId, htmlElement, options) { super._attachPartListeners(partId, htmlElement, options); - $(htmlElement).find('.effect-select').on('change', this.effectSelect.bind(this)); + if (partId === 'effects') + htmlElement.querySelector('.effect-select')?.addEventListener('change', this._effectSelect.bind(this)); } + /** + * Handles selection of a new effect type. + * @param {Event} event - Change Event + */ + _effectSelect(event) { + const value = event.currentTarget.value; + this._selectedEffectType = value; + this.render({ parts: ['effects'] }); + } + + /* -------------------------------------------- */ + + /**@inheritdoc */ async _prepareContext(_options) { const context = await super._prepareContext(_options); - context.document = this.document; - context.tabs = super._getTabs(this.constructor.TABS); - context.generalConfig = SYSTEM.GENERAL; - context.itemConfig = SYSTEM.ITEM; - context.properties = SYSTEM.ACTOR.featureProperties; - context.dice = SYSTEM.GENERAL.diceTypes; - context.selectedEffectType = this.selectedEffectType; - context.effectConfig = SYSTEM.EFFECTS; + context.properties = CONFIG.daggerheart.ACTOR.featureProperties; + context.dice = CONFIG.daggerheart.GENERAL.diceTypes; + context.effectConfig = CONFIG.daggerheart.EFFECTS; + + context.selectedEffectType = this._selectedEffectType; return context; } - effectSelect(event) { - this.selectedEffectType = event.currentTarget.value; - this.render(true); - } + /* -------------------------------------------- */ + /* Application Clicks Actions */ + /* -------------------------------------------- */ - static async addEffect() { - if (!this.selectedEffectType) return; - - const { id, name, ...rest } = SYSTEM.EFFECTS.effectTypes[this.selectedEffectType]; - const update = { - [foundry.utils.randomID()]: { - type: this.selectedEffectType, + /** + * Adds a new effect to the item, based on the selected effect type. + * @param {PointerEvent} _event - The originating click event + * @param {HTMLElement} _target - The capturing HTML element which defines the [data-action] + * @returns + */ + static async addEffect(_event, _target) { + const type = this._selectedEffectType; + if (!type) return; + const { id, name, ...rest } = CONFIG.daggerheart.EFFECTS.effectTypes[type]; + await this.item.update({ + [`system.effects.${foundry.utils.randomID()}`]: { + type, value: '', ...rest } - }; - await this.item.update({ 'system.effects': update }); + }); } - static async removeEffect(_, button) { - const path = `system.effects.-=${button.dataset.effect}`; + /** + * Removes an effect from the item. + * @param {PointerEvent} _event - The originating click event + * @param {HTMLElement} target - The capturing HTML element which defines the [data-action] + * @returns + */ + static async removeEffect(_event, target) { + const path = `system.effects.-=${target.dataset.effect}`; await this.item.update({ [path]: null }); } } diff --git a/module/applications/sheets/items/heritage.mjs b/module/applications/sheets/items/heritage.mjs deleted file mode 100644 index 44d950ca..00000000 --- a/module/applications/sheets/items/heritage.mjs +++ /dev/null @@ -1,147 +0,0 @@ -import { actionsTypes } from '../../../data/_module.mjs'; -import DHActionConfig from '../../config/Action.mjs'; -import DHItemMixin from '../item.mjs'; - -export default function DHHeritageMixin(Base) { - return class DHHeritageSheetV2 extends DHItemMixin(Base) { - static DEFAULT_OPTIONS = { - tag: 'form', - position: { width: 450, height: 700 }, - actions: { - addAction: this.addAction, - editAction: this.editAction, - removeAction: this.removeAction, - addEffect: this.addEffect, - editEffect: this.editEffect, - removeEffect: this.removeEffect - }, - form: { - handler: this.updateForm, - submitOnChange: true, - closeOnSubmit: false - } - }; - - static PARTS = { - tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, - description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' }, - actions: { - template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs', - scrollable: ['.actions'] - }, - effects: { - template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs', - scrollable: ['.effects'] - } - }; - - static TABS = { - description: { - active: true, - cssClass: '', - group: 'primary', - id: 'description', - icon: null, - label: 'DAGGERHEART.Sheets.Feature.Tabs.Description' - }, - actions: { - active: false, - cssClass: '', - group: 'primary', - id: 'actions', - icon: null, - label: 'DAGGERHEART.Sheets.Feature.Tabs.Actions' - }, - effects: { - active: false, - cssClass: '', - group: 'primary', - id: 'effects', - icon: null, - label: 'DAGGERHEART.Sheets.Feature.Tabs.Effects' - } - }; - - async _prepareContext(_options) { - const context = await super._prepareContext(_options); - context.document = this.document; - context.tabs = super._getTabs(this.constructor.TABS); - - return context; - } - - static async updateForm(event, _, formData) { - await this.document.update(formData.object); - this.render(); - } - - async selectActionType() { - const content = await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/views/actionType.hbs', - { types: SYSTEM.ACTIONS.actionTypes } - ), - title = 'Select Action Type', - type = 'form', - data = {}; - return Dialog.prompt({ - title, - label: title, - content, - type, - callback: html => { - const form = html[0].querySelector('form'), - fd = new foundry.applications.ux.FormDataExtended(form); - foundry.utils.mergeObject(data, fd.object, { inplace: true }); - return data; - }, - rejectClose: false - }); - } - - static async addAction() { - const actionType = await this.selectActionType(); - const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack, - action = new cls( - { - _id: foundry.utils.randomID(), - type: actionType.type, - name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name), - ...cls.getSourceConfig(this.document) - }, - { - parent: this.document - } - ); - await this.document.update({ 'system.actions': [...this.document.system.actions, action] }); - } - - static async editAction(_, button) { - const action = this.document.system.actions[button.dataset.index]; - await new DHActionConfig(action).render(true); - } - - static async removeAction(event, button) { - event.stopPropagation(); - await this.document.update({ - 'system.actions': this.document.system.actions.filter( - (_, index) => index !== Number.parseInt(button.dataset.index) - ) - }); - } - - static async addEffect() { - await this.document.createEmbeddedDocuments('ActiveEffect', [ - { name: game.i18n.localize('DAGGERHEART.Feature.NewEffect') } - ]); - } - - static async editEffect(_, target) { - const effect = this.document.effects.get(target.dataset.effect); - effect.sheet.render(true); - } - - static async removeEffect(_, target) { - await this.document.effects.get(target.dataset.effect).delete(); - } - }; -} diff --git a/module/applications/sheets/items/miscellaneous.mjs b/module/applications/sheets/items/miscellaneous.mjs index dd22d216..62df4e8c 100644 --- a/module/applications/sheets/items/miscellaneous.mjs +++ b/module/applications/sheets/items/miscellaneous.mjs @@ -1,12 +1,13 @@ -import DHItemSheetV2 from '../item.mjs'; +import DHBaseItemSheet from '../api/base-item.mjs'; -const { ItemSheetV2 } = foundry.applications.sheets; -export default class MiscellaneousSheet extends DHItemSheetV2(ItemSheetV2) { +export default class MiscellaneousSheet extends DHBaseItemSheet { + /**@inheritdoc */ static DEFAULT_OPTIONS = { classes: ['miscellaneous'], position: { width: 550 } }; + /**@override */ static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/items/miscellaneous/header.hbs' }, tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, diff --git a/module/applications/sheets/items/subclass.mjs b/module/applications/sheets/items/subclass.mjs index e6e9725f..841c3eb2 100644 --- a/module/applications/sheets/items/subclass.mjs +++ b/module/applications/sheets/items/subclass.mjs @@ -1,26 +1,21 @@ +import DHBaseItemSheet from '../api/base-item.mjs'; import { actionsTypes } from '../../../data/_module.mjs'; import DHActionConfig from '../../config/Action.mjs'; -import DhpApplicationMixin from '../daggerheart-sheet.mjs'; -const { ItemSheetV2 } = foundry.applications.sheets; -export default class SubclassSheet extends DhpApplicationMixin(ItemSheetV2) { +export default class SubclassSheet extends DHBaseItemSheet { + /**@inheritdoc */ static DEFAULT_OPTIONS = { - tag: 'form', - classes: ['daggerheart', 'sheet', 'item', 'dh-style', 'subclass'], + classes: ['subclass'], position: { width: 600 }, window: { resizable: false }, actions: { addFeature: this.addFeature, editFeature: this.editFeature, deleteFeature: this.deleteFeature - }, - form: { - handler: this.updateForm, - submitOnChange: true, - closeOnSubmit: false } }; + /**@override */ static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/items/subclass/header.hbs' }, tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, @@ -35,46 +30,16 @@ export default class SubclassSheet extends DhpApplicationMixin(ItemSheetV2) { } }; + /** @inheritdoc */ static TABS = { - description: { - active: true, - cssClass: '', - group: 'primary', - id: 'description', - icon: null, - label: 'DAGGERHEART.Sheets.Feature.Tabs.Description' - }, - features: { - active: false, - cssClass: '', - group: 'primary', - id: 'features', - icon: null, - label: 'DAGGERHEART.Sheets.Feature.Tabs.Features' - }, - settings: { - active: false, - cssClass: '', - group: 'primary', - id: 'settings', - icon: null, - label: 'DAGGERHEART.Sheets.Feature.Tabs.Settings' + primary: { + tabs: [{ id: 'description' }, { id: 'features' }, { id: 'settings' }], + initial: 'description', + labelPrefix: 'DAGGERHEART.Sheets.TABS' } }; - async _prepareContext(_options) { - const context = await super._prepareContext(_options); - context.document = this.document; - context.config = CONFIG.daggerheart; - context.tabs = super._getTabs(this.constructor.TABS); - - return context; - } - - static async updateForm(event, _, formData) { - await this.document.update(formData.object); - this.render(); - } + //TODO redo everything below this message static addFeature(_, target) { if (target.dataset.type === 'action') this.addAction(target.dataset.level); diff --git a/module/applications/sheets/items/weapon.mjs b/module/applications/sheets/items/weapon.mjs index b381e8bf..5f2a12d7 100644 --- a/module/applications/sheets/items/weapon.mjs +++ b/module/applications/sheets/items/weapon.mjs @@ -1,13 +1,19 @@ -import { weaponFeatures } from '../../../config/itemConfig.mjs'; -import { tagifyElement } from '../../../helpers/utils.mjs'; -import DHItemSheetV2 from '../item.mjs'; +import DHBaseItemSheet from '../api/base-item.mjs'; -const { ItemSheetV2 } = foundry.applications.sheets; -export default class WeaponSheet extends DHItemSheetV2(ItemSheetV2) { +export default class WeaponSheet extends DHBaseItemSheet { + /**@inheritdoc */ static DEFAULT_OPTIONS = { - classes: ['weapon'] + classes: ['weapon'], + tagifyConfigs: [ + { + selector: '.features-input', + options: () => CONFIG.daggerheart.ITEM.weaponFeatures, + callback: WeaponSheet.#onFeatureSelect + } + ] }; + /**@override */ static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/items/weapon/header.hbs' }, tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, @@ -22,6 +28,7 @@ export default class WeaponSheet extends DHItemSheetV2(ItemSheetV2) { } }; + /**@inheritdoc */ async _preparePartContext(partId, context) { super._preparePartContext(partId, context); @@ -34,15 +41,11 @@ export default class WeaponSheet extends DHItemSheetV2(ItemSheetV2) { return context; } - _attachPartListeners(partId, htmlElement, options) { - super._attachPartListeners(partId, htmlElement, options); - - const featureInput = htmlElement.querySelector('.features-input'); - tagifyElement(featureInput, weaponFeatures, this.onFeatureSelect.bind(this)); - } - - async onFeatureSelect(features) { - await this.document.update({ 'system.features': features.map(x => ({ value: x.value })) }); - this.render(true); + /** + * Callback function used by `tagifyElement`. + * @param {Array} selectedOptions - The currently selected tag objects. + */ + static async #onFeatureSelect(selectedOptions) { + await this.document.update({ 'system.features': selectedOptions.map(x => ({ value: x.value })) }); } } diff --git a/templates/sheets/global/tabs/tab-description.hbs b/templates/sheets/global/tabs/tab-description.hbs index 58a90ed7..4e2de269 100755 --- a/templates/sheets/global/tabs/tab-description.hbs +++ b/templates/sheets/global/tabs/tab-description.hbs @@ -5,6 +5,6 @@ >
{{localize "DAGGERHEART.Sheets.Feature.Description"}} - {{formInput systemFields.description value=source.system.description enriched=source.system.description localize=true toggled=true}} + {{formInput systemFields.description value=document.system.description enriched=enrichedDescription toggled=true}}
\ No newline at end of file diff --git a/templates/sheets/items/class/features.hbs b/templates/sheets/items/class/features.hbs index 8b615121..5d8d8a70 100644 --- a/templates/sheets/items/class/features.hbs +++ b/templates/sheets/items/class/features.hbs @@ -36,16 +36,17 @@
diff --git a/templates/sheets/items/class/settings.hbs b/templates/sheets/items/class/settings.hbs index 1333bc9a..1a211f2b 100644 --- a/templates/sheets/items/class/settings.hbs +++ b/templates/sheets/items/class/settings.hbs @@ -39,11 +39,11 @@ {{localize "DAGGERHEART.Sheets.Class.Guide.SuggestedPrimaryWeaponTitle"}}
{{#if document.system.characterGuide.suggestedPrimaryWeapon}} -
+
{{document.system.characterGuide.suggestedPrimaryWeapon.name}}
- +
{{/if}} @@ -54,11 +54,11 @@ {{localize "DAGGERHEART.Sheets.Class.Guide.SuggestedSecondaryWeaponTitle"}}
{{#if document.system.characterGuide.suggestedSecondaryWeapon}} -
+
{{document.system.characterGuide.suggestedSecondaryWeapon.name}}
- +
{{/if}} @@ -69,11 +69,11 @@ {{localize "DAGGERHEART.Sheets.Class.Guide.SuggestedArmorTitle"}}
{{#if document.system.characterGuide.suggestedArmor}} -
+
{{document.system.characterGuide.suggestedArmor.name}}
- +
{{/if}} @@ -87,11 +87,11 @@ {{localize "DAGGERHEART.Sheets.Class.Guide.Inventory.Take"}}
{{#each source.system.inventory.take}} -
+
{{this.name}}
- +
{{/each}} @@ -102,11 +102,11 @@ {{localize "DAGGERHEART.Sheets.Class.Guide.Inventory.ThenChoose"}}
{{#each source.system.inventory.choiceA}} -
+
{{this.name}}
- +
{{/each}} @@ -117,11 +117,11 @@ {{localize "DAGGERHEART.Sheets.Class.Guide.Inventory.AndEither"}}
{{#each source.system.inventory.choiceB}} -
+
{{this.name}}
- +
{{/each}} diff --git a/templates/sheets/items/feature/effects.hbs b/templates/sheets/items/feature/effects.hbs index 68fed719..52beb934 100755 --- a/templates/sheets/items/feature/effects.hbs +++ b/templates/sheets/items/feature/effects.hbs @@ -8,15 +8,15 @@ {{localize "DAGGERHEART.Sheets.Feature.effects.addEffect"}}
- - + +
- {{#each this.document.system.effects as |effect key|}} + {{#each document.system.effects as |effect key|}}
{{localize (concat 'DAGGERHEART.Effects.Types.' effect.type '.Name')}} @@ -32,8 +32,8 @@ {{selectOptions effect.applyLocationChoices selected=effect.appliesOn localize=true}} {{/if}} - {{#if (eq effect.valueType ../this.effectConfig.valueTypes.numberString.id)}} - {{#if (eq effect.type ../this.effectConfig.effectTypes.damage.id) }} + {{#if (eq effect.valueType @root.effectConfig.valueTypes.numberString.id)}} + {{#if (eq effect.type @root.effectConfig.effectTypes.damage.id) }} {{localize "DAGGERHEART.Sheets.Feature.effects.value"}} @@ -47,7 +47,7 @@ {{/if}} {{/if}} - {{#if (eq effect.valueType ../this.effectConfig.valueTypes.select.id)}} + {{#if (eq effect.valueType @root.effectConfig.valueTypes.select.id)}} {{localize effect.valueData.fromValue}} diff --git a/templates/sheets/items/feature/settings.hbs b/templates/sheets/items/feature/settings.hbs index cca672ea..dc5cb884 100755 --- a/templates/sheets/items/feature/settings.hbs +++ b/templates/sheets/items/feature/settings.hbs @@ -22,13 +22,13 @@ {{localize "DAGGERHEART.Sheets.Feature.ValueType.Title"}} {{localize "DAGGERHEART.Sheets.Feature.ValueType.Title"}} {{#if (eq document.system.featureType.type "dice")}} {{localize "DAGGERHEART.Sheets.Feature.ValueType.Dice"}} {{localize "DAGGERHEART.Feature.Max"}} @@ -36,7 +36,7 @@ {{localize "DAGGERHEART.Sheets.Feature.ValueType.Property"}} {{/if}}
diff --git a/templates/views/actionType.hbs b/templates/views/actionType.hbs index 75dceeb7..fdffaabe 100644 --- a/templates/views/actionType.hbs +++ b/templates/views/actionType.hbs @@ -3,6 +3,7 @@ {{#each types}}