Add scrollbar to NPC actor sheet
- Create npc-body.hbs as combined body template with all sections - Simplify PARTS to header + body instead of individual sections - Add flex layout and custom scrollbar CSS for sheet-body - Preload npc-body.hbs template in vagabond.mjs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
8f9a3dc14a
commit
ee124af202
@ -47,17 +47,8 @@ export default class VagabondNPCSheet extends VagabondActorSheet {
|
||||
header: {
|
||||
template: "systems/vagabond/templates/actor/npc-header.hbs",
|
||||
},
|
||||
stats: {
|
||||
template: "systems/vagabond/templates/actor/npc-stats.hbs",
|
||||
},
|
||||
actions: {
|
||||
template: "systems/vagabond/templates/actor/npc-actions.hbs",
|
||||
},
|
||||
abilities: {
|
||||
template: "systems/vagabond/templates/actor/npc-abilities.hbs",
|
||||
},
|
||||
notes: {
|
||||
template: "systems/vagabond/templates/actor/npc-notes.hbs",
|
||||
body: {
|
||||
template: "systems/vagabond/templates/actor/npc-body.hbs",
|
||||
},
|
||||
};
|
||||
|
||||
@ -172,8 +163,8 @@ export default class VagabondNPCSheet extends VagabondActorSheet {
|
||||
_configureRenderOptions(options) {
|
||||
super._configureRenderOptions(options);
|
||||
|
||||
// NPC sheets render all parts (no tabs)
|
||||
options.parts = ["header", "stats", "actions", "abilities", "notes"];
|
||||
// NPC sheets render header and body (no tabs)
|
||||
options.parts = ["header", "body"];
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
@ -67,6 +67,7 @@ async function preloadHandlebarsTemplates() {
|
||||
"systems/vagabond/templates/actor/parts/status-bar.hbs",
|
||||
// NPC sheet parts
|
||||
"systems/vagabond/templates/actor/npc-header.hbs",
|
||||
"systems/vagabond/templates/actor/npc-body.hbs",
|
||||
"systems/vagabond/templates/actor/npc-stats.hbs",
|
||||
"systems/vagabond/templates/actor/npc-actions.hbs",
|
||||
"systems/vagabond/templates/actor/npc-abilities.hbs",
|
||||
|
||||
@ -2141,6 +2141,16 @@
|
||||
min-width: 450px;
|
||||
min-height: 500px;
|
||||
|
||||
// Form is a flex column to allow scrollable body
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
// Header wrapper stays fixed at top
|
||||
.header-wrapper {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
// NPC Header
|
||||
// ----------------------------------------
|
||||
@ -2383,6 +2393,7 @@
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
@include custom-scrollbar;
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
|
||||
310
templates/actor/npc-body.hbs
Normal file
310
templates/actor/npc-body.hbs
Normal file
@ -0,0 +1,310 @@
|
||||
{{!-- NPC Sheet Body - Scrollable Content Wrapper --}}
|
||||
<div class="sheet-body">
|
||||
{{!-- Stats Section --}}
|
||||
<section class="npc-stats">
|
||||
<div class="stats-row">
|
||||
{{!-- Zone --}}
|
||||
<div class="stat-group zone">
|
||||
<label>{{localize "VAGABOND.Zone"}}</label>
|
||||
<select name="system.zone">
|
||||
{{#each zoneOptions}}
|
||||
<option value="{{@key}}" {{#if (eq @key ../zone)}}selected{{/if}}>
|
||||
{{localize this}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<p class="zone-hint">{{zoneBehavior}}</p>
|
||||
</div>
|
||||
|
||||
{{!-- Speed --}}
|
||||
<div class="stat-group speed">
|
||||
<label>{{localize "VAGABOND.Speed"}}</label>
|
||||
<div class="speed-value">
|
||||
<input type="number" name="system.speed.value" value="{{speed}}" min="0" />
|
||||
<span class="unit">ft</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- Size & Type --}}
|
||||
<div class="stat-group type">
|
||||
<div class="type-field">
|
||||
<label>{{localize "VAGABOND.Size"}}</label>
|
||||
<select name="system.size">
|
||||
{{#each sizeOptions}}
|
||||
<option value="{{@key}}" {{#if (eq @key ../size)}}selected{{/if}}>
|
||||
{{localize this}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="type-field">
|
||||
<label>{{localize "VAGABOND.BeingType"}}</label>
|
||||
<select name="system.beingType">
|
||||
{{#each beingTypeOptions}}
|
||||
<option value="{{@key}}" {{#if (eq @key ../beingType)}}selected{{/if}}>
|
||||
{{localize this}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- Appearing --}}
|
||||
<div class="stat-group appearing">
|
||||
<label class="rollable" data-action="rollAppearing" data-tooltip="{{localize 'VAGABOND.RollAppearing'}}">
|
||||
<i class="fa-solid fa-dice"></i>
|
||||
{{localize "VAGABOND.Appearing"}}
|
||||
</label>
|
||||
<input type="text" name="system.appearing" value="{{appearing}}" placeholder="1d6" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- Movement Types --}}
|
||||
<div class="biography-section movement">
|
||||
<h2 class="section-header">{{localize "VAGABOND.Movement"}}</h2>
|
||||
<div class="movement-grid">
|
||||
<div class="movement-field">
|
||||
<label data-tooltip="{{localize 'VAGABOND.MovementClimbHint'}}">
|
||||
<input type="checkbox" name="system.movement.climb" {{#if movement.climb}}checked{{/if}} />
|
||||
{{localize "VAGABOND.Climb"}}
|
||||
</label>
|
||||
</div>
|
||||
<div class="movement-field">
|
||||
<label data-tooltip="{{localize 'VAGABOND.MovementClingHint'}}">
|
||||
<input type="checkbox" name="system.movement.cling" {{#if movement.cling}}checked{{/if}} />
|
||||
{{localize "VAGABOND.Cling"}}
|
||||
</label>
|
||||
</div>
|
||||
<div class="movement-field">
|
||||
<label data-tooltip="{{localize 'VAGABOND.MovementFlyHint'}}">
|
||||
<input type="checkbox" name="system.movement.fly" {{#if movement.fly}}checked{{/if}} />
|
||||
{{localize "VAGABOND.Fly"}}
|
||||
</label>
|
||||
</div>
|
||||
<div class="movement-field">
|
||||
<label data-tooltip="{{localize 'VAGABOND.MovementPhaseHint'}}">
|
||||
<input type="checkbox" name="system.movement.phase" {{#if movement.phase}}checked{{/if}} />
|
||||
{{localize "VAGABOND.Phase"}}
|
||||
</label>
|
||||
</div>
|
||||
<div class="movement-field">
|
||||
<label data-tooltip="{{localize 'VAGABOND.MovementSwimHint'}}">
|
||||
<input type="checkbox" name="system.movement.swim" {{#if movement.swim}}checked{{/if}} />
|
||||
{{localize "VAGABOND.Swim"}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- Senses --}}
|
||||
{{#if hasSenses}}
|
||||
<div class="senses-row">
|
||||
<label>{{localize "VAGABOND.Senses"}}:</label>
|
||||
{{#if senses.allsight}}
|
||||
<span class="sense-tag">{{localize "VAGABOND.Allsight"}}</span>
|
||||
{{/if}}
|
||||
{{#if senses.blindsight}}
|
||||
<span class="sense-tag">{{localize "VAGABOND.Blindsight"}}</span>
|
||||
{{/if}}
|
||||
{{#if senses.darkvision}}
|
||||
<span class="sense-tag">{{localize "VAGABOND.Darkvision"}}</span>
|
||||
{{/if}}
|
||||
{{#if senses.echolocation}}
|
||||
<span class="sense-tag">{{localize "VAGABOND.Echolocation"}}</span>
|
||||
{{/if}}
|
||||
{{#if senses.seismicsense}}
|
||||
<span class="sense-tag">{{localize "VAGABOND.Seismicsense"}}</span>
|
||||
{{/if}}
|
||||
{{#if senses.telepathy}}
|
||||
<span class="sense-tag">{{localize "VAGABOND.Telepathy"}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{!-- Damage Modifiers --}}
|
||||
{{#if hasDamageModifiers}}
|
||||
<div class="damage-modifiers">
|
||||
{{#if immunities.length}}
|
||||
<div class="modifier-row immunities">
|
||||
<label>{{localize "VAGABOND.Immunities"}}:</label>
|
||||
<div class="modifier-tags">
|
||||
{{#each immunities}}
|
||||
<span class="modifier-tag immune">{{this}}</span>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if resistances.length}}
|
||||
<div class="modifier-row resistances">
|
||||
<label>{{localize "VAGABOND.Resistances"}}:</label>
|
||||
<div class="modifier-tags">
|
||||
{{#each resistances}}
|
||||
<span class="modifier-tag resist">{{this}}</span>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if weaknesses.length}}
|
||||
<div class="modifier-row weaknesses">
|
||||
<label>{{localize "VAGABOND.Weaknesses"}}:</label>
|
||||
<div class="modifier-tags">
|
||||
{{#each weaknesses}}
|
||||
<span class="modifier-tag weak">{{this}}</span>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</section>
|
||||
|
||||
{{!-- Actions Section --}}
|
||||
<section class="npc-actions">
|
||||
<div class="section-header-row">
|
||||
<h2 class="section-header">{{localize "VAGABOND.Actions"}}</h2>
|
||||
<button type="button" class="action-add" data-action="addAction"
|
||||
data-tooltip="{{localize 'VAGABOND.AddAction'}}">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ul class="action-list">
|
||||
{{#each actions}}
|
||||
<li class="action-item" data-action-index="{{this.index}}">
|
||||
<div class="action-header">
|
||||
<input type="text" class="action-name" name="system.actions.{{this.index}}.name"
|
||||
value="{{this.name}}" placeholder="{{localize 'VAGABOND.ActionName'}}" />
|
||||
<div class="action-controls">
|
||||
<button type="button" class="action-roll" data-action="rollAction"
|
||||
data-action-index="{{this.index}}" data-tooltip="{{localize 'VAGABOND.RollAction'}}">
|
||||
<i class="fa-solid fa-dice-d20"></i>
|
||||
</button>
|
||||
<button type="button" class="action-delete" data-action="deleteAction"
|
||||
data-action-index="{{this.index}}" data-tooltip="{{localize 'VAGABOND.DeleteAction'}}">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-details">
|
||||
<div class="action-field attack-type">
|
||||
<label>{{localize "VAGABOND.AttackType"}}</label>
|
||||
<select name="system.actions.{{this.index}}.attackType">
|
||||
{{#each ../attackTypeOptions}}
|
||||
<option value="{{@key}}" {{#if (eq @key ../this.attackType)}}selected{{/if}}>
|
||||
{{localize this}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="action-field damage">
|
||||
<label>{{localize "VAGABOND.Damage"}}</label>
|
||||
<input type="text" name="system.actions.{{this.index}}.damage"
|
||||
value="{{this.damage}}" placeholder="1d6" />
|
||||
</div>
|
||||
|
||||
<div class="action-field damage-type">
|
||||
<label>{{localize "VAGABOND.DamageType"}}</label>
|
||||
<select name="system.actions.{{this.index}}.damageType">
|
||||
{{#each ../damageTypeOptions}}
|
||||
<option value="{{@key}}" {{#if (eq @key ../this.damageType)}}selected{{/if}}>
|
||||
{{localize this}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="action-field range">
|
||||
<label>{{localize "VAGABOND.Range"}}</label>
|
||||
<input type="text" name="system.actions.{{this.index}}.range"
|
||||
value="{{this.range}}" placeholder="60 ft" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-description">
|
||||
<textarea name="system.actions.{{this.index}}.description"
|
||||
placeholder="{{localize 'VAGABOND.ActionDescription'}}">{{this.description}}</textarea>
|
||||
</div>
|
||||
</li>
|
||||
{{else}}
|
||||
<li class="action-item empty">
|
||||
<p>{{localize "VAGABOND.NoActions"}}</p>
|
||||
<button type="button" data-action="addAction">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
{{localize "VAGABOND.AddAction"}}
|
||||
</button>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{{!-- Abilities Section --}}
|
||||
<section class="npc-abilities">
|
||||
<div class="section-header-row">
|
||||
<h2 class="section-header">{{localize "VAGABOND.Abilities"}}</h2>
|
||||
<button type="button" class="ability-add" data-action="addAbility"
|
||||
data-tooltip="{{localize 'VAGABOND.AddAbility'}}">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ul class="ability-list">
|
||||
{{#each abilities}}
|
||||
<li class="ability-item" data-ability-index="{{this.index}}">
|
||||
<div class="ability-header">
|
||||
<input type="text" class="ability-name" name="system.abilities.{{this.index}}.name"
|
||||
value="{{this.name}}" placeholder="{{localize 'VAGABOND.AbilityName'}}" />
|
||||
<div class="ability-controls">
|
||||
<label class="ability-passive">
|
||||
<input type="checkbox" name="system.abilities.{{this.index}}.passive"
|
||||
{{#if this.passive}}checked{{/if}} />
|
||||
{{localize "VAGABOND.Passive"}}
|
||||
</label>
|
||||
<button type="button" class="ability-delete" data-action="deleteAbility"
|
||||
data-ability-index="{{this.index}}" data-tooltip="{{localize 'VAGABOND.DeleteAbility'}}">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ability-description">
|
||||
<textarea name="system.abilities.{{this.index}}.description"
|
||||
placeholder="{{localize 'VAGABOND.AbilityDescription'}}">{{this.description}}</textarea>
|
||||
</div>
|
||||
</li>
|
||||
{{else}}
|
||||
<li class="ability-item empty">
|
||||
<p>{{localize "VAGABOND.NoAbilities"}}</p>
|
||||
<button type="button" data-action="addAbility">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
{{localize "VAGABOND.AddAbility"}}
|
||||
</button>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{{!-- Notes Section --}}
|
||||
<section class="npc-notes">
|
||||
{{!-- Loot --}}
|
||||
<div class="notes-section loot">
|
||||
<h2 class="section-header">{{localize "VAGABOND.Loot"}}</h2>
|
||||
<div class="editor-container">
|
||||
<textarea name="system.loot"
|
||||
placeholder="{{localize 'VAGABOND.LootPlaceholder'}}">{{loot}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- GM Notes --}}
|
||||
<div class="notes-section gm-notes">
|
||||
<h2 class="section-header">{{localize "VAGABOND.GMNotes"}}</h2>
|
||||
<div class="editor-container">
|
||||
<textarea name="system.gmNotes"
|
||||
placeholder="{{localize 'VAGABOND.GMNotesPlaceholder'}}">{{gmNotes}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
Loading…
Reference in New Issue
Block a user