Compare commits

..

No commits in common. "feature/foundry-v13-tabs-implementation" and "main" have entirely different histories.

79 changed files with 1003 additions and 2189 deletions

View file

@ -8,6 +8,6 @@ root = true
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
indent_size = 4
charset = utf-8
trim_trailing_whitespace = true

View file

@ -3,11 +3,9 @@
"DS4.UserInteractionAddItemTitle": "Item Erstellen",
"DS4.UserInteractionEditItemTitle": "Item Bearbeiten",
"DS4.UserInteractionDeleteItemTitle": "Item Löschen",
"DS4.UserInteractionDeleteItemContent": "Sind Sie sicher, dass Sie {item} löschen möchten?",
"DS4.UserInteractionAddEffectTitle": "Effekt Erstellen",
"DS4.UserInteractionEditEffectTitle": "Effekt Bearbeiten",
"DS4.UserInteractionDeleteEffectTitle": "Effekt Löschen",
"DS4.UserInteractionDeleteEffectContent": "Sind Sie sicher, dass Sie {effect} löschen möchten?",
"DS4.DocumentImageAltText": "Bild von {name}",
"DS4.RollableImageRollableTitle": "Für {name} würfeln",
"DS4.DiceOverlayImageAltText": "Bild eines W20",
@ -184,9 +182,7 @@
"DS4.EffectFactor": "Faktor (wie oft der Effekt angewendet wird)",
"DS4.EffectFactorAbbr": "F",
"DS4.ActorName": "Name",
"DS4.ActorSheet": "Aktorbogen",
"DS4.ActorImageAltText": "Bild des Aktors",
"DS4.ItemSheet": "Itembogen",
"DS4.ActorTypeCharacter": "Charakter",
"DS4.ActorTypeCreature": "Kreatur",
"DS4.Attribute": "Attribut",

View file

@ -3,11 +3,9 @@
"DS4.UserInteractionAddItemTitle": "Create Item",
"DS4.UserInteractionEditItemTitle": "Edit Item",
"DS4.UserInteractionDeleteItemTitle": "Delete Item",
"DS4.UserInteractionDeleteItemContent": "Are you sure you want to delete {item}?",
"DS4.UserInteractionAddEffectTitle": "Create Effect",
"DS4.UserInteractionEditEffectTitle": "Edit Effect",
"DS4.UserInteractionDeleteEffectTitle": "Delete Effect",
"DS4.UserInteractionDeleteEffectContent": "Are you sure you want to delete {effect}?",
"DS4.DocumentImageAltText": "Image of {name}",
"DS4.RollableImageRollableTitle": "Roll for {name}",
"DS4.DiceOverlayImageAltText": "Image of a d20",
@ -184,9 +182,7 @@
"DS4.EffectFactor": "Factor (the number of times the effect is being applied)",
"DS4.EffectFactorAbbr": "F",
"DS4.ActorName": "Name",
"DS4.ActorSheet": "Actor Sheet",
"DS4.ActorImageAltText": "Image of the Actor",
"DS4.ItemSheet": "Item Sheet",
"DS4.ActorTypeCharacter": "Character",
"DS4.ActorTypeCreature": "Creature",
"DS4.Attribute": "Attribute",

View file

@ -42,7 +42,6 @@
align-items: center;
border-bottom: 0;
margin: 0;
container-type: inline-size;
}
&__name-input[type="text"] {
@ -50,10 +49,7 @@
background-color: transparent;
border: none;
flex: 1;
font-size: clamp(0.75em, 8cqi, 1.25em);
font-size: 1.25em;
height: auto;
// Use lighter font weight for better readability
font-weight: 300;
}
}

View file

@ -29,11 +29,10 @@
margin: 0;
padding: 0;
text-align: right;
font-weight: 300;
}
&__input {
flex: 0 0 8ch;
flex: 0 0 5ch;
&--slayer-points {
&::-webkit-inner-spin-button,

View file

@ -26,7 +26,7 @@
&__property-select {
width: 100%;
height: var(--input-height);
height: var(--form-field-height);
}
&__property-multi-input {

View file

@ -8,10 +8,4 @@
display: grid;
grid-template-columns: 1fr 3fr;
column-gap: 1em;
height: 100%;
}
.ds4-biography {
height: 100%;
overflow: hidden;
}

View file

@ -71,13 +71,3 @@
flex: 1 1 4em;
}
}
// Dark mode filter for combat value backgrounds when in dark theme
.theme-dark .ds4-combat-value__value {
filter: brightness(0) invert(1) brightness(0.8);
// Counter-invert the text to keep it normal
.ds4-combat-value__text {
filter: brightness(1.25) invert(1) brightness(0.8);
}
}

View file

@ -35,7 +35,7 @@
&--trait {
.ds4-core-value__label {
-webkit-text-stroke: 1px light-dark(colors.$c-black, colors.$c-white);
-webkit-text-stroke: 1px colors.$c-black;
color: transparent;
}
}

View file

@ -33,7 +33,6 @@
&__name {
border: none;
margin: 0;
container-type: inline-size;
}
&__name-label {
@ -44,10 +43,9 @@
@include mixins.font-heading-upper;
background-color: transparent;
border: none;
font-size: clamp(0.75em, 8cqi, 1.25em);
font-size: 1.25em;
height: auto;
padding-left: 0;
padding-right: 0;
font-weight: 300;
}
}

View file

@ -16,38 +16,3 @@
}
}
}
// General ProseMirror editor styles
prose-mirror {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
// Edit mode with editor container
&.active {
.editor-container {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
.editor-content {
flex: 1;
overflow-y: auto;
min-height: 0;
}
}
}
// View mode - direct editor-content
&.inactive {
.editor-content {
position: relative;
flex: 1;
overflow-y: auto;
min-height: 0;
inset: auto;
}
}
}

View file

@ -150,8 +150,3 @@
margin-top: 1em;
padding-left: 1em;
}
// Dark mode filter for embedded document list images when in dark theme
.theme-dark .ds4-embedded-document-list__image {
filter: brightness(0) invert(1) brightness(0.8);
}

View file

@ -4,13 +4,12 @@
* SPDX-License-Identifier: MIT
*/
.ds4-form-group,
.ds4-item-sheet .form-group {
.ds4-form-group {
clear: both;
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 8px 0;
margin: 3px 0;
align-items: center;
&--start {
@ -23,107 +22,6 @@
&__label {
flex: 2;
line-height: var(--input-height);
}
// Add spacing between form groups
& + & {
margin-top: 12px;
}
// Style for slim form groups (input + select combinations)
&.slim {
margin: 6px 0;
.form-fields {
display: flex;
align-items: center;
gap: 4px;
input {
flex: 1;
min-width: 60px;
}
select {
flex: 0 0 auto;
min-width: 120px;
width: auto !important;
}
label {
flex: 0 0 auto;
margin-right: 8px;
font-weight: bold;
line-height: var(--form-field-height);
}
}
}
}
// Style standard Foundry form-fields containers
.ds4-item-sheet .form-fields {
display: flex;
align-items: center;
gap: 6px;
flex: 1;
input, select, textarea {
background: rgba(255, 255, 255, 0.05);
border: 1px solid var(--color-border);
border-radius: 3px;
padding: 4px 8px;
color: var(--color-text-primary);
&:focus {
border-color: var(--color-text-accent);
box-shadow: 0 0 3px var(--color-text-accent);
}
}
select {
background: rgba(255, 255, 255, 0.05);
appearance: none;
background-image: url("data:image/svg+xml;charset=US-ASCII,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'><path fill='%23efe6d8' d='M2 0L0 2h4zm0 5L0 3h4z'/></svg>");
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 8px;
padding-right: 24px;
width: auto !important;
}
}
// Dark mode: try to achieve dark background for select options
.theme-dark .ds4-item-sheet .form-fields select {
color-scheme: dark;
option {
background-color: var(--color-cool-4) !important;
background: var(--color-cool-4) !important;
color: var(--color-light-2) !important;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
option:checked {
background-color: var(--color-cool-3) !important;
background: var(--color-cool-3) !important;
color: var(--color-light-1) !important;
}
option:hover {
background-color: var(--color-cool-3) !important;
background: var(--color-cool-3) !important;
}
}
// Improve label styling
.ds4-item-sheet .form-group > label {
flex: 0 0 160px;
font-weight: bold;
color: var(--color-form-label);
line-height: var(--input-height);
margin-right: 12px;
}

View file

@ -1,63 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Alexander Minges
*
* SPDX-License-Identifier: MIT
*/
/* Token Ruler Waypoint Styling with Color-Coded Movement Ranges */
.system-ds4 .waypoint-label {
font-size: 1.5em;
// Main icon styling for movement types
&.move-range > i.fa-person-walking {
color: var(--color-level-success);
}
&.dash-range > i.fa-person-running {
color: var(--color-level-warning);
}
&.out-of-range > i.fa-person-rays {
color: var(--color-level-error);
animation: pulse 2s infinite;
}
.distance {
&.move-range {
color: var(--color-level-success);
}
&.dash-range {
color: var(--color-level-warning);
font-weight: bold;
}
&.out-of-range {
color: var(--color-level-error);
font-weight: bold;
animation: pulse 2s infinite;
}
}
.delta {
opacity: 0.8;
font-size: 0.9em;
}
.elevation {
font-style: italic;
opacity: 0.9;
}
}
/* Icon animations */
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.6;
}
}

View file

@ -4,14 +4,7 @@
* SPDX-License-Identifier: MIT
*/
.ds4-sheet-body,
.sheet-body {
.ds4-sheet-body {
height: 100%;
overflow-y: auto;
// Prevent double scrollbars on biography tab
.ds4-sheet-tab.tab.biography.active,
.tab[data-tab="biography"].active {
overflow: hidden;
}
}

View file

@ -4,8 +4,7 @@
* SPDX-License-Identifier: MIT
*/
.ds4-sheet-tab,
.tab {
.ds4-sheet-tab {
flex-direction: column;
flex-wrap: nowrap;
height: 100%;

View file

@ -6,31 +6,20 @@
@use "../../utils/variables";
.ds4-sheet-tab-nav,
nav.tabs {
.ds4-sheet-tab-nav {
border-bottom: variables.$border-groove;
border-top: variables.$border-groove;
display: flex;
flex-wrap: nowrap;
height: calc(2.5 * var(--line-height-16));
height: calc(2 * var(--line-height-16));
justify-content: space-around;
line-height: calc(2 * var(--line-height-16));
margin: variables.$margin-sm 0;
.ds4-sheet-tab-nav__item,
.item {
&__item {
flex: 0 1 auto !important; // necessary to override the styling from lang-de, see https://gitlab.com/henry4k/foundryvtt-lang-de/-/issues/9
font-weight: bold;
white-space: nowrap;
display: flex;
align-items: center;
justify-content: center;
gap: 0.25rem;
padding: 0.5rem 0.75rem;
line-height: 1;
i {
font-size: 0.875rem;
}
&.active {
text-shadow: 0 0 variables.$padding-md var(--color-shadow-primary);

View file

@ -19,7 +19,6 @@
@import "components/shared/embedded_document_list";
@import "components/shared/form_group";
@import "components/shared/rollable_image";
@import "components/shared/ruler";
@import "components/shared/sheet_body";
@import "components/shared/sheet_form";
@import "components/shared/sheet_tab_nav";

View file

@ -58,27 +58,3 @@
--ds4-font-primary: Lora, serif;
--ds4-font-heading: "Wood Stamp", sans-serif;
}
// Apply Wood Stamp font only to DS4 sheet-specific elements (excluding window titles)
.ds4-actor-sheet h2,
.ds4-actor-sheet h4,
.ds4-actor-sheet h5,
.ds4-actor-sheet h6,
.ds4-item-sheet h2,
.ds4-item-sheet h4,
.ds4-item-sheet h5,
.ds4-item-sheet h6,
.ds4-currency-title,
.ds4-embedded-document-list-title {
font-family: var(--ds4-font-heading) !important;
text-transform: uppercase;
font-weight: 100 !important;
}
// Keep window titles readable with standard font
.ds4-actor-sheet .window-title,
.ds4-item-sheet .window-title {
font-family: var(--font-sans) !important;
text-transform: none !important;
font-weight: normal !important;
}

View file

@ -2,55 +2,31 @@
//
// SPDX-License-Identifier: MIT
/**
* DS4 Active Effect Configuration Sheet
*/
export class DS4ActiveEffectConfig extends foundry.applications.sheets.ActiveEffectConfig {
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
classes: ["sheet", "ds4-active-effect-config", "themed"],
};
export class DS4ActiveEffectConfig extends ActiveEffectConfig {
/** @override */
get template() {
return "systems/ds4/templates/sheets/active-effect/active-effect-config.hbs";
}
/** @override */
async _prepareContext(options) {
const context = await super._prepareContext(options);
// Add DS4-specific context
context.itemEffectConfig = this.document.flags?.ds4?.itemEffectConfig || {};
context.applyToItems = context.itemEffectConfig.applyToItems || false;
return context;
}
/** @override */
async _onRender(context, options) {
await super._onRender(context, options);
// Set up initial visibility of item effect config section
const applyToItems = this.document.flags?.ds4?.itemEffectConfig?.applyToItems || false;
this._toggleItemEffectConfigVisibility(applyToItems);
// Add event listener for the checkbox
const checkbox = this.element.querySelector('input[name="flags.ds4.itemEffectConfig.applyToItems"]');
if (checkbox) {
checkbox.addEventListener("change", (event) => {
this._toggleItemEffectConfigVisibility(event.target.checked);
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
template: "systems/ds4/templates/sheets/active-effect/active-effect-config.hbs",
});
}
/**
* @override
* @param {JQuery} html
*/
activateListeners(html) {
super.activateListeners(html);
const checkbox = html[0]?.querySelector('input[name="flags.ds4.itemEffectConfig.applyToItems"]');
checkbox?.addEventListener("change", () => this.#toggleItemEffectConfig(checkbox.checked));
}
/**
* Toggle the visibility of the item effect config section
* @param {boolean} active - The target state
* @param {boolean} active The target state
*/
_toggleItemEffectConfigVisibility(active) {
const elements = this.element.querySelectorAll(".ds4-item-effect-config");
elements.forEach((element) => {
#toggleItemEffectConfig(active) {
const elements = this.element[0]?.querySelectorAll(".ds4-item-effect-config");
elements?.forEach((element) => {
if (active) {
element.classList.remove("ds4-hidden");
} else {

File diff suppressed because it is too large Load diff

View file

@ -1,34 +1,25 @@
// SPDX-FileCopyrightText: 2021 Johannes Loher
// SPDX-FileCopyrightText: 2025 Alexander Minges
//
// SPDX-License-Identifier: MIT
import { DS4ActorSheet } from "./base-sheet.js";
import { DS4ActorSheet } from "./base-sheet";
/**
* The Sheet class for DS4 Character Actors
*/
export class DS4CharacterActorSheet extends DS4ActorSheet {
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
classes: ["sheet", "ds4-actor-sheet", "ds4-character-sheet", "themed"],
};
/** @override */
async _prepareContext(options) {
const context = await super._prepareContext(options);
// Enrich biography HTML content
if (context.data.system.profile.biography) {
context.data.system.profile.biography = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
context.data.system.profile.biography,
{
async: true,
relativeTo: this.document,
},
);
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["sheet", "ds4-actor-sheet", "ds4-character-sheet"],
});
}
/** @override */
async getData(options = {}) {
const context = await super.getData(options);
context.data.system.profile.biography = await TextEditor.enrichHTML(context.data.system.profile.biography, {
async: true,
});
return context;
}
}

View file

@ -1,51 +1,25 @@
// SPDX-FileCopyrightText: 2021 Johannes Loher
// SPDX-FileCopyrightText: 2025 Alexander Minges
//
// SPDX-License-Identifier: MIT
import { DS4ActorSheet } from "./base-sheet.js";
import { DS4ActorSheet } from "./base-sheet";
/**
* The Sheet class for DS4 Creature Actors
*/
export class DS4CreatureActorSheet extends DS4ActorSheet {
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
classes: ["sheet", "ds4-actor-sheet", "ds4-creature-sheet", "themed"],
};
static TABS = {
primary: {
initial: "values",
tabs: [
{ id: "values", label: "DS4.HeadingValues", icon: "fas fa-chart-bar" },
{ id: "inventory", label: "DS4.HeadingInventory", icon: "fas fa-backpack" },
{ id: "spells", label: "DS4.HeadingSpells", icon: "fas fa-magic" },
{ id: "abilities", label: "DS4.HeadingAbilities", icon: "fas fa-fist-raised" },
{ id: "effects", label: "DS4.HeadingEffects", icon: "fas fa-sparkles" },
{ id: "description", label: "DS4.HeadingDescription", icon: "fas fa-file-text" }
]
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["sheet", "ds4-actor-sheet", "ds4-creature-sheet"],
});
}
};
/** @override */
async _prepareContext(options) {
const context = await super._prepareContext(options);
// Enrich description HTML content
if (context.data.system.baseInfo.description) {
context.data.system.baseInfo.description = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
context.data.system.baseInfo.description,
{
async getData(options = {}) {
const context = await super.getData(options);
context.data.system.baseInfo.description = await TextEditor.enrichHTML(context.data.system.baseInfo.description, {
async: true,
relativeTo: this.document,
},
);
}
// Add tabs configuration using ApplicationTab typedef
context.tabs = this._prepareTabs("primary");
});
return context;
}
}

View file

@ -3,129 +3,19 @@
// SPDX-License-Identifier: MIT
/**
* A simple extension to the DialogV2 class that allows attaching additional listeners.
* @typedef {DialogOptions} DialogWithListenersOptions
* @property {(html: JQuery, app: DialogWithListeners) => void} [activateAdditionalListeners] An optional function to attach additional listeners to the dialog
*/
export class DialogWithListeners extends foundry.applications.api.DialogV2 {
constructor(options = {}) {
super(options);
this.activateAdditionalListeners = options.activateAdditionalListeners;
}
static DEFAULT_OPTIONS = {
...super.DEFAULT_OPTIONS,
classes: ["dialog", "dialog-with-listeners"],
tag: "dialog",
window: {
resizable: true,
},
};
/**
* A simple extension to the {@link Dialog} class that allows attaching additional listeners.
*/
export class DialogWithListeners extends Dialog {
/** @override */
async _onRender(context, options) {
await super._onRender(context, options);
// Attach additional listeners if provided
if (this.activateAdditionalListeners && typeof this.activateAdditionalListeners === "function") {
this.activateAdditionalListeners(this.element, this);
activateListeners(html) {
super.activateListeners(html);
if (this.options.activateAdditionalListeners !== undefined) {
this.options.activateAdditionalListeners(html, this);
}
}
/**
* Create a confirmation dialog using the V2 framework with additional listeners support
* @param {object} options - Dialog options
* @param {string} options.title - Dialog title (deprecated, use window.title)
* @param {object} options.window - Window configuration
* @param {string} options.window.title - Dialog title
* @param {string} options.content - Dialog content HTML
* @param {boolean} options.defaultYes - Whether "Yes" is the default button
* @param {Function} options.activateAdditionalListeners - Function to attach additional listeners
* @returns {Promise<boolean>} True if confirmed, false if cancelled
*/
static async confirm(options = {}) {
const { title, window = {}, content, defaultYes = true, activateAdditionalListeners, ...rest } = options;
// Handle backward compatibility with title parameter
if (title && !window.title) {
window.title = title;
}
return new Promise((resolve) => {
const dialog = new DialogWithListeners({
window: {
title: window.title || "Confirm",
...window,
},
content,
activateAdditionalListeners,
buttons: [
{
action: "yes",
label: game.i18n.localize("Yes"),
icon: "fas fa-check",
default: defaultYes,
callback: () => resolve(true),
},
{
action: "no",
label: game.i18n.localize("No"),
icon: "fas fa-times",
default: !defaultYes,
callback: () => resolve(false),
},
],
close: () => resolve(false),
...rest,
});
dialog.render(true);
});
}
/**
* Create a prompt dialog using the V2 framework with additional listeners support
* @param {object} options - Dialog options
* @param {string} options.title - Dialog title (deprecated, use window.title)
* @param {object} options.window - Window configuration
* @param {string} options.window.title - Dialog title
* @param {string} options.content - Dialog content HTML
* @param {object} options.buttons - Button configuration
* @param {Function} options.activateAdditionalListeners - Function to attach additional listeners
* @returns {Promise} Promise that resolves with the result
*/
static async prompt(options = {}) {
const { title, window = {}, content, buttons = {}, activateAdditionalListeners, ...rest } = options;
// Handle backward compatibility with title parameter
if (title && !window.title) {
window.title = title;
}
return new Promise((resolve) => {
// Convert V1 button format to V2 format
const v2Buttons = Object.entries(buttons).map(([key, button]) => ({
action: key,
label: button.label || key,
icon: button.icon || "",
default: button.default || false,
callback: (event) => {
const result = button.callback ? button.callback(event) : key;
resolve(result);
},
}));
const dialog = new DialogWithListeners({
window: {
title: window.title || "Dialog",
...window,
},
content,
activateAdditionalListeners,
buttons: v2Buttons,
close: () => resolve(null),
...rest,
});
dialog.render(true);
});
}
}

View file

@ -1,63 +1,27 @@
// SPDX-FileCopyrightText: 2021 Johannes Loher
// SPDX-FileCopyrightText: 2021 Oliver Rümpelein
// SPDX-FileCopyrightText: 2021 Gesina Schwalbe
// SPDX-FileCopyrightText: 2025 Alexander Minges
//
// SPDX-License-Identifier: MIT
import { DS4 } from "../config";
import { DS4ActiveEffect } from "../documents/active-effect";
import { enforce, getGame } from "../utils/utils";
import { disableOverriddenFields } from "./sheet-helpers";
/**
* The Sheet class for DS4 Items
*/
export class DS4ItemSheet extends foundry.applications.api.HandlebarsApplicationMixin(
foundry.applications.sheets.ItemSheetV2,
) {
static DEFAULT_OPTIONS = {
classes: ["sheet", "ds4-item-sheet", "themed"],
tag: "form",
form: {
submitOnChange: true,
closeOnSubmit: false,
},
position: {
width: 560,
export class DS4ItemSheet extends ItemSheet {
/** @override */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["sheet", "ds4-item-sheet"],
height: 400,
},
window: {
resizable: true,
},
actions: {
controlEffect: DS4ItemSheet.prototype._onControlEffect,
createEffect: DS4ItemSheet.prototype._onCreateEffect,
editEffect: DS4ItemSheet.prototype._onEditEffect,
deleteEffect: DS4ItemSheet.prototype._onDeleteEffect,
editImage: DS4ItemSheet.prototype._onEditImage,
},
};
static TABS = {
primary: {
initial: "description",
tabs: [
{ id: "description", label: "DS4.HeadingDescription", icon: "fas fa-book" },
{ id: "effects", label: "DS4.HeadingEffects", icon: "fas fa-sparkles" }
]
}
};
constructor(options = {}) {
super(options);
// Initialize tabGroups with default values
if (!this.tabGroups) {
this.tabGroups = {};
}
// Set default tab for primary group
if (!this.tabGroups.primary) {
this.tabGroups.primary = this.constructor.TABS.primary?.initial || "description";
}
}
get title() {
return `${this.item.name} [${game.i18n.localize("DS4.ItemSheet")}]`;
scrollY: [".ds4-sheet-body"],
tabs: [{ navSelector: ".ds4-sheet-tab-nav", contentSelector: ".ds4-sheet-body", initial: "description" }],
width: 540,
});
}
/** @override */
@ -67,358 +31,120 @@ export class DS4ItemSheet extends foundry.applications.api.HandlebarsApplication
}
/** @override */
async _renderHTML(context) {
return await foundry.applications.handlebars.renderTemplate(this.template, context);
}
/** @override */
_replaceHTML(result, content) {
content.innerHTML = result;
}
/** @override */
async _prepareContext(options) {
const context = await super._prepareContext(options);
// Validate document exists
if (!this.item) {
throw new Error("Item not available for sheet rendering");
}
// Add document data
context.data = this.item;
context.system = this.item.system;
context.source = this.item.toObject();
context.cssClass = this.item.constructor.name.toLowerCase();
context.editable = this.isEditable;
context.owner = this.item.isOwner;
context.isOwned = this.item.isOwned;
context.actor = this.actor;
// Add configuration
context.config = CONFIG.DS4;
// Add tabs configuration using ApplicationTab typedef
context.tabs = this._prepareTabs("primary");
// Add enriched effects
context.enrichedEffects = [];
if (this.item.effects && this.item.effects.size > 0) {
for (const effect of this.item.effects) {
const enrichedEffect = effect.toObject();
enrichedEffect.id = effect.id;
enrichedEffect.uuid = effect.uuid;
enrichedEffect.sourceName = effect.sourceName;
context.enrichedEffects.push(enrichedEffect);
}
}
// Enrich description content for display
if (this.item.system.description) {
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
this.item.system.description,
{
secrets: this.item.isOwner,
relativeTo: this.item,
},
);
} else {
context.enrichedDescription = "";
}
async getData(options = {}) {
const superContext = await super.getData(options);
superContext.data.system.description = await TextEditor.enrichHTML(superContext.data.system.description, {
async: true,
});
const context = {
...superContext,
config: DS4,
isOwned: this.item.isOwned,
actor: this.item.actor,
};
return context;
}
/**
* Process form data for submission
* @param {Event} event - The form submission event
* @param {HTMLFormElement} form - The form element
* @param {FormDataExtended} formData - The form data
* @returns {object} The processed form data
*/
_processFormData(event, form, formData) {
const submitData = foundry.utils.expandObject(formData.object);
/** @override */
_getSubmitData(updateData = {}) {
const data = super._getSubmitData(updateData);
// Prevent submitting overridden values
const overrides = foundry.utils.flattenObject(this.item.overrides);
for (const k of Object.keys(overrides)) {
foundry.utils.setProperty(submitData, k, undefined);
delete submitData[k];
delete data[k];
}
return submitData;
}
/**
* Handle effect control actions
* @param {Event} event - The triggering event
* @param {HTMLElement} target - The target element
*/
async _onControlEffect(event, target) {
const action = target.dataset.action;
const effectId = target.closest("[data-effect-id]")?.dataset.effectId;
if (!effectId) return;
const effect = this.item.effects.get(effectId);
if (!effect) return;
switch (action) {
case "edit":
await this._onEditEffect(event, target);
break;
case "delete":
await this._onDeleteEffect(event, target);
break;
}
}
/**
* Handle creating a new effect
*/
async _onCreateEffect() {
const effectData = {
name: game.i18n.localize("DS4.NewEffectName"),
icon: "icons/svg/aura.svg",
};
await this.item.createEmbeddedDocuments("ActiveEffect", [effectData]);
}
/**
* Handle editing an effect
* @param {Event} event - The triggering event
* @param {HTMLElement} target - The target element
*/
async _onEditEffect(event, target) {
const effectId = target.closest("[data-effect-id]")?.dataset.effectId;
if (!effectId) return;
const effect = this.item.effects.get(effectId);
if (!effect) {
throw new Error(
game.i18n.format("DS4.ErrorItemDoesNotHaveEffect", {
id: effectId,
item: this.item.name,
}),
);
}
await effect.sheet.render(true);
}
/**
* Handle deleting an effect
* @param {Event} event - The triggering event
* @param {HTMLElement} target - The target element
*/
async _onDeleteEffect(event, target) {
const effectId = target.closest("[data-effect-id]")?.dataset.effectId;
if (!effectId) return;
const effect = this.item.effects.get(effectId);
if (!effect) return;
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: { title: game.i18n.localize("DS4.UserInteractionDeleteEffectTitle") },
content: game.i18n.format("DS4.UserInteractionDeleteEffectContent", { effect: effect.name }),
defaultYes: false,
});
if (confirmed) {
await effect.delete();
}
}
/**
* Handle editing the items's image
* @param {Event} event - The triggering event
* @param {HTMLElement} target - The target element
*/
async _onEditImage(event, target) {
const field = target.dataset.field || "img";
const current = foundry.utils.getProperty(this.item, field);
const fp = new foundry.applications.apps.FilePicker({
type: "image",
current: current,
callback: (path) => this.item.update({ [field]: path }),
});
return fp.browse();
return data;
}
/** @override */
async _onRender(context, options) {
await super._onRender(context, options);
this._initializeTabHandlers();
setPosition(options = {}) {
const position = super.setPosition(options);
if (position) {
const sheetBody = this.element.find(".sheet-body");
const bodyHeight = position.height - 192;
sheetBody.css("height", bodyHeight);
}
return position;
}
/**
* Initialize tab event handlers manually since ApplicationV2 doesn't do this automatically
* @override
* @param {JQuery} html
*/
_initializeTabHandlers() {
const tabNavigation = this.element.querySelector('.tabs[data-group="primary"]');
if (!tabNavigation) {
return;
activateListeners(html) {
super.activateListeners(html);
if (!this.options.editable) return;
html.find(".control-effect").on("click", this.onControlEffect.bind(this));
disableOverriddenFields(this.form, this.item.overrides, (key) => `[name="${key}"]`);
}
// Remove existing event listeners to prevent memory leaks
this._removeTabHandlers();
// Store tab navigation reference for cleanup
this._tabNavigation = tabNavigation;
// Create bound handler function for later removal
this._tabClickHandler = (event) => {
/**
* Handles a click on an element of this sheet to control an embedded effect of the item corresponding to this
* sheet.
*
* @param {JQuery.ClickEvent} event The originating click event
* @protected
*/
onControlEffect(event) {
event.preventDefault();
const item = event.target.closest('.item[data-tab]');
if (!item) {
return;
}
const tabId = item.dataset.tab;
const groupId = item.dataset.group;
if (tabId && groupId) {
this.changeTab(tabId, groupId);
}
};
// Add single delegated event listener to the navigation container
tabNavigation.addEventListener('click', this._tabClickHandler);
}
/**
* Override form submission to prevent re-rendering
*/
async _processSubmitData(event, form, submitData, options = {}) {
// Always prevent re-rendering on form updates
options.render = false;
return super._processSubmitData(event, form, submitData, options);
}
/**
* Remove tab event handlers to prevent memory leaks
*/
_removeTabHandlers() {
if (this._tabNavigation && this._tabClickHandler) {
this._tabNavigation.removeEventListener('click', this._tabClickHandler);
const a = event.currentTarget;
switch (a.dataset["action"]) {
case "create":
return this.onCreateEffect();
case "edit":
return this.onEditEffect(event);
case "delete":
return this.onDeleteEffect(event);
}
}
/**
* Override changeTab to ensure proper tab switching without forced re-render
* Creates a new embedded effect.
* @protected
*/
changeTab(tab, group, options = {}) {
// Call parent changeTab method
const result = super.changeTab(tab, group, options);
// Update tab display without full re-render
this._updateTabDisplay(tab, group);
return result;
onCreateEffect() {
DS4ActiveEffect.createDefault(this.item);
}
/**
* Update tab display without full re-render for better performance
* Opens the sheet of the embedded effect corresponding to the clicked element.
*
* @param {JQuery.ClickEvent} event The originating click event
* @porotected
*/
_updateTabDisplay(activeTab, group) {
const tabNavigation = this.element.querySelector(`.tabs[data-group="${group}"]`);
const tabContent = this.element.querySelector('.sheet-body');
if (!tabNavigation || !tabContent) return;
// Update navigation active states
const navItems = tabNavigation.querySelectorAll('.item[data-tab]');
navItems.forEach(item => {
if (item.dataset.tab === activeTab) {
item.classList.add('active');
} else {
item.classList.remove('active');
onEditEffect(event) {
const id = $(event.currentTarget)
.parents(embeddedDocumentListEntryProperties.ActiveEffect.selector)
.data(embeddedDocumentListEntryProperties.ActiveEffect.idDataAttribute);
const effect = this.item.effects.get(id);
enforce(effect, getGame().i18n.format("DS4.ErrorItemDoesNotHaveEffect", { id, item: this.item.name }));
effect.sheet?.render(true);
}
/**
* Deletes the embedded item corresponding to the clicked element.
*
* @param {JQuery.ClickEvent} event The originating click event
* @protected
*/
onDeleteEffect(event) {
const li = $(event.currentTarget).parents(embeddedDocumentListEntryProperties.ActiveEffect.selector);
const id = li.data(embeddedDocumentListEntryProperties.ActiveEffect.idDataAttribute);
this.item.deleteEmbeddedDocuments("ActiveEffect", [id]);
li.slideUp(200, () => this.render(false));
}
}
/**
* This object contains information about specific properties embedded document list entries for each different type.
*/
const embeddedDocumentListEntryProperties = Object.freeze({
ActiveEffect: {
selector: ".effect",
idDataAttribute: "effectId",
},
});
// Update content tab visibility
const contentTabs = tabContent.querySelectorAll('.tab[data-tab]');
contentTabs.forEach(tab => {
if (tab.dataset.tab === activeTab) {
tab.classList.add('active');
} else {
tab.classList.remove('active');
}
});
}
/**
* Override _onClose to cleanup event listeners
*/
async _onClose(options) {
this._removeTabHandlers();
return super._onClose(options);
}
/**
* Prepare tabs for a given group using ApplicationTab typedef
* @param {string} group - The tab group identifier
* @returns {Record<string, ApplicationTab>} Prepared tab data
*/
_prepareTabs(group) {
const config = this._getTabsConfigForItemType();
if (!config) return {};
// Ensure tabGroups is initialized
if (!this.tabGroups[group]) {
this.tabGroups[group] = config.initial || "description";
}
const tabs = {};
for (const tabConfig of config.tabs) {
const isActive = this.tabGroups[group] === tabConfig.id;
tabs[tabConfig.id] = {
id: tabConfig.id,
group: group,
icon: tabConfig.icon,
label: game.i18n.localize(tabConfig.label),
active: isActive,
cssClass: isActive ? "active" : ""
};
}
return tabs;
}
/**
* Get tab configuration based on item type
* @returns {Object} Tab configuration for this item type
*/
_getTabsConfigForItemType() {
const tabs = [
{ id: "description", label: "DS4.HeadingDescription", icon: "fas fa-book" }
];
// Item types that have properties tabs
const itemsWithProperties = [
"weapon", "armor", "shield", "equipment", "loot",
"spell", "talent", "specialCreatureAbility"
];
if (itemsWithProperties.includes(this.item.type)) {
tabs.push({
id: "properties",
label: "DS4.HeadingProperties",
icon: "fas fa-cogs"
});
}
// All items can have effects
tabs.push({
id: "effects",
label: "DS4.HeadingEffects",
icon: "fas fa-sparkles"
});
return { tabs, initial: "description" };
}
}

View file

@ -1,44 +0,0 @@
// SPDX-FileCopyrightText: 2025 Alexander Minges
//
// SPDX-License-Identifier: MIT
/**
* DS4 Token Ruler implementation with color-coded movement ranges
* Based on actor movement combat value
*/
export class DS4TokenRuler extends foundry.canvas.placeables.tokens.TokenRuler {
static WAYPOINT_LABEL_TEMPLATE = "systems/ds4/templates/partials/waypoint-label.hbs";
/**
* Enhance waypoint label context with movement range information
* @param {object} waypoint - The waypoint data
* @param {object} state - The current ruler state
* @returns {object} Enhanced context with range class
*/
_getWaypointLabelContext(waypoint, state) {
const context = super._getWaypointLabelContext(waypoint, state);
// Only apply movement coloring for distance measurements in meters
if (context?.cost?.units === "m" || context?.distance?.units === "m") {
const movement = this.token?.actor?.system?.combatValues?.movement?.total;
if (movement) {
const total = Number(context.cost?.total || context.distance?.total || 0);
// DS4 movement rules:
// - Normal movement: up to movement value
// - Dash: up to 2x movement value (requires action)
// - Beyond 2x: impossible in single turn
if (total > 2 * movement) {
context.rangeClass = "out-of-range";
} else if (total <= movement) {
context.rangeClass = "move-range";
} else {
context.rangeClass = "dash-range";
}
}
}
return context;
}
}

View file

@ -111,11 +111,6 @@ export async function createCheckRoll(checkTargetNumber, options = {}) {
// Ask for additional required data;
const interactiveRollData = await askForInteractiveRollData(checkTargetNumber, options);
// Handle cancelled dialog
if (!interactiveRollData) {
return undefined;
}
const newTargetValue = interactiveRollData.checkTargetNumber ?? checkTargetNumber;
const checkModifier = interactiveRollData.checkModifier ?? 0;
/** @type {Partial<DS4CheckFactoryOptions>} */
@ -171,23 +166,27 @@ async function askForInteractiveRollData(checkTargetNumber, options = {}, { temp
}),
id,
};
const renderedHtml = await foundry.applications.handlebars.renderTemplate(usedTemplate, templateData);
const renderedHtml = await renderTemplate(usedTemplate, templateData);
const dialogPromise = new Promise((resolve) => {
new DialogWithListeners({
window: {
title: usedTitle,
},
content: renderedHtml,
buttons: [
new DialogWithListeners(
{
action: "ok",
icon: "fas fa-check",
title: usedTitle,
content: renderedHtml,
buttons: {
ok: {
icon: '<i class="fas fa-check"></i>',
label: getGame().i18n.localize("DS4.GenericOkButton"),
default: true,
callback: (event) => {
const dialog = event.target.closest("dialog");
const innerForm = dialog?.querySelector("form");
callback: (html) => {
if (!("jquery" in html)) {
throw new Error(
getGame().i18n.format("DS4.ErrorUnexpectedHtmlType", {
exType: "JQuery",
realType: "HTMLElement",
}),
);
} else {
const innerForm = html[0]?.querySelector("form");
if (!innerForm) {
throw new Error(
getGame().i18n.format("DS4.ErrorCouldNotFindHtmlElement", {
@ -196,44 +195,34 @@ async function askForInteractiveRollData(checkTargetNumber, options = {}, { temp
);
}
resolve(innerForm);
}
},
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: getGame().i18n.localize("DS4.GenericCancelButton"),
},
},
default: "ok",
},
{
action: "cancel",
icon: "fas fa-times",
label: getGame().i18n.localize("DS4.GenericCancelButton"),
callback: () => resolve(null),
},
],
close: () => resolve(null),
activateAdditionalListeners: (html, app) => {
const checkModifierCustomFormGroup = html.querySelector(`#check-modifier-custom-${id}`)?.closest(".form-group");
const checkModifierSelect = html.querySelector(`#check-modifier-${id}`);
if (checkModifierSelect) {
checkModifierSelect.addEventListener("change", (event) => {
if (
event.currentTarget.value === "custom" &&
checkModifierCustomFormGroup?.classList.contains("ds4-hidden")
) {
checkModifierCustomFormGroup.classList.remove("ds4-hidden");
const checkModifierCustomFormGroup = html.find(`#check-modifier-custom-${id}`).parent(".form-group");
html.find(`#check-modifier-${id}`).on("change", (event) => {
if (event.currentTarget.value === "custom" && checkModifierCustomFormGroup.hasClass("ds4-hidden")) {
checkModifierCustomFormGroup.removeClass("ds4-hidden");
app.setPosition({ height: "auto" });
} else if (checkModifierCustomFormGroup && !checkModifierCustomFormGroup.classList.contains("ds4-hidden")) {
checkModifierCustomFormGroup.classList.add("ds4-hidden");
} else if (!checkModifierCustomFormGroup.hasClass("ds4-hidden")) {
checkModifierCustomFormGroup.addClass("ds4-hidden");
app.setPosition({ height: "auto" });
}
});
}
},
}).render(true);
id,
},
).render(true);
});
const dialogForm = await dialogPromise;
// Handle cancelled dialog
if (!dialogForm) {
return null;
}
return parseDialogFormData(dialogForm);
}
@ -243,11 +232,6 @@ async function askForInteractiveRollData(checkTargetNumber, options = {}, { temp
* @returns {Partial<IntermediateInteractiveRollData>}
*/
function parseDialogFormData(formData) {
// Handle cancelled dialog
if (!formData) {
return {};
}
const chosenCheckTargetNumber = parseInt(formData["check-target-number"]?.value);
const chosenCheckModifier = formData["check-modifier"]?.value;

View file

@ -29,6 +29,6 @@ export class DS4Roll extends Roll {
isCoup: isPrivate ? null : isCoup,
isFumble: isPrivate ? null : isFumble,
};
return foundry.applications.handlebars.renderTemplate(template, chatData);
return renderTemplate(template, chatData);
}
}

View file

@ -1,6 +1,5 @@
// SPDX-FileCopyrightText: 2021 Johannes Loher
// SPDX-FileCopyrightText: 2021 Oliver RÜmpelein
// SPDX-FileCopyrightText: 2025 Alexander Minges
//
// SPDX-License-Identifier: MIT
@ -464,11 +463,9 @@ export class DS4Actor extends Actor {
async selectAttributeAndTrait() {
const attributeIdentifier = "attribute-trait-selection-attribute";
const traitIdentifier = "attribute-trait-selection-trait";
return foundry.applications.api.DialogV2.prompt({
window: { title: getGame().i18n.localize("DS4.DialogAttributeTraitSelection") },
content: await foundry.applications.handlebars.renderTemplate(
"systems/ds4/templates/dialogs/simple-select-form.hbs",
{
return Dialog.prompt({
title: getGame().i18n.localize("DS4.DialogAttributeTraitSelection"),
content: await renderTemplate("systems/ds4/templates/dialogs/simple-select-form.hbs", {
selects: [
{
label: getGame().i18n.localize("DS4.Attribute"),
@ -491,12 +488,10 @@ export class DS4Actor extends Actor {
),
},
],
},
),
ok: {
}),
label: getGame().i18n.localize("DS4.GenericOkButton"),
callback: (_event, button) => {
const selectedAttribute = button.form.elements[attributeIdentifier].value;
callback: (html) => {
const selectedAttribute = html.find(`#${attributeIdentifier}`).val();
if (!isAttribute(selectedAttribute)) {
throw new Error(
getGame().i18n.format("DS4.ErrorUnexpectedAttribute", {
@ -507,7 +502,7 @@ export class DS4Actor extends Actor {
}),
);
}
const selectedTrait = button.form.elements[traitIdentifier].value;
const selectedTrait = html.find(`#${traitIdentifier}`).val();
if (!isTrait(selectedTrait)) {
throw new Error(
getGame().i18n.format("DS4.ErrorUnexpectedTrait", {
@ -523,7 +518,6 @@ export class DS4Actor extends Actor {
trait: selectedTrait,
};
},
},
rejectClose: false,
});
}

View file

@ -1,5 +1,4 @@
// SPDX-FileCopyrightText: 2022 Johannes Loher
// SPDX-FileCopyrightText: 2025 Alexander Minges
//
// SPDX-License-Identifier: MIT
@ -84,11 +83,9 @@ export class DS4Weapon extends DS4Item {
const { melee, ranged } = { ...DS4.i18n.attackTypes };
const identifier = `attack-type-selection-${foundry.utils.randomID()}`;
return foundry.applications.api.DialogV2.prompt({
window: { title: getGame().i18n.localize("DS4.DialogAttackTypeSelection") },
content: await foundry.applications.handlebars.renderTemplate(
"systems/ds4/templates/dialogs/simple-select-form.hbs",
{
return Dialog.prompt({
title: getGame().i18n.localize("DS4.DialogAttackTypeSelection"),
content: await renderTemplate("systems/ds4/templates/dialogs/simple-select-form.hbs", {
selects: [
{
label: getGame().i18n.localize("DS4.AttackType"),
@ -96,12 +93,10 @@ export class DS4Weapon extends DS4Item {
options: { melee, ranged },
},
],
},
),
ok: {
}),
label: getGame().i18n.localize("DS4.GenericOkButton"),
callback: (_event, button) => {
const selectedAttackType = button.form.elements[identifier].value;
callback: (html) => {
const selectedAttackType = html.find(`#${identifier}`).val();
if (selectedAttackType !== "melee" && selectedAttackType !== "ranged") {
throw new Error(
getGame().i18n.format("DS4.ErrorUnexpectedAttackType", {
@ -112,7 +107,6 @@ export class DS4Weapon extends DS4Item {
}
return selectedAttackType;
},
},
});
}
}

View file

@ -14,11 +14,6 @@ const helpers = {
isEmpty: (input: Array<unknown> | null | undefined): boolean => (input?.length ?? 0) === 0,
capitalize: (str: string): string => {
if (typeof str !== "string") return "";
return str.charAt(0).toUpperCase() + str.slice(1);
},
toRomanNumerals,
};

View file

@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: 2021 Johannes Loher
// SPDX-FileCopyrightText: 2021 Oliver Rümpelein
// SPDX-FileCopyrightText: 2021 Gesina Schwalbe
// SPDX-FileCopyrightText: 2025 Alexander Minges
//
// SPDX-License-Identifier: MIT
@ -56,7 +55,6 @@ export async function registerHandlebarsPartials() {
"systems/ds4/templates/sheets/shared/components/add-button.hbs",
"systems/ds4/templates/sheets/shared/components/control-button-group.hbs",
"systems/ds4/templates/sheets/shared/components/rollable-image.hbs",
"systems/ds4/templates/partials/waypoint-label.hbs",
];
await foundry.applications.handlebars.loadTemplates(templatePaths);
await loadTemplates(templatePaths);
}

View file

@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: 2021 Johannes Loher
// SPDX-FileCopyrightText: 2021 Oliver Rümpelein
// SPDX-FileCopyrightText: 2021 Gesina Schwalbe
// SPDX-FileCopyrightText: 2025 Alexander Minges
//
// SPDX-License-Identifier: MIT
@ -9,7 +8,6 @@ import { DS4ActiveEffectConfig } from "../apps/active-effect-config";
import { DS4CharacterActorSheet } from "../apps/actor/character-sheet";
import { DS4CreatureActorSheet } from "../apps/actor/creature-sheet";
import { DS4ItemSheet } from "../apps/item-sheet";
import { DS4TokenRuler } from "../apps/ruler/token-ruler";
import { DS4 } from "../config";
import { DS4Check } from "../dice/check";
import { createCheckRoll } from "../dice/check-factory";
@ -53,8 +51,6 @@ async function init() {
CONFIG.ChatMessage.documentClass = DS4ChatMessage;
CONFIG.Token.documentClass = DS4TokenDocument;
CONFIG.Token.rulerClass = DS4TokenRuler;
CONFIG.ActiveEffect.legacyTransferral = false;
CONFIG.Actor.typeLabels = DS4.i18n.actorTypes;
@ -69,23 +65,16 @@ async function init() {
registerSystemSettings();
foundry.applications.apps.DocumentSheetConfig.registerSheet(Actor, "ds4", DS4CharacterActorSheet, {
DocumentSheetConfig.unregisterSheet(Actor, "core", ActorSheet);
DocumentSheetConfig.registerSheet(Actor, "ds4", DS4CharacterActorSheet, {
types: ["character"],
makeDefault: true,
});
foundry.applications.apps.DocumentSheetConfig.registerSheet(Actor, "ds4", DS4CreatureActorSheet, {
types: ["creature"],
makeDefault: true,
});
foundry.applications.apps.DocumentSheetConfig.registerSheet(Item, "ds4", DS4ItemSheet, { makeDefault: true });
foundry.applications.apps.DocumentSheetConfig.unregisterSheet(
ActiveEffect,
"core",
foundry.applications.sheets.ActiveEffectConfig,
);
foundry.applications.apps.DocumentSheetConfig.registerSheet(ActiveEffect, "ds4", DS4ActiveEffectConfig, {
makeDefault: true,
});
DocumentSheetConfig.registerSheet(Actor, "ds4", DS4CreatureActorSheet, { types: ["creature"], makeDefault: true });
DocumentSheetConfig.unregisterSheet(Item, "core", ItemSheet);
DocumentSheetConfig.registerSheet(Item, "ds4", DS4ItemSheet, { makeDefault: true });
DocumentSheetConfig.unregisterSheet(ActiveEffect, "core", ActiveEffectConfig);
DocumentSheetConfig.registerSheet(ActiveEffect, "ds4", DS4ActiveEffectConfig, { makeDefault: true });
preloadFonts();
await registerHandlebarsPartials();

View file

@ -17,18 +17,10 @@ export function registerForRenderHooks() {
* Select the text of input elements in given application when focused via an on focus listener.
*
* @param {Application} app The application in which to activate the listener.
* @param {HTMLElement} element The HTML element representing the HTML of the application.
* @param {JQuery} html The {@link JQuery} representing the HTML of the application.
*/
function selectTargetInputOnFocus(app, element) {
// V13: element is always a plain DOM element
if (!element || typeof element.querySelectorAll !== "function") {
return;
}
const inputs = element.querySelectorAll("input");
inputs.forEach((input) => {
input.addEventListener("focus", (ev) => {
function selectTargetInputOnFocus(app, html) {
html.find("input").on("focus", (ev) => {
ev.currentTarget.select();
});
});
}

View file

@ -30,10 +30,7 @@ export function enforce(value, message) {
* @returns {Canvas}
*/
export function getCanvas() {
enforce(
canvas instanceof foundry.canvas.Canvas && canvas.ready,
getGame().i18n.localize("DS4.ErrorCanvasIsNotInitialized"),
);
enforce(canvas instanceof Canvas && canvas.ready, getGame().i18n.localize("DS4.ErrorCanvasIsNotInitialized"));
return canvas;
}
@ -43,7 +40,7 @@ export function getCanvas() {
* @returns {Game}
*/
export function getGame() {
enforce(game instanceof foundry.Game, "Game is not initialized yet.");
enforce(game instanceof Game, "Game is not initialized yet.");
return game;
}

View file

@ -32,7 +32,7 @@
"readme": "https://git.f3l.de/dungeonslayers/ds4/raw/tag/2.0.5/README.md",
"bugs": "https://git.f3l.de/dungeonslayers/ds4/issues",
"changelog": "https://git.f3l.de/dungeonslayers/ds4/releases/tag/2.0.5",
"version": "3.0.0",
"version": "2.0.5",
"flags": {
"hotReload": {
"extensions": ["css", "hbs", "json"],
@ -40,8 +40,8 @@
}
},
"compatibility": {
"minimum": "13",
"verified": "13.346"
"minimum": "12.331",
"verified": "12"
},
"esmodules": ["ds4.js"],
"styles": ["css/ds4.css"],

View file

@ -4,7 +4,7 @@ SPDX-FileCopyrightText: 2021 Johannes Loher
SPDX-License-Identifier: MIT
--}}
<div class="dice-roll" data-action="expandRoll">
<div class="dice-roll">
{{#if flavor}}
<div class="dice-flavor">{{flavor}}</div>
{{/if}}

View file

@ -1,43 +0,0 @@
{{!--
SPDX-FileCopyrightText: 2025 Alexander Minges
SPDX-License-Identifier: MIT
--}}
{{!--
!-- Waypoint label template with color-coded movement ranges
!-- Based on DS4 movement combat values
--}}
<div class="waypoint-label {{rangeClass}}">
{{#if (eq rangeClass "dash-range")}}
<i class="fas fa-person-running"></i>
{{else if (eq rangeClass "out-of-range")}}
<i class="fas fa-person-rays"></i>
{{else if action.icon}}
<i class="{{action.icon}}"></i>
{{else if action.label}}
{{localize action.label}}
{{/if}}
{{#if cost}}
<span class="distance {{rangeClass}}">{{cost.total}} {{cost.units}}</span>
{{#if cost.delta}}
<span class="delta">({{cost.delta}})</span>
{{/if}}
{{else}}
<span class="distance {{rangeClass}}">{{distance.total}} {{units}}</span>
{{#if distance.delta}}
<span class="delta">({{distance.delta}})</span>
{{/if}}
{{/if}}
{{#if (and elevation (not elevation.hidden))}}
<span class="elevation">
{{elevation.total}} {{units}}
{{#if elevation.delta}}
({{elevation.delta}})
{{/if}}
</span>
{{/if}}
{{#if secret}}
<i class="fas fa-eye-slash"></i>
{{/if}}
</div>

View file

@ -32,18 +32,8 @@ SPDX-License-Identifier: MIT
<div class="form-group stacked">
<label>{{ localize "EFFECT.Description" }}</label>
{{#if editable}}
<prose-mirror
name="description"
button="false"
editable="{{editable}}"
toggled="false"
value="{{data.description}}">
{{{descriptionHTML}}}
</prose-mirror>
{{else}}
{{{descriptionHTML}}}
{{/if}}
{{editor descriptionHTML target="description" button=false editable=editable engine="prosemirror"
collaborate=false}}
</div>
<div class="form-group">

View file

@ -13,47 +13,38 @@ SPDX-License-Identifier: MIT
{{/systems/ds4/templates/sheets/actor/components/actor-header.hbs}}
{{!-- Sheet Tab Navigation --}}
<nav class="tabs" data-group="primary">
{{#each tabs}}
<a class="item {{cssClass}}" data-tab="{{id}}" data-group="{{group}}">
{{#if icon}}<i class="{{icon}}"></i>{{/if}}
{{label}}
</a>
{{/each}}
<nav class="ds4-sheet-tab-nav tabs" data-group="primary">
<a class="ds4-sheet-tab-nav__item" data-tab="values">{{localize 'DS4.HeadingValues'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="inventory">{{localize 'DS4.HeadingInventory'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="spells">{{localize 'DS4.HeadingSpells'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="abilities">{{localize 'DS4.HeadingAbilities'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="effects">{{localize 'DS4.HeadingEffects'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="biography">{{localize 'DS4.HeadingBiography'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
<!-- beautify ignore:start -->
<!-- prettier-ignore-start -->
{{!-- Sheet Body (remove indentation to avoid annoying Handlebars auto-indent) --}}
<section class="ds4-sheet-body">
{{!-- Values Tab --}}
<div class="tab {{#if tabs.values}}{{tabs.values.cssClass}}{{/if}}" data-tab="values" data-group="primary">
{{> systems/ds4/templates/sheets/actor/components/core-values.hbs}}
{{> systems/ds4/templates/sheets/actor/components/combat-values.hbs}}
{{> systems/ds4/templates/sheets/actor/components/checks.hbs}}
</div>
{{> systems/ds4/templates/sheets/actor/tabs/values.hbs}}
{{!-- Inventory Tab --}}
<div class="tab {{#if tabs.inventory}}{{tabs.inventory.cssClass}}{{/if}}" data-tab="inventory" data-group="primary">
{{> systems/ds4/templates/sheets/actor/tabs/character-inventory.hbs}}
</div>
{{!-- Spells Tab --}}
<div class="tab {{#if tabs.spells}}{{tabs.spells.cssClass}}{{/if}}" data-tab="spells" data-group="primary">
{{> systems/ds4/templates/sheets/actor/tabs/spells.hbs}}
</div>
{{!-- Abilities Tab --}}
<div class="tab {{#if tabs.abilities}}{{tabs.abilities.cssClass}}{{/if}}" data-tab="abilities" data-group="primary">
{{> systems/ds4/templates/sheets/actor/tabs/character-abilities.hbs}}
</div>
{{!-- Effects Tab --}}
<div class="tab {{#if tabs.effects}}{{tabs.effects.cssClass}}{{/if}}" data-tab="effects" data-group="primary">
{{> systems/ds4/templates/sheets/actor/tabs/effects.hbs}}
</div>
{{!-- Biography Tab --}}
<div class="tab {{#if tabs.biography}}{{tabs.biography.cssClass}}{{/if}}" data-tab="biography" data-group="primary">
{{> systems/ds4/templates/sheets/actor/tabs/biography.hbs}}
</div>
</section>
<!-- prettier-ignore-end -->
<!-- beautify ignore:end -->
</form>

View file

@ -11,7 +11,7 @@ SPDX-License-Identifier: MIT
!-- @param @partial-block: Properties to render in the second header row.
--}}
<header class="ds4-actor-header">
<img class="ds4-actor-header__img{{#if editable}} ds4-actor-header__img--editable{{/if}}" src="{{data.img}}" data-action="editImage" alt="{{localize 'DS4.ActorImageAltText'}}"
<img class="ds4-actor-header__img{{#if editable}} ds4-actor-header__img--editable{{/if}}" src="{{data.img}}" data-edit="img" alt="{{localize 'DS4.ActorImageAltText'}}"
title="{{data.name}}" height="100" width="100" />
<div class="ds4-actor-header__data">
<div class="ds4-actor-header__data-row">

View file

@ -5,16 +5,6 @@ SPDX-License-Identifier: MIT
--}}
<div class="ds4-biography">
{{#if editable}}
<prose-mirror
name="system.profile.biography"
button="true"
editable="{{editable}}"
toggled="false"
value="{{data.system.profile.biography}}">
{{{data.system.profile.biography}}}
</prose-mirror>
{{else}}
{{{data.system.profile.biography}}}
{{/if}}
{{editor data.system.profile.biography target="system.profile.biography" button=true owner=owner
editable=editable engine="prosemirror"}}
</div>

View file

@ -12,7 +12,7 @@ SPDX-License-Identifier: MIT
!-- @param check-label: The label for the check
--}}
<button class="ds4-check" data-action="rollCheck" data-check="{{check-key}}"
<button class="ds4-check rollable-check" data-check="{{check-key}}"
title="{{localize 'DS4.CheckTooltip' check=check-label}}">
<span>{{check-label}}</span>
<span>{{check-target-number}}</span>

View file

@ -17,7 +17,7 @@ SPDX-License-Identifier: MIT
<div class="ds4-combat-value">
<div class="ds4-combat-value__value ds4-combat-value__value--{{combat-value-key}}"
title="{{combat-value-title}}: {{combat-value-data.tooltip}}">
<span class="ds4-combat-value__text">{{combat-value-data.total}}</span>
{{combat-value-data.total}}
</div>
<span title="{{combat-value-title}}" class="ds4-combat-value__label">{{combat-value-label}}</span>
<div class="ds4-combat-value__formula">

View file

@ -5,16 +5,6 @@ SPDX-License-Identifier: MIT
--}}
<div class="ds4-description">
{{#if editable}}
<prose-mirror
name="system.baseInfo.description"
button="true"
editable="{{editable}}"
toggled="false"
value="{{data.system.baseInfo.description}}">
{{{data.system.baseInfo.description}}}
</prose-mirror>
{{else}}
{{{data.system.baseInfo.description}}}
{{/if}}
{{editor data.system.baseInfo.description target="system.baseInfo.description" button=true owner=owner
editable=editable engine="prosemirror"}}
</div>

View file

@ -9,11 +9,11 @@ SPDX-License-Identifier: MIT
!-- Render an effect list entry row.
!-- @param effectData: The data of the item.
--}}
<li class="ds4-embedded-document-list__row effect draggable" data-effect-uuid="{{effectData.uuid}}" data-effect-id="{{effectData.id}}">
<li class="ds4-embedded-document-list__row effect" data-effect-uuid="{{effectData.uuid}}" data-effect-id="{{effectData.id}}">
{{!-- enabled --}}
<input class="ds4-embedded-document-list__editable ds4-embedded-document-list__editable--checkbox"
<input class="ds4-embedded-document-list__editable ds4-embedded-document-list__editable--checkbox change-effect"
type="checkbox" {{checked (ne effectData.disabled true)}} data-dtype="Boolean" data-property="disabled"
data-inverted="true" data-action="changeEffect" title="{{localize 'DS4.EffectEnabled'}}">
data-inverted="true" title="{{localize 'DS4.EffectEnabled'}}">
{{!-- active --}}
{{#if effectData.active}}<i class="fas fa-check"></i>{{else}}<i class="fas fa-ban"></i>{{/if}}

View file

@ -15,30 +15,30 @@ SPDX-License-Identifier: MIT
!-- @param hideDescription: A flag to disable the description column.
!-- @param @partial-block: Custom column headers can be passed using the partial block.
--}}
<li class="ds4-embedded-document-list__row item draggable" data-item-uuid="{{item.uuid}}" data-item-id="{{item.id}}">
<li class="ds4-embedded-document-list__row item" data-item-uuid="{{item.uuid}}" data-item-id="{{item.id}}">
{{!-- equipped --}}
{{#if isEquipable}}
<input class="ds4-embedded-document-list__editable ds4-embedded-document-list__editable--checkbox"
<input class="ds4-embedded-document-list__editable ds4-embedded-document-list__editable--checkbox change-item"
type="checkbox" {{checked item.system.equipped}} data-dtype="Boolean" data-property="system.equipped"
data-action="changeItem" title="{{localize 'DS4.ItemEquipped'}}">
title="{{localize 'DS4.ItemEquipped'}}">
{{/if}}
{{!-- image --}}
{{> systems/ds4/templates/sheets/shared/components/rollable-image.hbs rollable=(and item.system.rollable
@root/editable)
src=item.img alt=(localize "DS4.DocumentImageAltText" name=item.name) title=item.name
rollableTitle=(localize "DS4.RollableImageRollableTitle" name=item.name) rollableAction="rollItem"}}
rollableTitle=(localize "DS4.RollableImageRollableTitle" name=item.name) rollableClass="rollable-item"}}
{{!-- amount --}}
{{#if hasQuantity}}
<input class="ds4-embedded-document-list__editable" type="number" min="0" step="1"
<input class="ds4-embedded-document-list__editable change-item" type="number" min="0" step="1"
value="{{item.system.quantity}}" data-dtype="Number" data-property="system.quantity"
data-action="changeItem" title="{{localize 'DS4.Quantity'}}" />
title="{{localize 'DS4.Quantity'}}" />
{{/if}}
{{!-- name --}}
<input class="ds4-embedded-document-list__editable" type="text" value="{{item.name}}"
data-dtype="String" data-property="name" data-action="changeItem" title="{{htmlToPlainText item.system.description}}" />
<input class="ds4-embedded-document-list__editable change-item" type="text" value="{{item.name}}"
data-dtype="String" data-property="name" title="{{htmlToPlainText item.system.description}}" />
{{!-- item type specifics --}}
{{#if @partial-block }}

View file

@ -18,7 +18,7 @@ SPDX-License-Identifier: MIT
<li class="ds4-embedded-document-list__row ds4-embedded-document-list__row--header" data-type={{type}}>
{{!-- equipped --}}
{{#if isEquipable}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.equipped"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.equipped"
title="{{localize 'DS4.SortByItemEquipped'}}">
{{localize 'DS4.ItemEquippedAbbr'}}</div>
{{/if}}
@ -28,12 +28,12 @@ SPDX-License-Identifier: MIT
{{!-- amount --}}
{{#if hasQuantity}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.quantity"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.quantity"
title="{{localize 'DS4.SortByQuantity'}}">#</div>
{{/if}}
{{!-- name --}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="name"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="name"
title="{{localize 'DS4.SortByItemName'}}">{{localize 'DS4.ItemName'}}
</div>
@ -44,7 +44,7 @@ SPDX-License-Identifier: MIT
{{!-- description --}}
{{#unless hideDescription}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.description"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.description"
title="{{localize 'DS4.SortByDescription'}}">{{localize
'DS4.Description'}}</div>
{{/unless}}

View file

@ -13,19 +13,19 @@ SPDX-License-Identifier: MIT
{{#> systems/ds4/templates/sheets/actor/components/item-list-header.hbs isEquipable=true hasQuantity=true
type='weapon'}}
{{!-- attack type --}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.attackType"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.attackType"
title="{{localize 'DS4.SortByAttackType'}}">
{{localize
'DS4.AttackTypeAbbr'}}</div>
{{!-- weapon bonus --}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.weaponBonus"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.weaponBonus"
title="{{localize 'DS4.SortByWeaponBonus'}}">
{{localize 'DS4.WeaponBonusAbbr'}}
</div>
{{!-- opponent defense --}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.opponentDefense"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.opponentDefense"
title="{{localize 'DS4.SortByOpponentDefense'}}">
{{localize 'DS4.OpponentDefenseAbbr'}}
</div>
@ -70,15 +70,15 @@ documentType='item' type='weapon'}}
{{#> systems/ds4/templates/sheets/actor/components/item-list-header.hbs isEquipable=true hasQuantity=true
type="armor"}}
{{!-- armor material type --}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.armorMaterialType"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.armorMaterialType"
title="{{localize 'DS4.SortByArmorMaterialType'}}">{{localize 'DS4.ArmorMaterialTypeAbbr'}}</div>
{{!-- armor type --}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.armorType"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.armorType"
title="{{localize 'DS4.SortByArmorType'}}">{{localize 'DS4.ArmorTypeAbbr'}}</div>
{{!-- armor value --}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.armorValue"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.armorValue"
title="{{localize 'DS4.SortByArmorValue'}}">
{{localize 'DS4.ArmorValueAbbr'}}
</div>
@ -113,7 +113,7 @@ documentType='item' type='armor'}}
{{#> systems/ds4/templates/sheets/actor/components/item-list-header.hbs isEquipable=true hasQuantity=true
type='shield'}}
{{!-- armor value --}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.armorValue"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.armorValue"
title="{{localize 'DS4.SortByArmorValue'}}">
{{localize 'DS4.ArmorValueAbbr'}}
</div>
@ -137,15 +137,15 @@ documentType='item' type='shield'}}
{{#> systems/ds4/templates/sheets/actor/components/item-list-header.hbs isEquipable=true hasQuantity=true
type='equipment'}}
{{!-- storage location --}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.storageLocation"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.storageLocation"
title="{{localize 'DS4.SortByStorageLocation'}}">{{localize 'DS4.StorageLocation'}}</div>
{{/systems/ds4/templates/sheets/actor/components/item-list-header.hbs}}
{{#each itemsByType.equipment as |item id|}}
{{#> systems/ds4/templates/sheets/actor/components/item-list-entry.hbs item=item isEquipable=true
hasQuantity=true}}
{{!-- storage location --}}
<input class="ds4-embedded-document-list__editable" type="text" value="{{item.system.storageLocation}}"
data-dtype="String" data-property="system.storageLocation" data-action="changeItem" title="{{localize 'DS4.StorageLocation'}}">
<input class="ds4-embedded-document-list__editable change-item" type="text" value="{{item.system.storageLocation}}"
data-dtype="String" data-property="system.storageLocation" title="{{localize 'DS4.StorageLocation'}}">
{{/systems/ds4/templates/sheets/actor/components/item-list-entry.hbs}}
{{/each}}
</ol>
@ -159,14 +159,14 @@ documentType='item' type='equipment'}}
<ol class="ds4-embedded-document-list ds4-embedded-document-list--loot item-list">
{{#> systems/ds4/templates/sheets/actor/components/item-list-header.hbs hasQuantity=true type='loot'}}
{{!-- storage location --}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.storageLocation"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.storageLocation"
title="{{localize 'DS4.SortByStorageLocation'}}">{{localize 'DS4.StorageLocation'}}</div>
{{/systems/ds4/templates/sheets/actor/components/item-list-header.hbs}}
{{#each itemsByType.loot as |item id|}}
{{#> systems/ds4/templates/sheets/actor/components/item-list-entry.hbs item=item hasQuantity=true}}
{{!-- storage location --}}
<input class="ds4-embedded-document-list__editable" type="text" value="{{item.system.storageLocation}}"
data-dtype="String" data-property="system.storageLocation" data-action="changeItem" title="{{localize 'DS4.StorageLocation'}}">
<input class="ds4-embedded-document-list__editable change-item" type="text" value="{{item.system.storageLocation}}"
data-dtype="String" data-property="system.storageLocation" title="{{localize 'DS4.StorageLocation'}}">
{{/systems/ds4/templates/sheets/actor/components/item-list-entry.hbs}}
{{/each}}
</ol>

View file

@ -12,46 +12,35 @@ SPDX-License-Identifier: MIT
{{> systems/ds4/templates/sheets/actor/components/creature-properties.hbs}}
{{/systems/ds4/templates/sheets/actor/components/actor-header.hbs}}
{{!-- Sheet Tab Navigation --}}
<nav class="tabs" data-group="primary">
{{#each tabs}}
<a class="item {{cssClass}}" data-tab="{{id}}" data-group="{{group}}">
{{#if icon}}<i class="{{icon}}"></i>{{/if}}
{{label}}
</a>
{{/each}}
<nav class="ds4-sheet-tab-nav" data-group="primary">
<a class="ds4-sheet-tab-nav__item" data-tab="values">{{localize 'DS4.HeadingValues'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="inventory">{{localize 'DS4.HeadingInventory'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="spells">{{localize 'DS4.HeadingSpells'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="abilities">{{localize 'DS4.HeadingAbilities'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="effects">{{localize 'DS4.HeadingEffects'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="description">{{localize 'DS4.HeadingDescription'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
<section class="ds4-sheet-body">
{{!-- Values Tab --}}
<div class="tab {{#if tabs.values}}{{tabs.values.cssClass}}{{/if}}" data-tab="values" data-group="primary">
{{> systems/ds4/templates/sheets/actor/tabs/values.hbs}}
</div>
{{!-- Inventory Tab --}}
<div class="tab {{#if tabs.inventory}}{{tabs.inventory.cssClass}}{{/if}}" data-tab="inventory" data-group="primary">
{{> systems/ds4/templates/sheets/actor/tabs/creature-inventory.hbs}}
</div>
{{!-- Spells Tab --}}
<div class="tab {{#if tabs.spells}}{{tabs.spells.cssClass}}{{/if}}" data-tab="spells" data-group="primary">
{{> systems/ds4/templates/sheets/actor/tabs/spells.hbs}}
</div>
{{!-- Abilities Tab --}}
<div class="tab {{#if tabs.abilities}}{{tabs.abilities.cssClass}}{{/if}}" data-tab="abilities" data-group="primary">
{{> systems/ds4/templates/sheets/actor/tabs/creature-abilities.hbs}}
</div>
{{!-- Effects Tab --}}
<div class="tab {{#if tabs.effects}}{{tabs.effects.cssClass}}{{/if}}" data-tab="effects" data-group="primary">
{{> systems/ds4/templates/sheets/actor/tabs/effects.hbs}}
</div>
{{!-- Description Tab --}}
<div class="tab {{#if tabs.description}}{{tabs.description.cssClass}}{{/if}}" data-tab="description" data-group="primary">
{{> systems/ds4/templates/sheets/actor/tabs/description.hbs}}
</div>
</section>
</form>

View file

@ -9,6 +9,7 @@ SPDX-License-Identifier: MIT
{{#> systems/ds4/templates/sheets/actor/components/actor-header.hbs}}
{{/systems/ds4/templates/sheets/actor/components/actor-header.hbs}}
{{!-- Sheet Body --}}
<section class="ds4-sheet-body">
{{#if (eq data.type 'character')}}

View file

@ -4,6 +4,7 @@ SPDX-FileCopyrightText: 2021 Johannes Loher
SPDX-License-Identifier: MIT
--}}
<div class="ds4-sheet-tab tab biography" data-group="primary" data-tab="biography">
<div class="ds4-biography-tab-content">
<!-- beautify ignore:start -->
<!-- prettier-ignore-start -->
@ -13,3 +14,4 @@ SPDX-License-Identifier: MIT
<!-- beautify ignore:end -->
<!-- prettier-ignore-end -->
</div>
</div>

View file

@ -5,14 +5,14 @@ SPDX-FileCopyrightText: 2021 Gesina Schwalbe
SPDX-License-Identifier: MIT
--}}
<div class="ds4-sheet-tab tab abilities" data-group="primary" data-tab="abilities">
{{!-- TALENT --}}
<h4 class="ds4-embedded-document-list-title">{{localize 'DS4.ItemTypeTalentPlural'}}</h4>
{{#unless (isEmpty itemsByType.talent)}}
<ol class="ds4-embedded-document-list ds4-embedded-document-list--talent item-list">
{{#> systems/ds4/templates/sheets/actor/components/item-list-header.hbs type='talent'}}
{{!-- rank --}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.rank.total"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.rank.total"
title="{{localize 'DS4.SortByTalentRank'}}">{{localize 'DS4.TalentRank'}}</div>
{{/systems/ds4/templates/sheets/actor/components/item-list-header.hbs}}
{{#each itemsByType.talent as |item id|}}
@ -64,3 +64,4 @@ SPDX-License-Identifier: MIT
{{/unless}}
{{> systems/ds4/templates/sheets/shared/components/add-button.hbs title='DS4.UserInteractionAddItemTitle'
documentType='item' type='alphabet'}}
</div>

View file

@ -5,5 +5,7 @@ SPDX-FileCopyrightText: 2021 Gesina Schwalbe
SPDX-License-Identifier: MIT
--}}
<div class="ds4-sheet-tab tab inventory" data-group="primary" data-tab="inventory">
{{> systems/ds4/templates/sheets/actor/components/currency.hbs}}
{{> systems/ds4/templates/sheets/actor/components/items-overview.hbs}}
</div>

View file

@ -5,6 +5,7 @@ SPDX-FileCopyrightText: 2021 Gesina Schwalbe
SPDX-License-Identifier: MIT
--}}
<div class="ds4-sheet-tab tab abilities" data-group="primary" data-tab="abilities">
{{#unless (isEmpty itemsByType.specialCreatureAbility)}}
<ol class="ds4-embedded-document-list ds4-embedded-document-list--special-creature-ability item-list">
{{> systems/ds4/templates/sheets/actor/components/item-list-header.hbs type='specialCreatureAbility'}}
@ -15,3 +16,4 @@ SPDX-License-Identifier: MIT
{{/unless}}
{{> systems/ds4/templates/sheets/shared/components/add-button.hbs title='DS4.UserInteractionAddItemTitle'
documentType='item' type='specialCreatureAbility'}}
</div>

View file

@ -5,4 +5,6 @@ SPDX-FileCopyrightText: 2021 Gesina Schwalbe
SPDX-License-Identifier: MIT
--}}
<div class="ds4-sheet-tab tab inventory" data-group="primary" data-tab="inventory">
{{> systems/ds4/templates/sheets/actor/components/items-overview.hbs}}
</div>

View file

@ -4,4 +4,6 @@ SPDX-FileCopyrightText: 2021 Johannes Loher
SPDX-License-Identifier: MIT
--}}
<div class="ds4-sheet-tab tab description" data-group="primary" data-tab="description">
{{> systems/ds4/templates/sheets/actor/components/description.hbs}}
</div>

View file

@ -5,6 +5,7 @@ SPDX-FileCopyrightText: 2021 Gesina Schwalbe
SPDX-License-Identifier: MIT
--}}
<div class="ds4-sheet-tab tab effects" data-group="primary" data-tab="effects">
{{#unless (isEmpty enrichedEffects)}}
<ol class="ds4-embedded-document-list ds4-embedded-document-list--effect effect-list">
{{> systems/ds4/templates/sheets/actor/components/effect-list-header.hbs}}
@ -15,3 +16,4 @@ SPDX-License-Identifier: MIT
{{/unless}}
{{> systems/ds4/templates/sheets/shared/components/add-button.hbs title='DS4.UserInteractionAddEffectTitle'
documentType='effect'}}
</div>

View file

@ -40,19 +40,21 @@ titleKey=titleKey}}
titleKey=titleKey}}
{{/inline}}
{{!-- ======================================================================== --}}
<div class="ds4-sheet-tab tab spells" data-group="primary" data-tab="spells">
{{#unless (isEmpty itemsByType.spell)}}
<ol class="ds4-embedded-document-list ds4-embedded-document-list--spell item-list">
{{#> systems/ds4/templates/sheets/actor/components/item-list-header.hbs isEquipable=true hideDescription=true
type='spell'}}
{{!-- spell type --}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.spellType"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.spellType"
title="{{localize 'DS4.SortBySpellType'}}">{{localize 'DS4.SpellTypeAbbr'}}</div>
{{!-- spell modifier --}}
<div class="ds4-embedded-document-list__clickable" data-action="sortItems" data-data-path="system.spellModifier.complex"
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="system.spellModifier.complex"
data-data-path2="system.spellModifier.numerical" title="{{localize 'DS4.SortBySpellModifier'}}">{{localize
'DS4.SpellModifierAbbr'}}</div>
@ -79,7 +81,6 @@ titleKey=titleKey}}
'')}}{{item.system.spellModifier.numerical}}{{else}}{{item.system.spellModifier.complex}}{{/if}}
</div>
{{!-- max. distance --}}
{{> distanceUnit titleKey='DS4.SpellDistance' unitDatum=item.system.maxDistance
config=@root/config}}
@ -97,3 +98,4 @@ titleKey=titleKey}}
{{/unless}}
{{> systems/ds4/templates/sheets/shared/components/add-button.hbs title='DS4.UserInteractionAddItemTitle'
documentType='item' type='spell'}}
</div>

View file

@ -4,6 +4,8 @@ SPDX-FileCopyrightText: 2021 Johannes Loher
SPDX-License-Identifier: MIT
--}}
<div class="ds4-sheet-tab tab values" data-group="primary" data-tab="values">
{{> systems/ds4/templates/sheets/actor/components/core-values.hbs}}
{{> systems/ds4/templates/sheets/actor/components/combat-values.hbs}}
{{> systems/ds4/templates/sheets/actor/components/checks.hbs}}
</div>

View file

@ -9,25 +9,17 @@ SPDX-License-Identifier: MIT
{{> systems/ds4/templates/sheets/item/components/item-header.hbs}}
{{!-- Sheet Tab Navigation --}}
<nav class="tabs" data-group="primary">
{{#each tabs}}
<a class="item {{cssClass}}" data-tab="{{id}}" data-group="{{group}}">
{{#if icon}}<i class="{{icon}}"></i>{{/if}}
{{label}}
</a>
{{/each}}
<nav class="ds4-sheet-tab-nav tabs" data-group="primary">
<a class="ds4-sheet-tab-nav__item" data-tab="description">{{localize 'DS4.HeadingDescription'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="effects">{{localize 'DS4.HeadingEffects'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
<section class="ds4-sheet-body">
{{!-- Description Tab --}}
<div class="tab {{tabs.description.cssClass}}" data-tab="description" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/description.hbs}}
</div>
{{!-- Effects Tab --}}
<div class="tab {{tabs.effects.cssClass}}" data-tab="effects" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/effects.hbs}}
</div>
</section>
</form>

View file

@ -9,35 +9,28 @@ SPDX-License-Identifier: MIT
{{> systems/ds4/templates/sheets/item/components/item-header.hbs}}
{{!-- Sheet Tab Navigation --}}
<nav class="tabs" data-group="primary">
{{#each tabs}}
<a class="item {{cssClass}}" data-tab="{{id}}" data-group="{{group}}">
{{#if icon}}<i class="{{icon}}"></i>{{/if}}
{{label}}
</a>
{{/each}}
<nav class="ds4-sheet-tab-nav tabs" data-group="primary">
<a class="ds4-sheet-tab-nav__item" data-tab="description">{{localize 'DS4.HeadingDescription'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="properties">{{localize 'DS4.HeadingProperties'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="effects">{{localize 'DS4.HeadingEffects'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
<section class="ds4-sheet-body">
{{!-- Description Tab --}}
<div class="tab {{#if tabs.description}}{{tabs.description.cssClass}}{{/if}}" data-tab="description" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/description.hbs}}
</div>
{{!-- Properties Tab --}}
<div class="tab {{#if tabs.properties}}{{tabs.properties.cssClass}}{{/if}}" data-tab="properties" data-group="primary">
{{#> systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{> systems/ds4/templates/sheets/item/components/properties/armor.hbs}}
{{> systems/ds4/templates/sheets/item/components/properties/protective.hbs}}
{{#if isOwned}}
{{> systems/ds4/templates/sheets/item/components/properties/equipable.hbs}}
{{/if}}
{{> systems/ds4/templates/sheets/item/components/properties/physical.hbs}}
</div>
{{/systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{!-- Effects Tab --}}
<div class="tab {{#if tabs.effects}}{{tabs.effects.cssClass}}{{/if}}" data-tab="effects" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/effects.hbs}}
</div>
</section>
</form>

View file

@ -5,7 +5,7 @@ SPDX-License-Identifier: MIT
--}}
<header class="ds4-item-header">
<img class="ds4-item-header__img" src="{{data.img}}" data-action="editImage" alt="{{localize 'DS4.ItemImageAltText'}}"
<img class="ds4-item-header__img" src="{{data.img}}" data-edit="img" alt="{{localize 'DS4.ItemImageAltText'}}"
title="{{data.name}}" />
<div class="ds4-item-header__data">
<h2 class="ds4-item-header__type">{{lookup config.i18n.itemTypes item.type}}</h2>

View file

@ -88,9 +88,11 @@ SPDX-License-Identifier: MIT
<div class="form-group">
<label for="system.allowsDefense-{{data._id}}" title="{{localize 'DS4.SpellAllowsDefenseDescription'}}">{{localize
"DS4.SpellAllowsDefense"}}</label>
<div class="form-fields">
<input id="system.allowsDefense-{{data._id}}" data-dtype="Boolean" type="checkbox" name="system.allowsDefense"
{{checked data.system.allowsDefense}} />
</div>
</div>
<div class="form-group slim">
<label title="{{localize 'DS4.SpellMinimumLevelDescription'}}">{{localize "DS4.SpellMinimumLevel"}}</label>
<div class="form-fields">

View file

@ -9,33 +9,26 @@ SPDX-License-Identifier: MIT
{{> systems/ds4/templates/sheets/item/components/item-header.hbs}}
{{!-- Sheet Tab Navigation --}}
<nav class="tabs" data-group="primary">
{{#each tabs}}
<a class="item {{cssClass}}" data-tab="{{id}}" data-group="{{group}}">
{{#if icon}}<i class="{{icon}}"></i>{{/if}}
{{label}}
</a>
{{/each}}
<nav class="ds4-sheet-tab-nav tabs" data-group="primary">
<a class="ds4-sheet-tab-nav__item" data-tab="description">{{localize 'DS4.HeadingDescription'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="properties">{{localize 'DS4.HeadingProperties'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="effects">{{localize 'DS4.HeadingEffects'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
<section class="ds4-sheet-body">
{{!-- Description Tab --}}
<div class="tab {{#if tabs.description}}{{tabs.description.cssClass}}{{/if}}" data-tab="description" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/description.hbs}}
</div>
{{!-- Properties Tab --}}
<div class="tab {{#if tabs.properties}}{{tabs.properties.cssClass}}{{/if}}" data-tab="properties" data-group="primary">
{{#> systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{#if isOwned}}
{{> systems/ds4/templates/sheets/item/components/properties/equipable.hbs}}
{{/if}}
{{> systems/ds4/templates/sheets/item/components/properties/physical.hbs}}
</div>
{{/systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{!-- Effects Tab --}}
<div class="tab {{#if tabs.effects}}{{tabs.effects.cssClass}}{{/if}}" data-tab="effects" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/effects.hbs}}
</div>
</section>
</form>

View file

@ -9,25 +9,17 @@ SPDX-License-Identifier: MIT
{{> systems/ds4/templates/sheets/item/components/item-header.hbs}}
{{!-- Sheet Tab Navigation --}}
<nav class="tabs" data-group="primary">
{{#each tabs}}
<a class="item {{cssClass}}" data-tab="{{id}}" data-group="{{group}}">
{{#if icon}}<i class="{{icon}}"></i>{{/if}}
{{label}}
</a>
{{/each}}
<nav class="ds4-sheet-tab-nav tabs" data-group="primary">
<a class="ds4-sheet-tab-nav__item" data-tab="description">{{localize 'DS4.HeadingDescription'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="effects">{{localize 'DS4.HeadingEffects'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
<section class="ds4-sheet-body">
{{!-- Description Tab --}}
<div class="tab {{#if tabs.description}}{{tabs.description.cssClass}}{{/if}}" data-tab="description" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/description.hbs}}
</div>
{{!-- Effects Tab --}}
<div class="tab {{#if tabs.effects}}{{tabs.effects.cssClass}}{{/if}}" data-tab="effects" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/effects.hbs}}
</div>
</section>
</form>

View file

@ -9,30 +9,23 @@ SPDX-License-Identifier: MIT
{{> systems/ds4/templates/sheets/item/components/item-header.hbs}}
{{!-- Sheet Tab Navigation --}}
<nav class="tabs" data-group="primary">
{{#each tabs}}
<a class="item {{cssClass}}" data-tab="{{id}}" data-group="{{group}}">
{{#if icon}}<i class="{{icon}}"></i>{{/if}}
{{label}}
</a>
{{/each}}
<nav class="ds4-sheet-tab-nav tabs" data-group="primary">
<a class="ds4-sheet-tab-nav__item" data-tab="description">{{localize 'DS4.HeadingDescription'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="properties">{{localize 'DS4.HeadingProperties'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="effects">{{localize 'DS4.HeadingEffects'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
<section class="ds4-sheet-body">
{{!-- Description Tab --}}
<div class="tab {{#if tabs.description}}{{tabs.description.cssClass}}{{/if}}" data-tab="description" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/description.hbs}}
</div>
{{!-- Properties Tab --}}
<div class="tab {{#if tabs.properties}}{{tabs.properties.cssClass}}{{/if}}" data-tab="properties" data-group="primary">
{{#> systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{> systems/ds4/templates/sheets/item/components/properties/physical.hbs}}
</div>
{{/systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{!-- Effects Tab --}}
<div class="tab {{#if tabs.effects}}{{tabs.effects.cssClass}}{{/if}}" data-tab="effects" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/effects.hbs}}
</div>
</section>
</form>

View file

@ -9,25 +9,17 @@ SPDX-License-Identifier: MIT
{{> systems/ds4/templates/sheets/item/components/item-header.hbs}}
{{!-- Sheet Tab Navigation --}}
<nav class="tabs" data-group="primary">
{{#each tabs}}
<a class="item {{cssClass}}" data-tab="{{id}}" data-group="{{group}}">
{{#if icon}}<i class="{{icon}}"></i>{{/if}}
{{label}}
</a>
{{/each}}
<nav class="ds4-sheet-tab-nav tabs" data-group="primary">
<a class="ds4-sheet-tab-nav__item" data-tab="description">{{localize 'DS4.HeadingDescription'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="effects">{{localize 'DS4.HeadingEffects'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
<section class="ds4-sheet-body">
{{!-- Description Tab --}}
<div class="tab {{#if tabs.description}}{{tabs.description.cssClass}}{{/if}}" data-tab="description" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/description.hbs}}
</div>
{{!-- Effects Tab --}}
<div class="tab {{#if tabs.effects}}{{tabs.effects.cssClass}}{{/if}}" data-tab="effects" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/effects.hbs}}
</div>
</section>
</form>

View file

@ -9,34 +9,27 @@ SPDX-License-Identifier: MIT
{{> systems/ds4/templates/sheets/item/components/item-header.hbs}}
{{!-- Sheet Tab Navigation --}}
<nav class="tabs" data-group="primary">
{{#each tabs}}
<a class="item {{cssClass}}" data-tab="{{id}}" data-group="{{group}}">
{{#if icon}}<i class="{{icon}}"></i>{{/if}}
{{label}}
</a>
{{/each}}
<nav class="ds4-sheet-tab-nav tabs" data-group="primary">
<a class="ds4-sheet-tab-nav__item" data-tab="description">{{localize 'DS4.HeadingDescription'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="properties">{{localize 'DS4.HeadingProperties'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="effects">{{localize 'DS4.HeadingEffects'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
<section class="ds4-sheet-body">
{{!-- Description Tab --}}
<div class="tab {{#if tabs.description}}{{tabs.description.cssClass}}{{/if}}" data-tab="description" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/description.hbs}}
</div>
{{!-- Properties Tab --}}
<div class="tab {{#if tabs.properties}}{{tabs.properties.cssClass}}{{/if}}" data-tab="properties" data-group="primary">
{{#> systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{> systems/ds4/templates/sheets/item/components/properties/protective.hbs}}
{{#if isOwned}}
{{> systems/ds4/templates/sheets/item/components/properties/equipable.hbs}}
{{/if}}
{{> systems/ds4/templates/sheets/item/components/properties/physical.hbs}}
</div>
{{/systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{!-- Effects Tab --}}
<div class="tab {{#if tabs.effects}}{{tabs.effects.cssClass}}{{/if}}" data-tab="effects" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/effects.hbs}}
</div>
</section>
</form>

View file

@ -9,30 +9,23 @@ SPDX-License-Identifier: MIT
{{> systems/ds4/templates/sheets/item/components/item-header.hbs}}
{{!-- Sheet Tab Navigation --}}
<nav class="tabs" data-group="primary">
{{#each tabs}}
<a class="item {{cssClass}}" data-tab="{{id}}" data-group="{{group}}">
{{#if icon}}<i class="{{icon}}"></i>{{/if}}
{{label}}
</a>
{{/each}}
<nav class="ds4-sheet-tab-nav tabs" data-group="primary">
<a class="ds4-sheet-tab-nav__item" data-tab="description">{{localize 'DS4.HeadingDescription'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="properties">{{localize 'DS4.HeadingProperties'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="effects">{{localize 'DS4.HeadingEffects'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
<section class="ds4-sheet-body">
{{!-- Description Tab --}}
<div class="tab {{#if tabs.description}}{{tabs.description.cssClass}}{{/if}}" data-tab="description" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/description.hbs}}
</div>
{{!-- Properties Tab --}}
<div class="tab {{#if tabs.properties}}{{tabs.properties.cssClass}}{{/if}}" data-tab="properties" data-group="primary">
{{#> systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{> systems/ds4/templates/sheets/item/components/properties/special-creature-ability.hbs}}
</div>
{{/systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{!-- Effects Tab --}}
<div class="tab {{#if tabs.effects}}{{tabs.effects.cssClass}}{{/if}}" data-tab="effects" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/effects.hbs}}
</div>
</section>
</form>

View file

@ -9,33 +9,26 @@ SPDX-License-Identifier: MIT
{{> systems/ds4/templates/sheets/item/components/item-header.hbs}}
{{!-- Sheet Tab Navigation --}}
<nav class="tabs" data-group="primary">
{{#each tabs}}
<a class="item {{cssClass}}" data-tab="{{id}}" data-group="{{group}}">
{{#if icon}}<i class="{{icon}}"></i>{{/if}}
{{label}}
</a>
{{/each}}
<nav class="ds4-sheet-tab-nav tabs" data-group="primary">
<a class="ds4-sheet-tab-nav__item" data-tab="description">{{localize 'DS4.HeadingDescription'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="properties">{{localize 'DS4.HeadingProperties'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="effects">{{localize 'DS4.HeadingEffects'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
<section class="ds4-sheet-body">
{{!-- Description Tab --}}
<div class="tab {{tabs.description.cssClass}}" data-tab="description" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/description.hbs}}
</div>
{{!-- Properties Tab --}}
<div class="tab {{tabs.properties.cssClass}}" data-tab="properties" data-group="primary">
{{#> systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{> systems/ds4/templates/sheets/item/components/properties/spell.hbs}}
{{#if isOwned}}
{{> systems/ds4/templates/sheets/item/components/properties/equipable.hbs}}
{{/if}}
</div>
{{/systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{!-- Effects Tab --}}
<div class="tab {{tabs.effects.cssClass}}" data-tab="effects" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/effects.hbs}}
</div>
</section>
</form>

View file

@ -4,15 +4,7 @@ SPDX-FileCopyrightText: 2021 Johannes Loher
SPDX-License-Identifier: MIT
--}}
{{#if editable}}
<prose-mirror
name="system.description"
button="true"
editable="{{editable}}"
toggled="false"
value="{{data.system.description}}">
{{{enrichedDescription}}}
</prose-mirror>
{{else}}
{{{enrichedDescription}}}
{{/if}}
<div class="ds4-sheet-tab tab description" data-group="primary" data-tab="description">
{{editor data.system.description target="system.description" button=true owner=owner
editable=editable engine="prosemirror"}}
</div>

View file

@ -4,13 +4,15 @@ SPDX-FileCopyrightText: 2021 Johannes Loher
SPDX-License-Identifier: MIT
--}}
{{#unless (isEmpty enrichedEffects)}}
<div class="ds4-sheet-tab tab effects" data-group="primary" data-tab="effects">
{{#unless (isEmpty data.effects)}}
<ol class="ds4-embedded-document-list ds4-embedded-document-list--item-effect effect-list">
{{> systems/ds4/templates/sheets/item/components/effect-list-header.hbs}}
{{#each enrichedEffects as |effectData id| }}
{{#each data.effects as |effectData id| }}
{{> systems/ds4/templates/sheets/item/components/effect-list-entry.hbs effectData=effectData}}
{{/each}}
</ol>
{{/unless}}
{{> systems/ds4/templates/sheets/shared/components/add-button.hbs title='DS4.UserInteractionAddEffectTitle'
documentType='effect'}}
</div>

View file

@ -4,4 +4,6 @@ SPDX-FileCopyrightText: 2021 Johannes Loher
SPDX-License-Identifier: MIT
--}}
<div class="ds4-sheet-tab tab properties" data-group="primary" data-tab="properties">
{{> @partial-block}}
</div>

View file

@ -9,30 +9,23 @@ SPDX-License-Identifier: MIT
{{> systems/ds4/templates/sheets/item/components/item-header.hbs}}
{{!-- Sheet Tab Navigation --}}
<nav class="tabs" data-group="primary">
{{#each tabs}}
<a class="item {{cssClass}}" data-tab="{{id}}" data-group="{{group}}">
{{#if icon}}<i class="{{icon}}"></i>{{/if}}
{{label}}
</a>
{{/each}}
<nav class="ds4-sheet-tab-nav tabs" data-group="primary">
<a class="ds4-sheet-tab-nav__item" data-tab="description">{{localize 'DS4.HeadingDescription'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="properties">{{localize 'DS4.HeadingProperties'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="effects">{{localize 'DS4.HeadingEffects'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
<section class="ds4-sheet-body">
{{!-- Description Tab --}}
<div class="tab {{#if tabs.description}}{{tabs.description.cssClass}}{{/if}}" data-tab="description" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/description.hbs}}
</div>
{{!-- Properties Tab --}}
<div class="tab {{#if tabs.properties}}{{tabs.properties.cssClass}}{{/if}}" data-tab="properties" data-group="primary">
{{#> systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{> systems/ds4/templates/sheets/item/components/properties/talent.hbs}}
</div>
{{/systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{!-- Effects Tab --}}
<div class="tab {{#if tabs.effects}}{{tabs.effects.cssClass}}{{/if}}" data-tab="effects" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/effects.hbs}}
</div>
</section>
</form>

View file

@ -9,34 +9,27 @@ SPDX-License-Identifier: MIT
{{> systems/ds4/templates/sheets/item/components/item-header.hbs}}
{{!-- Sheet Tab Navigation --}}
<nav class="tabs" data-group="primary">
{{#each tabs}}
<a class="item {{cssClass}}" data-tab="{{id}}" data-group="{{group}}">
{{#if icon}}<i class="{{icon}}"></i>{{/if}}
{{label}}
</a>
{{/each}}
<nav class="ds4-sheet-tab-nav tabs" data-group="primary">
<a class="ds4-sheet-tab-nav__item" data-tab="description">{{localize 'DS4.HeadingDescription'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="properties">{{localize 'DS4.HeadingProperties'}}</a>
<a class="ds4-sheet-tab-nav__item" data-tab="effects">{{localize 'DS4.HeadingEffects'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
<section class="ds4-sheet-body">
{{!-- Description Tab --}}
<div class="tab {{#if tabs.description}}{{tabs.description.cssClass}}{{/if}}" data-tab="description" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/description.hbs}}
</div>
{{!-- Properties Tab --}}
<div class="tab {{#if tabs.properties}}{{tabs.properties.cssClass}}{{/if}}" data-tab="properties" data-group="primary">
{{#> systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{> systems/ds4/templates/sheets/item/components/properties/weapon.hbs}}
{{#if isOwned}}
{{> systems/ds4/templates/sheets/item/components/properties/equipable.hbs}}
{{/if}}
{{> systems/ds4/templates/sheets/item/components/properties/physical.hbs}}
</div>
{{/systems/ds4/templates/sheets/item/tabs/properties.hbs}}
{{!-- Effects Tab --}}
<div class="tab {{#if tabs.effects}}{{tabs.effects.cssClass}}{{/if}}" data-tab="effects" data-group="primary">
{{> systems/ds4/templates/sheets/item/tabs/effects.hbs}}
</div>
</section>
</form>

View file

@ -13,7 +13,7 @@ SPDX-License-Identifier: MIT
}}
{{#if @root/editable}}
<div class="ds4-add-button">
<a class="ds4-add-button__link" title="{{localize title}}" data-action="{{concat 'create' (capitalize documentType)}}" {{#if type}}data-type="{{type}}"
<a class="control-{{documentType}}" title="{{localize title}}" data-action="create" {{#if type}}data-type="{{type}}"
{{/if}}>
<i class="fas fa-plus"></i>
{{localize "DS4.UserInteractionAdd"}}

View file

@ -14,9 +14,9 @@ SPDX-License-Identifier: MIT
--}}
<div class="ds4-control-button-group">
{{#if @root/editable}}
<a class="ds4-control-button-group__button" data-action="{{concat 'edit' (capitalize documentType)}}" data-document-type="{{documentType}}"
<a class="ds4-control-button-group__button control-{{documentType}}" data-action="edit"
title="{{localize editTitle}}"><i class="fas fa-edit"></i></a>
<a class="ds4-control-button-group__button" data-action="{{concat 'delete' (capitalize documentType)}}" data-document-type="{{documentType}}"
<a class="ds4-control-button-group__button control-{{documentType}}" data-action="delete"
title="{{localize deleteTitle}}"><i class="fas fa-trash"></i></a>
{{/if}}
</div>

View file

@ -7,14 +7,13 @@ SPDX-License-Identifier: MIT
{{!--
!-- Render an image that has a dice overlay image.
!-- @param rollable: A flag indicating whether or not the image is actually rollable.
!-- @param rollableAction: The action to trigger when the image is clicked (e.g. "rollItem").
!-- @param rollableClass: The CSS class(es) to add if the image is rollable.
!-- @param title: The title for the rollable image if it is not actually rollable.
!-- @param rollableTitle: The title for the rollable image if it is rollable.
!-- @param src: The path to the image.
!-- @param alt: An alternate text for the image.
--}}
<div class="ds4-rollable-image{{#if rollable}} ds4-rollable-image--rollable{{/if}}"
{{#if rollable}}data-action="{{rollableAction}}"{{/if}}
<div class="ds4-rollable-image{{#if rollable}} ds4-rollable-image--rollable {{rollableClass}}{{/if}}"
title="{{#if rollable}}{{rollableTitle}}{{else}}{{title}}{{/if}}">
{{#if src}}
<img class="ds4-rollable-image__image" alt="{{alt}}" src="{{src}}" />