Skip to content

Commit 12d64a3

Browse files
arbronFyorl
andauthored
[#6813] Add expanded section showing effect changes & description (#6820)
* [#6813] Add expanded section showing effect changes & description Adds the ability to expand active effects on actor and items sheets to display a list of their changes as well as the effect's description and tags. The change rows display the key (with a human-readable name if available), the change type indicated by a symbol, and the value (displayed as tooltip instead if too long). This includes improvements to `getHumanReadableAttributeLabel` to support displaying as many active effect change keys as possible and to improve how certain keys are displayed: - Support `activities[attack]...` style keys - Support generic item keys that search through all item schemas - Improved labels within `MappingField` to include entry name Closes #6813 * [#6813] Improve labels for AC & concentration keys, fix bugs * [#6813] Add labels to non-persisted fields * [#6813] Add status indicator to changes to prepare for conditions * Style pass. Fix restoring container content expanded state on re-render. * [#6813] Fix issues, handle summary secrets, adjust localization --------- Co-authored-by: Kim Mantas <kim.mantas@gmail.com>
1 parent f7d1fa0 commit 12d64a3

33 files changed

Lines changed: 926 additions & 268 deletions

lang/en.json

Lines changed: 269 additions & 42 deletions
Large diffs are not rendered by default.

less/v2/apps.less

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,19 +2044,24 @@ dialog.dnd5e2.application {
20442044
/* ---------------------------------- */
20452045

20462046
.dnd5e2 .collapsible, .collapsible.dnd5e2-collapsible {
2047-
&.collapsed {
2048-
.fa-caret-down { transform: rotate(-90deg); }
2049-
.collapsible-content { grid-template-rows: 0fr; }
2050-
}
20512047
.fa-caret-down { transition: transform 250ms ease; }
20522048
.collapsible-content {
20532049
display: grid;
2054-
grid-template-rows: 1fr;
20552050
transition: grid-template-rows 250ms ease;
20562051
> .wrapper { overflow: hidden; }
20572052
}
20582053
}
20592054

2055+
@scope (.dnd5e2 .collapsible, .dnd5e2-collapsible.collapsible) to (.collapsible) {
2056+
:scope:not(.collapsed) {
2057+
.collapsible-content { grid-template-rows: 1fr; }
2058+
}
2059+
:scope.collapsed {
2060+
.fa-caret-down { transform: rotate(-90deg); }
2061+
.collapsible-content { grid-template-rows: 0fr; }
2062+
}
2063+
}
2064+
20602065
/* ---------------------------------- */
20612066
/* Combat Tracker */
20622067
/* ---------------------------------- */

less/v2/inventory.less

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,13 @@
361361
&.item-detail { color: var(--color-text-secondary); }
362362
}
363363

364+
/* Effect Value */
365+
.effect-value {
366+
width: 70px;
367+
}
368+
364369
/* Item Controls */
365-
.item-controls {
370+
.item-controls, .effect-controls {
366371
width: 70px;
367372
align-items: stretch;
368373
justify-content: center;
@@ -478,6 +483,57 @@
478483

479484
.item-name .title { font-size: var(--font-size-12); }
480485
}
486+
487+
.changes .change-row {
488+
border-block-start: var(--activity-row-border);
489+
min-block-size: 32px;
490+
padding-inline-start: 29px;
491+
position: relative;
492+
493+
&:last-child { border-block-end: var(--activity-row-border); }
494+
495+
&::before {
496+
content: "\f192";
497+
position: absolute;
498+
inset: 9px auto auto 13px;
499+
font-family: var(--font-awesome);
500+
font-size: var(--font-size-11);
501+
font-weight: 900;
502+
color: var(--activity-row-indent-color);
503+
}
504+
&[data-change-type="multiply"]::before { content: "\f057"; }
505+
&[data-change-type="add"]::before { content: "\f055"; }
506+
&[data-change-type="subtract"]::before { content: "\f056"; }
507+
&[data-change-type="downgrade"]::before { content: "\f358"; }
508+
&[data-change-type="upgrade"]::before { content: "\f35b"; }
509+
&[data-change-type="override"]::before {
510+
content: "\f28b";
511+
rotate: 90deg;
512+
}
513+
514+
.item-name .title {
515+
font-family: var(--dnd5e-font-roboto);
516+
font-size: var(--font-size-12);
517+
}
518+
519+
.application-status { align-content: center; }
520+
}
521+
522+
.riders .changes {
523+
position: relative;
524+
525+
&::before {
526+
content: "";
527+
position: absolute;
528+
inset: 0 auto 0 12px;
529+
border-inline-start: var(--dnd5e-border-dotted);
530+
}
531+
532+
.change-row {
533+
padding-inline-start: 39px;
534+
&::before { inset-inline-start: 23px; }
535+
}
536+
}
481537
}
482538

483539
.effect-name .duration, &.effect-tooltip .duration {

module/applications/activity/activity-usage-dialog.mjs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,25 +206,25 @@ export default class ActivityUsageDialog extends Dialog5e {
206206
return {
207207
value: effect.id,
208208
label: data?.data?.name ?? this.actor.items.get(data?.id)?.name
209-
?? _loc("DND5E.ConcentratingItemless")
209+
?? _loc("DND5E.CONCENTRATION.NoSource")
210210
};
211211
});
212212
if ( existingConcentration.length ) {
213213
const optional = existingConcentration.length < (this.actor.system.attributes?.concentration?.limit ?? 0);
214214
context.fields.push({
215215
field: new StringField({
216-
required: true, label: _loc("DND5E.ConcentratingEnd"), blank: optional
216+
required: true, label: _loc("DND5E.CONCENTRATION.Action.End"), blank: optional
217217
}),
218218
name: "concentration.end",
219219
value: this.config.concentration?.end,
220220
options: optional ? [{ value: "", label: "—" }, ...existingConcentration] : existingConcentration
221221
});
222222
context.notes.push({
223-
type: "info", message: _loc(`DND5E.ConcentratingWarnLimit${optional ? "Optional" : ""}`)
223+
type: "info", message: _loc(`DND5E.CONCENTRATION.Limit.${optional ? "Optional" : "Hard"}`)
224224
});
225225
} else if ( !this.actor.system.attributes?.concentration?.limit ) {
226226
context.notes.push({
227-
type: "warn", message: _loc("DND5E.ConcentratingWarnLimitZero")
227+
type: "warn", message: _loc("DND5E.CONCENTRATION.Limit.Zero")
228228
});
229229
}
230230
}

module/applications/actor/api/base-actor-sheet.mjs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ export default class BaseActorSheet extends PrimarySheetMixin(
184184
parts.features.templates ??= [];
185185
parts.features.templates.push(...customElements.get(this.options.elements.inventory).templates);
186186
}
187+
if ( "effects" in parts ) {
188+
parts.effects.templates ??= [];
189+
parts.effects.templates.push(...customElements.get(this.options.elements.effects).templates);
190+
}
187191
return parts;
188192
}
189193

@@ -249,26 +253,27 @@ export default class BaseActorSheet extends PrimarySheetMixin(
249253
return arr;
250254
}, []);
251255

256+
const columns = [EffectsElement.COLUMNS.source, EffectsElement.COLUMNS.value, EffectsElement.COLUMNS.controls];
252257
for ( const category of Object.values(context.effects) ) {
258+
category.columns = columns;
253259
category.effects = await category.effects.reduce(async (arr, effect) => {
254-
effect.updateDuration();
255260
if ( conditionIds.has(effect.id) && !effect.duration.remaining ) return arr;
256-
const { id, name, img, disabled, duration } = effect;
257261
const toggleable = !this._concentration?.effects.has(effect);
258-
let source = await effect.getSource();
262+
const isExpanded = this.expandedSections.get(`effects.${effect.id}`) === true;
263+
const ctx = {
264+
...(await effect.getSheetContext()), toggleable, isExpanded,
265+
parentId: effect.target === effect.parent ? null : effect.parent.id,
266+
expanded: isExpanded ? await effect.getPreviewContext({ secrets: effect.isOwner }) : null
267+
};
259268
// If the source is an ActiveEffect from another Actor, note the source as that Actor instead.
260-
if ( source instanceof ActiveEffect ) {
261-
source = source.target;
262-
if ( (source instanceof Item) && source.parent && (source.parent !== this.object) ) source = source.parent;
269+
ctx.hasTooltip = ctx.source instanceof dnd5e.documents.Item5e;
270+
if ( ctx.source instanceof ActiveEffect ) {
271+
const src = ctx.source;
272+
ctx.source = src.target;
273+
if ( (src instanceof Item) && src.parent && (src.parent !== this.object) ) ctx.source = src.parent;
263274
}
264275
arr = await arr;
265-
arr.push({
266-
id, name, img, disabled, duration, source, toggleable,
267-
parentId: effect.target === effect.parent ? null : effect.parent.id,
268-
durationParts: duration.remaining ? duration.label.split(", ") : [],
269-
showDuration: Number.isFinite(duration.value),
270-
hasTooltip: source instanceof dnd5e.documents.Item5e
271-
});
276+
arr.push(ctx);
272277
return arr;
273278
}, []);
274279
}
@@ -451,7 +456,7 @@ export default class BaseActorSheet extends PrimarySheetMixin(
451456
else await this._prepareItemFeature(item, ctx);
452457

453458
// Handle expanded data
454-
ctx.isExpanded = this.expandedSections.get(item.id) === true;
459+
ctx.isExpanded = this.expandedSections.get(`items.${item.id}`) === true;
455460
if ( ctx.isExpanded ) ctx.expanded = await item.getChatData({ secrets: item.isOwner });
456461

457462
// Place the item into specific categories.
@@ -1189,7 +1194,7 @@ export default class BaseActorSheet extends PrimarySheetMixin(
11891194
/** @override */
11901195
_addDocument(event, target) {
11911196
if ( this.tabGroups.primary === "effects" ) return ActiveEffect.implementation.create({
1192-
name: _loc("DND5E.EffectNew"),
1197+
name: _loc("DND5E.EFFECT.New"),
11931198
icon: "icons/svg/aura.svg"
11941199
}, { parent: this.actor, renderSheet: true });
11951200

0 commit comments

Comments
 (0)