From 2b88bcc2eff0d4701030963127a956cce5968e6c Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 20 Jul 2025 21:32:46 -0600 Subject: [PATCH] Add a component that handles displaying the person silhouette with some content inside of it --- module/Apps/components/ArmourSummary.mjs | 56 +++++++++++++++++++++ module/Apps/components/_index.mjs | 2 + templates/components/armour-summary.hbs | 54 ++++++++++++++++++++ templates/css/components/armour-summary.css | 34 +++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 module/Apps/components/ArmourSummary.mjs create mode 100644 templates/components/armour-summary.hbs create mode 100644 templates/css/components/armour-summary.css diff --git a/module/Apps/components/ArmourSummary.mjs b/module/Apps/components/ArmourSummary.mjs new file mode 100644 index 0000000..15ddf16 --- /dev/null +++ b/module/Apps/components/ArmourSummary.mjs @@ -0,0 +1,56 @@ +import { filePath } from "../../consts.mjs"; +import { StyledShadowElement } from "./mixins/StyledShadowElement.mjs"; + +const { renderTemplate } = foundry.applications.handlebars; + +export class ArmourSummary extends StyledShadowElement(HTMLElement) { + static elementName = `armour-summary`; + static formAssociated = false; + + /* Stuff for the mixin to use */ + static _stylePath = `css/components/armour-summary.css`; + #container; + + get type() { + return this.getAttribute(`type`) ?? `hero`; + }; + + set type(newValue) { + this.setAttribute(`type`, newValue); + }; + + _mounted = false; + async connectedCallback() { + super.connectedCallback(); + if (this._mounted) { return }; + + /* + This converts all of the double-dash prefixed properties on the element to + CSS variables so that they don't all need to be provided by doing style="" + */ + for (const attrVar of this.attributes) { + if (attrVar.name?.startsWith(`var:`)) { + const prop = attrVar.name.replace(`var:`, ``); + this.style.setProperty(`--` + prop, attrVar.value); + }; + }; + + this.#container = document.createElement(`div`); + this.#container.classList = `person`; + + this.#container.innerHTML = await renderTemplate( + filePath(`templates/components/armour-summary.hbs`), + { type: this.type }, + ); + + this._shadow.appendChild(this.#container); + + this._mounted = true; + }; + + disconnectedCallback() { + super.disconnectedCallback(); + if (!this._mounted) { return }; + this._mounted = false; + }; +}; diff --git a/module/Apps/components/_index.mjs b/module/Apps/components/_index.mjs index 3568481..8400462 100644 --- a/module/Apps/components/_index.mjs +++ b/module/Apps/components/_index.mjs @@ -1,9 +1,11 @@ +import { ArmourSummary } from "./ArmourSummary.mjs"; import { Logger } from "../../utils/Logger.mjs"; import { RipCryptBorder } from "./RipCryptBorder.mjs"; import { RipCryptIcon } from "./Icon.mjs"; import { RipCryptSVGLoader } from "./svgLoader.mjs"; const components = [ + ArmourSummary, RipCryptIcon, RipCryptSVGLoader, RipCryptBorder, diff --git a/templates/components/armour-summary.hbs b/templates/components/armour-summary.hbs new file mode 100644 index 0000000..cb9c563 --- /dev/null +++ b/templates/components/armour-summary.hbs @@ -0,0 +1,54 @@ +{{#if (eq type "hero")}} + +{{else}} + +{{/if}} +{{!-- {{#each armours as | slot |}} +
+ + {{ slot.defense }} + + {{#if slot.shielded}} + + {{/if}} +
+{{/each}} --}} +
+ + {{ rc-i18n "RipCrypt.common.anatomy.head" }} +
+
+ + {{ rc-i18n "RipCrypt.common.anatomy.body" }} +
+
+ + {{ rc-i18n "RipCrypt.common.anatomy.arms" }} +
+
+ + {{ rc-i18n "RipCrypt.common.anatomy.legs" }} +
diff --git a/templates/css/components/armour-summary.css b/templates/css/components/armour-summary.css new file mode 100644 index 0000000..fafb3a5 --- /dev/null +++ b/templates/css/components/armour-summary.css @@ -0,0 +1,34 @@ +:host { + display: inline-block; +} + +.person { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-template-rows: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.2fr); + justify-items: center; + align-items: center; + position: relative; + row-gap: var(--row-gap); + + > div { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + z-index: 1; + } + + > rc-svg { + position: absolute; + bottom: 0; + left: 0; + width: 58%; + } + + .head, .body, .legs { grid-column: 1; } + .arms { grid-column: 2; } + .head { grid-row: 1; } + .body, .arms { grid-row: 2; } + .legs { grid-row: 3; } +}