Files
fvtt-hawkmoon-cyd/modules/applications/sheets/base-item-sheet.mjs

380 lines
11 KiB
JavaScript

const { HandlebarsApplicationMixin } = foundry.applications.api
export default class HawkmoonItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
/**
* Different sheet modes.
* @enum {number}
*/
static SHEET_MODES = { EDIT: 0, PLAY: 1 }
constructor(options = {}) {
super(options)
this.#dragDrop = this.#createDragDropHandlers()
}
#dragDrop
/** @override */
static DEFAULT_OPTIONS = {
classes: ["fvtt-hawkmoon-cyd", "item"],
position: {
width: 620,
height: "auto",
},
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: {
toggleSheet: HawkmoonItemSheet.#onToggleSheet,
editImage: HawkmoonItemSheet.#onEditImage,
postItem: HawkmoonItemSheet.#onPostItem,
addPredilection: HawkmoonItemSheet.#onAddPredilection,
deletePredilection: HawkmoonItemSheet.#onDeletePredilection,
addAutomation: HawkmoonItemSheet.#onAddAutomation,
deleteAutomation: HawkmoonItemSheet.#onDeleteAutomation,
},
}
/**
* The current sheet mode.
* @type {number}
*/
_sheetMode = this.constructor.SHEET_MODES.PLAY
/**
* Tab groups state
* @type {object}
*/
tabGroups = { primary: "description" }
/**
* Is the sheet currently in 'Play' mode?
* @type {boolean}
*/
get isPlayMode() {
return this._sheetMode === this.constructor.SHEET_MODES.PLAY
}
/**
* Is the sheet currently in 'Edit' mode?
* @type {boolean}
*/
get isEditMode() {
return this._sheetMode === this.constructor.SHEET_MODES.EDIT
}
/** @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: this.isEditMode,
isPlayMode: this.isPlayMode,
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
/**
* Toggle between Edit and Play mode
* @param {Event} event The triggering event
* @param {HTMLElement} target The target element
* @private
*/
static #onToggleSheet(event, target) {
this._sheetMode = this.isEditMode ? this.constructor.SHEET_MODES.PLAY : this.constructor.SHEET_MODES.EDIT
this.render()
}
/**
* 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)
}
}