Group - guiled/LRE GitHub Wiki
Groups are a powerful feature introduced in LRE7 that allow you to handle multiple character sheet elements (components) as a single entity. Instead of writing repetitive code for each component, you can group them together and apply the same operations to all of them at once.
Groups are also Data Providers, which means you can use all the powerful data manipulation methods like where(), select(), transform(), etc. on your groups.
Imagine you have a character sheet with the six main D&D ability scores: Strength, Dexterity, Constitution, Intelligence, Wisdom, and Charisma. You want to highlight all of them when the character levels up.
Without groups, you would write:
sheet.get("strength").addClass("highlight");
sheet.get("dexterity").addClass("highlight");
sheet.get("constitution").addClass("highlight");
sheet.get("intelligence").addClass("highlight");
sheet.get("wisdom").addClass("highlight");
sheet.get("charisma").addClass("highlight");With groups, this becomes:
const abilityScores = sheet.group("abilities", ["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma"]);
abilityScores.addClass("highlight");To create a group, use the sheet.group() method:
// Create a group with component IDs
const savingThrows = sheet.group("saves", ["strSave", "dexSave", "conSave", "intSave", "wisSave", "chaSave"]);
// Create an empty group and add components later
const skills = sheet.group("allSkills");
skills.add("acrobatics");
skills.add("athletics");
// ...You can group most character sheet components:
- Input fields (text inputs, number inputs)
- Labels (display text)
- Checkboxes
- Choice (dropdowns/selects)
- MultiChoice (multiple selection)
- Toggle (clickable elements with states)
- Icons
- Containers (divs, rows, columns)
group.add(component: string | Component): Group
Adds a component to the group. You can pass either the component ID (as a string) or a component object.
const weapons = sheet.group("weaponGroup");
// Add by component ID
weapons.add("weapon1_name");
weapons.add("weapon1_damage");
// Add by component object
const attackBonus = sheet.get("weapon1_attack");
weapons.add(attackBonus);When a component is added, the group triggers the add event, followed by an update event.
group.remove(component: string | Component): Group
Removes a component from the group. Like add(), you can pass either the component ID or the component object.
// Remove the attack bonus from our weapon group
weapons.remove("weapon1_attack");When a component is removed, the group triggers the remove event, followed by an update event.
group.count(): number
Returns the number of components currently in the group.
const abilityScores = sheet.group("abilities", ["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma"]);
log(abilityScores.count()); // Output: 6group.knownChildren(): Array<Component>
Returns an array containing all the component objects in the group.
const abilities = sheet.group("abilities", ["strength", "dexterity", "constitution"]);
abilities.knownChildren().forEach(function(component) {
log("Component: " + component.realId() + " = " + component.value());
});
// Output:
// Component: strength = 16
// Component: dexterity = 14
// Component: constitution = 12group.includes(component: string | Component): boolean
group.contains(component: string | Component): boolean
group.has(component: string | Component): boolean
All three methods are equivalent. They check whether a component is part of the group.
const abilities = sheet.group("abilities", ["strength", "dexterity"]);
log(abilities.includes("strength")); // Output: true
log(abilities.has("intelligence")); // Output: falsegroup.get(realId: string): Component | null
group.find(realId: string): Component | null
Both methods are equivalent. They return the component matching the given realId, or null if not found.
const abilities = sheet.group("abilities", ["strength", "dexterity"]);
const strComponent = abilities.find("strength");
log(strComponent.value()); // Output: 16
const unknown = abilities.get("unknown_id");
log(unknown); // Output: nullGroups allow you to get or set the values of all components at once. The values are returned as an object where keys are the component realId and values are the component values.
group.value(): Object — Get all component values
group.value(newValues: Object): void — Set multiple component values
Getting values:
const abilities = sheet.group("abilities", ["strength", "dexterity", "constitution"]);
log(abilities.value());
// Output: {
// "strength": 16,
// "dexterity": 14,
// "constitution": 12
// }Setting values:
// Set all ability scores to specific values
abilities.value({
"strength": 18,
"dexterity": 16,
"constitution": 14
});This is extremely useful when loading data from a table. For example, loading a monster's stats:
// Assuming you have a "monsters" table with strength, dexterity, constitution columns
const monsterData = Tables.get("monsters").where("id", "goblin").singleValue();
abilities.value({
"strength": monsterData.strength,
"dexterity": monsterData.dexterity,
"constitution": monsterData.constitution
});group.virtualValue(): Object
group.virtualValue(newValues: Object): void
Same behavior as value(), but uses the component's virtualValue() method. Virtual values are temporary values that don't persist when the sheet is saved.
// Show what the stats would be with a buff, without actually changing them
const buffedStats = {
"strength": abilities.find("strength").value() + 4,
"dexterity": abilities.find("dexterity").value(),
"constitution": abilities.find("constitution").value() + 2
};
abilities.virtualValue(buffedStats);group.rawValue(): Object
Returns the raw values of all components without any transformation. This is the unprocessed value as stored by Let's Role.
log(abilities.rawValue());
// Output: { "strength": "16", "dexterity": "14", "constitution": "12" }group.text(): Object — Get all component texts
group.text(newTexts: Object): void — Set multiple component texts
Gets or sets the display text of all components.
const abilityLabels = sheet.group("abilityLabels", ["strLabel", "dexLabel", "conLabel"]);
// Get all label texts
log(abilityLabels.text());
// Output: { "strLabel": "Strength", "dexLabel": "Dexterity", "conLabel": "Constitution" }
// Set all labels (useful for localization)
abilityLabels.text({
"strLabel": "Force",
"dexLabel": "Dextérité",
"conLabel": "Constitution"
});group.visible(): boolean — Returns true only if ALL components are visible
group.visible(newValue: boolean | Object): void — Set visibility for all or specific components
const secrets = sheet.group("hiddenInfo", ["secretName", "secretLocation"]);
// Check if all components are visible
if (secrets.visible()) {
log("All secrets are revealed!");
}
// Hide all components
secrets.visible(false);
// Show only specific components
secrets.visible({
"secretName": true,
"secretLocation": false
});
// Dynamic visibility based on a checkbox
secrets.visible(sheet.get("revealSecrets"));group.show(): void
Makes all components in the group visible.
// When the player reaches level 5, reveal advanced abilities
sheet.get("characterLevel").on("update", function(levelComponent) {
if (levelComponent.value() >= 5) {
advancedAbilities.show();
}
});group.hide(): void
Hides all components in the group.
// Hide all spell slots when character is not a spellcaster
const spellSlots = sheet.group("spells", ["slot1", "slot2", "slot3", "slot4", "slot5"]);
if (!isSpellcaster) {
spellSlots.hide();
}group.toggle(): void
Toggles the visibility of all components. If visible, they become hidden; if hidden, they become visible.
// Create a collapsible inventory section
sheet.get("toggleInventoryBtn").on("click", function() {
inventoryGroup.toggle();
});group.addClass(className: string): Group
Adds a CSS class to all components in the group.
// Highlight all damage-related fields when in combat
const damageFields = sheet.group("damage", ["weapon1_damage", "weapon2_damage", "spell_damage"]);
damageFields.addClass("combat-highlight");group.removeClass(className: string): Group
Removes a CSS class from all components in the group.
// Remove highlight when combat ends
damageFields.removeClass("combat-highlight");group.toggleClass(className: string): Group
Toggles a CSS class on all components. If the class is present, it's removed; if absent, it's added.
// Toggle a "selected" style on all skill components
skills.toggleClass("selected");group.hasClass(className: string): boolean
Returns true only if ALL components in the group have the specified class.
const abilities = sheet.group("abilities", ["strength", "dexterity"]);
abilities.find("strength").addClass("proficient");
log(abilities.hasClass("proficient")); // Output: false (dexterity doesn't have it)
abilities.find("dexterity").addClass("proficient");
log(abilities.hasClass("proficient")); // Output: true (both have it now)group.getClasses(): Array<string>
Returns an array of CSS class names that are present on ALL components in the group.
const abilities = sheet.group("abilities", ["strength", "dexterity", "constitution"]);
// Add "stat-field" to all components, and "highlighted" to only some
abilities.addClass("stat-field");
abilities.find("strength").addClass("highlighted");
log(abilities.getClasses());
// Output: ["stat-field"] (only classes shared by ALL components)group.autoLoadSaveClasses(): Group
Enables automatic saving and loading of CSS class changes for all components in the group. This persists class modifications (like added/removed classes) between sessions.
// Remember which ability scores have proficiency markers
abilityScores.autoLoadSaveClasses();group.setToolTip(text: string, placement?: string): void
Sets a tooltip on all components in the group.
// Add tooltips to all saving throw fields
savingThrows.setToolTip("Roll this saving throw to resist effects");
// With specific placement
savingThrows.setToolTip("Saving Throw", "top");group.setChoices(choices): void
This method exists for API compatibility but does nothing on groups. Use it on individual Choice or MultiChoice components instead.
All three methods return the identifier used when creating the group with sheet.group("identifier").
const abilityScores = sheet.group("myAbilities", ["strength", "dexterity"]);
log(abilityScores.id()); // Output: "myAbilities"
log(abilityScores.realId()); // Output: "myAbilities"
log(abilityScores.name()); // Output: "myAbilities"Groups can trigger and respond to events. They automatically propagate update and click events from their components.
Triggered when a component is added to the group.
abilities.on("add", function(group, addedComponent) {
log("Added: " + addedComponent.realId());
});Triggered when a component is removed from the group.
abilities.on("remove", function(group, removedComponent) {
log("Removed: " + removedComponent.realId());
});Triggered when any component in the group is updated (value change), or when a component is added/removed.
// Recalculate ability modifiers whenever any score changes
abilities.on("update", function(group) {
group.knownChildren().forEach(function(cmp) {
const score = cmp.value();
const modifier = Math.floor((score - 10) / 2);
sheet.get(cmp.realId() + "_mod").value(modifier);
});
});Triggered when any component in the group is clicked.
// Log which ability was clicked
abilities.on("click", function(group, clickedComponent) {
log("Player clicked on " + clickedComponent.realId());
});Since groups are Data Providers, you can use all the powerful data manipulation methods. The data keys are the component realId and values are the component values.
const abilities = sheet.group("abilities", ["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma"]);
// Get the highest ability score
const best = abilities.max();
log("Best ability: " + best.singleId() + " = " + best.singleValue());
// Get the sum of all ability scores (useful for point-buy validation)
const total = abilities.sum();
log("Total points: " + total);
// Filter abilities with score >= 14
const highScores = abilities.filter(function(value, key) {
return value >= 14;
});
highScores.each(function(value, key) {
log(key + " is high: " + value);
});Here's a complete example showing how groups can simplify a D&D character sheet:
init = function(sheet) {
// Group all ability scores
const abilities = sheet.group("abilities", [
"strength", "dexterity", "constitution",
"intelligence", "wisdom", "charisma"
]);
// Group ability modifiers
const modifiers = sheet.group("modifiers", [
"str_mod", "dex_mod", "con_mod",
"int_mod", "wis_mod", "cha_mod"
]);
// Group saving throws
const saves = sheet.group("saves", [
"str_save", "dex_save", "con_save",
"int_save", "wis_save", "cha_save"
]);
// Calculate all modifiers when any ability changes
abilities.on("update", function() {
const values = abilities.value();
const mods = {};
mods["str_mod"] = Math.floor((values["strength"] - 10) / 2);
mods["dex_mod"] = Math.floor((values["dexterity"] - 10) / 2);
mods["con_mod"] = Math.floor((values["constitution"] - 10) / 2);
mods["int_mod"] = Math.floor((values["intelligence"] - 10) / 2);
mods["wis_mod"] = Math.floor((values["wisdom"] - 10) / 2);
mods["cha_mod"] = Math.floor((values["charisma"] - 10) / 2);
modifiers.value(mods);
});
// Highlight proficient saves
sheet.get("proficientSaves").on("update", function(cmp) {
const proficient = cmp.value(); // Array of proficient save IDs
saves.knownChildren().forEach(function(save) {
if (proficient.includes(save.realId())) {
save.addClass("proficient");
} else {
save.removeClass("proficient");
}
});
});
// Quick preset: apply a monster template
sheet.get("applyGoblinStats").on("click", function() {
abilities.value({
"strength": 8,
"dexterity": 14,
"constitution": 10,
"intelligence": 10,
"wisdom": 8,
"charisma": 8
});
});
// Toggle visibility of spellcasting section
const spellSection = sheet.group("spellcasting", [
"spellcastingAbility", "spellSaveDC", "spellAttackBonus",
"spellSlots1", "spellSlots2", "spellSlots3"
]);
sheet.get("isSpellcaster").on("update", function(cmp) {
if (cmp.value()) {
spellSection.show();
} else {
spellSection.hide();
}
});
};The following methods are available for API compatibility but behave differently for groups than for regular components:
| Method | Behavior for Groups |
|---|---|
parent() |
Always returns the Sheet that created the group |
sheet() |
Always returns the Sheet that created the group |
repeater() |
Always returns undefined
|
entry() |
Always returns undefined
|
exists() |
Always returns true
|
setChoices() |
Does nothing |
valueProvider() |
Returns undefined
|
dataProvider() |
Returns undefined
|
index() |
Always returns null
|
valueData() |
Always returns null
|