From 2f14e7fc8429d2ca9af89cd6ba460d913d81ed75 Mon Sep 17 00:00:00 2001 From: Kim Mantas Date: Mon, 22 Jul 2024 16:30:19 +0100 Subject: [PATCH] [#3725] Add preparation warnings to v2 sheets. --- less/v2/actors.less | 36 +++++++++++++ less/v2/apps.less | 2 +- module/applications/actor/sheet-v2-mixin.mjs | 50 ++++++++++++++++++- module/utils.mjs | 1 + templates/actors/character-sheet-2.hbs | 4 ++ templates/actors/npc-sheet-2.hbs | 3 ++ .../actors/parts/actor-warnings-dialog.hbs | 11 ++++ 7 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 templates/actors/parts/actor-warnings-dialog.hbs diff --git a/less/v2/actors.less b/less/v2/actors.less index ee9c7ef7f1..8adfd83e9b 100644 --- a/less/v2/actors.less +++ b/less/v2/actors.less @@ -352,6 +352,42 @@ form:is(.tab-inventory, .tab-features, .tab-spells, .tab-effects) .create-child { display: block; } + /* ---------------------------------- */ + /* Warnings */ + /* ---------------------------------- */ + + dialog.warnings:is(#tooltip, .locked-tooltip) { /* :is used here to override specificity of base tooltip styles */ + position: fixed; + width: 300px; + max-width: unset; + max-height: unset; + margin: 0; + outline: none; + padding: 4px 8px; + font-family: var(--dnd5e-font-roboto-condensed); + + li { + padding: 6px 8px; + border-bottom: 1px dotted var(--dnd5e-color-gold); + &:last-child { border: none; } + + a:hover { + text-shadow: none; + text-decoration: underline dotted; + } + + &.warning::before, &.error::before { + font-family: var(--font-awesome); + font-weight: bold; + color: var(--color-text-dark-5); + margin-right: 2px; + } + + &.warning::before { content: "\f071"; } + &.error::before { content: "\f06a"; } + } + } + /* ---------------------------------- */ /* Minimized */ /* ---------------------------------- */ diff --git a/less/v2/apps.less b/less/v2/apps.less index 7dc39de7fd..9167a23711 100644 --- a/less/v2/apps.less +++ b/less/v2/apps.less @@ -19,9 +19,9 @@ height: 18px; aspect-ratio: 1; line-height: unset; - display: grid; place-content: center; + &:not([hidden]) { display: grid; } > i { margin: 0 } } diff --git a/module/applications/actor/sheet-v2-mixin.mjs b/module/applications/actor/sheet-v2-mixin.mjs index 8e24f109ab..450103a297 100644 --- a/module/applications/actor/sheet-v2-mixin.mjs +++ b/module/applications/actor/sheet-v2-mixin.mjs @@ -88,14 +88,24 @@ export default function ActorSheetV2Mixin(Base) { header.insertAdjacentElement("afterbegin", toggle); } + // Document UUID link. + const firstButton = header.querySelector(".header-button"); const idLink = header.querySelector(".document-id-link"); if ( idLink ) { - const firstButton = header.querySelector(".header-button"); firstButton?.insertAdjacentElement("beforebegin", idLink); idLink.classList.add("header-button"); idLink.dataset.tooltipDirection = "DOWN"; } + // Preparation warnings. + const warnings = document.createElement("a"); + warnings.classList.add("header-button", "preparation-warnings"); + warnings.dataset.tooltip = "Warnings"; + warnings.setAttribute("aria-label", game.i18n.localize("Warnings")); + warnings.innerHTML = ''; + warnings.addEventListener("click", this._onOpenWarnings.bind(this)); + firstButton?.insertAdjacentElement("beforebegin", warnings); + // Render tabs. const nav = document.createElement("nav"); nav.classList.add("tabs", "tabs-right"); @@ -123,6 +133,15 @@ export default function ActorSheetV2Mixin(Base) { /* -------------------------------------------- */ + /** @inheritDoc */ + async _render(force=false, options={}) { + await super._render(force, options); + const [warnings] = this.element.find(".header-button.preparation-warnings"); + warnings?.toggleAttribute("hidden", !this.actor._preparationWarnings?.length); + } + + /* -------------------------------------------- */ + /** @inheritDoc */ async getData(options) { this._concentration = this.actor.concentration; // Cache concentration so it's not called for every item. @@ -430,6 +449,7 @@ export default function ActorSheetV2Mixin(Base) { html.find(".sidebar-collapser").on("click", this._onToggleSidebar.bind(this)); html.find("[data-item-id][data-action]").on("click", this._onItemAction.bind(this)); html.find("[data-toggle-description]").on("click", this._onToggleDescription.bind(this)); + html.find("dialog.warnings").on("click", this._onCloseWarnings.bind(this)); this.form.querySelectorAll(".item-tooltip").forEach(this._applyItemTooltips.bind(this)); this.form.querySelectorAll("[data-reference-tooltip]").forEach(this._applyReferenceTooltips.bind(this)); @@ -488,6 +508,18 @@ export default function ActorSheetV2Mixin(Base) { /* -------------------------------------------- */ + /** + * Handle closing the warnings dialog. + * @param {PointerEvent} event The triggering event. + * @protected + */ + _onCloseWarnings(event) { + if ( event.target instanceof HTMLDialogElement ) event.target.close(); + if ( event.target instanceof HTMLAnchorElement ) event.target.closest("dialog")?.close(); + } + + /* -------------------------------------------- */ + /** * Handle creating a new embedded child. * @returns {ActiveEffect5e|Item5e|void} @@ -548,6 +580,22 @@ export default function ActorSheetV2Mixin(Base) { /* -------------------------------------------- */ + /** + * Handle opening the warnings dialog. + * @param {PointerEvent} event The triggering event. + * @protected + */ + _onOpenWarnings(event) { + event.stopImmediatePropagation(); + const { top, left, height } = event.target.getBoundingClientRect(); + const { clientWidth } = document.documentElement; + const dialog = this.form.querySelector("dialog.warnings"); + Object.assign(dialog.style, { top: `${top + height}px`, left: `${Math.min(left - 16, clientWidth - 300)}px` }); + dialog.showModal(); + } + + /* -------------------------------------------- */ + /** * Toggle editing hit points. * @param {PointerEvent} event The triggering event. diff --git a/module/utils.mjs b/module/utils.mjs index 40788e594b..9111c7613b 100644 --- a/module/utils.mjs +++ b/module/utils.mjs @@ -327,6 +327,7 @@ export async function preloadHandlebarsTemplates() { "systems/dnd5e/templates/actors/parts/actor-inventory.hbs", "systems/dnd5e/templates/actors/parts/actor-spellbook.hbs", "systems/dnd5e/templates/actors/parts/actor-warnings.hbs", + "systems/dnd5e/templates/actors/parts/actor-warnings-dialog.hbs", "systems/dnd5e/templates/actors/parts/biography-textbox.hbs", "systems/dnd5e/templates/actors/tabs/character-biography.hbs", "systems/dnd5e/templates/actors/tabs/character-details.hbs", diff --git a/templates/actors/character-sheet-2.hbs b/templates/actors/character-sheet-2.hbs index c2bec0fc6c..0b7f007229 100644 --- a/templates/actors/character-sheet-2.hbs +++ b/templates/actors/character-sheet-2.hbs @@ -518,4 +518,8 @@ aria-label="{{ "SIDEBAR.Create" type=(localize "DOCUMENT.Item") }}"> + + {{!-- Warnings --}} + {{> "dnd5e.actor-warnings-dialog" }} + diff --git a/templates/actors/npc-sheet-2.hbs b/templates/actors/npc-sheet-2.hbs index be83a18307..0feb71e050 100644 --- a/templates/actors/npc-sheet-2.hbs +++ b/templates/actors/npc-sheet-2.hbs @@ -527,4 +527,7 @@ + {{!-- Warnings --}} + {{> "dnd5e.actor-warnings-dialog" }} + diff --git a/templates/actors/parts/actor-warnings-dialog.hbs b/templates/actors/parts/actor-warnings-dialog.hbs new file mode 100644 index 0000000000..63e89e3293 --- /dev/null +++ b/templates/actors/parts/actor-warnings-dialog.hbs @@ -0,0 +1,11 @@ + + +