worldbuilding/module/actor-sheet.js

141 lines
4.2 KiB
JavaScript

/**
* Extend the basic ActorSheet with some very simple modifications
*/
export class SimpleActorSheet extends ActorSheet {
constructor(...args) {
super(...args);
/**
* Keep track of the currently active sheet tab
* @type {string}
*/
this._sheetTab = "description";
}
/* -------------------------------------------- */
/**
* Extend and override the default options used by the 5e Actor Sheet
* @returns {Object}
*/
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["worldbuilding", "sheet", "actor"],
template: "public/systems/worldbuilding/templates/actor-sheet.html",
width: 600,
height: 600
});
}
/* -------------------------------------------- */
/**
* Prepare data for rendering the Actor sheet
* The prepared data object contains both the actor data as well as additional sheet options
*/
getData() {
const data = super.getData();
data.dtypes = ["String", "Number", "Boolean"];
for ( let attr of Object.values(data.data.attributes) ) {
attr.isCheckbox = attr.dtype === "Boolean";
}
return data;
}
/* -------------------------------------------- */
/**
* Activate event listeners using the prepared sheet HTML
* @param html {HTML} The prepared HTML object ready to be rendered into the DOM
*/
activateListeners(html) {
super.activateListeners(html);
// Activate tabs
let tabs = html.find('.tabs');
let initial = this._sheetTab;
new Tabs(tabs, {
initial: initial,
callback: clicked => this._sheetTab = clicked.data("tab")
});
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// Update Inventory Item
html.find('.item-edit').click(ev => {
const li = $(ev.currentTarget).parents(".item");
const item = this.actor.getOwnedItem(li.data("itemId"));
item.sheet.render(true);
});
// Delete Inventory Item
html.find('.item-delete').click(ev => {
const li = $(ev.currentTarget).parents(".item");
this.actor.deleteOwnedItem(li.data("itemId"));
li.slideUp(200, () => this.render(false));
});
// Add or Remove Attribute
html.find(".attributes").on("click", ".attribute-control", this._onClickAttributeControl.bind(this));
}
/* -------------------------------------------- */
async _onClickAttributeControl(event) {
event.preventDefault();
const a = event.currentTarget;
const action = a.dataset.action;
const attrs = this.object.data.data.attributes;
const form = this.form;
// Add new attribute
if ( action === "create" ) {
const nk = Object.keys(attrs).length + 1;
let newKey = document.createElement("div");
newKey.innerHTML = `<input type="text" name="data.attributes.attr${nk}.key" value="attr${nk}"/>`;
newKey = newKey.children[0];
form.appendChild(newKey);
await this._onSubmit(event);
}
// Remove existing attribute
else if ( action === "delete" ) {
const li = a.closest(".attribute");
li.parentElement.removeChild(li);
await this._onSubmit(event);
}
}
/* -------------------------------------------- */
/**
* Implement the _updateObject method as required by the parent class spec
* This defines how to update the subject of the form when the form is submitted
* @private
*/
_updateObject(event, formData) {
// Handle the free-form attributes list
const formAttrs = expandObject(formData).data.attributes || {};
const attributes = Object.fromEntries(Object.entries(formAttrs).map(attr => {
let [k, v] = attr;
k = v["key"].trim();
delete v["key"];
return [k, v];
}));
// Remove attributes which are no longer used
for ( let k of Object.keys(this.object.data.data.attributes) ) {
if ( !attributes.hasOwnProperty(k) ) attributes[`-=${k}`] = null;
}
// Re-combine formData
formData = Object.fromEntries(Object.entries(formData).filter(a => !a[0].startsWith("data.attributes")));
formData["_id"] = this.object._id;
formData["data.attributes"] = attributes;
// Update the Actor
return this.object.update(formData);
}
}