Add partial rerendering to document sheets via a custom mixin

This commit is contained in:
Oliver 2026-03-16 22:06:30 -06:00
parent 761f0b6563
commit 96eccc62f2
3 changed files with 77 additions and 30 deletions

View file

@ -1,10 +1,15 @@
import { __ID__, filePath } from "../consts.mjs"; import { __ID__, filePath } from "../consts.mjs";
import { TAFDocumentSheetMixin } from "./mixins/TAFDocumentSheetMixin.mjs";
const { HandlebarsApplicationMixin } = foundry.applications.api; const { HandlebarsApplicationMixin } = foundry.applications.api;
const { ItemSheetV2 } = foundry.applications.sheets; const { ItemSheetV2 } = foundry.applications.sheets;
const { setProperty } = foundry.utils; const { setProperty } = foundry.utils;
export class GenericItemSheet extends HandlebarsApplicationMixin(ItemSheetV2) { export class GenericItemSheet extends
TAFDocumentSheetMixin(
HandlebarsApplicationMixin(
ItemSheetV2,
)) {
// #region Options // #region Options
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
classes: [ classes: [
@ -29,6 +34,17 @@ export class GenericItemSheet extends HandlebarsApplicationMixin(ItemSheetV2) {
header: { template: filePath(`templates/GenericItemSheet/header.hbs`) }, header: { template: filePath(`templates/GenericItemSheet/header.hbs`) },
content: { template: filePath(`templates/GenericItemSheet/content.hbs`) }, content: { template: filePath(`templates/GenericItemSheet/content.hbs`) },
}; };
/**
* This tells the Application's TAFDocumentSheetMixin how to rerender this app
* when specific properties get changed on the actor, so that it doesn't need
* to full-app rendering if we can do a partial rerender instead.
*/
static PROPERTY_TO_PARTIAL = {
"name": [`header`],
"img": [`header`],
"system": [`content`],
};
// #endregion Options // #endregion Options
// #region Instance Data // #region Instance Data

View file

@ -2,24 +2,19 @@ import { __ID__, filePath } from "../consts.mjs";
import { AttributeManager } from "./AttributeManager.mjs"; import { AttributeManager } from "./AttributeManager.mjs";
import { attributeSorter } from "../utils/attributeSort.mjs"; import { attributeSorter } from "../utils/attributeSort.mjs";
import { TAFDocumentSheetConfig } from "./TAFDocumentSheetConfig.mjs"; import { TAFDocumentSheetConfig } from "./TAFDocumentSheetConfig.mjs";
import { TAFDocumentSheetMixin } from "./mixins/TAFDocumentSheetMixin.mjs";
import { toPrecision } from "../utils/roundToPrecision.mjs"; import { toPrecision } from "../utils/roundToPrecision.mjs";
const { HandlebarsApplicationMixin } = foundry.applications.api; const { HandlebarsApplicationMixin } = foundry.applications.api;
const { ActorSheetV2 } = foundry.applications.sheets; const { ActorSheetV2 } = foundry.applications.sheets;
const { getProperty, hasProperty } = foundry.utils; const { getProperty } = foundry.utils;
const { TextEditor } = foundry.applications.ux; const { TextEditor } = foundry.applications.ux;
const propertyToParts = { export class PlayerSheet extends
"name": [`header`], TAFDocumentSheetMixin(
"img": [`header`], HandlebarsApplicationMixin(
"system.attr": [`attributes`], ActorSheetV2,
"system.attr.value": [`attributes`, `content`], )) {
"system.attr.max": [`attributes`, `content`],
"system.content": [`content`],
"system.carryCapacity": [`items`],
};
export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
// #region Options // #region Options
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
@ -59,6 +54,21 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
}, },
}; };
/**
* This tells the Application's TAFDocumentSheetMixin how to rerender this app
* when specific properties get changed on the actor, so that it doesn't need
* to full-app rendering if we can do a partial rerender instead.
*/
static PROPERTY_TO_PARTIAL = {
"name": [`header`],
"img": [`header`],
"system.attr": [`attributes`],
"system.attr.value": [`attributes`, `content`],
"system.attr.max": [`attributes`, `content`],
"system.content": [`content`],
"system.carryCapacity": [`items`],
};
static TABS = { static TABS = {
primary: { primary: {
initial: `content`, initial: `content`,
@ -67,7 +77,7 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
{ id: `content` }, { id: `content` },
{ id: `items` }, { id: `items` },
], ],
} },
}; };
// #endregion Options // #endregion Options
@ -136,21 +146,6 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
return controls; return controls;
}; };
_configureRenderOptions(options) {
// Only rerender the parts of the app that got changed
if (options.renderContext === `updateActor`) {
const parts = new Set();
for (const property in propertyToParts) {
if (hasProperty(options.renderData, property)) {
propertyToParts[property].forEach(partID => parts.add(partID));
};
};
options.parts = options.parts?.filter(part => !parts.has(part)) ?? Array.from(parts);
};
super._configureRenderOptions(options);
};
async close() { async close() {
this.#attributeManager?.close(); this.#attributeManager?.close();
this.#attributeManager = null; this.#attributeManager = null;
@ -250,7 +245,7 @@ export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
}; };
async _prepareItem(item) { async _prepareItem(item) {
const weightUnit = game.settings.get(__ID__, `weightUnit`) const weightUnit = game.settings.get(__ID__, `weightUnit`);
const ctx = { const ctx = {
uuid: item.uuid, uuid: item.uuid,
img: item.img, img: item.img,

View file

@ -0,0 +1,36 @@
const { hasProperty } = foundry.utils;
export function TAFDocumentSheetMixin(HandlebarsApplication) {
class TAFDocumentSheet extends HandlebarsApplication {
/** @type {Record<string, string[]> | null} */
static PROPERTY_TO_PARTIAL = null;
/**
* This override is used by the mixin in order to allow for partial
* re-rendering of applications based on what properties changed.
* It requires that a static PROPERTY_TO_PARTIAL to be defined as
* an object of path keys to arrays of part IDs in order to work.
* This will not interfere with renders that are not started as
* part of the actor update lifecycle.
*/
_configureRenderOptions(options) {
if (options.renderContext === `updateActor`) {
const propertyToParts = this.constructor.PROPERTY_TO_PARTIAL;
if (propertyToParts) {
const parts = new Set();
for (const property in propertyToParts) {
if (hasProperty(options.renderData, property)) {
propertyToParts[property].forEach(partID => parts.add(partID));
};
};
options.parts = options.parts?.filter(part => !parts.has(part)) ?? Array.from(parts);
}
};
super._configureRenderOptions(options);
};
};
return TAFDocumentSheet;
};