Create PlayerSheet

This commit is contained in:
Oliver-Akins 2025-06-29 00:47:05 -06:00
parent e0578d425d
commit e224f819a3
11 changed files with 252 additions and 0 deletions

View file

@ -0,0 +1,86 @@
import { __ID__, filePath } from "../consts.mjs";
const { HandlebarsApplicationMixin } = foundry.applications.api;
const { ActorSheetV2 } = foundry.applications.sheets;
export class PlayerSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
// #region Options
static DEFAULT_OPTIONS = {
classes: [
__ID__,
`PlayerSheet`,
],
position: {
width: 400,
height: 500,
},
window: {
resizable: true,
},
form: {
submitOnChange: true,
closeOnSubmit: false,
},
actions: {},
};
static PARTS = {
header: { template: filePath(`templates/PlayerSheet/header.hbs`) },
attributes: { template: filePath(`templates/PlayerSheet/attributes.hbs`) },
content: { template: filePath(`templates/PlayerSheet/content.hbs`) },
};
// #endregion Options
// #region Lifecycle
// #endregion Lifecycle
// #region Data Prep
async _preparePartContext(partID) {
let ctx = {
actor: this.actor,
system: this.actor.system,
editable: this.isEditable,
};
switch (partID) {
case `attributes`: {
await this._prepareAttributes(ctx);
break;
};
case `content`: {
await this._prepareContent(ctx);
break;
};
};
return ctx;
};
async _prepareAttributes(ctx) {
ctx.hasAttributes = this.actor.system.hasAttributes;
const attrs = [];
for (const [id, data] of Object.entries(this.actor.system.attr)) {
attrs.push({
...data,
id,
path: `system.attr.${id}`,
});
};
ctx.attrs = attrs.toSorted((a, b) => a.name.localeCompare(b.name));
};
async _prepareContent(ctx) {
const TextEditor = foundry.applications.ux.TextEditor.implementation;
ctx.enriched = {
system: {
content: await TextEditor.enrichHTML(this.actor.system.content),
},
};
};
// #endregion Data Prep
// #region Actions
// #endregion Actions
};

View file

@ -0,0 +1,60 @@
.taf.PlayerSheet {
> .window-content {
padding: 0.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.sheet-header, fieldset, .content {
border-radius: 8px;
border: 1px solid rebeccapurple;
}
.window-content > header {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
padding: 4px;
}
.attributes {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-around;
gap: 0.5rem;
}
.attr-range {
display: flex;
flex-direction: row;
align-items: center;
gap: 4px;
width: 100px;
> input {
text-align: center;
}
}
.content {
flex-grow: 1;
overflow: hidden;
&:not(:has(> prose-mirror)) {
padding: 0.5rem;
}
}
prose-mirror {
height: 100%;
background: var(--color-cool-5);
menu {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
}

View file

@ -0,0 +1,5 @@
.taf > .window-content {
h1, h2, h3, h4, h5, h6 {
margin: 0;
}
}

View file

@ -0,0 +1,6 @@
.taf > .window-content input {
&.large {
--input-height: 2.5rem;
font-size: 1.75rem;
}
}

9
styles/elements/p.css Normal file
View file

@ -0,0 +1,9 @@
.taf > .window-content p {
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}

View file

@ -0,0 +1,5 @@
.taf > .window-content prose-mirror {
.editor-content {
padding: 0 8px 8px;
}
}

View file

@ -0,0 +1,12 @@
@layer resets, elements, components, partials, apps, exceptions;
/* Resets */
/* Elements */
@import url("./elements/headers.css") layer(elements);
@import url("./elements/input.css") layer(elements);
@import url("./elements/p.css") layer(elements);
@import url("./elements/prose-mirror.css") layer(elements);
/* Apps */
@import url("./Apps/PlayerSheet.css") layer(apps);

5
styles/resets/inputs.css Normal file
View file

@ -0,0 +1,5 @@
.taf > .window-content {
button, input {
all: initial;
}
}

View file

@ -0,0 +1,32 @@
{{#if hasAttributes}}
<div class="attributes">
{{#each attrs as | attr |}}
<fieldset>
<legend>
{{ attr.name }}
</legend>
<div class="attr-range">
<input
type="number"
class="attr-range__value"
name="{{attr.path}}.value"
value="{{attr.value}}"
aria-label="Current value"
>
{{#if attr.isRange}}
<span aria-hidden="true">/</span>
<input
type="number"
class="attr-range__max"
name="{{attr.path}}.max"
value="{{attr.max}}"
aria-label="Maximum value"
>
{{/if}}
</div>
</fieldset>
{{/each}}
</div>
{{else}}
<template />
{{/if}}

View file

@ -0,0 +1,16 @@
<div class="content">
{{log this}}
{{#if editable}}
<prose-mirror
class="actor-text"
name="system.content"
value="{{system.content}}"
collaborate="true"
data-document-uuid="{{actor.uuid}}"
>
{{{ enriched.system.content }}}
</prose-mirror>
{{else}}
{{{ enriched.system.content }}}
{{/if}}
</div>

View file

@ -0,0 +1,16 @@
<header class="sheet-header">
<img
src="{{actor.img}}"
data-action="editImage"
title="{{actor.name}}"
height="64"
width="64"
/>
<input
type="text"
name="name"
class="large"
value="{{actor.name}}"
placeholder="{{ localize 'Name' }}"
/>
</header>