diff --git a/module/apps/PlayerSheet.mjs b/module/apps/PlayerSheet.mjs new file mode 100644 index 0000000..53d14a9 --- /dev/null +++ b/module/apps/PlayerSheet.mjs @@ -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 +}; diff --git a/styles/Apps/PlayerSheet.css b/styles/Apps/PlayerSheet.css new file mode 100644 index 0000000..fe843ef --- /dev/null +++ b/styles/Apps/PlayerSheet.css @@ -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; + } + } +} diff --git a/styles/elements/headers.css b/styles/elements/headers.css new file mode 100644 index 0000000..2f59e8c --- /dev/null +++ b/styles/elements/headers.css @@ -0,0 +1,5 @@ +.taf > .window-content { + h1, h2, h3, h4, h5, h6 { + margin: 0; + } +} diff --git a/styles/elements/input.css b/styles/elements/input.css new file mode 100644 index 0000000..9b72811 --- /dev/null +++ b/styles/elements/input.css @@ -0,0 +1,6 @@ +.taf > .window-content input { + &.large { + --input-height: 2.5rem; + font-size: 1.75rem; + } +} diff --git a/styles/elements/p.css b/styles/elements/p.css new file mode 100644 index 0000000..160b0d0 --- /dev/null +++ b/styles/elements/p.css @@ -0,0 +1,9 @@ +.taf > .window-content p { + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } +} diff --git a/styles/elements/prose-mirror.css b/styles/elements/prose-mirror.css new file mode 100644 index 0000000..276c7cf --- /dev/null +++ b/styles/elements/prose-mirror.css @@ -0,0 +1,5 @@ +.taf > .window-content prose-mirror { + .editor-content { + padding: 0 8px 8px; + } +} diff --git a/styles/main.css b/styles/main.css index e69de29..e195149 100644 --- a/styles/main.css +++ b/styles/main.css @@ -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); diff --git a/styles/resets/inputs.css b/styles/resets/inputs.css new file mode 100644 index 0000000..aa6e6b7 --- /dev/null +++ b/styles/resets/inputs.css @@ -0,0 +1,5 @@ +.taf > .window-content { + button, input { + all: initial; + } +} diff --git a/templates/PlayerSheet/attributes.hbs b/templates/PlayerSheet/attributes.hbs new file mode 100644 index 0000000..86c033b --- /dev/null +++ b/templates/PlayerSheet/attributes.hbs @@ -0,0 +1,32 @@ +{{#if hasAttributes}} +