Throw some initial version of code at the wall for the tabbed character sheet
This commit is contained in:
parent
eb6d7fee94
commit
b72f22380f
10 changed files with 213 additions and 2 deletions
137
module/Apps/ActorSheets/TabbedHeroSheet.mjs
Normal file
137
module/Apps/ActorSheets/TabbedHeroSheet.mjs
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
import { filePath } from "../../consts.mjs";
|
||||
import { GenericAppMixin } from "../GenericApp.mjs";
|
||||
import { HeroCraftCardV1 } from "./HeroCraftCardV1.mjs";
|
||||
import { HeroSkillsCardV1 } from "./HeroSkillsCardV1.mjs";
|
||||
import { HeroSummaryCardV1 } from "./HeroSummaryCardV1.mjs";
|
||||
import { Logger } from "../../utils/Logger.mjs";
|
||||
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||
|
||||
export class TabbedHeroSheet extends GenericAppMixin(HandlebarsApplicationMixin(ActorSheetV2)) {
|
||||
|
||||
// #region Options
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: [
|
||||
`ripcrypt--actor`,
|
||||
`ripcrypt--TabbedHeroSheet`,
|
||||
],
|
||||
position: {
|
||||
width: `auto`,
|
||||
height: `auto`,
|
||||
},
|
||||
window: {
|
||||
resizable: false,
|
||||
},
|
||||
actions: {},
|
||||
form: {
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false,
|
||||
},
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
nav: {
|
||||
template: filePath(`templates/Apps/TabbedHeroSheet/tabs.hbs`),
|
||||
},
|
||||
summary: {
|
||||
template: filePath(`templates/Apps/HeroSummaryCardV1/content.hbs`),
|
||||
},
|
||||
skills: {
|
||||
template: filePath(`templates/Apps/HeroSkillsCardV1/content.hbs`),
|
||||
},
|
||||
};
|
||||
// #endregion
|
||||
|
||||
// #region Instance Data
|
||||
#tabs = {
|
||||
root: `HeroSummaryCardV1`,
|
||||
};
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options);
|
||||
|
||||
const summaryElement = this.element.querySelector(`.HeroSummaryCardV1`);
|
||||
HeroSummaryCardV1._onRender(
|
||||
context,
|
||||
{
|
||||
...options,
|
||||
element: summaryElement,
|
||||
isEditable: this.isEditable,
|
||||
},
|
||||
);
|
||||
|
||||
const skillsElement = this.element.querySelector(`.HeroSkillsCardV1`);
|
||||
HeroSkillsCardV1._onRender.bind(this)(
|
||||
context,
|
||||
{
|
||||
...options,
|
||||
element: skillsElement,
|
||||
isEditable: this.isEditable,
|
||||
},
|
||||
);
|
||||
|
||||
const craftsElement = this.element.querySelector(`.crafts-summary`);
|
||||
HeroCraftCardV1._onRender.bind(this)(
|
||||
context,
|
||||
{
|
||||
...options,
|
||||
element: craftsElement,
|
||||
isEditable: this.isEditable,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
async _preparePartContext(partId, ctx, opts) {
|
||||
ctx = await super._preparePartContext(partId, ctx, opts);
|
||||
ctx.actor = this.document;
|
||||
|
||||
ctx.classes = {
|
||||
tab: true,
|
||||
visible: false,
|
||||
};
|
||||
ctx.attrs = {};
|
||||
|
||||
|
||||
let tabName;
|
||||
switch (partId) {
|
||||
case `summary`: {
|
||||
tabName = `HeroSummaryCardV1`;
|
||||
ctx = await HeroSummaryCardV1.prepareGuts(ctx);
|
||||
ctx = await HeroSummaryCardV1.prepareWeapons(ctx);
|
||||
ctx = await HeroSummaryCardV1.prepareArmor(ctx);
|
||||
ctx = await HeroSummaryCardV1.prepareFatePath(ctx);
|
||||
ctx = await HeroSummaryCardV1.prepareAbilityRow(ctx);
|
||||
ctx = await HeroSummaryCardV1.prepareSpeed(ctx);
|
||||
ctx = await HeroSummaryCardV1.prepareLevelData(ctx);
|
||||
break;
|
||||
};
|
||||
case `skills`: {
|
||||
tabName = `HeroSkillsCardV1`;
|
||||
ctx = await HeroSkillsCardV1.prepareGear(ctx);
|
||||
ctx = await HeroSkillsCardV1.prepareAmmo(ctx);
|
||||
ctx = await HeroSkillsCardV1.prepareSkills(ctx);
|
||||
break;
|
||||
};
|
||||
case `craft`: {
|
||||
tabName = `HeroCraftCardV1`;
|
||||
ctx = await HeroCraftCardV1.prepareCraft(ctx);
|
||||
break;
|
||||
};
|
||||
};
|
||||
if (tabName) {
|
||||
ctx.attrs[`data-tab`] = tabName;
|
||||
ctx.attrs[`data-group`] = `root`;
|
||||
ctx.classes.visible = this.#tabs.root === tabName;
|
||||
};
|
||||
|
||||
Logger.debug(`Context keys:`, Object.keys(ctx));
|
||||
return ctx;
|
||||
};
|
||||
// #endregion
|
||||
|
||||
// #region Actions
|
||||
// #endregion
|
||||
};
|
||||
|
|
@ -1,12 +1,16 @@
|
|||
import { handlebarsLocalizer, localizer } from "../utils/Localizer.mjs";
|
||||
import { formFields } from "./inputs/formFields.mjs";
|
||||
import { options } from "./options.mjs";
|
||||
import { toAttributes } from "./toAttributes.mjs";
|
||||
import { toClasses } from "./toClasses.mjs";
|
||||
|
||||
export default {
|
||||
// #region Complex
|
||||
"rc-formFields": formFields,
|
||||
"rc-i18n": handlebarsLocalizer,
|
||||
"rc-options": options,
|
||||
"rc-toAttributes": toAttributes,
|
||||
"rc-toClasses": toClasses,
|
||||
|
||||
// #region Simple
|
||||
"rc-empty-state": (v) => v ?? localizer(`RipCrypt.common.empty`),
|
||||
|
|
|
|||
13
module/handlebarHelpers/toAttributes.mjs
Normal file
13
module/handlebarHelpers/toAttributes.mjs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* Allows converting an object of <attribute names, value> into HTML
|
||||
* attribute-value pairs that can be inserted into the DOM
|
||||
*
|
||||
* @param {Record<string, any>} obj The object of attributes to their value
|
||||
*/
|
||||
export function toAttributes(obj = {}) {
|
||||
let attributes = [];
|
||||
for (const [ attr, value] of Object.entries(obj)) {
|
||||
attributes.push(`${attr}=${Handlebars.escapeExpression(value)}`);
|
||||
};
|
||||
return new Handlebars.SafeString(attributes.join(` `));
|
||||
};
|
||||
14
module/handlebarHelpers/toClasses.mjs
Normal file
14
module/handlebarHelpers/toClasses.mjs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* Allows converting an object of <class names, boolean-likes> into an HTML-compatible class list.
|
||||
*
|
||||
* @param {Record<string, any>} obj The object of class names to boolean-like values for if that class should be included.
|
||||
*/
|
||||
export function toClasses(obj = {}) {
|
||||
let classes = [];
|
||||
for (const [ klass, include ] of Object.entries(obj)) {
|
||||
if (include) {
|
||||
classes.push(klass);
|
||||
};
|
||||
};
|
||||
return new Handlebars.SafeString(classes.join(` `));
|
||||
};
|
||||
|
|
@ -4,6 +4,7 @@ import { CombinedHeroSheet } from "../Apps/ActorSheets/CombinedHeroSheet.mjs";
|
|||
import { DelveTourApp } from "../Apps/DelveTourApp.mjs";
|
||||
import { HeroSkillsCardV1 } from "../Apps/ActorSheets/HeroSkillsCardV1.mjs";
|
||||
import { HeroSummaryCardV1 } from "../Apps/ActorSheets/HeroSummaryCardV1.mjs";
|
||||
import { TabbedHeroSheet } from "../Apps/ActorSheets/TabbedHeroSheet.mjs";
|
||||
|
||||
// Data Models
|
||||
import { AmmoData } from "../data/Item/Ammo.mjs";
|
||||
|
|
@ -69,6 +70,12 @@ Hooks.once(`init`, () => {
|
|||
label: `RipCrypt.sheet-names.CombinedHeroSheet`,
|
||||
themes: CombinedHeroSheet.themes,
|
||||
});
|
||||
Actors.registerSheet(game.system.id, TabbedHeroSheet, {
|
||||
makeDefault: false,
|
||||
types: [`hero`],
|
||||
label: `RipCrypt.sheet-names.TabbedHeroSheet`,
|
||||
themes: TabbedHeroSheet.themes,
|
||||
});
|
||||
Actors.registerSheet(game.system.id, HeroSummaryCardV1, {
|
||||
types: [`hero`],
|
||||
label: `RipCrypt.sheet-names.HeroSummaryCardV1`,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<div class="HeroSkillsCardV1">
|
||||
<div class="HeroSkillsCardV1 {{ rc-toClasses classes }}" {{ rc-toAttributes attrs }}>
|
||||
<div class="label col-header list-header gait-skills-header">
|
||||
<span>{{ rc-i18n "RipCrypt.Apps.grit-skills" }}</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<div class="HeroSummaryCardV1">
|
||||
<div class="HeroSummaryCardV1 {{ rc-toClasses classes }}" {{ rc-toAttributes attrs }}>
|
||||
{{!-- * Header --}}
|
||||
<div class="header">
|
||||
<div class="image">Logo Image</div>
|
||||
|
|
|
|||
9
templates/Apps/TabbedHeroSheet/style.css
Normal file
9
templates/Apps/TabbedHeroSheet/style.css
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
.ripcrypt.ripcrypt--TabbedHeroSheet {
|
||||
> .window-content {
|
||||
gap: 4px;
|
||||
background: var(--base-background);
|
||||
> .tab:not(.visible) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
templates/Apps/TabbedHeroSheet/tabs.hbs
Normal file
26
templates/Apps/TabbedHeroSheet/tabs.hbs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<nav>
|
||||
<button
|
||||
type="button"
|
||||
data-action="tab"
|
||||
data-group="root"
|
||||
data-tab="HeroSummaryCardV1"
|
||||
>
|
||||
Stats
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-action="tab"
|
||||
data-group="root"
|
||||
data-tab="HeroSkillsCardV1"
|
||||
>
|
||||
Skills
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-action="tab"
|
||||
data-group="root"
|
||||
data-tab="HeroCraftCardV1"
|
||||
>
|
||||
Craft
|
||||
</button>
|
||||
</nav>
|
||||
|
|
@ -5,3 +5,4 @@
|
|||
@import url("./HeroSummaryCardV1/style.css");
|
||||
@import url("./HeroSkillsCardV1/style.css");
|
||||
@import url("./RichEditor/style.css");
|
||||
@import url("./TabbedHeroSheet/style.css");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue