const { HandlebarsApplicationMixin } = foundry.applications.api export default class HawkmoonItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) { constructor(options = {}) { super(options) this.#dragDrop = this.#createDragDropHandlers() } #dragDrop /** @override */ static DEFAULT_OPTIONS = { classes: ["fvtt-hawkmoon-cyd", "item"], position: { width: 620, height: 600, }, form: { submitOnChange: true, }, window: { resizable: true, }, tabs: [ { navSelector: 'nav[data-group="primary"]', contentSelector: "section.sheet-body", initial: "description", }, ], dragDrop: [{ dragSelector: "[data-drag]", dropSelector: null }], actions: { editImage: HawkmoonItemSheet.#onEditImage, postItem: HawkmoonItemSheet.#onPostItem, addPredilection: HawkmoonItemSheet.#onAddPredilection, deletePredilection: HawkmoonItemSheet.#onDeletePredilection, addAutomation: HawkmoonItemSheet.#onAddAutomation, deleteAutomation: HawkmoonItemSheet.#onDeleteAutomation, }, } /** * Tab groups state * @type {object} */ tabGroups = { primary: "description" } /** * Is the sheet currently in 'Play' mode? * @type {boolean} */ /** @override */ async _prepareContext() { const context = { fields: this.document.schema.fields, systemFields: this.document.system.schema.fields, item: this.document, system: this.document.system, source: this.document.toObject(), enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true }), isEditMode: true, isEditable: this.isEditable, isGM: game.user.isGM, config: CONFIG.HAWKMOON, attributs: this.#getAttributs(), } return context } /** @override */ _onRender(context, options) { super._onRender(context, options) this.#dragDrop.forEach((d) => d.bind(this.element)) // Activate tab navigation manually const nav = this.element.querySelector('nav.tabs[data-group]') if (nav) { const group = nav.dataset.group // Activate the current tab const activeTab = this.tabGroups[group] || "description" nav.querySelectorAll('[data-tab]').forEach(link => { const tab = link.dataset.tab link.classList.toggle('active', tab === activeTab) link.addEventListener('click', (event) => { event.preventDefault() this.tabGroups[group] = tab this.render() }) }) // Show/hide tab content this.element.querySelectorAll('[data-group="' + group + '"][data-tab]').forEach(content => { content.classList.toggle('active', content.dataset.tab === activeTab) }) } } // #region Drag-and-Drop Workflow /** * Create drag-and-drop workflow handlers for this Application * @returns {DragDrop[]} An array of DragDrop handlers * @private */ #createDragDropHandlers() { return this.options.dragDrop.map((d) => { d.permissions = { dragstart: this._canDragStart.bind(this), drop: this._canDragDrop.bind(this), } d.callbacks = { dragstart: this._onDragStart.bind(this), dragover: this._onDragOver.bind(this), drop: this._onDrop.bind(this), } return new foundry.applications.ux.DragDrop.implementation(d) }) } /** * Can the User start a drag workflow for a given drag selector? * @param {string} selector The candidate HTML selector for the drag event * @returns {boolean} Can the current user drag this selector? * @protected */ _canDragStart(selector) { return this.isEditable } /** * Can the User drop an entry at a given drop selector? * @param {string} selector The candidate HTML selector for the drop event * @returns {boolean} Can the current user drop on this selector? * @protected */ _canDragDrop(selector) { return this.isEditable } /** * Callback for dragstart events. * @param {DragEvent} event The drag start event * @protected */ _onDragStart(event) { const target = event.currentTarget const dragData = { type: "Item", uuid: this.document.uuid } event.dataTransfer.setData("text/plain", JSON.stringify(dragData)) } /** * Callback for dragover events. * @param {DragEvent} event The drag over event * @protected */ _onDragOver(event) { // Default behavior is fine } /** * Callback for drop events. * @param {DragEvent} event The drop event * @protected */ async _onDrop(event) { const data = TextEditor.getDragEventData(event) const item = await fromUuid(data.uuid) if (!item) return // Handle drop logic here if needed console.log("Item dropped:", item) } // #endregion // #region Action Handlers /** * Edit the item image * @param {Event} event The triggering event * @param {HTMLElement} target The target element * @private */ static async #onEditImage(event, target) { const fp = new foundry.applications.ui.FilePicker({ type: "image", current: this.document.img, callback: (path) => { this.document.update({ img: path }) }, }) return fp.browse() } /** * Post item to chat * @param {Event} event The triggering event * @param {HTMLElement} target The target element * @private */ static async #onPostItem(event, target) { let chatData = foundry.utils.duplicate(this.document) if (this.document.actor) { chatData.actor = { id: this.document.actor.id } } // Don't post any image for the item if the default image is used if (chatData.img.includes("/blank.png")) { chatData.img = null } // JSON object for easy creation chatData.jsondata = JSON.stringify({ compendium: "postedItem", payload: chatData, }) const html = await renderTemplate('systems/fvtt-hawkmoon-cyd/templates/post-item.hbs', chatData) const chatOptions = { user: game.user.id, content: html, } ChatMessage.create(chatOptions) } /** * Add a predilection * @param {Event} event The triggering event * @param {HTMLElement} target The target element * @private */ static async #onAddPredilection(event, target) { let pred = foundry.utils.duplicate(this.document.system.predilections || []) pred.push({ name: "Nouvelle prédilection", id: foundry.utils.randomID(16), used: false, acquise: false, maitrise: false, description: "" }) await this.document.update({ 'system.predilections': pred }) } /** * Delete a predilection * @param {Event} event The triggering event * @param {HTMLElement} target The target element * @private */ static async #onDeletePredilection(event, target) { const index = parseInt(target.closest("[data-predilection-index]").dataset.predilectionIndex) let pred = foundry.utils.duplicate(this.document.system.predilections) pred.splice(index, 1) await this.document.update({ 'system.predilections': pred }) } /** * Add an automation * @param {Event} event The triggering event * @param {HTMLElement} target The target element * @private */ static async #onAddAutomation(event, target) { let autom = foundry.utils.duplicate(this.document.system.automations || []) autom.push({ eventtype: "on-drop", name: "Automatisation 1", bonusname: "vigueur", bonus: 0, competence: "", minLevel: 0, baCost: 0, id: foundry.utils.randomID(16) }) await this.document.update({ 'system.automations': autom }) } /** * Delete an automation * @param {Event} event The triggering event * @param {HTMLElement} target The target element * @private */ static async #onDeleteAutomation(event, target) { const index = parseInt(target.closest("[data-automation-index]").dataset.automationIndex) let autom = foundry.utils.duplicate(this.document.system.automations) autom.splice(index, 1) await this.document.update({ 'system.automations': autom }) } // #endregion // #region Helper Methods /** * Get list of attributs * @returns {Object} * @private */ #getAttributs() { return { "adr": "Adresse", "pui": "Puissance", "cla": "Clairvoyance", "pre": "Présence", "tre": "Trempe" } } // #endregion /** @override */ _onChangeForm(formConfig, event) { // Handle special form changes const target = event.target // Handle predilection field changes if (target.classList.contains('edit-predilection') || target.classList.contains('edit-predilection-description') || target.classList.contains('predilection-acquise') || target.classList.contains('predilection-maitrise') || target.classList.contains('predilection-used')) { const li = target.closest('.prediction-item') if (li) { const index = parseInt(li.dataset.predictionIndex) const field = target.classList.contains('edit-predilection') ? 'name' : target.classList.contains('edit-predilection-description') ? 'description' : target.classList.contains('predilection-acquise') ? 'acquise' : target.classList.contains('predilection-maitrise') ? 'maitrise' : 'used' let pred = foundry.utils.duplicate(this.document.system.predilections) if (target.type === 'checkbox') { pred[index][field] = target.checked } else { pred[index][field] = target.value } pred[index].id = pred[index].id || foundry.utils.randomID(16) this.document.update({ 'system.predilections': pred }) return } } // Handle automation field changes if (target.classList.contains('automation-edit-field')) { const index = parseInt(target.dataset.automationIndex) const field = target.dataset.automationField let auto = foundry.utils.duplicate(this.document.system.automations) auto[index][field] = target.value auto[index].id = auto[index].id || foundry.utils.randomID(16) this.document.update({ 'system.automations': auto }) return } super._onChangeForm(formConfig, event) } }