Provides extensibility APIs for customizing various aspects of Tidy 5e Sheets.
Constants for a variety of uses.
The attribute which indicates a particular part of a sheet.
Values used in conjunction with the attribute api.constants.SHEET_PART_ATTRIBUTE
to identify a part of a Tidy 5e sheet.
<div class="resources" data-tidy-sheet-part="resources-container">...</div>
// Every time Tidy renders, whether a full render or a partial
Hooks.on("tidy5e-sheet.renderActorSheet", (sheet, element, data) => {
const api = game.modules.get('tidy5e-sheet').api;
const selector = api.getSheetPartSelector(
api.constants.SHEET_PARTS.RESOURCES_CONTAINER
);
// get the resources container of the target actor sheet
element
.querySelector(selector)
// inject some HTML
?.insertAdjacentHTML(
// put it as the first element inside the resources container; see https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML#afterbegin
"afterbegin",
// here's my content
// pro tip: `data-tidy-render-scheme="handlebars"` causes this content to re-render on every Tidy render, full or partial
`<div style="display: contents;" data-tidy-render-scheme="handlebars">
<h2 type="button" style="width: 100%;">Resources for ${data.actor.name}</h2>
</div>`
);
});
Tidy 5e Sheets are tagged with data-tidy-sheet-part
attributes
so that most parts of the sheet can be generally identified.
This module uses specific attributes rather than classes because of
HTML classes' multiple purposes, including use for CSS styling.
Using sheet part attributes allows for identifying the same general thing
even when considering a potentially alternate Tidy sheet layout.
Readonly
ABILITY_An interactable control that can open configuration settings for a target ability.
Readonly
ABILITY_A rollable/interactable element which rolls an ability save or test.
Readonly
ABILITY_An interactable toggle for ability saving throw proficiency.
Readonly
ABILITY_A rollable/interactable element which rolls an ability save.
Readonly
ABILITY_An ability score (e.g., "str", "dex", "wis", etc.), whether readonly or editable.
Readonly
ABILITY_A container for a given ability score (e.g., "str", "dex", "wis", etc.).
Readonly
ABILITY_A rollable/interactable element which rolls an ability test.
Readonly
ACTOR_A container which houses an actor portrait and other parts related to actor portrait features.
Readonly
ACTOR_An overlay element for an actor portrait which provides a visual representation of remaining health.
Readonly
ACTOR_An actor portrait image element.
Readonly
ACTOR_An actor trait container, such as Senses, Languages, or Tools.
Readonly
ACTOR_The specific details of an actor trait container, such as Senses, Languages, or Tools.
Readonly
CONDITION_A control for toggling a condition
Readonly
DAMAGE_A container for all form fields related to a given damage part.
Readonly
DAMAGE_An interactable control which the user can execute to delete a damage part to an item.
Readonly
DAMAGE_An input element which contains a damage part formula, usually for an item.
Readonly
DAMAGE_An input element which represents the damage type of a damage part, usually for an item.
Readonly
DEATH_An input for the number of failed death saves an actor has.
Readonly
DEATH_A rollable/interactable element which rolls a death saving throw.
Readonly
DEATH_An input for the number of successful death saves an actor has.
Readonly
EXPANSION_An interactable element which toggles a view
Readonly
GROUP_An image representing a member of a group sheet.
Readonly
INLINE_An interactable element which toggles an inline container's contents view
Readonly
ITEM_An interactable control which the user can execute to create an item (e.g., consumable, feature, loot, spell, weapon, etc.).
Readonly
ITEM_An image element for an item.
Readonly
ITEM_The container element for an item image.
Readonly
ITEM_An element which contains the name of an item.
Readonly
ITEM_A series of properties related to an item, to be found on item summaries, item cards, and other locations where read-only item information is given.
Readonly
ITEM_A series of properties related to an item, viewed from that item's sheet. These include read-only properties, currency value, and other situational input.
Readonly
ITEM_A summary of an item, usually taken from the item's chat data.
Readonly
ITEM_A list-based tabular representation of items (e.g., equipment, loot, spells, etc.).
Readonly
ITEM_The header row of an item table.
Readonly
ITEM_A row in an item table.
Readonly
ITEM_An interactable control which the user can execute to use an item (e.g., consumable, feature, loot, spell, weapon, etc.).
Readonly
ITEMS_A containing element for a series of item lists or grids.
Readonly
MELEE_The element which contains the modifier text for melee spell attacks.
Readonly
NAME_A container for a sheet name. The sheet name is usually in input, and its container has some additional styles associated with it. The container typically sits in a sheet header row with other header-related elements.
Readonly
NAME_The sheet header row where the sheet name appears.
Readonly
NPC_A containing element for a series of item lists related to the NPC Abilities tab.
Readonly
RANGED_The element which contains the modifier text for ranged spell attacks.
Readonly
RESOURCE: "resource"A container for a single resource (first, second, third, etc.).
Readonly
RESOURCES_The container where all known resources (first, second, third, etc.) are kept.
Readonly
SEARCH_An interactable element that can clear the search input it is associated with.
Readonly
SEARCH_A container for a search input and its search clearing interactable element.
Readonly
SEARCH_A user input for performing searches.
Readonly
SHEET_A button for toggling whether the sheet is locked or unlocked.
Readonly
SKILL_An interactable control that can open configuration settings for a target skill.
Readonly
SKILL_A container for a single skill, including its roller, proficiency toggle, and any other elements related to the skill.
Readonly
SKILL_An interactable toggle for skill proficiency.
Readonly
SKILL_A rollable/interactable element which rolls a skill check.
Readonly
SKILLS_A list of skills for the target actor.
Readonly
SKILLS_An interactable toggle for showing/hiding unproficienct skills.
Readonly
SPELL_The element which contains the modifier text for spell attacks in general. This field is shown when melee and ranged spell attack mods are the same.
Readonly
SPELL_The element which contains spell DC.
Readonly
TABLE_A cell in a table row.
Readonly
TABLE_A cell in a table's header row.
Readonly
TABLE_The header row in a table.
Readonly
TABLE_A row in a table.
Readonly
TOOL_An interactable control that can open configuration settings for a target tool.
Readonly
TOOL_A container for a single tool, including its roller, proficiency toggle, and any other elements related to the tool.
Readonly
TOOL_An interactable toggle for tool proficiency.
Readonly
TOOL_A rollable/interactable element which rolls a tool check.
Readonly
TOOLS_A list of tools for the target actor.
Readonly
UTILITY_A toolbar that usually sits at the top of a section of content, providing features like search, filtering, etc.
Readonly
UTILITY_A toolbar command, usually a button
Various models can be used for API calls.
Adds custom content to all actor sheets at position
relative to selector
.
the information necessary to render custom content
Optional
options: ContentRegistrationOptionscustom content registration options
void
Hooks.once("tidy5e-sheet.ready", (api) => {
api.registerActorContent(
new api.models.HtmlContent({
html: `<a title="Example Button" class="my-custom-icon"><i class="fas fa-user"></i></a>`,
injectParams: {
selector: `[data-tidy-sheet-part="${api.constants.SHEET_PARTS.NAME_CONTAINER}"]`
position: "beforebegin",
},
onContentReady: (params) => {
console.log("content ready to render", params);
console.log("my content", params.content);
},
onRender: (params) => {
params.element
.querySelector(".my-custom-icon")
.addEventListener("click", () => alert("Clicked custom actor icon"));
},
})
);
});
Registers header controls for all Tidy Application V2 Actor sheets.
parameters for registering header controls
Hooks.once('tidy5e-sheet.ready', (api) => {
api.registerActorHeaderControls({
controls: [
{
icon: 'fas fa-hand-sparkles',
label: 'Say Hello',
async onClickAction() {
ui.notifications.info(`Hello, Foundry!`);
},
},
],
});
});
Hooks.once('tidy5e-sheet.ready', (api) => {
api.registerActorHeaderControls({
controls: [
{
icon: 'fas fa-broom',
label: 'Debug Button',
visible() {
return !this.document.compendium?.locked;
},
async onClickAction(event) {
ui.notifications.info(
`Logged document data for ${this.document.name} to console for review.`
);
console.log(this.document);
console.log(await this.document.sheet._prepareContext());
},
ownership: CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER,
},
],
});
});
Adds a tab to the available sheet tabs for all actor types that Tidy 5e supports.
the information necessary to render a tab
Optional
options: ActorTabRegistrationOptionssheet registration options
void
Adds custom content to player character sheets at position
relative to selector
.
the information necessary to render custom content
Optional
options: ContentRegistrationOptionscustom content registration options
void
Hooks.once("tidy5e-sheet.ready", (api) => {
api.registerCharacterContent(
new api.models.HtmlContent({
html: `<a title="Example Button" class="my-custom-icon"><i class="fas fa-user"></i></a>`,
injectParams: {
selector: `[data-tidy-sheet-part="${api.constants.SHEET_PARTS.NAME_CONTAINER}"]`
position: "beforebegin",
},
onContentReady: (params) => {
console.log("content ready to render", params);
console.log("my content", params.content);
},
onRender: (params) => {
params.element
.querySelector(".my-custom-icon")
.addEventListener("click", () => alert("Clicked custom PC icon"));
},
})
);
});
Registers header controls for all Tidy Application V2 Character sheets.
parameters for registering header controls
Hooks.once('tidy5e-sheet.ready', (api) => {
api.registerCharacterHeaderControls({
controls: [
{
icon: 'fas fa-broom',
label: 'Debug Button',
visible() {
return !this.document.compendium?.locked;
},
async onClickAction(event) {
ui.notifications.info(
`Logged document data for ${this.document.name} to console for review.`
);
console.log(this.document);
console.log(await this.document.sheet._prepareContext());
},
ownership: CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER,
},
],
});
});
Adds a tab to the available Character sheet tabs.
the information necessary to render a tab
Optional
options: ActorTabRegistrationOptionssheet registration options
void
Hooks.once('tidy5e-sheet.ready', (api) => {
api.registerCharacterTab(
new api.models.HandlebarsTab({
title: 'My Tab',
path: '/modules/my-module-id/templates/my-handlebars-template.hbs',
tabId: 'my-module-id-registered-character-tab',
getData: async (data) => {
data['my-message'] = 'Hello, world! 🌊🏄♂️';
return Promise.resolve(data);
},
onRender(params) {
const myTab = $(params.tabContentsElement);
myTab.find('.my-control').click(_myHandler.bind(params.app));
},
})
);
});
Hooks.once('tidy5e-sheet.ready', (api) => {
api.registerCharacterTab(
new api.models.HandlebarsTab({
title: 'The New Inventory Tab',
path: '/modules/my-module-id/templates/my-handlebars-template.hbs',
tabId: api.constants.TAB_ID_CHARACTER_INVENTORY,
getData: async (data) => {
data['my-message'] = 'Hello, world! 🌊🏄♂️';
return Promise.resolve(data);
},
onRender(params) {
const myTab = $(params.tabContentsElement);
myTab.find('.my-control').click(_myHandler.bind(params.app));
},
}),
{
overrideExisting: true,
}
);
});
A tab ID is always required (see TabId).
Adds custom content to group sheets at position
relative to selector
.
the information necessary to render custom content
Optional
options: ContentRegistrationOptionscustom content registration options
void
Hooks.once("tidy5e-sheet.ready", (api) => {
api.registerGroupContent(
new api.models.HtmlContent({
html: `<a title="Example Button" class="my-custom-icon"><i class="fas fa-user"></i></a>`,
injectParams: {
selector: `[data-tidy-sheet-part="${api.constants.SHEET_PARTS.NAME_CONTAINER}"]`
position: "beforebegin",
},
onContentReady: (params) => {
console.log("content ready to render", params);
console.log("my content", params.content);
},
onRender: (params) => {
params.element
.querySelector(".my-custom-icon")
.addEventListener("click", () => alert("Clicked custom PC icon"));
},
})
);
});
Registers header controls for all Tidy Application V2 Group sheets.
parameters for registering header controls
Hooks.once('tidy5e-sheet.ready', (api) => {
api.registerGroupHeaderControls({
controls: [
{
icon: 'fas fa-broom',
label: 'Debug Button',
visible() {
return !this.document.compendium?.locked;
},
async onClickAction(event) {
ui.notifications.info(
`Logged document data for ${this.document.name} to console for review.`
);
console.log(this.document);
console.log(await this.document.sheet._prepareContext());
},
ownership: CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER,
},
],
});
});
Adds a tab to the available Group sheet tabs.
the information necessary to render a tab
Optional
options: ActorTabRegistrationOptionssheet registration options
void
Hooks.once('tidy5e-sheet.ready', (api) => {
api.registerGroupTab(
new api.models.HandlebarsTab({
title: 'My Tab',
path: '/modules/my-module-id/templates/my-handlebars-template.hbs',
tabId: 'my-module-id-registered-group-tab',
getData: async (data) => {
data['my-message'] = 'Hello, world! 🌊🏄♂️';
return Promise.resolve(data);
},
onRender(params) {
const myTab = $(params.tabContentsElement);
myTab.find('.my-control').click(_myHandler.bind(params.app));
},
})
);
});
Hooks.once('tidy5e-sheet.ready', (api) => {
api.registerGroupTab(
new api.models.HandlebarsTab({
title: 'The New Inventory Tab',
path: '/modules/my-module-id/templates/my-handlebars-template.hbs',
tabId: api.constants.TAB_ID_GROUP_INVENTORY,
getData: async (data) => {
data['my-message'] = 'Hello, world! 🌊🏄♂️';
return Promise.resolve(data);
},
onRender(params) {
const myTab = $(params.tabContentsElement);
myTab.find('.my-control').click(_myHandler.bind(params.app));
},
}),
{
overrideExisting: true,
}
);
});
A tab ID is always required (see TabId).
Adds custom content to item sheets at position
relative to selector
.
the information necessary to render custom content
Optional
options: ContentRegistrationOptionscustom content registration options
void
Hooks.once("tidy5e-sheet.ready", (api) => {
api.registerItemContent(
new api.models.HtmlContent({
html: `<a title="Example Button" class="my-custom-icon"><i class="fas fa-flask"></i></a>`,
injectParams: {
selector: `[data-tidy-sheet-part="${api.constants.SHEET_PARTS.NAME_CONTAINER}"]`
position: "beforebegin",
},
onContentReady: (params) => {
console.log("content ready to render", params);
console.log("my content", params.content);
},
onRender: (params) => {
params.element
.querySelector(".my-custom-icon")
.addEventListener("click", () => alert("Clicked custom item icon"));
},
})
);
});
Registers header controls for all Tidy Application V2 Item sheets.
parameters for registering header controls
Hooks.once('tidy5e-sheet.ready', (api) => {
api.registerItemHeaderControls({
controls: [
{
icon: 'fas fa-coins',
label: 'Take all the coin!',
visible() {
return (
this.document.type === 'container' &&
this.document.actor &&
this.document.isOwner
);
},
async onClickAction(event) {
ui.notifications.info(`Taking all the money out of the bag!`);
const actorCurrency = this.actor.toObject().system.currency;
const containerCurrency = this.document.toObject().system.currency;
for (let [key, value] of Object.entries(containerCurrency)) {
actorCurrency[key] += containerCurrency[key];
containerCurrency[key] = 0;
}
this.actor.update({
system: {
currency: actorCurrency,
},
});
this.document.update({
system: {
currency: containerCurrency,
},
});
},
ownership: CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER,
},
],
});
});
Adds a tab to all relevant item sheets.
the custom tab settings to use when incorporating this tab.
Optional
options: ItemTabRegistrationOptionsCustomTabBase for options related to all tabs.
Hooks.once('tidy5e-sheet.ready', (api) => {
api.registerItemTab(
new api.models.HandlebarsTab({
title: 'My Item Tab',
tabId: 'my-module-id-my-item-tab',
path: '/modules/my-module-id/my-item-tab.hbs',
enabled: (data) => data.item.type === 'spell',
getData: (data) => {
data['my-extra-data'] = 'Hello, world! 👋';
return data;
},
onRender(params) {
const myTab = $(params.tabContentsElement);
myTab.find('.my-control').click(_myHandler.bind(params.app));
},
}));
});
Hooks.once("tidy5e-sheet.ready", (api) => {
api.registerItemTab(
new api.models.HtmlTab({
title: "My Item Tab",
tabId: "my-module-id-my-item-tab",
html: "<h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h1>LOREM! IPSUM! FIREBALLLLLL!!</h1><h2>🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥</h2>",
}),
{ autoHeight: true } // 👈 With Auto Height set to `true`, the item window will stretch as tall as it can to match the content height when this tab is viewed.
);
});
A tab ID is always required (see TabId).
Adds custom content to NPC sheets at position
relative to selector
.
the information necessary to render custom content
Optional
options: ContentRegistrationOptionscustom content registration options
void
Hooks.once("tidy5e-sheet.ready", (api) => {
api.registerNpcContent(
new api.models.HtmlContent({
html: `<a title="Example Button" class="my-custom-icon"><i class="fas fa-user"></i></a>`,
injectParams: {
selector: `[data-tidy-sheet-part="${api.constants.SHEET_PARTS.NAME_CONTAINER}"]`
position: "beforebegin",
},
onContentReady: (params) => {
console.log("content ready to render", params);
console.log("my content", params.content);
},
onRender: (params) => {
params.element
.querySelector(".my-custom-icon")
.addEventListener("click", () => alert("Clicked custom NPC icon"));
},
})
);
});
Registers header controls for all Tidy Application V2 NPC sheets.
parameters for registering header controls
Hooks.once('tidy5e-sheet.ready', (api) => {
api.registerNpcHeaderControls({
controls: [
{
icon: 'fas fa-broom',
label: 'Debug Button',
visible() {
return !this.document.compendium?.locked;
},
async onClickAction(event) {
ui.notifications.info(
`Logged document data for ${this.document.name} to console for review.`
);
console.log(this.document);
console.log(await this.document.sheet._prepareContext());
},
ownership: CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER,
},
],
});
});
Adds a tab to the available NPC sheet tabs.
the information necessary to render a tab
Optional
options: ActorTabRegistrationOptionssheet registration options
void
Hooks.once('tidy5e-sheet.ready', (api) => {
api.registerNpcTab(
new api.models.HandlebarsTab({
title: 'My Tab',
path: '/modules/my-module-id/templates/my-handlebars-template.hbs',
tabId: 'my-module-id-registered-npc-tab',
getData: async (data) => {
data['my-message'] = 'Hello, world! 🌊🏄♂️';
return Promise.resolve(data);
},
onRender(params) {
const myTab = $(params.tabContentsElement);
myTab.find('.my-control').click(_myHandler.bind(params.app));
},
})
);
});
A tab ID is always required (see TabId).
Adds custom content to vehicle sheets at position
relative to selector
.
the information necessary to render custom content
Optional
options: ContentRegistrationOptionscustom content registration options
void
Hooks.once("tidy5e-sheet.ready", (api) => {
api.registerVehicleContent(
new api.models.HtmlContent({
html: `<a title="Example Button" class="my-custom-icon"><i class="fas fa-user"></i></a>`,
injectParams: {
selector: `[data-tidy-sheet-part="${api.constants.SHEET_PARTS.NAME_CONTAINER}"]`
position: "beforebegin",
},
onContentReady: (params) => {
console.log("content ready to render", params);
console.log("my content", params.content);
},
onRender: (params) => {
params.element
.querySelector(".my-custom-icon")
.addEventListener("click", () => alert("Clicked Vehicle custom icon"));
},
})
);
});
Registers header controls for all Tidy Application V2 Vehicle sheets.
parameters for registering header controls
Hooks.once('tidy5e-sheet.ready', (api) => {
api.registerVehicleHeaderControls({
controls: [
{
icon: 'fas fa-broom',
label: 'Debug Button',
visible() {
return !this.document.compendium?.locked;
},
async onClickAction(event) {
ui.notifications.info(
`Logged document data for ${this.document.name} to console for review.`
);
console.log(this.document);
console.log(await this.document.sheet._prepareContext());
},
ownership: CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER,
},
],
});
});
Adds a tab to the available Vehicle sheet tabs.
the information necessary to render a tab
Optional
options: ActorTabRegistrationOptionssheet registration options
void
Hooks.once('tidy5e-sheet.ready', (api) => {
api.registerVehicleTab(
new api.models.HandlebarsTab({
title: 'My Tab',
path: '/modules/my-module-id/templates/my-handlebars-template.hbs',
tabId: 'my-module-id-registered-vehicle-tab',
getData: async (data) => {
data['my-message'] = 'Hello, world! 🌊🏄♂️';
return Promise.resolve(data);
},
onRender(params) {
const myTab = $(params.tabContentsElement);
myTab.find('.my-control').click(_myHandler.bind(params.app));
},
})
);
});
A tab ID is always required (see TabId).
Wraps the provided HTML so that Tidy will remove the content when handling document changes.
any HTML string that needs to be re-rendered in the style of Foundry Handlebars (usually, this is any time the target document or its embedded documents change).
the original HTML with a transparent element wrapped around which indicates to Tidy that this should be removed and re-rendered.
The intended use of this function is to accompany the use of the tidy5e-sheet.renderActorSheet
or tidy5e-sheet.renderItemSheet
hook.
Any content injected through those hooks needs to be wrapped in this way so that the old version
of the HTML can be removed before adding it back in.
Handlebars refreshes content in this way, but for Tidy purposes, the module needs to know when an arbitrary
segment of HTML is meant to be removed. This function provides that information to Tidy for your HTML.
Hooks.on('tidy5e-sheet.renderActorSheet', (app, element, data) => {
const api = game.modules.get('tidy5e-sheet').api;
const actorEmoji = data.actor.system.currency.pp > 0 ? '💹' : '📉';
let iconHtml = api.useHandlebarsRendering(`<h1>${actorEmoji}</h1>`);
// 👆 This HTML looks like `<div style="display: contents;" data-tidy-render-scheme="handlebars"><h1>📉</h1></div>`
// if the actor doesn't have at least 1 platinum.
// Tidy will remove this each time the sheet would normally re-render, and it will add it back.
// When the actor have more than 0 platinum, stonks will rise.
let actorNameElement = element.querySelector(`[data-tidy-field="name"]`);
actorNameElement?.insertAdjacentHTML('afterend', iconHtml);
});
Static
_getInternal
Gets an instance of the Tidy 5e Sheets API
instance of the Tidy 5e Sheets API
The Tidy 5e Sheets API. The API becomes available after the hook
tidy5e-sheet.ready
is called. When the hook fires, it provides an instance of the API.Example: Getting the API for extending Tidy 5e Sheets
Example: Getting the API from the module
Remarks
It is recommended to retrieve the API from the
tidy5e-sheet.ready
hook, since the hook guarantees that the API has been initialized.