Get items appearing on the sheet, still a lot to do but this is a strong step in the right direction

This commit is contained in:
Oliver 2026-03-15 22:37:55 -06:00
parent 6b03d62234
commit 23a402f11a
9 changed files with 254 additions and 10 deletions

View file

@ -9,6 +9,11 @@
},
"taf": {
"settings": {
"actorDefaultAttributes": {
"name": "Remove Default Attributes",
"hint": "This removes the default attributes that are applied when a new actor is created, making it so that no attributes get created alongside the actor.",
"label": "Remove Attributes"
},
"canPlayersManageAttributes": {
"name": "Players Can Manage Attributes",
"hint": "This allows players who have edit access to a document to be able to edit what attributes those characters have via the attribute editor"
@ -34,10 +39,9 @@
"true": "Resizable"
}
},
"actorDefaultAttributes": {
"name": "Remove Default Attributes",
"hint": "This removes the default attributes that are applied when a new actor is created, making it so that no attributes get created alongside the actor.",
"label": "Remove Attributes"
"weightUnit": {
"name": "Weight Unit",
"hint": "This unit is used to display the units for the weights of items and carrying capacity of actors. This does NOTHING beyond adding the unit into the displays, it will not automatically convert between any units."
}
},
"sheet-names": {

View file

@ -2,10 +2,12 @@ import { __ID__, filePath } from "../consts.mjs";
import { AttributeManager } from "./AttributeManager.mjs";
import { attributeSorter } from "../utils/attributeSort.mjs";
import { TAFDocumentSheetConfig } from "./TAFDocumentSheetConfig.mjs";
import { toPrecision } from "../utils/roundToPrecision.mjs";
const { HandlebarsApplicationMixin } = foundry.applications.api;
const { ActorSheetV2 } = foundry.applications.sheets;
const { getProperty, hasProperty } = foundry.utils;
const { TextEditor } = foundry.applications.ux;
const propertyToParts = {
"name": [`header`],
@ -39,6 +41,7 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
actions: {
manageAttributes: this.#manageAttributes,
configureSheet: this.#configureSheet,
toggleExpand: this.#toggleExpand,
},
};
@ -68,6 +71,15 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
};
// #endregion Options
// #region Instance Data
/**
* This Set is used to keep track of which items have had their full
* details expanded so that it can be persisted across rerenders as
* they occur.
*/
#expandedItems = new Set();
// #endregion Instance Data
// #region Lifecycle
_initializeApplicationOptions(options) {
const sizing = getProperty(options.document, `flags.${__ID__}.PlayerSheet.size`) ?? {};
@ -203,24 +215,73 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
ctx.toggled = true;
ctx.tabActive = this.tabGroups.primary === `content` || this.actor.items.size === 0;
const TextEditor = foundry.applications.ux.TextEditor.implementation;
ctx.enriched = {
system: {
content: await TextEditor.enrichHTML(this.actor.system.content),
content: await TextEditor.implementation.enrichHTML(this.actor.system.content),
},
};
};
async _prepareItems(ctx) {
ctx.tabActive = this.tabGroups.primary === `items`;
const weightUnit = game.settings.get(__ID__, `weightUnit`);
let totalWeight = 0;
ctx.itemGroups = [];
for (const [groupName, items] of Object.entries(this.actor.itemTypes)) {
const preparedItems = [];
let summedWeight = 0;
for (const item of items) {
summedWeight += item.system.quantifiedWeight;
preparedItems.push(await this._prepareItem(item));
};
totalWeight += summedWeight;
ctx.itemGroups.push({
name: groupName.titleCase(),
items: preparedItems,
weight: toPrecision(summedWeight, 2) + weightUnit,
});
};
async _prepareItem(item) {};
ctx.totalWeight = toPrecision(totalWeight, 2) + weightUnit;
};
async _prepareItem(item) {
const weightUnit = game.settings.get(__ID__, `weightUnit`)
const ctx = {
uuid: item.uuid,
img: item.img,
name: item.name,
equipped: item.system.equipped,
quantity: item.system.quantity,
weight: item.system.quantifiedWeight + weightUnit,
isExpanded: this.#expandedItems.has(item.uuid),
canExpand: item.system.description.length > 0,
};
ctx.description = ``;
if (item.system.description.length > 0) {
ctx.description = await TextEditor.implementation.enrichHTML(item.system.description);
};
return ctx;
};
// #endregion Data Prep
// #region Actions
#attributeManager = null;
/** @this {PlayerSheet} */
/**
* This action opens an instance of the AttributeManager application
* so that the user can edit and update all of the attributes for the
* actor. This persists the application instance for the duration of
* the ActorSheet's lifespan.
*
* @this {PlayerSheet}
*/
static async #manageAttributes() {
this.#attributeManager ??= new AttributeManager({ document: this.actor });
if (this.#attributeManager.rendered) {
@ -233,6 +294,13 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
};
};
/**
* This action overrides the default Foundry action in order to tell
* it to open my custom DocumentSheetConfig application instead of
* opening the non-customized sheet config app.
*
* @this {PlayerSheet}
*/
static async #configureSheet(event) {
event.stopPropagation();
if ( event.detail > 1 ) { return }
@ -248,5 +316,27 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
window: { windowId: this.window.windowId },
});
};
/**
* This action is used by the item lists in order to expand/collapse
* the descriptions while maintaining that state across renders.
*
* @this {PlayerSheet}
*/
static async #toggleExpand(event, target) {
if (event.srcElement instanceof HTMLInputElement) { return };
const { itemUuid } = target.closest(`[data-item-uuid]`)?.dataset ?? {};
if (!itemUuid) { return };
const expanded = this.#expandedItems.has(itemUuid);
if (expanded) {
this.#expandedItems.delete(itemUuid);
target.nextElementSibling.dataset.expanded = false;
} else {
this.#expandedItems.add(itemUuid);
target.nextElementSibling.dataset.expanded = true;
}
};
// #endregion Actions
};

View file

@ -1,3 +1,5 @@
import { toPrecision } from "../../utils/roundToPrecision.mjs";
export class GenericItemData extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
@ -28,4 +30,12 @@ export class GenericItemData extends foundry.abstract.TypeDataModel {
}),
};
};
/**
* Calculates the total weight of the item based on the quantity of it, this
* rounds the number to the nearest 2 decimal places.
*/
get quantifiedWeight() {
return toPrecision(this.weight * this.quantity, 2);
};
};

View file

@ -13,6 +13,15 @@ export function registerWorldSettings() {
scope: `world`,
});
game.settings.register(__ID__, `weightUnit`, {
name: `taf.settings.weightUnit.name`,
hint: `taf.settings.weightUnit.hint`,
config: true,
type: String,
default: ``,
scope: `world`,
});
game.settings.register(__ID__, `canPlayersManageAttributes`, {
name: `taf.settings.canPlayersManageAttributes.name`,
hint: `taf.settings.canPlayersManageAttributes.hint`,

View file

@ -0,0 +1,20 @@
/**
* Takes a possibly-decimal value and rounds after a certain precision, keeping
* only the specified amount of decimals.
*
* @param {number} value The value that is to be rounded.
* @param {number} precision The number of decimal places to round to. Must be a
* positive integer.
* @returns The rounded number
*/
export function toPrecision(value, precision = 1) {
if (!Number.isInteger(precision)) {
throw `Precision must be an integer`;
};
if (precision < 0) {
throw `Precision must be greater than or equal to 0`;
};
const m = 10 ** precision;
return Math.round(value * m) / m;
};

View file

@ -37,6 +37,64 @@
}
}
.item-list {
list-style: none;
margin: 0;
padding: 0;
}
.item {
background: var(--item-card-background);
color: var(--item-card-color);
border-radius: 4px;
overflow: hidden;
.summary {
display: grid;
grid-template-columns: auto 1fr 50px auto;
align-items: center;
gap: 8px;
background: var(--item-card-header-background);
color: var(--item-card-header-color);
padding: 4px;
img {
--size: 35px;
width: var(--size);
height: var(--size);
border-radius: 6px;
}
.title {
display: flex;
flex-direction: column;
gap: 4px;
}
.name {
font-size: 1.1rem;
}
.subtitle {
font-size: 0.7rem;
opacity: 90%;
}
input {
background: var(--item-card-header-input-background);
color: var(--item-card-header-input-color);
text-align: center;
}
}
.full-details {
padding: 4px;
&[data-expanded="false"] {
display: none;
}
}
}
.content {
flex-grow: 1;
overflow: hidden;

View file

@ -7,6 +7,13 @@
--tab-button-active-border: rebeccapurple;
--tab-button-hover-bg: var(--color-cool-3);
--item-card-background: #1d262f;
--item-card-color: white;
--item-card-header-background: #242d38;
--item-card-header-color: white;
--item-card-header-input-background: #2b3642;
--item-card-header-input-color: white;
/* Chip Variables */
--chip-color: #fff7ed;
--chip-background: #2b3642;

View file

@ -3,5 +3,23 @@
data-group="primary"
data-tab="items"
>
Item Tab Content Here
Total Weight: {{totalWeight}}
<hr>
{{#each itemGroups as | group |}}
<section>
<div class="header">
<span class="name">
{{ group.name }}
</span>
<span class="weight">
{{ group.weight }}
</span>
</div>
<ul class="item-list">
{{#each group.items as |item|}}
{{> (systemFilePath "templates/PlayerSheet/item.hbs") item }}
{{/each}}
</ul>
</section>
{{/each}}
</div>

View file

@ -1 +1,29 @@
<div></div>
<li
class="item"
data-item-uuid="{{uuid}}"
>
<div
class="summary"
{{#if canExpand}}
data-action="toggleExpand"
{{/if}}
>
<img
src="{{img}}"
alt=""
>
<div class="title">
<span class="name">{{ name }}</span>
<span class="subtitle">{{ weight }}</span>
</div>
<input
type="number"
value="{{quantity}}"
>
</div>
{{#if canExpand}}
<div class="full-details" data-expanded="{{isExpanded}}">
{{{ description }}}
</div>
{{/if}}
</li>