Begin making the BookGeistSheet have the final design

This commit is contained in:
Eldritch-Oliver 2025-10-12 21:29:59 -06:00
parent 798e7441b4
commit 28345bdef0
8 changed files with 360 additions and 64 deletions

View file

@ -2,21 +2,27 @@ import { filePath } from "../../consts.mjs";
import { GenericAppMixin } from "../mixins/GenericApp.mjs";
import { LaidOutAppMixin } from "../mixins/LaidOutAppMixin.mjs";
import { localizer } from "../../utils/Localizer.mjs";
import { editItemFromElement, deleteItemFromElement } from "../utils.mjs";
const { HandlebarsApplicationMixin } = foundry.applications.api;
const { ActorSheetV2 } = foundry.applications.sheets;
export class BookGeistSheet extends LaidOutAppMixin(GenericAppMixin(HandlebarsApplicationMixin(ActorSheetV2))) {
export class BookGeistSheet extends
LaidOutAppMixin(
GenericAppMixin(
HandlebarsApplicationMixin(
ActorSheetV2,
))) {
// #region Options
static DEFAULT_OPTIONS = {
classes: [
`ripcrypt--actor`,
`BookGeistSheet`,
],
// position: {
// width: ``,
// height: ``,
// },
position: {
width: `auto`,
height: `auto`,
},
window: {
resizable: true,
},
@ -31,52 +37,71 @@ export class BookGeistSheet extends LaidOutAppMixin(GenericAppMixin(HandlebarsAp
root: true,
template: filePath(`templates/Apps/BookGeistSheet/layout.hbs`),
},
image: {
template: filePath(`templates/Apps/BookGeistSheet/image.hbs`),
},
header: {
template: filePath(`templates/Apps/BookGeistSheet/header.hbs`),
},
stats: {
template: filePath(`templates/Apps/BookGeistSheet/stats.hbs`),
},
items: {
template: filePath(`templates/Apps/BookGeistSheet/items.hbs`),
},
image: { template: filePath(`templates/Apps/BookGeistSheet/image.hbs`) },
header: { template: filePath(`templates/Apps/BookGeistSheet/header.hbs`) },
stats: { template: filePath(`templates/Apps/BookGeistSheet/stats.hbs`) },
items: { template: filePath(`templates/Apps/BookGeistSheet/items.hbs`) },
};
// #endregion Options
// #region Lifecycle
async _onRender() {
await super._onRender();
new ContextMenu(
this.element,
`[data-ctx-menu="item"]`,
[
{
name: localizer(`RipCrypt.common.edit`),
condition: (el) => {
const itemId = el.dataset.itemId;
return itemId !== ``;
},
callback: editItemFromElement,
},
{
name: localizer(`RipCrypt.common.delete`),
condition: (el) => {
const itemId = el.dataset.itemId;
return itemId !== ``;
},
callback: deleteItemFromElement,
},
],
{ jQuery: false, fixed: true },
);
};
// #endregion Lifecycle
// #region Data Prep
_preparePartContext(partID, ctx) {
async _preparePartContext(partID, ctx) {
switch (partID) {
case `layout`: this._prepareLayoutContext(ctx); break;
case `image`: this._prepareImageContext(ctx); break;
case `header`: this._prepareHeaderContext(ctx); break;
case `stats`: this._prepareStatsContext(ctx); break;
case `items`: this._prepareItemsContext(ctx); break;
case `layout`: await this._prepareLayoutContext(ctx); break;
case `image`: await this._prepareImageContext(ctx); break;
case `header`: await this._prepareHeaderContext(ctx); break;
case `stats`: await this._prepareStatsContext(ctx); break;
case `items`: await this._prepareItemsContext(ctx); break;
};
return ctx;
};
_prepareLayoutContext(ctx) {
async _prepareLayoutContext(ctx) {
ctx.imageVisible = true;
};
_prepareImageContext(ctx) {
async _prepareImageContext(ctx) {
ctx.url = this.actor.img;
};
_prepareHeaderContext(ctx) {
async _prepareHeaderContext(ctx) {
ctx.name = this.actor.name;
ctx.description = null; // this.actor.system.description;
};
_prepareStatsContext(ctx) {
async _prepareStatsContext(ctx) {
const system = this.actor.system;
const fate = system.fate;
@ -98,14 +123,45 @@ export class BookGeistSheet extends LaidOutAppMixin(GenericAppMixin(HandlebarsAp
ctx.speed = system.speed;
};
_prepareItemsContext(ctx) {
async _prepareItemsContext(ctx) {
ctx.attacks = [];
ctx.defense = {
locations: `None`,
protection: 0,
shield: false,
};
ctx.traits = [];
ctx.traits = []; // Array<{name: string}>
for (const item of this.actor.items) {
switch (item.type) {
case `weapon`: {
if (!item.system.equipped) { continue };
ctx.attacks.push(this._prepareWeapon(item));
break;
};
case `trait`: {
ctx.traits.push(this._prepareTrait(item));
break;
};
};
};
};
_prepareWeapon(weapon) {
return {
uuid: weapon.uuid,
name: weapon.name,
damage: weapon.system.damage,
range: weapon.system.range,
};
};
_prepareTrait(trait) {
return {
uuid: trait.uuid,
name: trait.name,
description: trait.system.description,
};
};
// #endregion Data Prep

View file

@ -1,7 +1,22 @@
<div>
<div class="overview">
<input
type="text"
name="name"
value="{{name}}"
>
<div class="grow"></div>
Rank
<select name="">
<option value="novice">0</option>
<option value="adept">I</option>
<option value="master">II</option>
<option value="expert">III</option>
</select>
</div>
{{#if description}}
<div class="description">
{{{description}}}
</div>
{{/if}}
</div>

View file

@ -1,3 +1,8 @@
<div>
IMAGE
<div class="img-wrapper">
<img
src="{{url}}"
alt=""
data-action="editImage"
data-edit="img"
>
</div>

View file

@ -1 +1,47 @@
<div>ITEMS</div>
<div class="items">
<div>Attacks</div>
<div>
{{#each attacks as |attack|}}
<div
class="attack"
data-ctx-menu="item"
data-item-id="{{attack.uuid}}"
>
{{attack.name}}
</div>
{{else}}
Unarmed!
{{/each}}
</div>
<div>Defense</div>
<div>
Armour
{{rc-ifOut defense.protection}}
/
{{defense.locations}}
{{#if defense.shield}}
, <span>Shield</span>
{{/if}}
</div>
<div>Traits</div>
<ul class="traits">
{{#each traits as |trait|}}
<li
class="trait"
data-ctx-menu="item"
data-item-id="{{trait.uuid}}"
>
{{trait.name}}
{{#if trait.description}}
<rc-icon
var:fill="currentColor"
name="icons/info-circle"
data-tooltip="{{trait.description}}"
/>
{{/if}}
</li>
{{else}}
None
{{/each}}
</ul>
</div>

View file

@ -1,11 +1,98 @@
<div>
<ul>
<li>Path: {{path}}</li>
<li>Grit: {{grit}}</li>
<li>Gait: {{gait}}</li>
<li>Grip: {{grip}}</li>
<li>Glim: {{glim}}</li>
<li>Guts: {{guts.value}} / {{guts.max}}</li>
<li>Move: {{speed.move}} / {{speed.run}}</li>
</ul>
{{log this}}
<table>
<thead>
<tr>
<td class="alt">Path</td>
<td>Grit</td>
<td>Gait</td>
<td>Grip</td>
<td>Glim</td>
<td class="alt">Guts</td>
<td class="alt">Move</td>
</tr>
</thead>
<tbody>
<tr>
<td
class="alt"
data-tooltip="{{path.full}}"
>
{{path.abbv}}
</td>
<td>
{{#if meta.editable}}
<input
type="number"
id="{{meta.idp}}-grit"
value="{{grit}}"
name="system.ability.grit"
>
{{else if meta.limited}}
???
{{else}}
{{grit}}
{{/if}}
</td>
<td>
{{#if meta.editable}}
<input
type="number"
id="{{meta.idp}}-gait"
value="{{gait}}"
name="system.ability.gait"
>
{{else if meta.limited}}
???
{{else}}
{{gait}}
{{/if}}
</td>
<td>
{{#if meta.editable}}
<input
type="number"
id="{{meta.idp}}-grip"
value="{{grip}}"
name="system.ability.grip"
>
{{else if meta.limited}}
???
{{else}}
{{grip}}
{{/if}}
</td>
<td>
{{#if meta.editable}}
<input
type="number"
id="{{meta.idp}}-glim"
value="{{glim}}"
name="system.ability.glim"
>
{{else if meta.limited}}
???
{{else}}
{{glim}}
{{/if}}
</td>
<td class="alt">
{{#if meta.editable}}
<input
type="number"
id="{{meta.idp}}-guts-value"
value="{{guts.value}}"
name="system.guts.value"
>
/ {{guts.max}}
{{else if meta.limited}}
??/??
{{else}}
{{guts.value}}/{{guts.max}}
{{/if}}
</td>
<td class="alt">{{speed.move}} / {{speed.run}}</td>
</tr>
</tbody>
</table>
</div>

View file

@ -3,11 +3,98 @@
display: flex;
flex-direction: row;
gap: 4px;
padding: 8px;
color: var(--base-text);
background: var(--base-background);
}
.info {
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 4px;
}
.img-wrapper {
display: flex;
justify-content: center;
align-items: center;
img {
width: 150px;
height: 150px;
}
}
.overview {
display: flex;
flex-direction: row;
gap: 4px;
input {
width: 50%;
}
}
table {
td {
border: 1px solid var(--accent-1);
text-align: center;
input {
width: 30px;
background: unset;
text-align: center;
}
}
thead td {
font-weight: bold;
border-top-width: 0;
&:first-of-type, &:last-of-type {
border-left-width: 0;
border-right-width: 0;
}
}
tbody tr {
td:first-of-type, td:last-of-type {
border-left-width: 0;
border-right-width: 0;
}
&:last-of-type td {
border-bottom-width: 0;
}
}
.alt {
background-color: var(--alt-row-background);
}
}
.items {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 4fr);
grid-template-rows: repeat(3, auto);
gap: 2px;
.traits {
display: flex;
flex-direction: row;
gap: 8px;
list-style-type: none;
}
.trait {
display: flex;
flex-direction: row;
align-items: center;
gap: 4px;
background-color: var(--accent-2);
border-radius: 4px;
padding: 2px 4px;
}
}
}

View file

@ -1,3 +1,4 @@
@import url("./common.css");
@import url("./AllItemSheetV1/style.css");
@import url("./CombinedHeroSheet/style.css");
@import url("./DelveDiceHUD/style.css");
@ -12,21 +13,3 @@
@import url("./popover.css");
@import url("./popovers/AmmoTracker/style.css");
.ripcrypt {
.window-content {
flex: initial;
padding: 0;
margin: 0;
}
.StatsCardV1,
.SkillsCardV1,
.CraftCardV1 {
padding: 8px;
/* height: 270px; */
width: 680px;
--col-gap: 2px;
--row-gap: 4px;
}
}

17
templates/Apps/common.css Normal file
View file

@ -0,0 +1,17 @@
.ripcrypt {
.window-content {
flex: initial;
padding: 0;
margin: 0;
}
.StatsCardV1,
.SkillsCardV1,
.CraftCardV1 {
padding: 8px;
/* height: 270px; */
width: 680px;
--col-gap: 2px;
--row-gap: 4px;
}
}