Quellcode durchsuchen

Merge branch 'release/0.10.0'

# Conflicts:
#	src/assets/i18n/en.json
Warafear vor 11 Monaten
Ursprung
Commit
ca93058621
115 geänderte Dateien mit 1986 neuen und 1731 gelöschten Zeilen
  1. 1 1
      package.json
  2. 25 0
      src/app/app.component.html
  3. 7 0
      src/app/app.component.scss
  4. 6 0
      src/app/app.component.ts
  5. 3 3
      src/app/journal/journal-character/class/class.component.scss
  6. 17 18
      src/app/journal/journal-character/species/species.component.scss
  7. 4 0
      src/app/journal/journal-character/subclass/subclass.component.scss
  8. 1 1
      src/app/journal/journal-home/journal-home.component.html
  9. 1 0
      src/app/journal/journal-home/journal-home.component.scss
  10. 6 2
      src/app/journal/journal-home/navigation-panel/navigation-panel.component.scss
  11. 2 0
      src/app/journal/journal-spellbook/journal-spellbook.component.scss
  12. 1 1
      src/app/journal/journal-stats/ability-panel/ability-panel.component.html
  13. 1 4
      src/app/journal/journal-stats/ability-panel/ability-panel.component.scss
  14. 9 0
      src/app/journal/journal-stats/ability-panel/ability-table/ability-modal/ability-modal.component.scss
  15. 1 1
      src/app/journal/journal-stats/ability-panel/ability-table/ability-table.component.scss
  16. 7 0
      src/app/journal/journal-stats/ability-panel/ability-table/ability-table.component.ts
  17. 3 1
      src/app/journal/journal-stats/ability-panel/spellslots/spellslots-modal/spellslots-modal.component.html
  18. 1 1
      src/app/journal/journal-stats/ability-panel/spellslots/spellslots.component.ts
  19. 1 1
      src/app/journal/journal-stats/ability-panel/trait-table/trait-table.component.scss
  20. 0 0
      src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-details/attribute-details.component.html
  21. 0 0
      src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-details/attribute-details.component.scss
  22. 0 0
      src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-details/attribute-details.component.spec.ts
  23. 0 0
      src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-details/attribute-details.component.ts
  24. 0 1
      src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-field.component.html
  25. 2 1
      src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-field.component.scss
  26. 0 0
      src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-field.component.spec.ts
  27. 0 0
      src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-field.component.ts
  28. 5 0
      src/app/journal/journal-stats/attribute-panel/attribute-panel.component.html
  29. 5 0
      src/app/journal/journal-stats/attribute-panel/attribute-panel.component.scss
  30. 0 0
      src/app/journal/journal-stats/attribute-panel/attribute-panel.component.spec.ts
  31. 0 0
      src/app/journal/journal-stats/attribute-panel/attribute-panel.component.ts
  32. 0 6
      src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-panel.component.html
  33. 0 11
      src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-panel.component.scss
  34. 0 10
      src/app/journal/journal-stats/attribute-skill-container/attribute-skill-container.component.html
  35. 0 22
      src/app/journal/journal-stats/attribute-skill-container/attribute-skill-container.component.scss
  36. 0 21
      src/app/journal/journal-stats/attribute-skill-container/attribute-skill-container.component.spec.ts
  37. 0 10
      src/app/journal/journal-stats/attribute-skill-container/attribute-skill-container.component.ts
  38. 0 6
      src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-panel.component.scss
  39. 0 11
      src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-panel.component.scss
  40. 0 6
      src/app/journal/journal-stats/info-row/death-save/death-save-details/death-save-details.component.html
  41. 0 0
      src/app/journal/journal-stats/info-row/death-save/death-save-details/death-save-details.component.scss
  42. 0 22
      src/app/journal/journal-stats/info-row/death-save/death-save-details/death-save-details.component.spec.ts
  43. 0 8
      src/app/journal/journal-stats/info-row/death-save/death-save-details/death-save-details.component.ts
  44. 0 45
      src/app/journal/journal-stats/info-row/death-save/death-save.component.html
  45. 1 1
      src/app/journal/journal-stats/info-row/info-row.component.html
  46. 15 0
      src/app/journal/journal-stats/info-row/info-row.component.scss
  47. 16 12
      src/app/journal/journal-stats/journal-stats.component.html
  48. 79 35
      src/app/journal/journal-stats/journal-stats.component.scss
  49. 0 37
      src/app/journal/journal-stats/life-container/hit-dice/hit-dice.component.html
  50. 0 4
      src/app/journal/journal-stats/life-container/life-container.component.html
  51. 0 22
      src/app/journal/journal-stats/life-container/life-container.component.scss
  52. 0 21
      src/app/journal/journal-stats/life-container/life-container.component.spec.ts
  53. 0 8
      src/app/journal/journal-stats/life-container/life-container.component.ts
  54. 0 39
      src/app/journal/journal-stats/life-container/life/life-details/life-details.component.html
  55. 0 69
      src/app/journal/journal-stats/life-container/life/life-details/life-details.component.scss
  56. 0 51
      src/app/journal/journal-stats/life-container/life/life-details/life-details.component.ts
  57. 47 0
      src/app/journal/journal-stats/life/death-save/death-save.component.html
  58. 3 3
      src/app/journal/journal-stats/life/death-save/death-save.component.scss
  59. 0 0
      src/app/journal/journal-stats/life/death-save/death-save.component.spec.ts
  60. 1 10
      src/app/journal/journal-stats/life/death-save/death-save.component.ts
  61. 26 0
      src/app/journal/journal-stats/life/hit-dice/hit-dice.component.html
  62. 1 8
      src/app/journal/journal-stats/life/hit-dice/hit-dice.component.scss
  63. 0 0
      src/app/journal/journal-stats/life/hit-dice/hit-dice.component.spec.ts
  64. 15 9
      src/app/journal/journal-stats/life/hit-dice/hit-dice.component.ts
  65. 49 0
      src/app/journal/journal-stats/life/life-details/life-details.component.html
  66. 81 0
      src/app/journal/journal-stats/life/life-details/life-details.component.scss
  67. 0 0
      src/app/journal/journal-stats/life/life-details/life-details.component.spec.ts
  68. 44 0
      src/app/journal/journal-stats/life/life-details/life-details.component.ts
  69. 0 0
      src/app/journal/journal-stats/life/life.component.html
  70. 0 0
      src/app/journal/journal-stats/life/life.component.scss
  71. 0 0
      src/app/journal/journal-stats/life/life.component.spec.ts
  72. 17 36
      src/app/journal/journal-stats/life/life.component.ts
  73. 0 0
      src/app/journal/journal-stats/save-throw-panel/save-throw-details/save-throw-details.component.html
  74. 0 0
      src/app/journal/journal-stats/save-throw-panel/save-throw-details/save-throw-details.component.scss
  75. 0 0
      src/app/journal/journal-stats/save-throw-panel/save-throw-details/save-throw-details.component.spec.ts
  76. 0 0
      src/app/journal/journal-stats/save-throw-panel/save-throw-details/save-throw-details.component.ts
  77. 6 6
      src/app/journal/journal-stats/save-throw-panel/save-throw-field/save-throw-field.component.html
  78. 10 10
      src/app/journal/journal-stats/save-throw-panel/save-throw-field/save-throw-field.component.scss
  79. 0 0
      src/app/journal/journal-stats/save-throw-panel/save-throw-field/save-throw-field.component.spec.ts
  80. 0 0
      src/app/journal/journal-stats/save-throw-panel/save-throw-field/save-throw-field.component.ts
  81. 0 0
      src/app/journal/journal-stats/save-throw-panel/save-throw-panel.component.html
  82. 27 0
      src/app/journal/journal-stats/save-throw-panel/save-throw-panel.component.scss
  83. 0 0
      src/app/journal/journal-stats/save-throw-panel/save-throw-panel.component.spec.ts
  84. 0 0
      src/app/journal/journal-stats/save-throw-panel/save-throw-panel.component.ts
  85. 0 0
      src/app/journal/journal-stats/skill-panel/skill-details/skill-details.component.html
  86. 0 0
      src/app/journal/journal-stats/skill-panel/skill-details/skill-details.component.scss
  87. 0 0
      src/app/journal/journal-stats/skill-panel/skill-details/skill-details.component.spec.ts
  88. 0 0
      src/app/journal/journal-stats/skill-panel/skill-details/skill-details.component.ts
  89. 0 0
      src/app/journal/journal-stats/skill-panel/skill-field/skill-field.component.html
  90. 9 12
      src/app/journal/journal-stats/skill-panel/skill-field/skill-field.component.scss
  91. 0 0
      src/app/journal/journal-stats/skill-panel/skill-field/skill-field.component.spec.ts
  92. 0 0
      src/app/journal/journal-stats/skill-panel/skill-field/skill-field.component.ts
  93. 2 2
      src/app/journal/journal-stats/skill-panel/skill-panel.component.html
  94. 24 0
      src/app/journal/journal-stats/skill-panel/skill-panel.component.scss
  95. 0 0
      src/app/journal/journal-stats/skill-panel/skill-panel.component.spec.ts
  96. 0 0
      src/app/journal/journal-stats/skill-panel/skill-panel.component.ts
  97. 49 25
      src/app/journal/journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component.html
  98. 1 1
      src/app/journal/journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component.scss
  99. 5 19
      src/app/journal/journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component.ts
  100. 4 2
      src/app/journal/journal-stats/weapons-container/spell-table/spell-table.component.html
  101. 2 2
      src/app/journal/journal-stats/weapons-container/spell-table/spell-table.component.scss
  102. 37 0
      src/app/journal/journal-stats/weapons-container/weapon-table/weapon-modal/weapon-modal.component.scss
  103. 1 8
      src/app/journal/journal-stats/weapons-container/weapons-container.component.scss
  104. 1 1
      src/app/journal/journal-stats/weapons-container/weapons-container.component.ts
  105. 13 19
      src/app/journal/journal.module.ts
  106. 37 0
      src/app/journal/spell-modal/spell-modal.component.scss
  107. 15 12
      src/assets/i18n/de.json
  108. 768 766
      src/assets/i18n/en.json
  109. 47 0
      src/colors.scss
  110. 181 0
      src/responsive.scss
  111. 2 2
      src/services/class/class.service.ts
  112. 112 85
      src/services/data/data.service.ts
  113. 21 21
      src/services/species/species.service.ts
  114. 3 3
      src/services/subclass/subclass.service.ts
  115. 105 156
      src/styles.scss

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "dndtools",
-  "version": "0.9.5",
+  "version": "0.10.0",
   "scripts": {
     "ng": "ng",
     "start": "nx serve",

+ 25 - 0
src/app/app.component.html

@@ -1 +1,26 @@
+<div class="dimension">
+  height: {{ height }}px
+  <br />
+  width: {{ width }}px
+</div>
+<div class="h840"></div>
+<div class="h864"></div>
+
+<div class="h910"></div>
+<div class="h934"></div>
+
+<div class="h977"></div>
+<div class="h1001"></div>
+
+<div class="h1056"></div>
+<div class="h1080"></div>
+
+<div class="w1470"></div>
+<div class="w1494"></div>
+
+<div class="w1683"></div>
+<div class="w1707"></div>
+
+<div class="w1896"></div>
+<div class="w1920"></div>
 <router-outlet></router-outlet>

+ 7 - 0
src/app/app.component.scss

@@ -0,0 +1,7 @@
+@import "../responsive";
+
+.dimension {
+  position: absolute;
+  z-index: 100;
+  background: white;
+}

+ 6 - 0
src/app/app.component.ts

@@ -8,10 +8,16 @@ import { TranslateService } from '@ngx-translate/core';
 })
 export class AppComponent {
   title = 'Tools';
+  height!: number;
+  width!: number;
 
   constructor(public translate: TranslateService) {
     this.initTranslation();
   }
+  ngAfterViewInit() {
+    this.height = window.innerHeight;
+    this.width = window.innerWidth;
+  }
 
   private initTranslation(): void {
     this.translate.addLangs(['en', 'de']);

+ 3 - 3
src/app/journal/journal-character/class/class.component.scss

@@ -51,9 +51,9 @@
   margin-top: 1rem;
 }
 
-// :host ::ng-deep p {
-//   margin: 0;
-// }
+::ng-deep .table {
+  --bs-table-bg: transparent !important;
+}
 
 :host ::ng-deep h3,
 :host ::ng-deep h4 {

+ 17 - 18
src/app/journal/journal-character/species/species.component.scss

@@ -78,25 +78,24 @@
   margin-top: 1rem;
 }
 
-table {
-  width: 100%;
-  border-collapse: collapse;
+::ng-deep .table {
+  --bs-table-bg: transparent !important;
 }
 
-/* Style the table header */
-th {
-  background-color: #f2f2f2;
-  text-align: left;
-  padding: 8px;
-}
+// /* Style the table header */
+// th {
+//   background-color: #f2f2f2;
+//   text-align: left;
+//   padding: 8px;
+// }
 
-/* Style the table cells */
-td {
-  border: 1px solid #ddd;
-  padding: 8px;
-}
+// /* Style the table cells */
+// td {
+//   border: 1px solid #ddd;
+//   padding: 8px;
+// }
 
-/* Style the table rows */
-tr:nth-child(even) {
-  background-color: #f2f2f2;
-}
+// /* Style the table rows */
+// tr:nth-child(even) {
+//   background-color: #f2f2f2;
+// }

+ 4 - 0
src/app/journal/journal-character/subclass/subclass.component.scss

@@ -59,3 +59,7 @@
 :host ::ng-deep h4 {
   margin-top: 1rem;
 }
+
+::ng-deep .table {
+  --bs-table-bg: transparent !important;
+}

+ 1 - 1
src/app/journal/journal-home/journal-home.component.html

@@ -1,4 +1,4 @@
-<div style="position: relative" class="journal-container">
+<div class="journal-container">
   <!-- Here the different pages are rendered -->
   <router-outlet></router-outlet>
   <details-panel></details-panel>

+ 1 - 0
src/app/journal/journal-home/journal-home.component.scss

@@ -1,4 +1,5 @@
 .journal-container {
+  position: relative;
   overflow: auto;
   height: 100vh;
   width: 100vw;

+ 6 - 2
src/app/journal/journal-home/navigation-panel/navigation-panel.component.scss

@@ -1,3 +1,5 @@
+@import "../../../../responsive";
+
 .backdrop {
   position: fixed;
   top: 0;
@@ -41,8 +43,10 @@ ul {
   font-size: 1.25rem;
   font-weight: 500;
   margin-bottom: 1rem;
-  padding-left: 2rem;
-  padding: 0.5rem 0 0.5rem 2rem;
+  padding: 0.25rem 0 0.25rem 2rem;
+  @include height-small {
+    padding: 0.5rem 0 0.5rem 2rem;
+  }
 
   border: solid 1px var(--border-color);
   background-color: var(--field-background-color);

+ 2 - 0
src/app/journal/journal-spellbook/journal-spellbook.component.scss

@@ -0,0 +1,2 @@
+//
+@import url("src/responsive.scss");

+ 1 - 1
src/app/journal/journal-stats/ability-panel/ability-panel.component.html

@@ -19,7 +19,7 @@
       (click)="active = 3"
       [class]="active === 3 ? 'active' : ''"
     >
-      {{ isMonk ? "Ki" : "Zaubern" }}
+      {{ isMonk ? "Ki" : ("magic.spell" | translate) }}
     </button>
     <button
       class="tab-button"

+ 1 - 4
src/app/journal/journal-stats/ability-panel/ability-panel.component.scss

@@ -3,12 +3,9 @@
   background-color: var(--field-background-color);
   box-shadow: var(--shadow);
   border-radius: 10px;
-  height: 48.125rem;
   display: flex;
   flex-direction: column;
-  @media (height < 934px) {
-    height: 43.625rem;
-  }
+  height: 100%;
 }
 
 .tab-row {

+ 9 - 0
src/app/journal/journal-stats/ability-panel/ability-table/ability-modal/ability-modal.component.scss

@@ -1,3 +1,5 @@
+@import "../../../../../../responsive";
+
 .dimensions {
   width: 50rem;
   background-color: var(--modal-background);
@@ -6,3 +8,10 @@
   box-shadow: var(--shadow);
   padding: 0 2rem;
 }
+
+.flex-form {
+  gap: 0.5rem;
+  @include height-small {
+    gap: 1.5rem;
+  }
+}

+ 1 - 1
src/app/journal/journal-stats/ability-panel/ability-table/ability-table.component.scss

@@ -3,7 +3,7 @@
 }
 
 .item-list {
-  height: 40rem;
+  height: calc(100% - 5rem);
 }
 
 .item {

+ 7 - 0
src/app/journal/journal-stats/ability-panel/ability-table/ability-table.component.ts

@@ -77,7 +77,14 @@ export class AbilityTableComponent {
     this.updateDatabase();
   }
 
+  /**
+   * Ticks the first x checkboxes of an ability, where x is the amount of currently used charges.
+   * @param abilityIndex The Index of the ability to correct the charges view for
+   * @returns void
+   */
   public correctChargesView(abilityIndex: number): void {
+    // if there are more than 9 charges, the charges are not handled by checkboxes.
+    if (this.abilities[abilityIndex].charges > 9) return;
     const charges = this.abilities[abilityIndex].charges;
     const currentlyUsedCharges =
       this.abilities[abilityIndex].currentlyUsedCharges;

+ 3 - 1
src/app/journal/journal-stats/ability-panel/spellslots/spellslots-modal/spellslots-modal.component.html

@@ -31,7 +31,9 @@
           </div>
         }
       </div>
-      <icon-button [icon]="'add'" (click)="addSpellLevel()"></icon-button>
+      @if (spellslots.length < 9) {
+        <icon-button [icon]="'add'" (click)="addSpellLevel()"></icon-button>
+      }
     }
   } @else {
     <div class="title">{{ "magic.ki" | translate }}</div>

+ 1 - 1
src/app/journal/journal-stats/ability-panel/spellslots/spellslots.component.ts

@@ -73,7 +73,7 @@ export class SpellslotsComponent {
     const spellcastingAttribute = this.spellcastingAttribute;
 
     if (spellcastingAttribute !== undefined) {
-      const modifier = (this.attributeValue - 10) / 2;
+      const modifier = Math.floor((this.attributeValue - 10) / 2);
       this.spellAttackModifier = modifier + this.proficiencyBonus;
       this.spellSaveDC = 8 + modifier + this.proficiencyBonus;
     }

+ 1 - 1
src/app/journal/journal-stats/ability-panel/trait-table/trait-table.component.scss

@@ -9,7 +9,7 @@
 }
 
 .item-list {
-  height: 40rem;
+  height: calc(100% - 5rem);
 }
 
 .item {

+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-details/attribute-details.component.html → src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-details/attribute-details.component.html


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-details/attribute-details.component.scss → src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-details/attribute-details.component.scss


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-details/attribute-details.component.spec.ts → src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-details/attribute-details.component.spec.ts


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-details/attribute-details.component.ts → src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-details/attribute-details.component.ts


+ 0 - 1
src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-field.component.html → src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-field.component.html

@@ -1,6 +1,5 @@
 <div class="attribute-box" (click)="openDetails()">
   <div>
-    <!-- <label for="attribute-vale" class="attribute-name">{{ attributeNames[attributeName] }}</label> -->
     <label for="attribute-vale" class="attribute-name">
       {{ "attributes." + attribute.name | translate }}</label
     >

+ 2 - 1
src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-field.component.scss → src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-field.component.scss

@@ -1,5 +1,6 @@
 .attribute-box {
-  border: solid 1px var(--border-color);
+  width: 8rem;
+  border: var(--border);
   background-color: var(--field-background-color);
   box-shadow: var(--shadow);
   border-radius: 10px;

+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-field.component.spec.ts → src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-field.component.spec.ts


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-field.component.ts → src/app/journal/journal-stats/attribute-panel/attribute-field/attribute-field.component.ts


+ 5 - 0
src/app/journal/journal-stats/attribute-panel/attribute-panel.component.html

@@ -0,0 +1,5 @@
+<div class="attribute-panel">
+  @for (attribute of attributes; track attribute) {
+    <attribute-field [attributeName]="attribute"></attribute-field>
+  }
+</div>

+ 5 - 0
src/app/journal/journal-stats/attribute-panel/attribute-panel.component.scss

@@ -0,0 +1,5 @@
+.attribute-panel {
+  display: flex;
+  flex-direction: column;
+  gap: 2rem;
+}

+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-panel.component.spec.ts → src/app/journal/journal-stats/attribute-panel/attribute-panel.component.spec.ts


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-panel.component.ts → src/app/journal/journal-stats/attribute-panel/attribute-panel.component.ts


+ 0 - 6
src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-panel.component.html

@@ -1,6 +0,0 @@
-<div class="attribute-panel">
-  <attribute-field
-    *ngFor="let attribute of attributes"
-    [attributeName]="attribute"
-  ></attribute-field>
-</div>

+ 0 - 11
src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-panel.component.scss

@@ -1,11 +0,0 @@
-.attribute-panel {
-  display: flex;
-  flex-direction: column;
-  height: calc(100vh - 2.75rem);
-  gap: 2rem;
-
-  @media (height < 934px) {
-    justify-content: space-between;
-    gap: none;
-  }
-}

+ 0 - 10
src/app/journal/journal-stats/attribute-skill-container/attribute-skill-container.component.html

@@ -1,10 +0,0 @@
-<div class="attribute-skill-container">
-  <div class="skill-column-left">
-    <attribute-panel></attribute-panel>
-  </div>
-
-  <div class="skill-column-right">
-    <save-throw-panel></save-throw-panel>
-    <skill-panel></skill-panel>
-  </div>
-</div>

+ 0 - 22
src/app/journal/journal-stats/attribute-skill-container/attribute-skill-container.component.scss

@@ -1,22 +0,0 @@
-.attribute-skill-container {
-  display: flex;
-  flex-direction: row;
-  justify-content: space-between;
-
-  .skill-column-left {
-    width: 35%;
-    @media (width < 1700px) {
-      width: 32%;
-    }
-  }
-
-  .skill-column-right {
-    display: flex;
-    flex-direction: column;
-    width: 60%;
-    @media (width < 1700px) {
-      width: 64%;
-    }
-    gap: 1.5rem;
-  }
-}

+ 0 - 21
src/app/journal/journal-stats/attribute-skill-container/attribute-skill-container.component.spec.ts

@@ -1,21 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AttributeSkillContainerComponent } from './attribute-skill-container.component';
-
-describe('AttributeSkillContainerComponent', () => {
-  let component: AttributeSkillContainerComponent;
-  let fixture: ComponentFixture<AttributeSkillContainerComponent>;
-
-  beforeEach(() => {
-    TestBed.configureTestingModule({
-      declarations: [AttributeSkillContainerComponent],
-    });
-    fixture = TestBed.createComponent(AttributeSkillContainerComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});

+ 0 - 10
src/app/journal/journal-stats/attribute-skill-container/attribute-skill-container.component.ts

@@ -1,10 +0,0 @@
-import { Component, Input } from '@angular/core';
-
-@Component({
-  selector: 'attribute-skill-container',
-  templateUrl: './attribute-skill-container.component.html',
-  styleUrls: ['./attribute-skill-container.component.scss'],
-})
-export class AttributeSkillContainerComponent {
-  @Input() attributeObject: any = {};
-}

+ 0 - 6
src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-panel.component.scss

@@ -1,6 +0,0 @@
-.save-throw-panel {
-  border: var(--border);
-  background-color: var(--field-background-color);
-  box-shadow: var(--shadow);
-  border-radius: 10px;
-}

+ 0 - 11
src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-panel.component.scss

@@ -1,11 +0,0 @@
-.skill-panel {
-  display: flex;
-  flex-direction: column;
-  border: solid 1px var(--border-color);
-  background-color: var(--field-background-color);
-  box-shadow: var(--shadow);
-  border-radius: 10px;
-  @media (height < 900px) {
-    margin-bottom: 4rem;
-  }
-}

+ 0 - 6
src/app/journal/journal-stats/info-row/death-save/death-save-details/death-save-details.component.html

@@ -1,6 +0,0 @@
-<div class="title">{{ "deathSave.label" | translate }}</div>
-
-<div class="content">{{ "deathSave.description" | translate }}</div>
-
-<div class="subheading left">{{ "deathSave.criticalHeader" | translate }}</div>
-<div class="content t-05">{{ "deathSave.criticalContent" | translate }}</div>

+ 0 - 0
src/app/journal/journal-stats/info-row/death-save/death-save-details/death-save-details.component.scss


+ 0 - 22
src/app/journal/journal-stats/info-row/death-save/death-save-details/death-save-details.component.spec.ts

@@ -1,22 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { DeathSaveDetailsComponent } from './death-save-details.component';
-
-describe('DeathSaveDetailsComponent', () => {
-  let component: DeathSaveDetailsComponent;
-  let fixture: ComponentFixture<DeathSaveDetailsComponent>;
-
-  beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [DeathSaveDetailsComponent],
-    }).compileComponents();
-
-    fixture = TestBed.createComponent(DeathSaveDetailsComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});

+ 0 - 8
src/app/journal/journal-stats/info-row/death-save/death-save-details/death-save-details.component.ts

@@ -1,8 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
-  selector: 'app-death-save-details',
-  templateUrl: './death-save-details.component.html',
-  styleUrl: './death-save-details.component.scss',
-})
-export class DeathSaveDetailsComponent {}

+ 0 - 45
src/app/journal/journal-stats/info-row/death-save/death-save.component.html

@@ -1,45 +0,0 @@
-<div class="death-save-container" (click)="openDetails()">
-  <div style="margin: 0.625rem 0 0 0.4rem">
-    <div class="save-row">
-      <div class="save-label">{{ "deathSave.success" | translate }}</div>
-      <div class="save-checkbox" (click)="$event.stopPropagation()">
-        <input
-          type="checkbox"
-          [(ngModel)]="deathSaveSucc1"
-          (change)="updateValue()"
-        />
-        <input
-          type="checkbox"
-          [(ngModel)]="deathSaveSucc2"
-          (change)="updateValue()"
-        />
-        <input
-          type="checkbox"
-          [(ngModel)]="deathSaveSucc3"
-          (change)="updateValue()"
-        />
-      </div>
-    </div>
-    <div class="save-row">
-      <div class="save-label">{{ "deathSave.fail" | translate }}</div>
-      <div class="save-checkbox fail" (click)="$event.stopPropagation()">
-        <input
-          type="checkbox"
-          [(ngModel)]="deathSaveFail1"
-          (change)="updateValue()"
-        />
-        <input
-          type="checkbox"
-          [(ngModel)]="deathSaveFail2"
-          (change)="updateValue()"
-        />
-        <input
-          type="checkbox"
-          [(ngModel)]="deathSaveFail3"
-          (change)="updateValue()"
-        />
-      </div>
-    </div>
-  </div>
-  <div class="death-save">{{ "deathSave.label" | translate }}</div>
-</div>

+ 1 - 1
src/app/journal/journal-stats/info-row/info-row.component.html

@@ -4,5 +4,5 @@
   <movement></movement>
   <proficiency></proficiency>
   <conditions></conditions>
-  <death-save></death-save>
+  <!-- <death-save></death-save> -->
 </div>

+ 15 - 0
src/app/journal/journal-stats/info-row/info-row.component.scss

@@ -1,4 +1,19 @@
+@import "../../../../responsive";
+
 .row-container {
   display: flex;
   justify-content: space-between;
+  width: 797px;
+
+  @include width-small {
+    width: 1011px;
+  }
+
+  @include width-medium {
+    width: 1225px;
+  }
+  // Width large is probably too large
+  // @include width-large {
+  //   width: 1436px;
+  // }
 }

+ 16 - 12
src/app/journal/journal-stats/journal-stats.component.html

@@ -1,19 +1,23 @@
-<div class="flex-center">
-  <div class="stats-container">
-    <!-- Attribute and skill container -->
-    <attribute-skill-container></attribute-skill-container>
+<div class="dashboard-container">
+  <div class="attribute-skill-container">
+    <attribute-panel></attribute-panel>
 
-    <div class="container2">
-      <info-row></info-row>
+    <div class="save-skills-container">
+      <save-throw-panel></save-throw-panel>
+      <skill-panel></skill-panel>
+    </div>
+  </div>
 
-      <div class="rest">
-        <div class="life-weapon-container">
-          <life></life>
-          <app-weapons-container></app-weapons-container>
-        </div>
+  <div class="info-tabels-container">
+    <info-row></info-row>
 
-        <ability-panel></ability-panel>
+    <div class="tables">
+      <div class="life-weapon-container">
+        <life></life>
+        <weapons-container></weapons-container>
       </div>
+
+      <ability-panel></ability-panel>
     </div>
   </div>
 </div>

+ 79 - 35
src/app/journal/journal-stats/journal-stats.component.scss

@@ -1,63 +1,107 @@
-.flex-center {
-  display: flex;
-  justify-content: center;
-}
+@import "../../../responsive";
 
-.stats-container {
+.dashboard-container {
   display: flex;
-  justify-content: space-between;
-  margin: 1.5rem 1.5rem 0 1.5rem;
-  gap: 1.5rem;
-  width: 100%;
-}
+  gap: 1rem;
+  // TODO: Add to center
+  // margin: auto;
+  padding: 1.5rem 1.5rem 0 1.5rem;
+  border: 3px solid red;
+  width: 1280px;
+  height: 840px;
 
-attribute-skill-container {
-  width: 25%;
+  @include width-small {
+    width: 1494px;
+  }
+  @include width-medium {
+    width: 1707px;
+  }
 
-  @media (width < 1700px) {
-    width: 28%;
+  // Width large is probably too large
+  // @include width-large {
+  //   width: 1920px;
+  // }
+  @include height-small {
+    height: 910px;
+  }
+  @include height-medium {
+    height: 977px;
+  }
+  @include height-large {
+    height: 1056px;
   }
 }
 
-.container2 {
+.attribute-skill-container {
   display: flex;
-  flex-direction: column;
-  gap: 1.5rem;
-  width: 75%;
+  gap: 1rem;
 
-  @media (width < 1700px) {
-    width: 72%;
+  .save-skills-container {
+    display: flex;
+    flex-direction: column;
+    gap: 1rem;
   }
 }
 
-.rest {
+.info-tabels-container {
   display: flex;
-  justify-content: space-between;
-  width: 100%;
+  flex-direction: column;
+  gap: 1rem;
+}
+
+.tables {
+  display: flex;
+  gap: 1rem;
 }
 
 .life-weapon-container {
   display: flex;
   flex-direction: column;
-  gap: 2rem;
-  width: 49%;
+  gap: 1rem;
+  width: 388px;
 
-  @media (height < 934px) {
-    gap: 1.5rem;
+  @include width-small {
+    width: 500px;
+  }
+  @include width-medium {
+    width: 600px;
   }
 }
 
 ability-panel {
-  width: 49%;
-}
+  width: 391px;
+  height: 702px;
 
-.life {
-  width: 100%;
-  height: 20%;
+  @include width-small {
+    width: 495px;
+  }
+
+  @include width-medium {
+    width: 609px;
+  }
+
+  @include height-small {
+    height: 772px;
+  }
+  @include height-medium {
+    height: 839px;
+  }
+  @include height-large {
+    height: 917px;
+  }
 }
 
-.weapon {
+weapons-container {
   width: 100%;
-  min-height: 50%;
-  max-height: 70%;
+  height: 515px;
+
+  @include height-small {
+    height: 585px;
+  }
+  @include height-medium {
+    height: 652px;
+  }
+  @include height-large {
+    height: 730px;
+  }
 }

+ 0 - 37
src/app/journal/journal-stats/life-container/hit-dice/hit-dice.component.html

@@ -1,37 +0,0 @@
-<div class="heading-line">
-  <div class="heading left t-0">{{ "life.hitdice" | translate }}</div>
-  <icon-button
-    [icon]="showEditButtons ? 'cross' : 'edit'"
-    (click)="showEditButtons = !showEditButtons"
-  ></icon-button>
-</div>
-<div class="content">{{ "life.hitdiceDescription" | translate }}</div>
-<p>
-  <b>{{ "general.diceType" | translate }}: </b>
-  {{ "general.dice" | translate }}{{ hitDice.diceType }}
-</p>
-
-<div class="hit-dice-container">
-  <div class="input-container">
-    @for (_ of getArray(hitDice.diceNumber); track _; let index = $index) {
-      <input
-        [id]="'checkbox' + index"
-        type="checkbox"
-        (change)="handleUsedHitDice(index, $event.target)"
-      />
-    }
-  </div>
-  @if (showEditButtons) {
-    <div style="display: flex; flex-direction: column">
-      <icon-button
-        [icon]="'add'"
-        (click)="hitDice.diceNumber = hitDice.diceNumber + 1"
-      ></icon-button>
-      <icon-button
-        [icon]="'remove'"
-        style="margin-top: 1rem"
-        (click)="hitDice.diceNumber = hitDice.diceNumber - 1"
-      ></icon-button>
-    </div>
-  }
-</div>

+ 0 - 4
src/app/journal/journal-stats/life-container/life-container.component.html

@@ -1,4 +0,0 @@
-<div class="life-container">
-  <!-- Hit Points -->
-  <life style="width: 100%"></life>
-</div>

+ 0 - 22
src/app/journal/journal-stats/life-container/life-container.component.scss

@@ -1,22 +0,0 @@
-.life-container {
-  display: flex;
-  flex-direction: column;
-  // flex-wrap: wrap;
-  gap: 2rem;
-}
-
-.ac-speed-container {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: wrap;
-  justify-content: space-between;
-  row-gap: 1rem;
-}
-
-.small {
-  width: 25%;
-}
-
-.large {
-  width: 40%;
-}

+ 0 - 21
src/app/journal/journal-stats/life-container/life-container.component.spec.ts

@@ -1,21 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { LifeContainerComponent } from './life-container.component';
-
-describe('LifeContainerComponent', () => {
-  let component: LifeContainerComponent;
-  let fixture: ComponentFixture<LifeContainerComponent>;
-
-  beforeEach(() => {
-    TestBed.configureTestingModule({
-      declarations: [LifeContainerComponent],
-    });
-    fixture = TestBed.createComponent(LifeContainerComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});

+ 0 - 8
src/app/journal/journal-stats/life-container/life-container.component.ts

@@ -1,8 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
-  selector: 'app-life-container',
-  templateUrl: './life-container.component.html',
-  styleUrls: ['./life-container.component.scss'],
-})
-export class LifeContainerComponent {}

+ 0 - 39
src/app/journal/journal-stats/life-container/life/life-details/life-details.component.html

@@ -1,39 +0,0 @@
-<div class="title">{{ "life.hitpoints" | translate }}</div>
-
-<div class="value-row t-2 b-15">
-  <value-box
-    [isInput]="true"
-    [(ngModel)]="maxHitPoints"
-    [label]="'life.max'"
-    (change)="checkValidity()"
-  ></value-box>
-  <value-box
-    [isInput]="true"
-    [(ngModel)]="currentHitPoints"
-    [label]="'life.current'"
-    (change)="checkValidity()"
-  ></value-box>
-  <value-box
-    [isInput]="true"
-    [(ngModel)]="temporaryHitPoints"
-    [label]="'life.temporary'"
-  ></value-box>
-</div>
-<hit-dice style="margin-top: 5rem" (setHitDice)="setHitDice($event)"></hit-dice>
-
-<div class="vertical-buttons bottom">
-  <ui-button
-    [color]="'green'"
-    [type]="'apply'"
-    [width]="'w20'"
-    (click)="close('update')"
-  >
-  </ui-button>
-  <ui-button
-    [color]="'red'"
-    [type]="'discard'"
-    [width]="'w20'"
-    (click)="close('cancel')"
-  >
-  </ui-button>
-</div>

+ 0 - 69
src/app/journal/journal-stats/life-container/life/life-details/life-details.component.scss

@@ -1,69 +0,0 @@
-.life-container {
-  display: flex;
-  flex-direction: row;
-  justify-content: space-evenly;
-  // gap: 1.5rem;
-  margin-top: 3rem;
-  margin-bottom: 2rem;
-}
-
-.life-box {
-  display: grid;
-  grid-template-rows: 1fr 1fr;
-  justify-content: center;
-  gap: 0.5rem;
-  text-align: center;
-}
-
-.life-input {
-  width: 20%;
-  font-size: 1.5rem;
-  margin: auto;
-  text-align: center;
-  appearance: textfield;
-  -moz-appearance: textfield;
-  border-radius: 10px;
-  border: none;
-  box-shadow: var(--shadow);
-
-  &::-webkit-inner-spin-button,
-  &::-webkit-outer-spin-button {
-    -webkit-appearance: none;
-    margin: 0;
-  }
-}
-
-hit-dice {
-  margin-top: 1.5rem;
-}
-
-.button-container {
-  height: 9rem;
-  width: 27rem;
-  position: absolute;
-  bottom: 0;
-  display: flex;
-  justify-content: space-evenly;
-  align-items: center;
-  flex-direction: column;
-
-  .confirm {
-    width: 20rem;
-    margin: 0 auto;
-    background-color: var(--accept);
-
-    &:hover {
-      background-color: var(--accept-hover);
-    }
-  }
-
-  .cancel {
-    width: 20rem;
-    margin: 0 auto;
-    background-color: var(--delete);
-
-    &:hover {
-      background-color: var(--delete-hover);
-    }
-  }
-}

+ 0 - 51
src/app/journal/journal-stats/life-container/life/life-details/life-details.component.ts

@@ -1,51 +0,0 @@
-import { Component, Input } from '@angular/core';
-import { DetailsService } from 'src/services/details/details.service';
-
-@Component({
-  selector: 'app-life-details',
-  templateUrl: './life-details.component.html',
-  styleUrls: ['./life-details.component.scss'],
-})
-export class LifeDetailsComponent {
-  public constructor(public detailsAccessor: DetailsService) {}
-
-  @Input() lifeData: any;
-  private hitDice: any;
-
-  public maxHitPoints: number = 0;
-  public currentHitPoints: number = 0;
-  public temporaryHitPoints: number = 0;
-
-  public ngOnInit(): void {
-    this.maxHitPoints = this.lifeData.maxHitPoints;
-    this.currentHitPoints = this.lifeData.currentHitPoints;
-    this.temporaryHitPoints = this.lifeData.temporaryHitPoints;
-  }
-
-  public checkValidity(): void {
-    if (this.currentHitPoints > this.maxHitPoints) {
-      this.currentHitPoints = this.maxHitPoints;
-    }
-  }
-
-  public close(result: string): void {
-    if (result === 'update') {
-      this.detailsAccessor.closePanel(result, {
-        maxHitPoints: this.maxHitPoints,
-        currentHitPoints: this.currentHitPoints,
-        temporaryHitPoints: this.temporaryHitPoints,
-        hitDice: this.hitDice,
-      });
-    } else if (result === 'cancel') {
-      this.detailsAccessor.closePanel(result);
-    }
-  }
-
-  public setHitDice(hitDiceData: any): void {
-    this.hitDice = hitDiceData;
-  }
-
-  test() {
-    console.log('TEST');
-  }
-}

+ 47 - 0
src/app/journal/journal-stats/life/death-save/death-save.component.html

@@ -0,0 +1,47 @@
+<div class="content t-05">{{ "life.description" | translate }}</div>
+
+<div class="left">
+  <b>{{ "life.criticalHeader" | translate }}</b>
+</div>
+<div class="content t-05">{{ "life.criticalContent" | translate }}</div>
+
+<div class="save-row">
+  <div class="save-label">{{ "life.success" | translate }}</div>
+  <div class="save-checkbox" (click)="$event.stopPropagation()">
+    <input
+      type="checkbox"
+      [(ngModel)]="deathSaveSucc1"
+      (change)="updateValue()"
+    />
+    <input
+      type="checkbox"
+      [(ngModel)]="deathSaveSucc2"
+      (change)="updateValue()"
+    />
+    <input
+      type="checkbox"
+      [(ngModel)]="deathSaveSucc3"
+      (change)="updateValue()"
+    />
+  </div>
+</div>
+<div class="save-row t-05">
+  <div class="save-label">{{ "life.fail" | translate }}</div>
+  <div class="save-checkbox fail" (click)="$event.stopPropagation()">
+    <input
+      type="checkbox"
+      [(ngModel)]="deathSaveFail1"
+      (change)="updateValue()"
+    />
+    <input
+      type="checkbox"
+      [(ngModel)]="deathSaveFail2"
+      (change)="updateValue()"
+    />
+    <input
+      type="checkbox"
+      [(ngModel)]="deathSaveFail3"
+      (change)="updateValue()"
+    />
+  </div>
+</div>

+ 3 - 3
src/app/journal/journal-stats/info-row/death-save/death-save.component.scss → src/app/journal/journal-stats/life/death-save/death-save.component.scss

@@ -14,12 +14,12 @@
   display: flex;
   width: 100%;
   justify-content: space-between;
+  padding-right: 6rem;
 }
 
 .save-label {
-  font-weight: 600;
-  text-align: center;
-  padding: 0 0.5rem 0 0.5rem;
+  font-weight: 700;
+  padding-left: 0.5rem;
 }
 
 .save-checkbox {

+ 0 - 0
src/app/journal/journal-stats/info-row/death-save/death-save.component.spec.ts → src/app/journal/journal-stats/life/death-save/death-save.component.spec.ts


+ 1 - 10
src/app/journal/journal-stats/info-row/death-save/death-save.component.ts → src/app/journal/journal-stats/life/death-save/death-save.component.ts

@@ -1,7 +1,5 @@
 import { Component } from '@angular/core';
 import { DataService } from 'src/services/data/data.service';
-import { DetailsService } from 'src/services/details/details.service';
-import { DeathSaveDetailsComponent } from './death-save-details/death-save-details.component';
 
 @Component({
   selector: 'death-save',
@@ -9,10 +7,7 @@ import { DeathSaveDetailsComponent } from './death-save-details/death-save-detai
   styleUrls: ['./death-save.component.scss'],
 })
 export class DeathSaveComponent {
-  public constructor(
-    private dataAccessor: DataService,
-    public detailsAccessor: DetailsService,
-  ) {}
+  public constructor(private dataAccessor: DataService) {}
 
   public deathSave: number[] = [0, 0];
 
@@ -94,8 +89,4 @@ export class DeathSaveComponent {
       }
     });
   }
-
-  public openDetails(): void {
-    this.detailsAccessor.openPanel(DeathSaveDetailsComponent, {});
-  }
 }

+ 26 - 0
src/app/journal/journal-stats/life/hit-dice/hit-dice.component.html

@@ -0,0 +1,26 @@
+<div class="content t-05">{{ "life.hitdiceDescription" | translate }}</div>
+<p>
+  <b>{{ "general.diceType" | translate }}: </b>
+  {{ "general.dice" | translate }}{{ hitDice.diceType }}
+</p>
+
+<div class="hit-dice-container">
+  <div class="input-container">
+    @for (_ of getArray(hitDice.diceNumber); track _; let index = $index) {
+      <input
+        [id]="'checkbox' + index"
+        type="checkbox"
+        (change)="handleUsedHitDice(index, $event.target)"
+      />
+    }
+  </div>
+  <div style="display: flex; flex-direction: column">
+    <icon-button [icon]="'add'" (click)="addDice()"></icon-button>
+    <icon-button
+      [icon]="'remove'"
+      [disabled]="hitDice.diceNumber === 1"
+      style="margin-top: 1rem"
+      (click)="removeDice()"
+    ></icon-button>
+  </div>
+</div>

+ 1 - 8
src/app/journal/journal-stats/life-container/hit-dice/hit-dice.component.scss → src/app/journal/journal-stats/life/hit-dice/hit-dice.component.scss

@@ -1,8 +1,3 @@
-.heading-line {
-  display: flex;
-  align-items: flex-start;
-  gap: 0.5rem;
-}
 .hit-dice-container {
   display: flex;
   flex-direction: row;
@@ -13,11 +8,9 @@
     flex-direction: row;
     flex-wrap: wrap;
     width: 20rem;
+    padding-right: 5rem;
     row-gap: 0.5rem;
     column-gap: 3rem;
-    // input {
-    //     flex: 0 0 4rem;
-    // }
   }
 }
 

+ 0 - 0
src/app/journal/journal-stats/life-container/hit-dice/hit-dice.component.spec.ts → src/app/journal/journal-stats/life/hit-dice/hit-dice.component.spec.ts


+ 15 - 9
src/app/journal/journal-stats/life-container/hit-dice/hit-dice.component.ts → src/app/journal/journal-stats/life/hit-dice/hit-dice.component.ts

@@ -8,7 +8,6 @@ import { DataService } from 'src/services/data/data.service';
 })
 export class HitDiceComponent {
   public hitDice: any;
-  showEditButtons: boolean = false;
 
   @Output() public setHitDice: EventEmitter<any> = new EventEmitter();
 
@@ -18,26 +17,21 @@ export class HitDiceComponent {
 
   public ngOnInit(): void {
     this.correctHitDiceView();
-    this.emitHitDice();
   }
 
   public handleUsedHitDice(index: number, element: any): void {
-    console.log('HITDICE in hitdice first: ', this.hitDice);
     if (element.checked) {
-      console.log('The checkbox is checked now ');
       this.hitDice.diceUsed++;
       if (index + 1 !== this.hitDice.diceUsed) {
         this.correctHitDiceView();
       }
     } else {
-      console.log('The checkbox is unchecked now ');
       this.hitDice.diceUsed--;
       if (index !== this.hitDice.diceUsed) {
         this.correctHitDiceView();
       }
     }
-    console.log('HITDICE in hitdice: ', this.hitDice);
-    this.emitHitDice();
+    this.updateDatabase();
   }
 
   private correctHitDiceView(): void {
@@ -64,10 +58,22 @@ export class HitDiceComponent {
     return Array.from({ length: length });
   }
 
-  private emitHitDice(): void {
+  public addDice(): void {
+    this.hitDice.diceNumber++;
+    this.updateDatabase();
+  }
+
+  public removeDice(): void {
+    if (this.hitDice.diceNumber > 1) {
+      this.hitDice.diceNumber--;
+    }
     if (this.hitDice.diceUsed > this.hitDice.diceNumber) {
       this.hitDice.diceUsed = this.hitDice.diceNumber;
     }
-    this.setHitDice.emit(this.hitDice);
+    this.updateDatabase();
+  }
+
+  public updateDatabase(): void {
+    this.dataAccessor.hitDice = this.hitDice;
   }
 }

+ 49 - 0
src/app/journal/journal-stats/life/life-details/life-details.component.html

@@ -0,0 +1,49 @@
+<div class="title">{{ "life.hitpoints" | translate }}</div>
+
+<div class="value-row t-2 b-15">
+  <value-box
+    [isInput]="true"
+    [(ngModel)]="currentHitPoints"
+    [label]="'life.current'"
+    (change)="checkValidity()"
+  ></value-box>
+  <value-box
+    [isInput]="true"
+    [(ngModel)]="maxHitPoints"
+    [label]="'life.max'"
+    (change)="checkValidity()"
+  ></value-box>
+  <value-box
+    [isInput]="true"
+    [(ngModel)]="temporaryHitPoints"
+    [label]="'life.temporary'"
+  ></value-box>
+</div>
+
+<div class="tab-row">
+  <button
+    class="tab-button"
+    (click)="active = 1"
+    [class]="active === 1 ? 'active' : ''"
+  >
+    {{ "life.hitdice" | translate }}
+  </button>
+  <button
+    class="tab-button"
+    (click)="active = 2"
+    [class]="active === 2 ? 'active' : ''"
+  >
+    {{ "life.deathSave" | translate }}
+  </button>
+</div>
+
+<div class="life-data-container">
+  @switch (active) {
+    @case (1) {
+      <hit-dice style="margin-top: 5rem"></hit-dice>
+    }
+    @case (2) {
+      <death-save></death-save>
+    }
+  }
+</div>

+ 81 - 0
src/app/journal/journal-stats/life/life-details/life-details.component.scss

@@ -0,0 +1,81 @@
+.life-container {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-evenly;
+  // gap: 1.5rem;
+  margin-top: 3rem;
+  margin-bottom: 2rem;
+}
+
+.life-box {
+  display: grid;
+  grid-template-rows: 1fr 1fr;
+  justify-content: center;
+  gap: 0.5rem;
+  text-align: center;
+}
+
+.life-input {
+  width: 20%;
+  font-size: 1.5rem;
+  margin: auto;
+  text-align: center;
+  appearance: textfield;
+  -moz-appearance: textfield;
+  border-radius: 10px;
+  border: none;
+  box-shadow: var(--shadow);
+
+  &::-webkit-inner-spin-button,
+  &::-webkit-outer-spin-button {
+    -webkit-appearance: none;
+    margin: 0;
+  }
+}
+
+.tab-button {
+  height: 3rem;
+  font-size: 1.375rem;
+  font-weight: 600;
+  color: black;
+  transition: all 0.25s ease-in-out;
+  background-color: var(--tab);
+  border-top: 0;
+  border-left: 0;
+  border-bottom: 1px solid var(--border-color);
+  border-right: 0;
+
+  &.active {
+    background-color: var(--tab-active);
+    border-bottom: 3px solid var(--border-color);
+  }
+}
+
+.tab-row {
+  display: flex;
+  flex: 0 0 3rem;
+  > * {
+    flex: 1 1 0;
+  }
+  > :first-child {
+    border-radius: 10px 0 0 0;
+    &.active {
+      border-right: 1px solid var(--border-color);
+    }
+  }
+  > :last-child {
+    border-radius: 0 10px 0 0;
+    border-right: 1px solid var(--border-color);
+    &.active {
+      border-left: 1px solid var(--border-color);
+    }
+  }
+}
+
+.life-data-container {
+  height: calc(100% - 30rem);
+  background-color: var(--field-background-color);
+  border: var(--border);
+  border-radius: 0 0 10px 10px;
+  padding: 0.5rem;
+}

+ 0 - 0
src/app/journal/journal-stats/life-container/life/life-details/life-details.component.spec.ts → src/app/journal/journal-stats/life/life-details/life-details.component.spec.ts


+ 44 - 0
src/app/journal/journal-stats/life/life-details/life-details.component.ts

@@ -0,0 +1,44 @@
+import { Component, Input } from '@angular/core';
+import { DetailsService } from 'src/services/details/details.service';
+import { DataService } from 'src/services/data/data.service';
+
+@Component({
+  selector: 'app-life-details',
+  templateUrl: './life-details.component.html',
+  styleUrls: ['./life-details.component.scss'],
+})
+export class LifeDetailsComponent {
+  public constructor(
+    public detailsAccessor: DetailsService,
+    private dataAccessor: DataService,
+  ) {}
+
+  public active: number = 1;
+
+  public maxHitPoints: number = 0;
+  public currentHitPoints: number = 0;
+  public temporaryHitPoints: number = 0;
+
+  public ngOnInit(): void {
+    this.dataAccessor.hitPoints$.subscribe((hitPoints: any) => {
+      this.maxHitPoints = hitPoints.maxHitPoints;
+      this.currentHitPoints = hitPoints.currentHitPoints;
+      this.temporaryHitPoints = hitPoints.temporaryHitPoints;
+    });
+  }
+
+  public checkValidity(): void {
+    if (this.currentHitPoints > this.maxHitPoints) {
+      this.currentHitPoints = this.maxHitPoints;
+    }
+    this.updateHitPointsDatabas();
+  }
+
+  public updateHitPointsDatabas(): void {
+    this.dataAccessor.updateHitPoints({
+      maxHitPoints: this.maxHitPoints,
+      currentHitPoints: this.currentHitPoints,
+      temporaryHitPoints: this.temporaryHitPoints,
+    });
+  }
+}

+ 0 - 0
src/app/journal/journal-stats/life-container/life/life.component.html → src/app/journal/journal-stats/life/life.component.html


+ 0 - 0
src/app/journal/journal-stats/life-container/life/life.component.scss → src/app/journal/journal-stats/life/life.component.scss


+ 0 - 0
src/app/journal/journal-stats/life-container/life/life.component.spec.ts → src/app/journal/journal-stats/life/life.component.spec.ts


+ 17 - 36
src/app/journal/journal-stats/life-container/life/life.component.ts → src/app/journal/journal-stats/life/life.component.ts

@@ -17,22 +17,28 @@ export class LifeComponent {
   public temporaryHitPointsPercentage: number = 0;
   public missingHitPointsPercentage: number = 0;
 
-  private hitDice: any;
-
   public constructor(
     public dataAccessor: DataService,
     public detailsAccessor: DetailsService,
   ) {}
 
   ngOnInit(): void {
-    const hitPointsData = this.dataAccessor.hitPoints;
-    this.maxHitPoints = hitPointsData.maxHitPoints;
-    this.currentHitPoints = hitPointsData.currentHitPoints;
-    this.temporaryHitPoints = hitPointsData.temporaryHitPoints;
-    this.hitDice = this.dataAccessor.hitDice;
-    this.calculatePercentages();
+    this.dataAccessor.hitPoints$.subscribe((hitPoints: any) => {
+      this.maxHitPoints = hitPoints.maxHitPoints;
+      this.currentHitPoints = hitPoints.currentHitPoints;
+      this.temporaryHitPoints = hitPoints.temporaryHitPoints;
+      this.calculatePercentages();
+    });
   }
 
+  //   const hitPointsData = this.dataAccessor.hitPoints;
+  //   this.maxHitPoints = hitPointsData.maxHitPoints;
+  //   this.currentHitPoints = hitPointsData.currentHitPoints;
+  //   this.temporaryHitPoints = hitPointsData.temporaryHitPoints;
+  //   this.hitDice = this.dataAccessor.hitDice;
+  //   this.calculatePercentages();
+  // }
+
   public addHitPoints(): void {
     if (this.currentHitPoints < this.maxHitPoints) {
       this.currentHitPoints = this.currentHitPoints + 1;
@@ -73,39 +79,14 @@ export class LifeComponent {
   }
 
   public openDetailsPanel(): void {
-    this.detailsAccessor.openPanel(LifeDetailsComponent, {
-      lifeData: {
-        maxHitPoints: this.maxHitPoints,
-        currentHitPoints: this.currentHitPoints,
-        temporaryHitPoints: this.temporaryHitPoints,
-      },
-    });
-    // The result from the details panel with return values
-    const resultSubscription = this.detailsAccessor.result$.subscribe(
-      (result) => {
-        if (result.state === 'update') {
-          this.maxHitPoints = result.data.maxHitPoints;
-          this.currentHitPoints = result.data.currentHitPoints;
-          this.temporaryHitPoints = result.data.temporaryHitPoints;
-          this.hitDice = result.data.hitDice;
-          console.log('HITDICE IN LIFE-RESPONSE: ', this.hitDice);
-          this.calculatePercentages();
-          this.updateDatabase();
-        } else if (result.state === 'cancel') {
-          // Do nothing
-        }
-        resultSubscription.unsubscribe();
-      },
-    );
+    this.detailsAccessor.openPanel(LifeDetailsComponent, {});
   }
 
   public updateDatabase() {
-    console.warn(this.hitDice.diceNumber);
-    this.dataAccessor.hitPoints = {
+    this.dataAccessor.updateHitPoints({
       maxHitPoints: this.maxHitPoints,
       currentHitPoints: this.currentHitPoints,
       temporaryHitPoints: this.temporaryHitPoints,
-    };
-    this.dataAccessor.hitDice = this.hitDice;
+    });
   }
 }

+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-details/save-throw-details.component.html → src/app/journal/journal-stats/save-throw-panel/save-throw-details/save-throw-details.component.html


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-details/save-throw-details.component.scss → src/app/journal/journal-stats/save-throw-panel/save-throw-details/save-throw-details.component.scss


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-details/save-throw-details.component.spec.ts → src/app/journal/journal-stats/save-throw-panel/save-throw-details/save-throw-details.component.spec.ts


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-details/save-throw-details.component.ts → src/app/journal/journal-stats/save-throw-panel/save-throw-details/save-throw-details.component.ts


+ 6 - 6
src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-field/save-throw-field.component.html → src/app/journal/journal-stats/save-throw-panel/save-throw-field/save-throw-field.component.html

@@ -11,12 +11,12 @@
   </div>
 
   <div [class]="attribute.advantage" class="save-throw-field__value">
+    <!-- TODO: Potential Advantage style -->
+    <!-- @if (attribute.advantage !== "none") {
+      <span>
+        <icon [size]="'xs'" [type]="'UI'" [icon]="attribute.advantage!"></icon>
+      </span>
+    } -->
     {{ saveModifier }}
   </div>
-  <!-- TODO: Potential Advantage style -->
-  <!-- @if (skill.advantage !== "none") {
-    <span>
-      <icon [size]="'xs'" [type]="'UI'" [icon]="skill.advantage!"></icon>
-    </span>
-  } -->
 </div>

+ 10 - 10
src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-field/save-throw-field.component.scss → src/app/journal/journal-stats/save-throw-panel/save-throw-field/save-throw-field.component.scss

@@ -2,28 +2,28 @@
   display: flex;
   flex-direction: row;
   align-items: center;
-  justify-content: space-between;
-  text-align: center;
-  height: 2.25rem;
-  @media (height < 934px) {
-    height: 2.0675rem;
-  }
+
   .save-throw-field__input {
-    width: 15%;
+    width: 2rem;
   }
 
   .save-throw-field__name {
-    width: 50%;
+    width: 11rem;
     font-size: 1.125rem;
     font-weight: 600;
     text-align: start;
+    padding-left: 0.75rem;
   }
 
   .save-throw-field__value {
-    width: 20%;
-    font-size: 1.5rem;
+    width: 4rem;
+    font-size: 1.25rem;
     font-weight: 600;
+    text-align: end;
+    padding-right: 0.75rem;
+    line-height: 1.25rem;
   }
+
   .advantage {
     color: var(--accept);
   }

+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-field/save-throw-field.component.spec.ts → src/app/journal/journal-stats/save-throw-panel/save-throw-field/save-throw-field.component.spec.ts


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-field/save-throw-field.component.ts → src/app/journal/journal-stats/save-throw-panel/save-throw-field/save-throw-field.component.ts


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-panel.component.html → src/app/journal/journal-stats/save-throw-panel/save-throw-panel.component.html


+ 27 - 0
src/app/journal/journal-stats/save-throw-panel/save-throw-panel.component.scss

@@ -0,0 +1,27 @@
+@import "../../../../responsive";
+
+.save-throw-panel {
+  border: var(--border);
+  background-color: var(--field-background-color);
+  box-shadow: var(--shadow);
+  border-radius: 10px;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-evenly;
+  gap: 0rem;
+  padding: 0rem;
+  height: 12rem;
+
+  @include height-small {
+    height: 14rem;
+  }
+
+  @include height-medium {
+    height: 15.25rem;
+  }
+
+  // Too large, doesn't look good
+  @include height-large {
+    height: 16.5rem;
+  }
+}

+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-panel.component.spec.ts → src/app/journal/journal-stats/save-throw-panel/save-throw-panel.component.spec.ts


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-panel.component.ts → src/app/journal/journal-stats/save-throw-panel/save-throw-panel.component.ts


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-details/skill-details.component.html → src/app/journal/journal-stats/skill-panel/skill-details/skill-details.component.html


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-details/skill-details.component.scss → src/app/journal/journal-stats/skill-panel/skill-details/skill-details.component.scss


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-details/skill-details.component.spec.ts → src/app/journal/journal-stats/skill-panel/skill-details/skill-details.component.spec.ts


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-details/skill-details.component.ts → src/app/journal/journal-stats/skill-panel/skill-details/skill-details.component.ts


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-field/skill-field.component.html → src/app/journal/journal-stats/skill-panel/skill-field/skill-field.component.html


+ 9 - 12
src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-field/skill-field.component.scss → src/app/journal/journal-stats/skill-panel/skill-field/skill-field.component.scss

@@ -1,35 +1,32 @@
 .skill-box {
   display: flex;
   align-items: center;
-  justify-content: space-between;
-  text-align: center;
-  height: 2.25rem;
-
-  @media (height < 934px) {
-    height: 2.0675rem;
-  }
 
   .skill-proficiency-input {
-    width: 15%;
+    width: 2rem;
   }
 
   .skill-attribute-name {
-    width: 15%;
+    width: 3rem;
     font-weight: 600;
     color: #494949;
+    padding-left: 0.25rem;
   }
 
   .skill-name {
-    width: 52%;
+    width: 9rem;
     font-size: 1.125em;
     font-weight: 600;
     text-align: start;
   }
 
   .skill-modifier {
-    width: 18%;
-    font-size: 1.5em;
+    width: 3rem;
+    font-size: 1.25em;
     font-weight: 600;
+    line-height: 1.25rem;
+    text-align: end;
+    padding-right: 0.75rem;
   }
 
   .advantage {

+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-field/skill-field.component.spec.ts → src/app/journal/journal-stats/skill-panel/skill-field/skill-field.component.spec.ts


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-field/skill-field.component.ts → src/app/journal/journal-stats/skill-panel/skill-field/skill-field.component.ts


+ 2 - 2
src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-panel.component.html → src/app/journal/journal-stats/skill-panel/skill-panel.component.html

@@ -1,5 +1,5 @@
 <div class="skill-panel">
-  <ng-container *ngFor="let skill of skills">
+  @for (skill of skills; track skill) {
     <app-skill-field [skillName]="skill"></app-skill-field>
-  </ng-container>
+  }
 </div>

+ 24 - 0
src/app/journal/journal-stats/skill-panel/skill-panel.component.scss

@@ -0,0 +1,24 @@
+@import "../../../../responsive";
+.skill-panel {
+  display: flex;
+  flex-direction: column;
+  justify-content: space-evenly;
+  border: solid 1px var(--border-color);
+  background-color: var(--field-background-color);
+  box-shadow: var(--shadow);
+  border-radius: 10px;
+  height: 37.875rem;
+
+  @include height-small {
+    height: 40.125rem;
+  }
+
+  @include height-medium {
+    height: 43.15rem;
+  }
+
+  // Too large, doesn't look good
+  @include height-large {
+    height: 46.8rem;
+  }
+}

+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-panel.component.spec.ts → src/app/journal/journal-stats/skill-panel/skill-panel.component.spec.ts


+ 0 - 0
src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-panel.component.ts → src/app/journal/journal-stats/skill-panel/skill-panel.component.ts


+ 49 - 25
src/app/journal/journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component.html

@@ -1,14 +1,14 @@
 <div class="dimensions">
   <div class="shadow-box">
-    <div class="title t-0">Ausgewählte Zauber</div>
+    <div class="title t-0">{{ "spells.favorite" | translate }}</div>
     <hr style="margin-bottom: 0; margin: 1.5rem 2rem 0 2rem" />
     <div class="heading-list">
-      <div>Kosten</div>
-      <div>Name</div>
-      <div>Stufe</div>
-      <div>Bonus</div>
-      <div>Effekt</div>
-      <div>Reichweite</div>
+      <div>{{ "spells.header.cost" | translate }}</div>
+      <div>{{ "spells.header.name" | translate }}</div>
+      <div>{{ "spells.header.level" | translate }}</div>
+      <div>{{ "spells.header.bonus" | translate }}</div>
+      <div>{{ "spells.header.effect" | translate }}</div>
+      <div>{{ "spells.header.range" | translate }}</div>
     </div>
   </div>
   <div id="spells-table" class="item-list table-content">
@@ -68,11 +68,8 @@
           font-size: 1.25rem;
           font-weight: 500;
         "
-      >
-        Noch keine Zauber vorbereitet.
-        <br />
-        Dies kannst du im Menü <i><b>Zauber</b></i> tun.
-      </div>
+        [innerHTML]="'spells.emptyFavorites' | translate"
+      ></div>
     }
   </div>
 
@@ -99,7 +96,7 @@
 <ng-template #costTemplate let-spell="spell">
   <div class="bold">
     <span *ngIf="spell.cost === 'action'">A</span>
-    <span *ngIf="spell.cost === 'bonus action'">B</span>
+    <span *ngIf="spell.cost === 'bonus'">B</span>
     <span *ngIf="spell.cost === 'reaction'">R</span>
   </div>
 </ng-template>
@@ -107,12 +104,26 @@
 <!-- NAME -->
 <ng-template #spellNameTemplate let-spell="spell">
   <div>
-    <div class="bold">{{ spell.german }}</div>
+    <div class="bold">
+      @if (translate.getDefaultLang() == "de") {
+        {{ spell.german }}
+      } @else {
+        {{ spell.english }}
+      }
+    </div>
     <div class="bold small">
-      <span *ngIf="spell.needsConcentration">C | </span>
-      <span *ngIf="spell.needsVerbal">V </span>
-      <span *ngIf="spell.needsSomatic">G </span>
-      <span *ngIf="spell.needsMaterial">M </span>
+      <span *ngIf="spell.needsConcentration"
+        >{{ "spells.concentrationAbbr" | translate }} |
+      </span>
+      <span *ngIf="spell.needsVerbal"
+        >{{ "spells.components.verbal" | translate }}
+      </span>
+      <span *ngIf="spell.needsSomatic"
+        >{{ "spells.components.somatic" | translate }}
+      </span>
+      <span *ngIf="spell.needsMaterial"
+        >{{ "spells.components.material" | translate }}
+      </span>
       <div></div>
     </div>
   </div>
@@ -121,7 +132,9 @@
 <!-- Level -->
 <ng-template #spellLevelTemplate let-spell="spell">
   <div *ngIf="spell.level !== 0" class="bold">{{ spell.level }}</div>
-  <div *ngIf="spell.level === 0" class="bold">Trick</div>
+  <div *ngIf="spell.level === 0" class="bold">
+    {{ "spells.cantrip" | translate }}
+  </div>
 </ng-template>
 
 <!-- Attack -->
@@ -129,7 +142,9 @@
   <div>
     <div *ngIf="spell.needsSavingThrow">
       <div>
-        {{ attributes[spell.savingThrowAttribute!] }}
+        {{
+          "attributesAbbreviations." + spell.savingThrowAttribute | translate
+        }}
       </div>
       <div>{{ spellSaveDC }}</div>
     </div>
@@ -145,10 +160,13 @@
   <div>
     @if (spell.doesDamage) {
       <div *ngFor="let damage of spell.damage; let index = index">
-        <span>{{ damage.diceNumber }} {{ damage.diceType }} </span>
+        <span>
+          {{ damage.diceNumber }}
+          {{ "general.dice" | translate }}{{ damage.diceType }}
+        </span>
         <span>
           <icon
-            [size]="'xs'"
+            [size]="'m'"
             [type]="'damage'"
             [icon]="damage.damageType"
           ></icon>
@@ -157,7 +175,10 @@
     }
     @if (spell.doesHeal) {
       <div class="heal">
-        <span>{{ spell.heal.diceNumber }} {{ spell.heal.diceType }} </span>
+        <span
+          >{{ spell.heal.diceNumber }} {{ "general.dice" | translate
+          }}{{ spell.heal.diceType }}
+        </span>
         <span *ngIf="spell.heal.additionalHeal"
           >+{{ spell.heal.additionalHeal }}
         </span>
@@ -173,10 +194,13 @@
 <ng-template #spellRangeTemplate let-spell="spell">
   <div class="spell-range">
     <div *ngIf="spell.isRanged">{{ spell.range }} ft.</div>
-    <div *ngIf="!spell.isRanged">Berührung</div>
+    <div *ngIf="!spell.isRanged">{{ "spells.touch" | translate }}</div>
 
     <div *ngIf="spell.hasAreaOfEffect">
-      <span>{{ spell.diameter }} ft. {{ areas[spell.areaOfEffectType] }} </span>
+      <span
+        >{{ spell.radius }} ft.
+        {{ "areaTypes." + spell.areaOfEffectType | translate }}</span
+      >
     </div>
   </div>
 </ng-template>

+ 1 - 1
src/app/journal/journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component.scss

@@ -9,7 +9,7 @@
   flex: 0 0 3rem;
   padding: 10px 2rem;
   display: grid;
-  grid-template-columns: 6fr 18fr 11fr 8fr 20fr 16fr;
+  grid-template-columns: 6fr 18fr 13fr 5fr 20fr 16fr;
   text-align: center;
   font-weight: 700;
 }

+ 5 - 19
src/app/journal/journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component.ts

@@ -1,6 +1,7 @@
 import { Component, Input } from '@angular/core';
 import { Spell } from 'src/interfaces/spell';
 import { ModalService } from 'src/services/modal/modal.service';
+import { TranslateService } from '@ngx-translate/core';
 
 @Component({
   selector: 'app-favorite-spells-modal',
@@ -15,25 +16,10 @@ export class FavoriteSpellsModalComponent {
 
   checkedSpells: boolean[] = [];
 
-  public attributes: any = {
-    strength: 'STR',
-    dexterity: 'DEX',
-    constitution: 'CON',
-    intelligence: 'INT',
-    wisdom: 'WIS',
-    charisma: 'CHA',
-  };
-
-  public areas: any = {
-    cone: 'Kegel',
-    sphere: 'Kugel',
-    circle: 'Kreis',
-    line: 'Linie',
-    square: 'Quadrat',
-    cube: 'Würfel',
-  };
-
-  public constructor(private modalAccessor: ModalService) {}
+  public constructor(
+    private modalAccessor: ModalService,
+    public translate: TranslateService,
+  ) {}
 
   public ngOnInit(): void {
     this.checkedSpells = Array(this.preparedSpells.length).fill(false);

+ 4 - 2
src/app/journal/journal-stats/weapons-container/spell-table/spell-table.component.html

@@ -85,7 +85,7 @@
   <ng-template #costTemplate let-spell="spell">
     <div class="bold">
       <span *ngIf="spell.cost === 'action'">A</span>
-      <span *ngIf="spell.cost === 'bonus action'">B</span>
+      <span *ngIf="spell.cost === 'bonus'">B</span>
       <span *ngIf="spell.cost === 'reaction'">R</span>
     </div>
   </ng-template>
@@ -121,7 +121,9 @@
   <!-- Level -->
   <ng-template #spellLevelTemplate let-spell="spell">
     <div *ngIf="spell.level !== 0" class="bold">{{ spell.level }}</div>
-    <div *ngIf="spell.level === 0" class="bold">Trick</div>
+    <div *ngIf="spell.level === 0" class="bold">
+      {{ "spells.cantrip" | translate }}
+    </div>
   </ng-template>
 
   <!-- Attack -->

+ 2 - 2
src/app/journal/journal-stats/weapons-container/spell-table/spell-table.component.scss

@@ -2,7 +2,7 @@
   flex: 0 0 3rem;
   padding: 10px 0.625rem;
   display: grid;
-  grid-template-columns: 4fr 17fr 9fr 8fr 19fr 16fr;
+  grid-template-columns: 1.75fr 6fr 3.75fr 2.25fr 5fr 3.25fr;
   text-align: center;
   font-weight: 700;
   box-shadow: var(--shadow-bottom);
@@ -22,7 +22,7 @@
   margin: 15px 10px;
   color: rgba(0, 0, 0, 0.87);
   display: grid;
-  grid-template-columns: 6fr 0.1fr 20fr 0.1fr 10fr 0.1fr 8fr 0.1fr 20fr 0.1fr 16fr;
+  grid-template-columns: 1.75fr 1px 6fr 1px 3.75fr 1px 2.25fr 1px 5fr 1px 3.25fr;
   align-items: center;
   justify-content: space-between;
   box-sizing: border-box;

+ 37 - 0
src/app/journal/journal-stats/weapons-container/weapon-table/weapon-modal/weapon-modal.component.scss

@@ -1,3 +1,5 @@
+@import "../../../../../../responsive";
+
 .checkbox-row {
   display: flex;
   flex-direction: row;
@@ -98,3 +100,38 @@ div.nav-pills.flex-column.nav {
     opacity: 0.5;
   }
 }
+
+// Responsive
+
+hr {
+  margin-top: 0.5rem;
+  margin-bottom: 0.5rem;
+
+  @include height-small {
+    margin-top: 1.5rem;
+    margin-bottom: 1.5rem;
+  }
+}
+
+.horizontal-buttons {
+  padding: 1rem 0;
+
+  @include height-small {
+    padding: 2rem 0;
+  }
+}
+
+.title {
+  margin-top: 1rem;
+  @include height-small {
+    margin-top: 2rem;
+  }
+}
+
+.content {
+  margin-top: 1rem;
+
+  @include height-small {
+    margin-top: 1.5rem;
+  }
+}

+ 1 - 8
src/app/journal/journal-stats/weapons-container/weapons-container.component.scss

@@ -3,12 +3,9 @@
   background-color: var(--field-background-color);
   box-shadow: var(--shadow);
   border-radius: 10px;
-  height: 35.5rem;
   display: flex;
   flex-direction: column;
-  @media (height < 934px) {
-    height: 31.5rem;
-  }
+  height: 100%;
 }
 
 .tab-button {
@@ -55,7 +52,3 @@ spell-table {
   // 100% - tabbar height
   height: calc(100% - 3rem);
 }
-
-// .button-margin {
-//   margin: 1rem 0rem;
-// }

+ 1 - 1
src/app/journal/journal-stats/weapons-container/weapons-container.component.ts

@@ -1,6 +1,6 @@
 import { Component, ViewChild } from '@angular/core';
 @Component({
-  selector: 'app-weapons-container',
+  selector: 'weapons-container',
   templateUrl: './weapons-container.component.html',
   styleUrls: ['./weapons-container.component.scss'],
 })

+ 13 - 19
src/app/journal/journal.module.ts

@@ -26,18 +26,16 @@ import { JournalInventoryComponent } from './journal-inventory/journal-inventory
 import { JournalSpellbookComponent } from './journal-spellbook/journal-spellbook.component';
 import { JournalSpellcardsComponent } from './journal-spellcards/journal-spellcards.component';
 import { JournalCharacterComponent } from './journal-character/journal-character.component';
-import { AttributeFieldComponent } from './journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-field.component';
-import { SkillFieldComponent } from './journal-stats/attribute-skill-container/skill-panel/skill-field/skill-field.component';
-import { AttributeSkillContainerComponent } from './journal-stats/attribute-skill-container/attribute-skill-container.component';
-import { LifeContainerComponent } from './journal-stats/life-container/life-container.component';
+import { AttributeFieldComponent } from './journal-stats/attribute-panel/attribute-field/attribute-field.component';
+import { SkillFieldComponent } from './journal-stats/skill-panel/skill-field/skill-field.component';
 import { WeaponsContainerComponent } from './journal-stats/weapons-container/weapons-container.component';
 import { ArmorClassComponent } from './journal-stats/info-row/armor-class/armor-class.component';
 import { MovementComponent } from './journal-stats/info-row/movement/movement.component';
-import { HitDiceComponent } from './journal-stats/life-container/hit-dice/hit-dice.component';
+import { HitDiceComponent } from './journal-stats/life/hit-dice/hit-dice.component';
 import { InitiativeComponent } from './journal-stats/info-row/initiative/initiative.component';
 import { ConditionsDetailsComponent } from './journal-stats/info-row/conditions/conditions-details/conditions-details.component';
-import { DeathSaveComponent } from './journal-stats/info-row/death-save/death-save.component';
-import { LifeComponent } from './journal-stats/life-container/life/life.component';
+import { DeathSaveComponent } from './journal-stats/life/death-save/death-save.component';
+import { LifeComponent } from './journal-stats/life/life.component';
 import { IconComponent } from './icon/icon.component';
 import { WeaponTableComponent } from './journal-stats/weapons-container/weapon-table/weapon-table.component';
 import { WeaponModalComponent } from './journal-stats/weapons-container/weapon-table/weapon-modal/weapon-modal.component';
@@ -52,7 +50,7 @@ import { SpellslotsComponent } from './journal-stats/ability-panel/spellslots/sp
 import { SpellslotsModalComponent } from './journal-stats/ability-panel/spellslots/spellslots-modal/spellslots-modal.component';
 import { InfoRowComponent } from './journal-stats/info-row/info-row.component';
 import { ConditionsComponent } from './journal-stats/info-row/conditions/conditions.component';
-import { AttributeDetailsComponent } from './journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-details/attribute-details.component';
+import { AttributeDetailsComponent } from './journal-stats/attribute-panel/attribute-field/attribute-details/attribute-details.component';
 import { SimpleItemDetailsComponent } from './journal-inventory/simple-item-details/simple-item-details.component';
 import { ModalComponent } from './journal-home/modal/modal.component';
 
@@ -62,20 +60,19 @@ import { ToolsModalComponent } from './journal-stats/ability-panel/proficiencies
 import { TraitDetailsComponent } from './journal-stats/ability-panel/trait-table/trait-details/trait-details.component';
 import { DetailsPanelComponent } from './journal-home/details-panel/details-panel.component';
 import { AbilityDetailsComponent } from './journal-stats/ability-panel/ability-table/ability-details/ability-details.component';
-import { LifeDetailsComponent } from './journal-stats/life-container/life/life-details/life-details.component';
+import { LifeDetailsComponent } from './journal-stats/life/life-details/life-details.component';
 import { WeaponDetailsComponent } from './journal-stats/weapons-container/weapon-table/weapon-details/weapon-details.component';
-import { AttributePanelComponent } from './journal-stats/attribute-skill-container/attribute-panel/attribute-panel.component';
-import { SkillPanelComponent } from './journal-stats/attribute-skill-container/skill-panel/skill-panel.component';
-import { SaveThrowPanelComponent } from './journal-stats/attribute-skill-container/save-throw-panel/save-throw-panel.component';
-import { SaveThrowFieldComponent } from './journal-stats/attribute-skill-container/save-throw-panel/save-throw-field/save-throw-field.component';
+import { AttributePanelComponent } from './journal-stats/attribute-panel/attribute-panel.component';
+import { SkillPanelComponent } from './journal-stats/skill-panel/skill-panel.component';
+import { SaveThrowPanelComponent } from './journal-stats/save-throw-panel/save-throw-panel.component';
+import { SaveThrowFieldComponent } from './journal-stats/save-throw-panel/save-throw-field/save-throw-field.component';
 import { ProficiencyFieldComponent } from './journal-stats/info-row/proficiency/proficiency-field.component';
-import { SaveThrowDetailsComponent } from './journal-stats/attribute-skill-container/save-throw-panel/save-throw-details/save-throw-details.component';
-import { SkillDetailsComponent } from './journal-stats/attribute-skill-container/skill-panel/skill-details/skill-details.component';
+import { SaveThrowDetailsComponent } from './journal-stats/save-throw-panel/save-throw-details/save-throw-details.component';
+import { SkillDetailsComponent } from './journal-stats/skill-panel/skill-details/skill-details.component';
 import { ArmorClassDetailsComponent } from './journal-stats/info-row/armor-class/armor-class-details/armor-class-details.component';
 import { InitiativeDetailsComponent } from './journal-stats/info-row/initiative/initiative-details/initiative-details.component';
 import { MovementDetailsComponent } from './journal-stats/info-row/movement/movement-details/movement-details.component';
 import { ProficiencyDetailsComponent } from './journal-stats/info-row/proficiency/proficiency-details/proficiency-details.component';
-import { DeathSaveDetailsComponent } from './journal-stats/info-row/death-save/death-save-details/death-save-details.component';
 import { ExhaustionDetailsComponent } from './journal-stats/info-row/conditions/exhaustion-details/exhaustion-details.component';
 import { NavigationPanelComponent } from './journal-home/navigation-panel/navigation-panel.component';
 import { JournalNotesComponent } from './journal-notes/journal-notes.component';
@@ -110,8 +107,6 @@ import { CombinedComponent } from './journal-character/combined/combined.compone
     JournalCharacterComponent,
     AttributeFieldComponent,
     SkillFieldComponent,
-    AttributeSkillContainerComponent,
-    LifeContainerComponent,
     WeaponsContainerComponent,
     ArmorClassComponent,
     MovementComponent,
@@ -152,7 +147,6 @@ import { CombinedComponent } from './journal-character/combined/combined.compone
     InitiativeDetailsComponent,
     MovementDetailsComponent,
     ProficiencyDetailsComponent,
-    DeathSaveDetailsComponent,
     ExhaustionDetailsComponent,
     NavigationPanelComponent,
     JournalNotesComponent,

+ 37 - 0
src/app/journal/spell-modal/spell-modal.component.scss

@@ -1,3 +1,5 @@
+@import "../../../responsive";
+
 .checkbox-column {
   width: 33.3%;
   display: flex;
@@ -121,3 +123,38 @@ div.nav-pills.flex-column.nav {
     opacity: 0.5;
   }
 }
+
+// Responsive
+
+hr {
+  margin-top: 0.5rem;
+  margin-bottom: 0.5rem;
+
+  @include height-medium {
+    margin-top: 1.5rem;
+    margin-bottom: 1.5rem;
+  }
+}
+
+.horizontal-buttons {
+  padding: 1.5rem 0;
+
+  @include height-small {
+    padding: 2rem 0;
+  }
+}
+
+.title {
+  margin-top: 1.5rem;
+  @include height-small {
+    margin-top: 2rem;
+  }
+}
+
+.content {
+  margin-top: 1rem;
+
+  @include height-small {
+    margin-top: 1.5rem;
+  }
+}

+ 15 - 12
src/assets/i18n/de.json

@@ -492,16 +492,9 @@
     "1": "Nachteil auf Attributwürfe",
     "2": "Bewegungsrate halbiert",
     "3": "Nachteil auf Angriffs- und Rettungswürfe",
-    "4": "Bewegungsrate fällt auf 0",
-    "5": "Sofortiger Tod"
-  },
-  "deathSave": {
-    "label": "Todesrettungswürfe",
-    "success": "Erfolg",
-    "fail": "Fehlschlag",
-    "description": "Immer wenn ein Charakter seinen Zug mit 0 Trefferpunkten beginnt und nicht stabilisiert wurde, muss er einen Todesrettungswurf ablegen. Ist das Ergebnis 10 oder höher, ist der Wurf erfolgreich, ansonsten fehlgeschlagen. Beim dritten Erfolg wird der Charakter stabilisiert, beim dritten Fehlschlag stirbt er.",
-    "criticalHeader": "Eine 1 oder 20 würfeln",
-    "criticalContent": "Würfelt ein Charakter eine 1, zählt der Wurf als zwei Fehlschläge. Würfelt er eine 20, erhält er sofort einen Lebenspunkt dazu und ist nicht mehr bewustlos."
+    "4": "Maximale Trefferpunkte halbiert",
+    "5": "Bewegungsrate fällt auf 0",
+    "6": "Sofortiger Tod"
   },
   "life": {
     "hitpoints": "Trefferpunkte",
@@ -509,7 +502,13 @@
     "temporary": "Temporäre Trefferpunkte",
     "current": "Aktuelle Trefferpunkte",
     "hitdice": "Trefferwürfel",
-    "hitdiceDescription": "Trefferwürfel werden verwendet, um bei einer kurzen Rast Trefferpunkte wiederherzustellen. Du kannst eine beliebige Anzahl deiner verfügbaren Trefferwürfel verwenden. Sie werden bei einer langen rast wiederhergestellt."
+    "hitdiceDescription": "Trefferwürfel werden verwendet, um bei einer kurzen Rast Trefferpunkte wiederherzustellen. Du kannst eine beliebige Anzahl deiner verfügbaren Trefferwürfel verwenden. Sie werden bei einer langen rast wiederhergestellt.",
+    "deathSave": "Todesrettungswürfe",
+    "success": "Erfolg",
+    "fail": "Fehlschlag",
+    "description": "Immer wenn ein Charakter seinen Zug mit 0 Trefferpunkten beginnt und nicht stabilisiert wurde, muss er einen Todesrettungswurf ablegen. Ist das Ergebnis 10 oder höher, ist der Wurf erfolgreich, ansonsten fehlgeschlagen. Beim dritten Erfolg wird der Charakter stabilisiert, beim dritten Fehlschlag stirbt er.",
+    "criticalHeader": "Eine 1 oder 20 würfeln",
+    "criticalContent": "Würfelt ein Charakter eine 1, zählt der Wurf als zwei Fehlschläge. Würfelt er eine 20, erhält er sofort einen Lebenspunkt dazu und ist nicht mehr bewustlos."
   },
   "weapons": {
     "header": {
@@ -565,6 +564,9 @@
       "material": "M",
       "ritual": "R"
     },
+    "favorite": "Ausgewählte Zauber",
+    "emptyFavorites": "Noch keine Zauber vorbereitet.<br /> Dies kannst du im Menü <i><b>Zauber</b></i> tun.",
+    "cantrip": "Trick",
     "touch": "Berührung",
     "empty": "Noch keine Zauber hinzugefügt",
     "concentrationAbbr": "K",
@@ -596,6 +598,7 @@
   "magic": {
     "spellCasting": "Zaubern",
     "ki": "Ki-Punkte",
+    "spell": "Zaubern",
     "spellslots": "Zauberplätze",
     "empty": "Noch keine Zauberplätze hinzugefügt",
     "spellcastingAttribute": "Zauberwirken-Attribut",
@@ -767,6 +770,6 @@
     "hint": "Die App befindet sich immer noch in einem Entwicklungsstadium und es können Fehler auftreten",
     "issues": "<p>Fehler und Anmerkungen bitte auf dem <a href='https://gogs.koljastrohm-games.com/Warafear/DNDTools/issues'>Git-Server in Issues</a> vermerken.<p>",
     "okay": "Verstanden",
-    "version": "0.9.5"
+    "version": "0.10.0"
   }
 }

+ 768 - 766
src/assets/i18n/en.json

@@ -1,771 +1,773 @@
 {
-    "general": {
-        "value": "Value",
-        "modifier": "Modifier",
-        "modifiers": "Modifiers",
-        "attributeValue": "Attribute Value",
-        "feet": "Feet",
-        "fields": "Fields",
-        "meter": "Meter",
-        "level": "Level",
-        "effect": "Effect",
-        "diceNumber": "Dice Number",
-        "diceType": "Dice Type",
-        "dice": "D",
-        "weapons": "Weapons",
-        "spells": "Spells",
-        "property": "Property",
-        "properties": "Properties",
-        "description": "Description",
-        "damageType": "Damage Type",
-        "german": "German",
-        "english": "English",
-        "light": "Light",
-        "dark": "Dark",
-        "round": "Round(s)",
-        "yes": "Yes",
-        "no": "No"
-    },
-    "genders": {
-        "male": "Male",
-        "female": "Female",
-        "diverse": "Diverse"
-    },
-    "backgrounds": {
-        "acolyte": "Acolyte",
-        "charlatan": "Charlatan",
-        "criminal": "Criminal",
-        "entertainer": "Entertainer",
-        "folkHero": "Folk Hero",
-        "guildArtisan": "Guild Artisan",
-        "hermit": "Hermit",
-        "noble": "Noble",
-        "outlander": "Outlander",
-        "sage": "Sage",
-        "sailor": "Sailor",
-        "soldier": "Soldier",
-        "urchin": "Urchin"
-    },
+  "general": {
+    "value": "Value",
+    "modifier": "Modifier",
+    "modifiers": "Modifiers",
+    "attributeValue": "Attribute Value",
+    "feet": "Feet",
+    "fields": "Fields",
+    "meter": "Meter",
+    "level": "Level",
+    "effect": "Effect",
+    "diceNumber": "Dice Number",
+    "diceType": "Dice Type",
+    "dice": "D",
+    "weapons": "Weapons",
+    "spells": "Spells",
+    "property": "Property",
+    "properties": "Properties",
+    "description": "Description",
+    "damageType": "Damage Type",
+    "german": "German",
+    "english": "English",
+    "light": "Light",
+    "dark": "Dark",
+    "round": "Round(s)",
+    "yes": "Yes",
+    "no": "No"
+  },
+  "genders": {
+    "male": "Male",
+    "female": "Female",
+    "diverse": "Diverse"
+  },
+  "backgrounds": {
+    "acolyte": "Acolyte",
+    "charlatan": "Charlatan",
+    "criminal": "Criminal",
+    "entertainer": "Entertainer",
+    "folkHero": "Folk Hero",
+    "guildArtisan": "Guild Artisan",
+    "hermit": "Hermit",
+    "noble": "Noble",
+    "outlander": "Outlander",
+    "sage": "Sage",
+    "sailor": "Sailor",
+    "soldier": "Soldier",
+    "urchin": "Urchin"
+  },
+  "modal": {
+    "name": "Name",
+    "shortDescription": "Short Description",
+    "shortHint": "Will be shown in overview",
+    "longDescription": "Detailed Description",
+    "longHint": "Will be shown in detail view",
+    "cost": "Cost:",
+    "uses": "Uses"
+  },
+  "buttons": {
+    "add": "Add",
+    "apply": "Apply",
+    "addEntry": "Add Entry",
+    "confirm": "Confirm",
+    "modify": "Modify",
+    "save": "Save",
+    "edit": "Edit",
+    "delete": "Delete",
+    "remove": "Remove",
+    "cancel": "Cancel",
+    "close": "Close",
+    "discard": "Discard",
+    "export": "Export",
+    "deleteSelected": "Delete Selected"
+  },
+  "duration": {
+    "rounds": "Round(s)",
+    "minutes": "Minute(s)",
+    "hours": "Hour(s)",
+    "days": "Day(s)",
+    "instantaneous": "Instantaneous"
+  },
+  "cost": {
+    "none": "None",
+    "action": "Action",
+    "bonus": "Bonus Action",
+    "reaction": "Reaction",
+    "ritual": "Ritual"
+  },
+  "uses": {
+    "0": "Umlimited",
+    "1": "1",
+    "2": "2",
+    "3": "3",
+    "4": "4",
+    "5": "5",
+    "6": "6",
+    "7": "7",
+    "8": "8",
+    "9": "9",
+    "10": "10"
+  },
+  "damageTypes": {
+    "acid": "Acid",
+    "bludgeoning": "Bludgeoning",
+    "cold": "Cold",
+    "fire": "Fire",
+    "force": "Force",
+    "lightning": "Lightning",
+    "necrotic": "Necrotic",
+    "piercing": "Piercing",
+    "poison": "Poison",
+    "psychic": "Psychic",
+    "radiant": "Radiant",
+    "slashing": "Slashing"
+  },
+  "areaTypes": {
+    "areaType": "Area Type",
+    "cone": "Cone",
+    "cube": "Cube",
+    "cylinder": "Cylinder",
+    "line": "Line",
+    "square": "Square",
+    "sphere": "Sphere",
+    "circle": "Circle"
+  },
+  "schools": {
+    "abjuration": "Abjuration",
+    "conjuration": "Conjuration",
+    "divination": "Divination",
+    "enchantment": "Enchantment",
+    "evocation": "Evocation",
+    "illusion": "Illusion",
+    "necromancy": "Necromancy",
+    "transmutation": "Transmutation"
+  },
+  "species": {
+    "human": "Human",
+    "elf": "Elf",
+    "dwarf": "Dwarf",
+    "halfling": "Halfling",
+    "gnome": "Gnome",
+    "halfElf": "Half-Elf",
+    "halfOrc": "Half-Orc",
+    "tiefling": "Tiefling",
+    "dragonborn": "Dragonborn",
+    "aarakocra": "Aarakocra",
+    "aasimar": "Asimar",
+    "autognome": "Autognome",
+    "bugbear": "Bugbear",
+    "centaur": "Centaur",
+    "changeling": "Changeling",
+    "duergar": "Duergar",
+    "drow": "Drow",
+    "eladrin": "Eladrin",
+    "halfElfDetection": "Half-Elf (Mark Of Detection)",
+    "fairy": "Fairy",
+    "firbolg": "Firbolg",
+    "genasi": "Genasi",
+    "gith": "Gith",
+    "goblin": "Goblin",
+    "goliath": "Goliath",
+    "hobgoblin": "Hobgoblin",
+    "kalashtar": "Kalashtar",
+    "kenku": "Kenku",
+    "kobold": "Kobold",
+    "leonin": "Leonin",
+    "lizardfolk": "Lizardfolk",
+    "locathah": "Locathah",
+    "loxodon": "Loxodon",
+    "minotaur": "Minotaur",
+    "orc": "Orc",
+    "owlin": "Owlin",
+    "satyr": "Satyr",
+    "shifter": "Shifter",
+    "tabaxi": "Tabaxi",
+    "simicHybrid": "Simic-Hybrid",
+    "tortle": "Tortle",
+    "triton": "Triton",
+    "vedalken": "Vedalken",
+    "verdan": "Verdan",
+    "warforged": "Warforged",
+    "yuanTi": "Yuan-Ti"
+  },
+  "classes": {
+    "barbarian": "Barbarian",
+    "bard": "Bard",
+    "cleric": "Cleric",
+    "druid": "Druid",
+    "fighter": "Fighter",
+    "monk": "Monk",
+    "paladin": "Paladin",
+    "ranger": "Ranger",
+    "rogue": "Rogue",
+    "sorcerer": "Sorcerer",
+    "warlock": "Warlock",
+    "wizard": "Wizard",
+    "test": "Test"
+  },
+  "subclasses": {
+    "empty": "No subclass chosen",
+    "hint": " Here you can choose your subclass. The subclass is a special specialization of your class. It gives you additional features and traits.",
+    "barbarian": {
+      "berserker": "Berserker"
+    },
+    "bard": {
+      "lore": "Lore",
+      "valor": "Valor"
+    },
+    "cleric": {
+      "peaceDomain": "Peace Domain"
+    },
+    "druid": {
+      "land": "Circle of the Land",
+      "moon": "Circle of the Moon"
+    },
+    "fighter": {
+      "champion": "Champion",
+      "battleMaster": "Battle Master",
+      "eldritchKnight": "Eldritch Knight",
+      "echoKnight": "Echo Knight"
+    },
+    "monk": {
+      "openHand": "Way of the Open Hand",
+      "wayOfShadows": "Way of Shadow",
+      "fourElements": "Way of the Four Elements"
+    },
+    "paladin": {
+      "devotion": "Oath of Devotion",
+      "ancients": "Oath of the Ancients",
+      "vengeance": "Oath of Vengeance",
+      "crown": "Oath of the Crown",
+      "conquest": "Oath of Conquest",
+      "redemption": "Oath of Redemption"
+    },
+    "ranger": {
+      "hunter": "Hunter",
+      "beastMaster": "Beast Master",
+      "gloomStalker": "Gloom Stalker"
+    },
+    "rogue": {
+      "thief": "Thief",
+      "assassin": "Assassin",
+      "arcaneTrickster": "Arcane Trickser",
+      "mastermind": "Mastermind",
+      "swashbuckler": "Swashbuckler"
+    },
+    "sorcerer": {
+      "draconic": "Draconic Bloodline",
+      "wildMagic": "Wild Magic",
+      "phoenixSorcery": "Phoenix Sorcery",
+      "shadowMagic": "Shadow Magic"
+    },
+    "warlock": {
+      "fiend": "The Fiend",
+      "archfey": "The Archfey",
+      "greatOldOne": "The Great Old One",
+      "celestial": "The Celestial",
+      "hexblade": "The Hexblade"
+    },
+    "wizard": {
+      "abjuration": "School of Abjuration",
+      "conjuration": "School of Conjuration",
+      "divination": "School of Divination",
+      "enchantment": "School of Enchantment",
+      "evocation": "School of Evocation",
+      "illusion": "School of Illusion",
+      "necromancy": "School of Necromancy"
+    }
+  },
+  "attributes": {
+    "value": "Value",
+    "modifier": "Modifier",
+    "strength": "Strength",
+    "dexterity": "Dexterity",
+    "constitution": "Constitution",
+    "intelligence": "Intelligence",
+    "wisdom": "Wisdom",
+    "charisma": "Charisma"
+  },
+  "attributesAbbreviations": {
+    "strength": "STR",
+    "dexterity": "DEX",
+    "constitution": "CON",
+    "intelligence": "INT",
+    "wisdom": "WIS",
+    "charisma": "CHA"
+  },
+  "attributeDetails": {
+    "affectedSkills": "Affected Skills:",
+    "strength": "Strength measures bodily power, athletic training, and the extent to which you can exert raw physical force. A high strength value indicates physical robustness, muscular strength and assertiveness. Characters with high strength are often better able to carry heavy loads, wield powerful weapons and overcome physical challenges.",
+    "dexterity": "Dexterity represents your character's agility, reflexes and general body control. A high dexterity value indicates quick reactions, skillful movements and good hand-eye coordination. Characters with high dexterity are often skilled thieves, archers or acrobats.",
+    "constitution": "Constitution stands for your character's resilience, health and stamina. A high constitution value means that your character is robust and can cope well with physical stress. Characters with a high constitution often have a higher health score and are more resistant to diseases and poison.",
+    "intelligence": "Intelligence measures your character's mental acuity, logical thinking and learning ability. A high intelligence score indicates a good general education, a thirst for knowledge and analytical skills. Characters with high intelligence are often mages, researchers or strategists.",
+    "wisdom": "Wisdom represents your character's perception, intuition and emotional intelligence. A high Wisdom score indicates good judgment, insight and inner strength. Characters with high wisdom are often clerics, druids or sages.",
+    "charisma": "Charisma reflects your character's charisma, persuasiveness and social skills. A high charisma score means that your character is charming, engaging and persuasive. Characters with high charisma are often better at communicating with others, persuading and taking on leadership roles. Charisma is often important for bards, leaders or diplomats.",
+    "strengths": "Resistances and Vulnerabilities"
+  },
+  "resistances": {
+    "resistances": "Resistances",
+    "immunities": "Immunities",
+    "vulnerabilities": "Vulnerabilities"
+  },
+  "skills": {
+    "acrobatics": "Acrobatics",
+    "animalHandling": "Animal Handling",
+    "arcana": "Arcana",
+    "athletics": "Athletics",
+    "deception": "Deception",
+    "history": "History",
+    "insight": "Insight",
+    "intimidation": "Intimidation",
+    "investigation": "Investigation",
+    "medicine": "Medicine",
+    "nature": "Nature",
+    "perception": "Perception",
+    "performance": "Performance",
+    "persuasion": "Persuasion",
+    "religion": "Religion",
+    "sleightOfHand": "Sleight of Hand",
+    "stealth": "Stealth",
+    "survival": "Survival"
+  },
+  "skillsDescription": {
+    "acrobatics": "Acrobatics rolls cover all attempts in which a character tries to stay on their feet in difficult situations. This includes, for example, balancing on a rope, running over unstable surfaces, balancing on a narrow ledge and jumping over obstacles.",
+    "animalHandling": "Animal Handling rolls cover all attempts to calm a domesticated animal, keep a mount from shying, or intuitively understand how a wild animal is feeling and what its intentions are.",
+    "arcana": "Arcana rolls cover all attempts to identify, understand or manipulate magical energy.",
+    "athletics": "Athletics rolls cover all attempts to test a character's physical strength. This includes, for example, climbing, swimming and jumping.",
+    "deception": "Rolls for Deception indicate whether a character can convincingly conceal the truth, whether verbally or through concrete actions. This includes, for example, lying, hiding emotions behind a mask and disguising oneself.",
+    "history": "Dice on history cover all attempts to remember historical events and people, past ages, ancient empires and forgotten cultures.",
+    "insight": "Insight rolls cover all attempts to recognize the true intentions of another person. This includes, for example, recognizing lies, interpreting body language and predicting their next moves.",
+    "intimidation": "Rolls for Intimidation cover all attempts to influence another person through threats, obvious violence or subtle hints.",
+    "investigation": "Investigation rolls cover all attempts to find and interpret clues that point to previous activity. This includes, for example, reconstructing the course of events or tracking down hidden objects.",
+    "medicine": "Medicine rolls cover all attempts to stabilize a character, heal an illness or dress a wound.",
+    "nature": "Nature rolls cover all attempts to recognize the characteristics of wild animals, plants and other creatures.",
+    "perception": "Perception rolls cover all attempts to observe the surroundings and notice what is nearby. This includes, for example, recognizing hidden enemies, listening to conversations behind a door, discovering clues and observing dangers.",
+    "performance": "Performance rolls cover all attempts to entertain an audience, be it through music, dancing, acting or other forms of entertainment.",
+    "persuasion": "Rolls for Persuasion cover all attempts to influence another person through persuasion, logic or argumentation.",
+    "religion": "Rolls for religion cover all attempts to understand religious rituals, recognize religious symbols and interpret religious traditions.",
+    "sleightOfHand": "Sleight of Hand rolls cover all attempts to steal something, perform a trick or carry out an action without anyone noticing.",
+    "stealth": "Stealth rolls cover all attempts to hide, move quietly and conceal oneself.",
+    "survival": "Survival rolls cover all attempts to survive in the wilderness. This includes, for example, hunting and navigating through the wilderness."
+  },
+  "skillsAttributes": {
+    "acrobatics": "dexterity",
+    "animalHandling": "wisdom",
+    "arcana": "intelligence",
+    "athletics": "strength",
+    "deception": "charisma",
+    "history": "intelligence",
+    "insight": "wisdom",
+    "intimidation": "charisma",
+    "investigation": "intelligence",
+    "medicine": "wisdom",
+    "nature": "wisdom",
+    "perception": "wisdom",
+    "performance": "charisma",
+    "persuasion": "charisma",
+    "religion": "intelligence",
+    "sleightOfHand": "dexterity",
+    "stealth": "dexterity",
+    "survival": "wisdom"
+  },
+  "skillDetails": {
+    "title": "Permanent (Dis-)Advantages",
+    "advantage": "Advantage",
+    "disadvantage": "Disadvantage",
+    "none": "None"
+  },
+  "savingThrow": {
+    "subheading": "Saving Throw",
+    "content": " A saving throw is always required when checking whether a player can resist an attack or external influence. Passing a saving throw can prevent or at least weaken the effect."
+  },
+  "armorClass": {
+    "label": "Armor Class",
+    "shortLabel": "Armor",
+    "description": "The armor class is used to determine whether an attack is successful or not. An attacker must make a dice roll (usually a D20) and achieve at least the armor class of the target. If the roll is successful, the attack causes damage, otherwise the attack is blocked."
+  },
+  "initiative": {
+    "label": "Initiative",
+    "description": "The initiative bonus is used to determine the initiative of the current battle or a time-critical situation. Each character rolls a d20 and adds up their initiative bonus. The character with the highest result may act first."
+  },
+  "movement": {
+    "label": "Movement",
+    "shortLabel": "Movement",
+    "movement": "Walking",
+    "translationTable": "Translation Table",
+    "description": "The movement speed indicates how far a character can walk per round. The movement speed is given in feet, with 5 feet corresponding to 1.5 meters and therefore one field. The movement can be used in one piece or divided into several parts. In difficult terrain, the movement speed is halved.",
+    "jumping": "Jumping",
+    "jumpingDescription": "A character can jump based on their strength. If you have moved 10 feet (2 fields) before jumping, you can jump twice the distance."
+  },
+  "proficiency": {
+    "label": "Proficiency",
+    "shortLabel": "Proficiency",
+    "description": "The proficiency bonus is added to all save, attack and skill rolls in which a player is proficient in. The proficiency bonus increases with the character level."
+  },
+  "conditions": {
+    "label": "Conditions",
+    "shortLabel": "Consition",
+    "noConditions": "No Conditions",
+    "addCondition": "Add Condition",
+    "currentConditions": "Current Conditions",
+    "description": "Conditions change a creature's abilities in various ways and can be the result of spells, class features, monster attacks or other abilities. The condition lasts until it is canceled or until its duration expires.",
+    "blinded": "Blinded",
+    "charmed": "Charmed",
+    "deafened": "Deafened",
+    "frightened": "Frightened",
+    "grappled": "Grappled",
+    "incapacitated": "Incapacitated",
+    "invisible": "Invisible",
+    "paralyzed": "Paralyzed",
+    "petrified": "Petrified",
+    "poisoned": "Poisoned",
+    "prone": "Prone",
+    "restrained": "Retrained",
+    "stunned": "Stunned",
+    "unconscious": "Unconscious",
+    "conditionDescriptions": {
+      "stunned": [
+        "A stunned creature is incapacitated, can't move, and can speak only falteringly.",
+        "The creature automatically fails Strength and Dexterity saving throws.",
+        "Attack rolls against the creature have advantage."
+      ],
+      "unconscious": [
+        "An unconscious creature is incapacitated, can't move or speak, and is unaware of its surroundings.",
+        "The creature drops whatever it's holding and falls prone.",
+        "The creature automatically fails Strength and Dexterity saving throws.",
+        "Attack rolls against the creature have advantage.",
+        "Any attack that hits the creature is a critical hit if the attacker is within 5 feet of the creature."
+      ],
+      "charmed": [
+        "A charmed creature can't attack the charmer or target the charmer with harmful abilities or magical effects.",
+        "The charmer has advantage on any ability check to interact socially with the creature."
+      ],
+      "blinded": [
+        "A blinded creature can't see and automatically fails any ability check that requires sight.",
+        "Attack rolls against the creature have advantage, and the creature's attack rolls have disadvantage."
+      ],
+      "restrained": [
+        "A restrained creature's speed becomes 0, and it can't benefit from any bonus to its speed.",
+        "Attack rolls against the creature have advantage, and the creature's attack rolls have disadvantage.",
+        "The creature has disadvantage on Dexterity saving throws."
+      ],
+      "paralyzed": [
+        "A paralyzed creature is incapacitated and can't move or speak.",
+        "The creature automatically fails Strength and Dexterity saving throws.",
+        "Attack rolls against the creature have advantage.",
+        "Any attack that hits the creature is a critical hit if the attacker is within 5 feet of the creature."
+      ],
+      "grappled": [
+        "A grappled creature's speed becomes 0, and it can't benefit from any bonus to its speed.",
+        "The condition ends if the grappler is incapacitated.",
+        "The condition also ends if an effect removes the grappled creature from the reach of the grappler or grappling effect, such as when a creature is hurled away by the thunderwave spell. "
+      ],
+      "incapacitated": [
+        "An incapacitated creature can't take actions or reactions."
+      ],
+      "prone": [
+        "A prone creature's only movement option is to crawl, unless it stands up and thereby ends the condition.",
+        "The creature has disadvantage on attack rolls.",
+        "An attack roll against the creature has advantage if the attacker is within 5 feet of the creature. Otherwise, the attack roll has disadvantage."
+      ],
+      "deafened": [
+        "A deafened creature can't hear and automatically fails any ability check that requires hearing."
+      ],
+      "invisible": [
+        "An invisible creature is impossible to see without the aid of magic or a special sense. For the purpose of hiding, the creature is heavily obscured. The creature's location can be detected by any noise it makes or any tracks it leaves.",
+        "Attack rolls against the creature have disadvantage, and the creature's attack rolls have advantage."
+      ],
+      "frightened": [
+        "A frightened creature has disadvantage on ability checks and attack rolls while the source of its fear is within line of sight.",
+        "The creature can't willingly move closer to the source of its fear."
+      ],
+      "poisoned": [
+        "    A poisoned creature has disadvantage on attack rolls and ability checks."
+      ],
+      "petrified": [
+        "A petrified creature is transformed, along with any nonmagical object it is wearing or carrying, into a solid inanimate substance (usually stone). Its weight increases by a factor of ten, and it ceases aging.",
+        "The creature is incapacitated, can't move or speak, and is unaware of its surroundings.",
+        "Attack rolls against the creature have advantage.",
+        "The creature automatically fails Strength and Dexterity saving throws.",
+        "The creature has resistance to all damage.",
+        "The creature is immune to poison and disease, although a poison or disease already in its system is suspended, not neutralized."
+      ]
+    }
+  },
+  "exhaustion": {
+    "label": "Exhaustion",
+    "description": "Some special abilities and environmental hazards, such as hunger or extreme temperatures, can lead to exhaustion. Each level adds a further penalty, so several penalties are possible at the same time. Exhaustion can be reduced by a long rest, for example.",
+    "currentExhaustion": "Current Exhaustion",
+    "0": "No Limitation",
+    "1": "Disadvantage on ability checks",
+    "2": "Speed halved",
+    "3": "Disadvantage on attack rolls and saving throws",
+    "4": "Hit point maximum halved",
+    "5": "Speed reduced to 0",
+    "6": "Death"
+  },
+  "life": {
+    "hitpoints": "Hit Points",
+    "max": "Maximum Hit Points",
+    "temporary": "Temporary Hit Points",
+    "current": "Current Hit Points",
+    "hitdice": "Hit Dice",
+    "hitdiceDescription": "Hit dice are used to restore hit points during a short rest. You can use any number of your available hit dice. They are restored during a long rest.",
+    "deathSave": "Death Saving Throws",
+    "success": "Success",
+    "fail": "Failure",
+    "description": "Whenever a character starts his turn with 0 hit points and has not been stabilized, he must make a death saving throw. If the result is 10 or higher, the roll is successful, otherwise it fails. On the third success the character is stabilized, on the third failure he dies.",
+    "criticalHeader": "Roll a 1 or 20",
+    "criticalContent": "If a character rolls a 1, the roll counts as two misses. If they roll a 20, they immediately gain one life point and are no longer unconscious."
+  },
+  "weapons": {
+    "header": {
+      "type": "Type",
+      "name": "Name",
+      "bonus": "Bonus",
+      "damage": "Damage",
+      "range": "Range"
+    },
+    "name": "Name",
+    "noWeapons": "No weapons added yet",
+    "meele": "Melee",
+    "ranged": "Ranged",
+    "attack": "Attack",
+    "single": "One-handed Damage",
+    "dual": "Two-handed Damage",
+    "proficient": "Proficient",
+    "versatile": "Versatile",
+    "oneHanded": "One-handed",
+    "twoHanded": "Two-handed",
+    "finesse": "Finesse",
+    "throwable": "Thrown",
+    "magical": "Magical",
+    "range": "Range",
+    "throwRange": "Throw Range",
     "modal": {
-        "name": "Name",
-        "shortDescription": "Short Description",
-        "shortHint": "Will be shown in overview",
-        "longDescription": "Detailed Description",
-        "longHint": "Will be shown in detail view",
-        "cost": "Cost:",
-        "uses": "Uses"
-    },
-    "buttons": {
-        "add": "Add",
-        "apply": "Apply",
-        "addEntry": "Add Entry",
-        "confirm": "Confirm",
-        "modify": "Modify",
-        "save": "Save",
-        "edit": "Edit",
-        "delete": "Delete",
-        "remove": "Remove",
-        "cancel": "Cancel",
-        "close": "Close",
-        "discard": "Discard",
-        "export": "Export",
-        "deleteSelected": "Delete Selected"
-    },
-    "duration": {
-        "rounds": "Round(s)",
-        "minutes": "Minute(s)",
-        "hours": "Hour(s)",
-        "days": "Day(s)",
-        "instantaneous": "Instantaneous"
-    },
-    "cost": {
-        "none": "None",
-        "action": "Action",
-        "bonus": "Bonus Action",
-        "reaction": "Reaction",
-        "ritual": "Ritual"
-    },
-    "uses": {
-        "0": "Umlimited",
-        "1": "1",
-        "2": "2",
-        "3": "3",
-        "4": "4",
-        "5": "5",
-        "6": "6",
-        "7": "7",
-        "8": "8",
-        "9": "9",
-        "10": "10"
-    },
-    "damageTypes": {
-        "acid": "Acid",
-        "bludgeoning": "Bludgeoning",
-        "cold": "Cold",
-        "fire": "Fire",
-        "force": "Force",
-        "lightning": "Lightning",
-        "necrotic": "Necrotic",
-        "piercing": "Piercing",
-        "poison": "Poison",
-        "psychic": "Psychic",
-        "radiant": "Radiant",
-        "slashing": "Slashing"
-    },
-    "areaTypes": {
-        "areaType": "Area Type",
-        "cone": "Cone",
-        "cube": "Cube",
-        "cylinder": "Cylinder",
-        "line": "Line",
-        "square": "Square",
-        "sphere": "Sphere",
-        "circle": "Circle"
-    },
-    "schools": {
-        "abjuration": "Abjuration",
-        "conjuration": "Conjuration",
-        "divination": "Divination",
-        "enchantment": "Enchantment",
-        "evocation": "Evocation",
-        "illusion": "Illusion",
-        "necromancy": "Necromancy",
-        "transmutation": "Transmutation"
+      "addWeapon": "Add Weapon",
+      "editWeapon": "Edit Weapon",
+      "applyModifier": "Apply Modifier",
+      "additionalDamage": "Additional Damage",
+      "attackBonus": "Attack Bonus",
+      "magicalModifier": "Magical Modifier",
+      "damage": "Damage",
+      "normalRange": "Normal Range",
+      "extendedRange": "Extended Range",
+      "normalThrowRange": "Normal Throw Range",
+      "extendedThrowRange": "Extended Throw Range",
+      "placeholder": "Description of the Weapon"
+    }
+  },
+  "spells": {
+    "header": {
+      "cost": "Cost",
+      "name": "Name",
+      "level": "Level",
+      "bonus": "Bonus",
+      "effect": "Effect",
+      "range": "Range"
+    },
+    "components": {
+      "verbal": "V",
+      "somatic": "S",
+      "material": "M",
+      "ritual": "R"
+    },
+    "favorite": "Favorite Spells",
+    "emptyFavorites": "No spells prepared yet.<br /> You can add the in the menu <i><b>Spells</b></i>.",
+    "cantrip": "Cantrip",
+    "touch": "Touch",
+    "empty": "No spells added yet",
+    "concentrationAbbr": "C",
+    "concentration": "Concentration"
+  },
+  "abilities": {
+    "label": "Features",
+    "empty": "No features added yet",
+    "uses": "Uses:",
+    "modal": {
+      "addAbility": "Add feature",
+      "editAbility": "Edit feature",
+      "shortPlaceholder": "Short description of the feature",
+      "longPlaceholder": "Detailed description of the feature"
+    }
+  },
+  "traits": {
+    "label": "Traits",
+    "empty": "No traits added yet",
+    "modal": {
+      "addTrait": "Add trait",
+      "editTrait": "Edit trait",
+      "shortPlaceholder": "Short description of the trait",
+      "shortHint": "Will be shown in overview",
+      "longPlaceholder": "Detailed description of the trait",
+      "longHint": "Will be shown in detail view"
+    }
+  },
+  "magic": {
+    "spellCasting": "Spell Casting",
+    "ki": "Ki Points",
+    "spell": "Spell",
+    "spellslots": "Spell Slots",
+    "empty": "No spell slots added yet",
+    "spellcastingAttribute": "Spell Casting Attribute",
+    "saveAC": "Save DC",
+    "spellAttackBonus": "Spell Attack Bonus",
+    "description": "The number of available spell slots per spell level can be adjusted here",
+    "showSlots": "Show spell slots in overview",
+    "showKi": "Show ki Points in overview",
+    "descriptioni": "The number of available ki points can be adjusted here",
+    "availableKi": "Available ki points",
+    "emptySpell": "No spell slots added yet",
+    "emptyKi": "No ki points added yet"
+  },
+  "proficiencies": {
+    "label": "Proficiencies",
+    "weapArm": "Weapons and Armor",
+    "lightArmor": "Light Armor",
+    "mediumArmor": "Medium Armor",
+    "heavyArmor": "Heavy Armor",
+    "shields": "Shields",
+    "simpleWeapons": "Simple Weapons",
+    "martialWeapons": "Martial Weapons",
+    "other": "Other Weapons",
+    "langAndTools": "Languages and Tools",
+    "languages": "Languages",
+    "tools": "Tools",
+    "modalHeader": "Languages and Tools"
+  },
+  "navigation": {
+    "menu": "Menu",
+    "dashboard": "Dashboard",
+    "character": "Character",
+    "inventory": "Inventory",
+    "spells": "Spells",
+    "notes": "Notes",
+    "spellbook": "Spellbook",
+    "quests": "Quests",
+    "npcs": "NPCs",
+    "places": "Places",
+    "maps": "Maps",
+    "rules": "Rules",
+    "settings": "Settings",
+    "characters": "Characters"
+  },
+  "character": {
+    "level": "Level",
+    "experience": "Experience Points",
+    "general": {
+      "label": "Genral",
+      "species": "Species",
+      "class": "Class",
+      "gender": "Gender",
+      "age": "Age",
+      "height": "Height",
+      "weight": "Weight",
+      "eyes": "Eyes",
+      "skin": "Skin",
+      "hair": "Hair",
+      "description": "Description",
+      "personality": "Personality",
+      "ideals": "Ideals",
+      "bonds": "Bonds",
+      "flaws": "Flaws"
     },
     "species": {
-        "human": "Human",
-        "elf": "Elf",
-        "dwarf": "Dwarf",
-        "halfling": "Halfling",
-        "gnome": "Gnome",
-        "halfElf": "Half-Elf",
-        "halfOrc": "Half-Orc",
-        "tiefling": "Tiefling",
-        "dragonborn": "Dragonborn",
-        "aarakocra": "Aarakocra",
-        "aasimar": "Asimar",
-        "autognome": "Autognome",
-        "bugbear": "Bugbear",
-        "centaur": "Centaur",
-        "changeling": "Changeling",
-        "duergar": "Duergar",
-        "drow": "Drow",
-        "eladrin": "Eladrin",
-        "halfElfDetection": "Half-Elf (Mark Of Detection)",
-        "fairy": "Fairy",
-        "firbolg": "Firbolg",
-        "genasi": "Genasi",
-        "gith": "Gith",
-        "goblin": "Goblin",
-        "goliath": "Goliath",
-        "hobgoblin": "Hobgoblin",
-        "kalashtar": "Kalashtar",
-        "kenku": "Kenku",
-        "kobold": "Kobold",
-        "leonin": "Leonin",
-        "lizardfolk": "Lizardfolk",
-        "locathah": "Locathah",
-        "loxodon": "Loxodon",
-        "minotaur": "Minotaur",
-        "orc": "Orc",
-        "owlin": "Owlin",
-        "satyr": "Satyr",
-        "shifter": "Shifter",
-        "tabaxi": "Tabaxi",
-        "simicHybrid": "Simic-Hybrid",
-        "tortle": "Tortle",
-        "triton": "Triton",
-        "vedalken": "Vedalken",
-        "verdan": "Verdan",
-        "warforged": "Warforged",
-        "yuanTi": "Yuan-Ti"
-    },
-    "classes": {
-        "barbarian": "Barbarian",
-        "bard": "Bard",
-        "cleric": "Cleric",
-        "druid": "Druid",
-        "fighter": "Fighter",
-        "monk": "Monk",
-        "paladin": "Paladin",
-        "ranger": "Ranger",
-        "rogue": "Rogue",
-        "sorcerer": "Sorcerer",
-        "warlock": "Warlock",
-        "wizard": "Wizard",
-        "test": "Test"
-    },
-    "subclasses": {
-        "empty": "No subclass chosen",
-        "hint": " Here you can choose your subclass. The subclass is a special specialization of your class. It gives you additional features and traits.",
-        "barbarian": {
-            "berserker": "Berserker"
-        },
-        "bard": {
-            "lore": "Lore",
-            "valor": "Valor"
-        },
-        "cleric": {
-            "peaceDomain": "Peace Domain"
-        },
-        "druid": {
-            "land": "Circle of the Land",
-            "moon": "Circle of the Moon"
-        },
-        "fighter": {
-            "champion": "Champion",
-            "battleMaster": "Battle Master",
-            "eldritchKnight": "Eldritch Knight",
-            "echoKnight": "Echo Knight"
-        },
-        "monk": {
-            "openHand": "Way of the Open Hand",
-            "wayOfShadows": "Way of Shadow",
-            "fourElements": "Way of the Four Elements"
-        },
-        "paladin": {
-            "devotion": "Oath of Devotion",
-            "ancients": "Oath of the Ancients",
-            "vengeance": "Oath of Vengeance",
-            "crown": "Oath of the Crown",
-            "conquest": "Oath of Conquest",
-            "redemption": "Oath of Redemption"
-        },
-        "ranger": {
-            "hunter": "Hunter",
-            "beastMaster": "Beast Master",
-            "gloomStalker": "Gloom Stalker"
-        },
-        "rogue": {
-            "thief": "Thief",
-            "assassin": "Assassin",
-            "arcaneTrickster": "Arcane Trickser",
-            "mastermind": "Mastermind",
-            "swashbuckler": "Swashbuckler"
-        },
-        "sorcerer": {
-            "draconic": "Draconic Bloodline",
-            "wildMagic": "Wild Magic",
-            "phoenixSorcery": "Phoenix Sorcery",
-            "shadowMagic": "Shadow Magic"
-        },
-        "warlock": {
-            "fiend": "The Fiend",
-            "archfey": "The Archfey",
-            "greatOldOne": "The Great Old One",
-            "celestial": "The Celestial",
-            "hexblade": "The Hexblade"
-        },
-        "wizard": {
-            "abjuration": "School of Abjuration",
-            "conjuration": "School of Conjuration",
-            "divination": "School of Divination",
-            "enchantment": "School of Enchantment",
-            "evocation": "School of Evocation",
-            "illusion": "School of Illusion",
-            "necromancy": "School of Necromancy"
-        }
-    },
-    "attributes": {
-        "value": "Value",
-        "modifier": "Modifier",
-        "strength": "Strength",
-        "dexterity": "Dexterity",
-        "constitution": "Constitution",
-        "intelligence": "Intelligence",
-        "wisdom": "Wisdom",
-        "charisma": "Charisma"
-    },
-    "attributesAbbreviations": {
-        "strength": "STR",
-        "dexterity": "DEX",
-        "constitution": "CON",
-        "intelligence": "INT",
-        "wisdom": "WIS",
-        "charisma": "CHA"
-    },
-    "attributeDetails": {
-        "affectedSkills": "Affected Skills:",
-        "strength": "Strength measures bodily power, athletic training, and the extent to which you can exert raw physical force. A high strength value indicates physical robustness, muscular strength and assertiveness. Characters with high strength are often better able to carry heavy loads, wield powerful weapons and overcome physical challenges.",
-        "dexterity": "Dexterity represents your character's agility, reflexes and general body control. A high dexterity value indicates quick reactions, skillful movements and good hand-eye coordination. Characters with high dexterity are often skilled thieves, archers or acrobats.",
-        "constitution": "Constitution stands for your character's resilience, health and stamina. A high constitution value means that your character is robust and can cope well with physical stress. Characters with a high constitution often have a higher health score and are more resistant to diseases and poison.",
-        "intelligence": "Intelligence measures your character's mental acuity, logical thinking and learning ability. A high intelligence score indicates a good general education, a thirst for knowledge and analytical skills. Characters with high intelligence are often mages, researchers or strategists.",
-        "wisdom": "Wisdom represents your character's perception, intuition and emotional intelligence. A high Wisdom score indicates good judgment, insight and inner strength. Characters with high wisdom are often clerics, druids or sages.",
-        "charisma": "Charisma reflects your character's charisma, persuasiveness and social skills. A high charisma score means that your character is charming, engaging and persuasive. Characters with high charisma are often better at communicating with others, persuading and taking on leadership roles. Charisma is often important for bards, leaders or diplomats.",
-        "strengths": "Resistances and Vulnerabilities"
-    },
-    "resistances": {
-        "resistances": "Resistances",
-        "immunities": "Immunities",
-        "vulnerabilities": "Vulnerabilities"
-    },
-    "skills": {
-        "acrobatics": "Acrobatics",
-        "animalHandling": "Animal Handling",
-        "arcana": "Arcana",
-        "athletics": "Athletics",
-        "deception": "Deception",
-        "history": "History",
-        "insight": "Insight",
-        "intimidation": "Intimidation",
-        "investigation": "Investigation",
-        "medicine": "Medicine",
-        "nature": "Nature",
-        "perception": "Perception",
-        "performance": "Performance",
-        "persuasion": "Persuasion",
-        "religion": "Religion",
-        "sleightOfHand": "Sleight of Hand",
-        "stealth": "Stealth",
-        "survival": "Survival"
-    },
-    "skillsDescription": {
-        "acrobatics": "Acrobatics rolls cover all attempts in which a character tries to stay on their feet in difficult situations. This includes, for example, balancing on a rope, running over unstable surfaces, balancing on a narrow ledge and jumping over obstacles.",
-        "animalHandling": "Animal Handling rolls cover all attempts to calm a domesticated animal, keep a mount from shying, or intuitively understand how a wild animal is feeling and what its intentions are.",
-        "arcana": "Arcana rolls cover all attempts to identify, understand or manipulate magical energy.",
-        "athletics": "Athletics rolls cover all attempts to test a character's physical strength. This includes, for example, climbing, swimming and jumping.",
-        "deception": "Rolls for Deception indicate whether a character can convincingly conceal the truth, whether verbally or through concrete actions. This includes, for example, lying, hiding emotions behind a mask and disguising oneself.",
-        "history": "Dice on history cover all attempts to remember historical events and people, past ages, ancient empires and forgotten cultures.",
-        "insight": "Insight rolls cover all attempts to recognize the true intentions of another person. This includes, for example, recognizing lies, interpreting body language and predicting their next moves.",
-        "intimidation": "Rolls for Intimidation cover all attempts to influence another person through threats, obvious violence or subtle hints.",
-        "investigation": "Investigation rolls cover all attempts to find and interpret clues that point to previous activity. This includes, for example, reconstructing the course of events or tracking down hidden objects.",
-        "medicine": "Medicine rolls cover all attempts to stabilize a character, heal an illness or dress a wound.",
-        "nature": "Nature rolls cover all attempts to recognize the characteristics of wild animals, plants and other creatures.",
-        "perception": "Perception rolls cover all attempts to observe the surroundings and notice what is nearby. This includes, for example, recognizing hidden enemies, listening to conversations behind a door, discovering clues and observing dangers.",
-        "performance": "Performance rolls cover all attempts to entertain an audience, be it through music, dancing, acting or other forms of entertainment.",
-        "persuasion": "Rolls for Persuasion cover all attempts to influence another person through persuasion, logic or argumentation.",
-        "religion": "Rolls for religion cover all attempts to understand religious rituals, recognize religious symbols and interpret religious traditions.",
-        "sleightOfHand": "Sleight of Hand rolls cover all attempts to steal something, perform a trick or carry out an action without anyone noticing.",
-        "stealth": "Stealth rolls cover all attempts to hide, move quietly and conceal oneself.",
-        "survival": "Survival rolls cover all attempts to survive in the wilderness. This includes, for example, hunting and navigating through the wilderness."
-    },
-    "skillsAttributes": {
-        "acrobatics": "dexterity",
-        "animalHandling": "wisdom",
-        "arcana": "intelligence",
-        "athletics": "strength",
-        "deception": "charisma",
-        "history": "intelligence",
-        "insight": "wisdom",
-        "intimidation": "charisma",
-        "investigation": "intelligence",
-        "medicine": "wisdom",
-        "nature": "wisdom",
-        "perception": "wisdom",
-        "performance": "charisma",
-        "persuasion": "charisma",
-        "religion": "intelligence",
-        "sleightOfHand": "dexterity",
-        "stealth": "dexterity",
-        "survival": "wisdom"
-    },
-    "skillDetails": {
-        "title": "Permanent (Dis-)Advantages",
-        "advantage": "Advantage",
-        "disadvantage": "Disadvantage",
-        "none": "None"
-    },
-    "savingThrow": {
-        "subheading": "Saving Throw",
-        "content": " A saving throw is always required when checking whether a player can resist an attack or external influence. Passing a saving throw can prevent or at least weaken the effect."
-    },
-    "armorClass": {
-        "label": "Armor Class",
-        "shortLabel": "AC",
-        "description": "The armor class is used to determine whether an attack is successful or not. An attacker must make a dice roll (usually a D20) and achieve at least the armor class of the target. If the roll is successful, the attack causes damage, otherwise the attack is blocked."
-    },
-    "initiative": {
-        "label": "Initiative",
-        "description": "The initiative bonus is used to determine the initiative of the current battle or a time-critical situation. Each character rolls a d20 and adds up their initiative bonus. The character with the highest result may act first."
-    },
-    "movement": {
-        "label": "Movement Speed",
-        "shortLabel": "Speed",
-        "movement": "Walking",
-        "translationTable": "Translation Table",
-        "description": "The movement speed indicates how far a character can walk per round. The movement speed is given in feet, with 5 feet corresponding to 1.5 meters and therefore one field. The movement can be used in one piece or divided into several parts. In difficult terrain, the movement speed is halved.",
-        "jumping": "Jumping",
-        "jumpingDescription": "A character can jump based on their strength. If you have moved 10 feet (2 fields) before jumping, you can jump twice the distance."
-    },
-    "proficiency": {
-        "label": "Proficiency Bonus",
-        "shortLabel": "Proficiency",
-        "description": "The proficiency bonus is added to all save, attack and skill rolls in which a player is proficient in. The proficiency bonus increases with the character level."
-    },
-    "conditions": {
-        "label": "Conditions",
-        "shortLabel": "Consition",
-        "noConditions": "No Conditions",
-        "addCondition": "Add Condition",
-        "currentConditions": "Current Conditions",
-        "description": "Conditions change a creature's abilities in various ways and can be the result of spells, class features, monster attacks or other abilities. The condition lasts until it is canceled or until its duration expires.",
-        "blinded": "Blinded",
-        "charmed": "Charmed",
-        "deafened": "Deafened",
-        "frightened": "Frightened",
-        "grappled": "Grappled",
-        "incapacitated": "Incapacitated",
-        "invisible": "Invisible",
-        "paralyzed": "Paralyzed",
-        "petrified": "Petrified",
-        "poisoned": "Poisoned",
-        "prone": "Prone",
-        "restrained": "Retrained",
-        "stunned": "Stunned",
-        "unconscious": "Unconscious",
-        "conditionDescriptions": {
-            "stunned": [
-                "A stunned creature is incapacitated, can't move, and can speak only falteringly.",
-                "The creature automatically fails Strength and Dexterity saving throws.",
-                "Attack rolls against the creature have advantage."
-            ],
-            "unconscious": [
-                "An unconscious creature is incapacitated, can't move or speak, and is unaware of its surroundings.",
-                "The creature drops whatever it's holding and falls prone.",
-                "The creature automatically fails Strength and Dexterity saving throws.",
-                "Attack rolls against the creature have advantage.",
-                "Any attack that hits the creature is a critical hit if the attacker is within 5 feet of the creature."
-            ],
-            "charmed": [
-                "A charmed creature can't attack the charmer or target the charmer with harmful abilities or magical effects.",
-                "The charmer has advantage on any ability check to interact socially with the creature."
-            ],
-            "blinded": [
-                "A blinded creature can't see and automatically fails any ability check that requires sight.",
-                "Attack rolls against the creature have advantage, and the creature's attack rolls have disadvantage."
-            ],
-            "restrained": [
-                "A restrained creature's speed becomes 0, and it can't benefit from any bonus to its speed.",
-                "Attack rolls against the creature have advantage, and the creature's attack rolls have disadvantage.",
-                "The creature has disadvantage on Dexterity saving throws."
-            ],
-            "paralyzed": [
-                "A paralyzed creature is incapacitated and can't move or speak.",
-                "The creature automatically fails Strength and Dexterity saving throws.",
-                "Attack rolls against the creature have advantage.",
-                "Any attack that hits the creature is a critical hit if the attacker is within 5 feet of the creature."
-            ],
-            "grappled": [
-                "A grappled creature's speed becomes 0, and it can't benefit from any bonus to its speed.",
-                "The condition ends if the grappler is incapacitated.",
-                "The condition also ends if an effect removes the grappled creature from the reach of the grappler or grappling effect, such as when a creature is hurled away by the thunderwave spell. "
-            ],
-            "incapacitated": [
-                "An incapacitated creature can't take actions or reactions."
-            ],
-            "prone": [
-                "A prone creature's only movement option is to crawl, unless it stands up and thereby ends the condition.",
-                "The creature has disadvantage on attack rolls.",
-                "An attack roll against the creature has advantage if the attacker is within 5 feet of the creature. Otherwise, the attack roll has disadvantage."
-            ],
-            "deafened": [
-                "A deafened creature can't hear and automatically fails any ability check that requires hearing."
-            ],
-            "invisible": [
-                "An invisible creature is impossible to see without the aid of magic or a special sense. For the purpose of hiding, the creature is heavily obscured. The creature's location can be detected by any noise it makes or any tracks it leaves.",
-                "Attack rolls against the creature have disadvantage, and the creature's attack rolls have advantage."
-            ],
-            "frightened": [
-                "A frightened creature has disadvantage on ability checks and attack rolls while the source of its fear is within line of sight.",
-                "The creature can't willingly move closer to the source of its fear."
-            ],
-            "poisoned": [
-                "    A poisoned creature has disadvantage on attack rolls and ability checks."
-            ],
-            "petrified": [
-                "A petrified creature is transformed, along with any nonmagical object it is wearing or carrying, into a solid inanimate substance (usually stone). Its weight increases by a factor of ten, and it ceases aging.",
-                "The creature is incapacitated, can't move or speak, and is unaware of its surroundings.",
-                "Attack rolls against the creature have advantage.",
-                "The creature automatically fails Strength and Dexterity saving throws.",
-                "The creature has resistance to all damage.",
-                "The creature is immune to poison and disease, although a poison or disease already in its system is suspended, not neutralized."
-            ]
-        }
-    },
-    "exhaustion": {
-        "label": "Exhaustion",
-        "description": "Some special abilities and environmental hazards, such as hunger or extreme temperatures, can lead to exhaustion. Each level adds a further penalty, so several penalties are possible at the same time. Exhaustion can be reduced by a long rest, for example.",
-        "currentExhaustion": "Current Exhaustion",
-        "0": "No Limitation",
-        "1": "Disadvantage on ability checks",
-        "2": "Speed halved",
-        "3": "Disadvantage on attack rolls and saving throws",
-        "4": "Hit point maximum halved",
-        "5": "Speed reduced to 0",
-        "6": "Death"
-    },
-    "deathSave": {
-        "label": "Death Saving Throws",
-        "success": "Success",
-        "fail": "Failure",
-        "description": "Whenever a character starts his turn with 0 hit points and has not been stabilized, he must make a death saving throw. If the result is 10 or higher, the roll is successful, otherwise it fails. On the third success the character is stabilized, on the third failure he dies.",
-        "criticalHeader": "Roll a 1 or 20",
-        "criticalContent": "If a character rolls a 1, the roll counts as two misses. If they roll a 20, they immediately gain one life point and are no longer unconscious."
-    },
-    "life": {
-        "hitpoints": "Hit Points",
-        "max": "Maximum Hit Points",
-        "temporary": "Temporary Hit Points",
-        "current": "Current Hit Points",
-        "hitdice": "Hit Dice",
-        "hitdiceDescription": "Hit dice are used to restore hit points during a short rest. You can use any number of your available hit dice. They are restored during a long rest."
-    },
-    "weapons": {
-        "header": {
-            "type": "Type",
-            "name": "Name",
-            "bonus": "Bonus",
-            "damage": "Damage",
-            "range": "Range"
-        },
-        "name": "Name",
-        "noWeapons": "No weapons added yet",
-        "meele": "Melee",
-        "ranged": "Ranged",
-        "attack": "Attack",
-        "single": "One-handed Damage",
-        "dual": "Two-handed Damage",
-        "proficient": "Proficient",
-        "versatile": "Versatile",
-        "oneHanded": "One-handed",
-        "twoHanded": "Two-handed",
-        "finesse": "Finesse",
-        "throwable": "Thrown",
-        "magical": "Magical",
-        "range": "Range",
-        "throwRange": "Throw Range",
-        "modal": {
-            "addWeapon": "Add Weapon",
-            "editWeapon": "Edit Weapon",
-            "applyModifier": "Apply Modifier",
-            "additionalDamage": "Additional Damage",
-            "attackBonus": "Attack Bonus",
-            "magicalModifier": "Magical Modifier",
-            "damage": "Damage",
-            "normalRange": "Normal Range",
-            "extendedRange": "Extended Range",
-            "normalThrowRange": "Normal Throw Range",
-            "extendedThrowRange": "Extended Throw Range",
-            "placeholder": "Description of the Weapon"
-        }
-    },
-    "spells": {
-        "header": {
-            "cost": "Cost",
-            "name": "Name",
-            "level": "Level",
-            "bonus": "Bonus",
-            "effect": "Effect",
-            "range": "range"
-        },
-        "components": {
-            "verbal": "V",
-            "somatic": "S",
-            "material": "M",
-            "ritual": "R"
-        },
-        "touch": "Touch",
-        "empty": "No spells added yet",
-        "concentrationAbbr": "C",
-        "concentration": "Concentration"
-    },
-    "abilities": {
-        "label": "Features",
-        "empty": "No features added yet",
-        "uses": "Uses:",
-        "modal": {
-            "addAbility": "Add feature",
-            "editAbility": "Edit feature",
-            "shortPlaceholder": "Short description of the feature",
-            "longPlaceholder": "Detailed description of the feature"
-        }
-    },
-    "traits": {
-        "label": "Traits",
-        "empty": "No traits added yet",
-        "modal": {
-            "addTrait": "Add trait",
-            "editTrait": "Edit trait",
-            "shortPlaceholder": "Short description of the trait",
-            "shortHint": "Will be shown in overview",
-            "longPlaceholder": "Detailed description of the trait",
-            "longHint": "Will be shown in detail view"
-        }
-    },
-    "magic": {
-        "spellCasting": "Spell Casting",
-        "ki": "Ki Points",
-        "spellslots": "Spell Slots",
-        "empty": "No spell slots added yet",
-        "spellcastingAttribute": "Spell Casting Attribute",
-        "saveAC": "Save DC",
-        "spellAttackBonus": "Spell Attack Bonus",
-        "description": "The number of available spell slots per spell level can be adjusted here",
-        "showSlots": "Show spell slots in overview",
-        "showKi": "Show ki Points in overview",
-        "descriptioni": "The number of available ki points can be adjusted here",
-        "availableKi": "Available ki points",
-        "emptySpell": "No spell slots added yet",
-        "emptyKi": "No ki points added yet"
-    },
-    "proficiencies": {
-        "label": "Proficiencies",
-        "weapArm": "Weapons and Armor",
-        "lightArmor": "Light Armor",
-        "mediumArmor": "Medium Armor",
-        "heavyArmor": "Heavy Armor",
-        "shields": "Shields",
-        "simpleWeapons": "Simple Weapons",
-        "martialWeapons": "Martial Weapons",
-        "other": "Other Weapons",
-        "langAndTools": "Languages and Tools",
-        "languages": "Languages",
-        "tools": "Tools",
-        "modalHeader": "Languages and Tools"
-    },
-    "navigation": {
-        "menu": "Menu",
-        "dashboard": "Dashboard",
-        "character": "Character",
-        "inventory": "Inventory",
-        "spells": "Spells",
-        "notes": "Notes",
-        "spellbook": "Spellbook",
-        "quests": "Quests",
-        "npcs": "NPCs",
-        "places": "Places",
-        "maps": "Maps",
-        "rules": "Rules",
-        "settings": "Settings",
-        "characters": "Characters"
-    },
-    "character": {
-        "level": "Level",
-        "experience": "Experience Points",
-        "general": {
-            "label": "Genral",
-            "species": "Species",
-            "class": "Class",
-            "gender": "Gender",
-            "age": "Age",
-            "height": "Height",
-            "weight": "Weight",
-            "eyes": "Eyes",
-            "skin": "Skin",
-            "hair": "Hair",
-            "description": "Description",
-            "personality": "Personality",
-            "ideals": "Ideals",
-            "bonds": "Bonds",
-            "flaws": "Flaws"
-        },
-        "species": {
-            "label": "Species"
-        },
-        "class": {
-            "label": "Class"
-        },
-        "subclass": {
-            "barbarian": "Pfad",
-            "bard": "College",
-            "druid": "Circle",
-            "monk": "Monastic Tradition",
-            "fighter": "Archetype",
-            "ranger": "Conclave",
-            "cleric": "Domain",
-            "paladin": "Oath",
-            "rogue": "Archetype",
-            "wizard": "Tradition",
-            "warlock": "Pact",
-            "sorcerer": "Origin"
-        },
-        "complete": {
-            "label": "Combined Overview",
-            "description": "The combined properties and abilities of your class and subclass are displayed here."
-        },
-        "background": {
-            "label": "Background"
-        },
-        "story": {
-            "label": "Backstory",
-            "editStory": "Edit Backstory",
-            "edit": "Edit"
-        }
-    },
-    "inventory": {
-        "weapArm": "Weapons and Armor",
-        "misc": "Miscellaneous",
-        "food": "Food",
-        "usable": "Consumables",
-        "name": "Name",
-        "weight": "Weight",
-        "value": "Value",
-        "quantity": "Quanitty",
-        "ready": "Ready to eat",
-        "total": "Total Weight",
-        "description": "Description",
-        "modal": {
-            "edit": "Edit Object",
-            "add": "Add Object",
-            "placeholder": "Description of the Object",
-            "pieces": "Qty."
-        }
-    },
-    "spellcards": {
-        "manage": "Manage Custom Spells",
-        "cantrips": "Cantrips",
-        "delete": "Drop here to delete",
-        "add": {
-            "official": "Official Spells",
-            "custom": "Custom Spells",
-            "edit": "Edit Official Spells",
-            "lookup": "Search Spells",
-            "empty": "No spells found",
-            "cancel": "Cancel"
-        }
-    },
-    "spellmodal": {
-        "edit": "Edit Spell",
-        "add": "Add Spell",
-        "name": "Name",
-        "level": "Level",
-        "school": "School",
-        "verbal": "Verbal",
-        "somatic": "Somatic",
-        "material": "Material",
-        "concentration": "Concentration",
-        "canRitual": "Ritual possible",
-        "isRitual": "Is Ritual",
-        "damage": "Damage",
-        "heal": "Healing",
-        "range": "Range",
-        "hasRange": "Has Range",
-        "hasAreaOfEffect": "Has Area of Effect",
-        "needsSavingThrow": "Needs Saving Throw",
-        "needsAttackRoll": "Needs Attack Roll",
-        "cost": "Cost",
-        "duration": "Duration",
-        "rounds": "Rounds",
-        "additionalDamage": "Additional Damage",
-        "description": "Description",
-        "placeholder": "Description of the Spell",
-        "diameter": "Diameter"
-    },
-    "creator": {
-        "new": "Create New Character",
-        "name": "Name",
-        "species": "Species",
-        "class": "Class",
-        "background": "Background",
-        "gender": "Gender"
-    },
-    "picker": {
-        "label": "DND-Tools",
-        "delete": "Delete Character",
-        "confirm": "Do you want to permanently delete <b>{{name}}</b>?",
-        "hint": "The app is still in a development stage and errors may occur",
-        "issues": "<p>Please note errors and comments on the <a href='https://gogs.koljastrohm-games.com/Warafear/DNDTools/issues'>Git server in Issues</a>.<p>",
-        "okay": "Understood",
-        "version": "0.9.5"
+      "label": "Species"
+    },
+    "class": {
+      "label": "Class"
+    },
+    "subclass": {
+      "barbarian": "Pfad",
+      "bard": "College",
+      "druid": "Circle",
+      "monk": "Monastic Tradition",
+      "fighter": "Archetype",
+      "ranger": "Conclave",
+      "cleric": "Domain",
+      "paladin": "Oath",
+      "rogue": "Archetype",
+      "wizard": "Tradition",
+      "warlock": "Pact",
+      "sorcerer": "Origin"
+    },
+    "complete": {
+      "label": "Combined Overview",
+      "description": "The combined properties and abilities of your class and subclass are displayed here."
+    },
+    "background": {
+      "label": "Background"
+    },
+    "story": {
+      "label": "Backstory",
+      "editStory": "Edit Backstory",
+      "edit": "Edit"
+    }
+  },
+  "inventory": {
+    "weapArm": "Weapons and Armor",
+    "misc": "Miscellaneous",
+    "food": "Food",
+    "usable": "Consumables",
+    "name": "Name",
+    "weight": "Weight",
+    "value": "Value",
+    "quantity": "Quanitty",
+    "ready": "Ready to eat",
+    "total": "Total Weight",
+    "description": "Description",
+    "modal": {
+      "edit": "Edit Object",
+      "add": "Add Object",
+      "placeholder": "Description of the Object",
+      "pieces": "Qty."
+    }
+  },
+  "spellcards": {
+    "manage": "Manage Custom Spells",
+    "cantrips": "Cantrips",
+    "delete": "Drop here to delete",
+    "add": {
+      "official": "Official Spells",
+      "custom": "Custom Spells",
+      "edit": "Edit Official Spells",
+      "lookup": "Search Spells",
+      "empty": "No spells found",
+      "cancel": "Cancel"
     }
-}
+  },
+  "spellmodal": {
+    "edit": "Edit Spell",
+    "add": "Add Spell",
+    "name": "Name",
+    "level": "Level",
+    "school": "School",
+    "verbal": "Verbal",
+    "somatic": "Somatic",
+    "material": "Material",
+    "concentration": "Concentration",
+    "canRitual": "Ritual possible",
+    "isRitual": "Is Ritual",
+    "damage": "Damage",
+    "heal": "Healing",
+    "range": "Range",
+    "hasRange": "Has Range",
+    "hasAreaOfEffect": "Has Area of Effect",
+    "needsSavingThrow": "Needs Saving Throw",
+    "needsAttackRoll": "Needs Attack Roll",
+    "cost": "Cost",
+    "duration": "Duration",
+    "rounds": "Rounds",
+    "additionalDamage": "Additional Damage",
+    "description": "Description",
+    "placeholder": "Description of the Spell",
+    "diameter": "Diameter"
+  },
+  "creator": {
+    "new": "Create New Character",
+    "name": "Name",
+    "species": "Species",
+    "class": "Class",
+    "background": "Background",
+    "gender": "Gender"
+  },
+  "picker": {
+    "label": "DND-Tools",
+    "delete": "Delete Character",
+    "confirm": "Do you want to permanently delete <b>{{name}}</b>?",
+    "hint": "The app is still in a development stage and errors may occur",
+    "issues": "<p>Please note errors and comments on the <a href='https://gogs.koljastrohm-games.com/Warafear/DNDTools/issues'>Git server in Issues</a>.<p>",
+    "okay": "Understood",
+    "version": "0.10.0"
+  }
+}

+ 47 - 0
src/colors.scss

@@ -0,0 +1,47 @@
+:root {
+  --accept: #84a36f;
+  --accept-hover: #6f9158;
+  --delete: #a45a52;
+  --delete-hover: #8f4a42;
+  --edit: #86a5b7;
+  --edit-hover: #6f8e9f;
+  --neutral: #b9835d;
+  --neutral-hover: #a76b43;
+  --tab: #c79980;
+  --tab-active: #bc8567;
+
+  --abjuration-background: #cab8de;
+  --abjuration-border: 0 0 0 2px #171314, 0 0 0 5px #9586a5;
+  --conjuration-background: #714c81;
+  --conjuration-border: 0 0 0 2px #171314, 0 0 0 5px #744987;
+  --divination-background: #f0e3ce;
+  --divination-border: 0 0 0 2px #171314, 0 0 0 5px #ffffff;
+  --enchantment-background: #629179;
+  --enchantment-border: 0 0 0 2px #171314, 0 0 0 5px #26714a;
+  --evocation-background: #a46a7d;
+  --evocation-border: 0 0 0 2px #171314, 0 0 0 5px #863f57;
+  --illusion-background: #69a;
+  --illusion-border: 0 0 0 2px #171314, 0 0 0 5px #2d6475;
+  --necromancy-background: #000000;
+  --necromancy-border: 0 0 0 2px #171314, 0 0 0 5px #36353a;
+  --transmutation-background: rgb(51, 52, 138);
+  --transmutation-border: 0 0 0 2px #171314, 0 0 3px #8f088f, 0 0 6px #7f088f,
+    0 0 9px #69088f, 0 0 12px #3a088f, 0 0 15px #080a8f;
+
+  --shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.5);
+  --shadow-inverted: 0px -5px 10px 0px rgba(0, 0, 0, 0.5);
+  --shadow-bottom: 0px 5px 10px 0px rgba(0, 0, 0, 0.5);
+  --shadow-top: 0px -5px 10px 0px rgba(0, 0, 0, 0.5);
+
+  --primary: #b0826b;
+  --background-color: #fff2e9;
+  --field-background-color: #efc8af;
+  --border-color: #8d8c8c;
+
+  --modal-background: antiquewhite;
+  --header: #ffdec6;
+  --text: #000000;
+  --items: #fff2e9;
+  --items-hover: #f7e3d7;
+  --border: 1px solid var(--border-color);
+}

+ 181 - 0
src/responsive.scss

@@ -0,0 +1,181 @@
+@mixin width-large {
+  @media (width >= 1920px) {
+    @content;
+  }
+}
+
+@mixin width-medium {
+  @media (width >= 1707px) {
+    @content;
+  }
+}
+
+@mixin width-small {
+  @media (width >= 1494px) {
+    @content;
+  }
+}
+
+@mixin width-xsmall {
+  @media (width < 1445px) {
+    @content;
+  }
+}
+
+@mixin height-large {
+  @media (height >= 1080px) {
+    @content;
+  }
+}
+
+@mixin height-medium {
+  @media (height >= 1001px) {
+    @content;
+  }
+}
+
+@mixin height-small {
+  @media (height >= 934px) {
+    @content;
+  }
+}
+
+@mixin height-xsmall {
+  @media (height < 934px) {
+    @content;
+  }
+}
+
+.h840 {
+  position: absolute;
+  border-top: 2px solid green;
+  top: 840px;
+  width: 100%;
+  z-index: 9999999;
+}
+.h864 {
+  position: absolute;
+  border-top: 2px solid black;
+  top: 864px;
+  width: 100%;
+  z-index: 9999999;
+  &:before {
+    content: "864";
+  }
+}
+
+.h910 {
+  position: absolute;
+  border-top: 2px solid green;
+  z-index: 9999999;
+  top: 910px;
+  width: 100%;
+}
+.h934 {
+  position: absolute;
+  border-top: 2px solid black;
+  top: 934px;
+  width: 100%;
+  z-index: 9999999;
+  &:before {
+    content: "934";
+  }
+}
+
+.h977 {
+  position: absolute;
+  border-top: 2px solid green;
+  top: 977px;
+  width: 100%;
+  z-index: 9999999;
+}
+.h1001 {
+  position: absolute;
+  border-top: 2px solid black;
+  top: 1001px;
+  z-index: 9999999;
+  width: 100%;
+  &:before {
+    content: "1001";
+  }
+}
+
+.h1056 {
+  position: absolute;
+  border-top: 2px solid green;
+  top: 1056px;
+  z-index: 9999999;
+  width: 100%;
+}
+.h1080 {
+  position: absolute;
+  border-top: 2px solid black;
+  top: 1080px;
+  width: 100%;
+  z-index: 9999999;
+  &:before {
+    content: "1080";
+  }
+}
+
+.w1470 {
+  position: absolute;
+  border-left: 2px solid green;
+  left: 1470px;
+  height: 100%;
+  top: 0;
+  z-index: 9999999;
+}
+.w1494 {
+  position: absolute;
+  border-left: 2px solid black;
+  left: 1494px;
+  top: 0;
+  z-index: 9999999;
+  height: 100%;
+  &:before {
+    content: "1494";
+  }
+}
+
+.w1683 {
+  position: absolute;
+  border-left: 2px solid green;
+  left: 1683px;
+  height: 100%;
+  z-index: 9999999;
+  top: 0;
+}
+
+.w1707 {
+  position: absolute;
+  border-left: 2px solid black;
+  left: 1707px;
+  height: 100%;
+  top: 0;
+  z-index: 9999999;
+  &:before {
+    content: "1705";
+  }
+}
+
+.w1896 {
+  position: absolute;
+  border-left: 2px solid green;
+  left: 1896px;
+  height: 100%;
+  z-index: 9999999;
+  top: 0;
+}
+
+.w1920 {
+  position: absolute;
+  border-left: 2px solid black;
+  left: 1920px;
+  height: 100%;
+  z-index: 9999999;
+  top: 0;
+  &:before {
+    content: "1920";
+  }
+}

+ 2 - 2
src/services/class/class.service.ts

@@ -1870,7 +1870,7 @@ export class ClassService {
       </ul>
       </li>
       </ul>
-      <table class="wiki-content-table">
+      <table
       <tr>
       <th colspan="2">Creating Spell Slots</th>
       </tr>
@@ -2074,7 +2074,7 @@ export class ClassService {
         description: `
         <p>Starting at 2nd level, you can use your action to magically assume the shape of a beast that you have seen before. You can use this feature twice. You regain expended uses when you finish a short or long rest.</p>
         <p>Your druid level determines the beasts you can transform into, as shown in the Beast Shapes table. At 2nd level, for example, you can transform into any beast that has a challenge rating of 1/4 or lower that doesn't have a flying or swimming speed.</p>
-        <table class="wiki-content-table">
+        <table class='table'>
         <tr>
         <th colspan="4">Beast Shapes</th>
         </tr>

+ 112 - 85
src/services/data/data.service.ts

@@ -96,7 +96,7 @@ export class DataService {
     });
 
     // Hit Points
-    this.hitPoints = hitPointsData.hitPoints;
+    this.hitPointsSubject.next(hitPointsData.hitPoints);
     this.hitDice = hitPointsData.hitDice;
 
     // Combat Stats
@@ -622,19 +622,16 @@ export class DataService {
 
   // #region # HIT POINTS
 
-  private _hitPoints = {
+  private hitPointsSubject = new BehaviorSubject<any>({
     maxHitPoints: 6,
     currentHitPoints: 6,
     temporaryHitPoints: 1,
-  };
-
-  public get hitPoints(): any {
-    return this._hitPoints;
-  }
+  });
+  public hitPoints$ = this.hitPointsSubject.asObservable();
 
-  public set hitPoints(newValue: any) {
-    this._hitPoints = newValue;
-    this.updateHitPoints();
+  public updateHitPoints(newValue: any) {
+    this.hitPointsSubject.next(newValue);
+    this.updateLifeData();
   }
 
   private _hitDice = {
@@ -649,12 +646,12 @@ export class DataService {
 
   public set hitDice(newValue: any) {
     this._hitDice = newValue;
-    this.updateHitPoints();
+    this.updateLifeData();
   }
 
-  private updateHitPoints() {
+  private updateLifeData() {
     const hitPointsData = {
-      hitPoints: this._hitPoints,
+      hitPoints: this.hitPointsSubject.getValue(),
       hitDice: this._hitDice,
     };
     this.setData('hitPoints', hitPointsData);
@@ -677,34 +674,46 @@ export class DataService {
     this.setData('attributes', attributesData);
   }
 
-  private strengthSubject = new BehaviorSubject<Attribute>(
-    { name: 'strength', value: 10, proficiency: true }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private strengthSubject = new BehaviorSubject<Attribute>({
+    name: 'strength',
+    value: 10,
+    proficiency: true,
+  });
   public strength$ = this.strengthSubject.asObservable();
 
-  private dexteritySubject = new BehaviorSubject<Attribute>(
-    { name: 'dexterity', value: 10, proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private dexteritySubject = new BehaviorSubject<Attribute>({
+    name: 'dexterity',
+    value: 10,
+    proficiency: false,
+  });
   public dexterity$ = this.dexteritySubject.asObservable();
 
-  private constitutionSubject = new BehaviorSubject<Attribute>(
-    { name: 'constitution', value: 10, proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private constitutionSubject = new BehaviorSubject<Attribute>({
+    name: 'constitution',
+    value: 10,
+    proficiency: false,
+  });
   public constitution$ = this.constitutionSubject.asObservable();
 
-  private intelligenceSubject = new BehaviorSubject<Attribute>(
-    { name: 'intelligence', value: 10, proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private intelligenceSubject = new BehaviorSubject<Attribute>({
+    name: 'intelligence',
+    value: 10,
+    proficiency: false,
+  });
   public intelligence$ = this.intelligenceSubject.asObservable();
 
-  private wisdomSubject = new BehaviorSubject<Attribute>(
-    { name: 'wisdom', value: 10, proficiency: true }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private wisdomSubject = new BehaviorSubject<Attribute>({
+    name: 'wisdom',
+    value: 10,
+    proficiency: true,
+  });
   public wisdom$ = this.wisdomSubject.asObservable();
 
-  private charismaSubject = new BehaviorSubject<Attribute>(
-    { name: 'charisma', value: 10, proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private charismaSubject = new BehaviorSubject<Attribute>({
+    name: 'charisma',
+    value: 10,
+    proficiency: false,
+  });
   public charisma$ = this.charismaSubject.asObservable();
 
   // #endregion
@@ -737,94 +746,112 @@ export class DataService {
     this.setData('skills', skillsData);
   }
 
-  private acrobaticsSubject = new BehaviorSubject<Skill>(
-    { name: 'acrobatics', proficiency: true }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private acrobaticsSubject = new BehaviorSubject<Skill>({
+    name: 'acrobatics',
+    proficiency: true,
+  });
   public acrobatics$ = this.acrobaticsSubject.asObservable();
 
-  private animalHandlingSubject = new BehaviorSubject<Skill>(
-    { name: 'animalHandling', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private animalHandlingSubject = new BehaviorSubject<Skill>({
+    name: 'animalHandling',
+    proficiency: false,
+  });
   public animalHandling$ = this.animalHandlingSubject.asObservable();
 
-  private arcanaSubject = new BehaviorSubject<Skill>(
-    { name: 'arcana', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private arcanaSubject = new BehaviorSubject<Skill>({
+    name: 'arcana',
+    proficiency: false,
+  });
   public arcana$ = this.arcanaSubject.asObservable();
 
-  private athleticsSubject = new BehaviorSubject<Skill>(
-    { name: 'athletics', proficiency: true }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private athleticsSubject = new BehaviorSubject<Skill>({
+    name: 'athletics',
+    proficiency: true,
+  });
   public athletics$ = this.athleticsSubject.asObservable();
 
-  private deceptionSubject = new BehaviorSubject<Skill>(
-    { name: 'deception', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private deceptionSubject = new BehaviorSubject<Skill>({
+    name: 'deception',
+    proficiency: false,
+  });
   public deception$ = this.deceptionSubject.asObservable();
 
-  private historySubject = new BehaviorSubject<Skill>(
-    { name: 'history', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private historySubject = new BehaviorSubject<Skill>({
+    name: 'history',
+    proficiency: false,
+  });
   public history$ = this.historySubject.asObservable();
 
-  private insightSubject = new BehaviorSubject<Skill>(
-    { name: 'insight', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private insightSubject = new BehaviorSubject<Skill>({
+    name: 'insight',
+    proficiency: false,
+  });
   public insight$ = this.insightSubject.asObservable();
 
-  private intimidationSubject = new BehaviorSubject<Skill>(
-    { name: 'intimidation', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private intimidationSubject = new BehaviorSubject<Skill>({
+    name: 'intimidation',
+    proficiency: false,
+  });
   public intimidation$ = this.intimidationSubject.asObservable();
 
-  private investigationSubject = new BehaviorSubject<Skill>(
-    { name: 'investigation', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private investigationSubject = new BehaviorSubject<Skill>({
+    name: 'investigation',
+    proficiency: false,
+  });
   public investigation$ = this.investigationSubject.asObservable();
 
-  private medicineSubject = new BehaviorSubject<Skill>(
-    { name: 'medicine', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private medicineSubject = new BehaviorSubject<Skill>({
+    name: 'medicine',
+    proficiency: false,
+  });
   public medicine$ = this.medicineSubject.asObservable();
 
-  private natureSubject = new BehaviorSubject<Skill>(
-    { name: 'nature', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private natureSubject = new BehaviorSubject<Skill>({
+    name: 'nature',
+    proficiency: false,
+  });
   public nature$ = this.natureSubject.asObservable();
 
-  private perceptionSubject = new BehaviorSubject<Skill>(
-    { name: 'perception', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private perceptionSubject = new BehaviorSubject<Skill>({
+    name: 'perception',
+    proficiency: false,
+  });
   public perception$ = this.perceptionSubject.asObservable();
 
-  private performanceSubject = new BehaviorSubject<Skill>(
-    { name: 'performance', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private performanceSubject = new BehaviorSubject<Skill>({
+    name: 'performance',
+    proficiency: false,
+  });
   public performance$ = this.performanceSubject.asObservable();
 
-  private persuasionSubject = new BehaviorSubject<Skill>(
-    { name: 'persuasion', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private persuasionSubject = new BehaviorSubject<Skill>({
+    name: 'persuasion',
+    proficiency: false,
+  });
   public persuasion$ = this.persuasionSubject.asObservable();
 
-  private religionSubject = new BehaviorSubject<Skill>(
-    { name: 'religion', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private religionSubject = new BehaviorSubject<Skill>({
+    name: 'religion',
+    proficiency: false,
+  });
   public religion$ = this.religionSubject.asObservable();
 
-  private sleightOfHandSubject = new BehaviorSubject<Skill>(
-    { name: 'sleightOfHand', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private sleightOfHandSubject = new BehaviorSubject<Skill>({
+    name: 'sleightOfHand',
+    proficiency: false,
+  });
   public sleightOfHand$ = this.sleightOfHandSubject.asObservable();
 
-  private stealthSubject = new BehaviorSubject<Skill>(
-    { name: 'stealth', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private stealthSubject = new BehaviorSubject<Skill>({
+    name: 'stealth',
+    proficiency: false,
+  });
   public stealth$ = this.stealthSubject.asObservable();
 
-  private survivalSubject = new BehaviorSubject<Skill>(
-    { name: 'survival', proficiency: false }, //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
-  );
+  private survivalSubject = new BehaviorSubject<Skill>({
+    name: 'survival',
+    proficiency: false,
+  });
   public survival$ = this.survivalSubject.asObservable();
 
   // #endregion

+ 21 - 21
src/services/species/species.service.ts

@@ -133,7 +133,7 @@ export class SpeciesService {
           <li><strong>Spells of the Mark.</strong> If you have the Spellcasting or Pact Magic class feature, the spells on the Mark of Warding Spells table are added to the spell list of your Spellcasting class.</li>
           </ul>
           <p><strong>Mark of Warding Spells</strong></p>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th>Spell Level</th>
           <th>Spell</th>
@@ -399,7 +399,7 @@ export class SpeciesService {
           <li><strong>Spells of the Mark.</strong> If you have the Spellcasting or Pact Magic class features, the spells on the Mark of Shadow Spells table are added to the spell list of your spellcasting class.</li>
           </ul>
           <p><strong>Mark of Shadow Spells</strong></p>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th>Spell Level</th>
           <th>Spell</th>
@@ -828,7 +828,7 @@ export class SpeciesService {
           <li><strong>Spells of the Mark.</strong> If you have the Spellcasting or Pact Magic class feature, the spells on the Mark of Scribing Spells table are added to the spell list of your Spellcasting class.</li>
           </ul>
           <p><strong>Mark of Scribing Spells</strong></p>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th>Spell Level</th>
           <th>Spell</th>
@@ -941,7 +941,7 @@ export class SpeciesService {
           <li><strong>Spells of the Mark.</strong> If you have the Spellcasting or Pact Magic class feature, the spells on the Mark of Detection Spells table are added to the spell list of your Spellcasting class.</li>
           </ul>
           <p><strong>Mark of Detection Spells</strong></p>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th>Spell Level</th>
           <th>Spell</th>
@@ -993,7 +993,7 @@ export class SpeciesService {
           <li><strong>Spells of the Mark.</strong> If you have the Spellcasting or Pact Magic class feature, the spells on the Mark of Storm Spells table are added to the spell list of your Spellcasting class.</li>
           </ul>
           <p><strong>Mark of Storm Spells</strong></p>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th>Spell Level</th>
           <th>Spell</th>
@@ -1116,7 +1116,7 @@ export class SpeciesService {
           <li><strong>Spells of the Mark.</strong> If you have the Spellcasting or Pact Magic class feature, the spells on the Mark of Finding Spells table are added to the spell list of your Spellcasting class.</li>
           </ul>
           <p><strong>Mark of Finding Spells</strong></p>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th>Spell Level</th>
           <th>Spell</th>
@@ -1316,7 +1316,7 @@ export class SpeciesService {
           <li><strong>Spells of the Mark.</strong> If you have the Spellcasting or Pact Magic class features, the spells on the Mark of Hospitality Spells table are added to the spell list of your spellcasting class.</li>
           </ul>
           <p><strong>Mark of Hospitality Spells</strong></p>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th>Spell Level</th>
           <th>Spell</th>
@@ -1368,7 +1368,7 @@ export class SpeciesService {
           <li><strong>Spells of the Mark.</strong> If you have the Spellcasting or Pact Magic class features, the spells on the Mark of Healing Spells table are added to the spell list of your spellcasting class.</li>
           </ul>
           <p><strong>Mark of Healing Spells</strong></p>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th>Spell Level</th>
           <th>Spell</th>
@@ -1468,7 +1468,7 @@ export class SpeciesService {
           <li><strong>Spells of the Mark.</strong> If you have the Spellcasting or Pact Magic class feature, the spells on the Mark of Finding Spells table are added to the spell list of your Spellcasting class.</li>
           </ul>
           <p><strong>Mark of Finding Spells</strong></p>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th>Spell Level</th>
           <th>Spell</th>
@@ -1531,7 +1531,7 @@ export class SpeciesService {
           <li><strong>Spells of the Mark.</strong> If you have the Spellcasting or Pact Magic class feature, the spells on the Mark of Handling Spells table are added to the spell list of your Spellcasting class.</li>
           </ul>
           <p><strong>Mark of Handling Spells</strong></p>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th>Spell Level</th>
           <th>Spell</th>
@@ -1614,7 +1614,7 @@ export class SpeciesService {
           <li><strong>Spells of the Mark.</strong> If you have the Spellcasting or Pact Magic class feature, the spells on the Mark of Passage Spells table are added to the spell list of your Spellcasting class.</li>
           </ul>
           <p><strong>Mark of Passage Spells</strong></p>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th>Spell Level</th>
           <th>Spell</th>
@@ -1666,7 +1666,7 @@ export class SpeciesService {
           <li><strong>Spells of the Mark.</strong> If you have the Spellcasting or Pact Magic class feature, the spells on the Mark of Sentinel Spells table are added to the spell list of your Spellcasting class.</li>
           </ul>
           <p><strong>Mark of Sentinel Spells</strong></p>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th>Spell Level</th>
           <th>Spell</th>
@@ -1883,7 +1883,7 @@ export class SpeciesService {
         <ul>
         <li><strong>Draconic Ancestry.</strong> You are distantly related to a particular kind of dragon. Choose a type of dragon from the below list; this determines the damage and area of your breath weapon, and the type of resistance you gain.</li>
         </ul>
-        <table class="wiki-content-table">
+        <table class='table'>
         <tr>
         <th>Dragon Color</th>
         <th>Damage Type</th>
@@ -2039,7 +2039,7 @@ export class SpeciesService {
           <ul>
           <li><strong>Chromatic Ancestry.</strong> You trace your ancestry to a chromatic dragon, granting you a special magical affinity. Choose one type of dragon from the Chromatic Ancestry table. This determines the damage type for your other traits as shown in the table.</li>
           </ul>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th colspan="2">Chromatic Ancestry</th>
           </tr>
@@ -2118,7 +2118,7 @@ export class SpeciesService {
           <ul>
           <li><strong>Metallic Ancestry.</strong> You trace your ancestry to a metallic dragon, granting you a special magical affinity. Choose one type of dragon from the Metallic Ancestry table. This determines the damage type for your other traits as shown in the table.</li>
           </ul>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th colspan="2">Metallic Ancestry</th>
           </tr>
@@ -2202,7 +2202,7 @@ export class SpeciesService {
           <ul>
           <li><strong>Gem Ancestry.</strong> You trace your ancestry to a Gem dragon, granting you a special magical affinity. Choose one type of dragon from the Gem Ancestry table. This determines the damage type for your other traits as shown in the table.</li>
           </ul>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th colspan="2">Gem Ancestry</th>
           </tr>
@@ -2289,7 +2289,7 @@ export class SpeciesService {
           <ul>
           <li><strong>Chromatic Ancestry.</strong> You trace your ancestry to a chromatic dragon, granting you a special magical affinity. Choose one type of dragon from the Chromatic Ancestry table. This determines the damage type for your other traits as shown in the table.</li>
           </ul>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th colspan="2">Chromatic Ancestry</th>
           </tr>
@@ -2368,7 +2368,7 @@ export class SpeciesService {
           <ul>
           <li><strong>Metallic Ancestry.</strong> You trace your ancestry to a metallic dragon, granting you a special magical affinity. Choose one type of dragon from the Metallic Ancestry table. This determines the damage type for your other traits as shown in the table.</li>
           </ul>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th colspan="2">Metallic Ancestry</th>
           </tr>
@@ -2452,7 +2452,7 @@ export class SpeciesService {
           <ul>
           <li><strong>Gem Ancestry.</strong> You trace your ancestry to a Gem dragon, granting you a special magical affinity. Choose one type of dragon from the Gem Ancestry table. This determines the damage type for your other traits as shown in the table.</li>
           </ul>
-          <table class="wiki-content-table">
+          <table class='table'>
           <tr>
           <th colspan="2">Gem Ancestry</th>
           </tr>
@@ -2728,7 +2728,7 @@ export class SpeciesService {
         <ul>
         <li><strong>Languages.</strong> You can speak, read, and write Abyssal.</li>
         </ul>
-        <table class="wiki-content-table">
+        <table class='table'>
         <tr>
         <th colspan="4">Abyssal Arcana Spells</th>
         </tr>
@@ -2870,7 +2870,7 @@ export class SpeciesService {
     <p><b>Magical Detection:</b> You can cast the Detect Magic and the Detect Poison and Disease spells with this trait. Starting at 3rd level, you can also cast the See Invisibility spell with it. Once you cast either spell with this trait, you can't cast that spell again until you finish a Long Rest. Intelligence is your Spellcasting Ability for these spells, and you don't require material components for them.</p>
     <p><b>Spells of the Mark:</b> If you have the Spellcasting or Pact Magic class feature, the spells on the Mark of Detection Spells table are added to the spell list of your Spellcasting class.</p>
     <p><b>Mark of Detection Spells</b></p>
-    <table>
+    <table class='table'>>
       <tr>
         <th>Spell Level</th>
         <th>Spells</th>

+ 3 - 3
src/services/subclass/subclass.service.ts

@@ -336,7 +336,7 @@ export class SubclassService {
           <p>In combat, the beast acts during your turn. It can move and use its reaction on its own, but the only action it takes is the Dodge action, unless you take a bonus action on your turn to command it to take another action. That action can be one in its stat block or some other action. You can also sacrifice one of your attacks when you take the Attack action to command the beast to take the Attack action. If<br />
           you are incapacitated, the beast can take any action of its choice, not just Dodge.</p>
           <p>If the beast has died within the last hour, you can use your action to touch it and expend a spell slot of 1st level or higher. The beast returns to life after 1 minute with all its hit points restored. When you finish a long rest, you can summon a different primal beast. The new beast appears in an unoccupied space within 5 feet of you, and you choose its stat block and appearance. If you already have a beast from this feature, it vanishes when the new beast appears. The beast also vanishes if you die.</p>
-          <table class="wiki-content-table">
+          <table 
           <tr>
           <th colspan="6">Beast of the Land</th>
           </tr>
@@ -393,7 +393,7 @@ export class SubclassService {
           <td colspan="6"><strong><em>Maul.</em></strong> <em>Melee Weapon Attack:</em> your spell attack modifier to hit, reach 5 ft., one target. <em>Hit:</em> 1d8 + 2 + PB slashing damage.</td>
           </tr>
           </table>
-          <table class="wiki-content-table">
+          <table 
           <tr>
           <th colspan="6">Beast of the Sea</th>
           </tr>
@@ -450,7 +450,7 @@ export class SubclassService {
           <td colspan="6"><strong><em>Binding Strike.</em></strong> <em>Melee Weapon Attack:</em> your spell attack modifier to hit, reach 5 ft., one target. <em>Hit:</em> 1d6 + 2 + PB piercing or bludgeoning damage (your choice), and the target is grappled (escape DC equals your spell save DC). Until this grapple ends, the beast can't use this attack on another target.</td>
           </tr>
           </table>
-          <table class="wiki-content-table">
+          <table 
           <tr>
           <th colspan="6">Beast of the Sky</th>
           </tr>

+ 105 - 156
src/styles.scss

@@ -5,180 +5,111 @@
 @import "bootstrap/scss/bootstrap";
 @import url("./helpers.scss");
 @import url("./button-styles.scss");
+@import url("./colors.scss");
+// @import url("./responsive.scss");
 
-:root {
-  // shadows
-  --shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.5);
-  --shadow-hover: 0px 5px 10px 0px rgba(0, 0, 0, 0.7);
-  --shadow: 4px 4px 10px 4px rgba(0, 0, 0, 0.2);
-  --shadow-medium: 4px 4px 10px 6px rgba(0, 0, 0, 0.2);
-  --shadow-large: 4px 4px 10px 8px rgba(0, 0, 0, 0.2);
-
-  // OFFICIAL COLORS
-
-  --primary-old: #d8ac96;
-  --primary: #b0826b;
-
-  // Buttons
-  --accept: #84a36f;
-  --accept-hover: #6f9158;
-  --delete: #a45a52;
-  --delete-hover: #8f4a42;
-  --edit: #86a5b7;
-  --edit-hover: #6f8e9f;
-  --neutral: #b9835d;
-  --neutral-hover: #a76b43;
-  --tab: #c79980;
-  --tab-active: #bc8567;
-
-  // Spellcards
-
-  --abjuration-background: #cab8de;
-  --abjuration-border: 0 0 0 2px #171314, 0 0 0 5px #9586a5;
-  --conjuration-background: #714c81;
-  --conjuration-border: 0 0 0 2px #171314, 0 0 0 5px #744987;
-  --divination-background: #f0e3ce;
-  --divination-border: 0 0 0 2px #171314, 0 0 0 5px #ffffff;
-  --enchantment-background: #629179;
-  --enchantment-border: 0 0 0 2px #171314, 0 0 0 5px #26714a;
-  --evocation-background: #a46a7d;
-  --evocation-border: 0 0 0 2px #171314, 0 0 0 5px #863f57;
-  --illusion-background: #69a;
-  --illusion-border: 0 0 0 2px #171314, 0 0 0 5px #2d6475;
-  --necromancy-background: #000000;
-  --necromancy-border: 0 0 0 2px #171314, 0 0 0 5px #36353a;
-  --transmutation-background: rgb(51, 52, 138);
-  --transmutation-border: 0 0 0 2px #171314, 0 0 3px #8f088f, 0 0 6px #7f088f,
-    0 0 9px #69088f, 0 0 12px #3a088f, 0 0 15px #080a8f;
-
-  // General
-
-  --shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.5);
-  --shadow-inverted: 0px -5px 10px 0px rgba(0, 0, 0, 0.5);
-  --shadow-bottom: 0px 5px 10px 0px rgba(0, 0, 0, 0.5);
-  --shadow-top: 0px -5px 10px 0px rgba(0, 0, 0, 0.5);
-
-  --primary-color: #a9836f;
-  --background-color: #fff2e9;
-  --field-background-color: #efc8af;
-  --border-color: #8d8c8c;
-
-  --modal-background: antiquewhite;
-  --header: #ffdec6;
-  --text: #000000;
-  --items: #fff2e9;
-  --items-hover: #f7e3d7;
-
-  --border: 1px solid var(--border-color);
-
-  // Responsiveness
-  .responsive-small {
-    display: none;
-  }
+@import "responsive";
 
-  .responsive-large {
-    display: block;
-  }
+// Hide scrollbar for Chrome, Safari and Opera
+*::-webkit-scrollbar {
+  display: none;
+}
 
-  @media (width < 1640px) {
-    .responsive-large {
-      display: none;
-    }
-    .responsive-small {
-      display: block;
-    }
-  }
+// Hide scrollbar for Firefox
+* {
+  scrollbar-width: none;
+  -ms-overflow-style: none; // IE and Edge
+}
 
-  // LISTS
+// LISTS
 
-  .item-list {
-    width: 100%;
-    overflow: auto;
-  }
+.item-list {
+  width: 100%;
+  overflow: auto;
+}
 
-  .item {
-    background-color: var(--items);
-    box-sizing: border-box;
-    border: var(--border);
-    border-radius: 10px;
-    box-shadow: var(--shadow);
-    cursor: move;
-    transition: background-color 0.2s ease-in-out;
-    &:hover {
-      background-color: var(--items-hover);
-    }
+.item {
+  background-color: var(--items);
+  box-sizing: border-box;
+  border: var(--border);
+  border-radius: 10px;
+  box-shadow: var(--shadow);
+  cursor: move;
+  transition: background-color 0.2s ease-in-out;
+  &:hover {
+    background-color: var(--items-hover);
   }
+}
 
-  .empty-list {
-    text-align: center;
-    margin-top: 2rem;
-    font-size: 1.25rem;
-    font-weight: 500;
-  }
+.empty-list {
+  text-align: center;
+  margin-top: 2rem;
+  font-size: 1.25rem;
+  font-weight: 500;
+}
 
-  .footer {
-    height: 5rem;
-    width: 100%;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    border-radius: 0 0 10px 10px;
-    box-shadow: var(--shadow-top);
-  }
+.footer {
+  height: 5rem;
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 0 0 10px 10px;
+  box-shadow: var(--shadow-top);
+}
 
-  // DRAG AND DROP
+// DRAG AND DROP
 
-  .cdk-drag-preview {
-    box-sizing: border-box;
-    border-radius: 10px;
-    background-color: var(--items-hover);
-    box-shadow:
-      0 5px 5px -3px rgba(0, 0, 0, 0.2),
-      0 8px 10px 1px rgba(0, 0, 0, 0.14),
-      0 3px 14px 2px rgba(0, 0, 0, 0.12);
-  }
+.cdk-drag-preview {
+  box-sizing: border-box;
+  border-radius: 10px;
+  background-color: var(--items-hover);
+  box-shadow:
+    0 5px 5px -3px rgba(0, 0, 0, 0.2),
+    0 8px 10px 1px rgba(0, 0, 0, 0.14),
+    0 3px 14px 2px rgba(0, 0, 0, 0.12);
+}
 
-  .cdk-drag-placeholder {
-    opacity: 0;
-  }
+.cdk-drag-placeholder {
+  opacity: 0;
+}
 
-  .cdk-drag-animating {
-    transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
-  }
+.cdk-drag-animating {
+  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
+}
 
-  .item-list.cdk-drop-list-dragging .item:not(.cdk-drag-placeholder) {
-    transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
-  }
+.item-list.cdk-drop-list-dragging .item:not(.cdk-drag-placeholder) {
+  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
+}
 
-  // GENERAL STYLES
+// GENERAL STYLES
 
-  //  Checkbox
+//  Checkbox
 
-  input[type="checkbox"] {
-    accent-color: var(--accept) !important;
-  }
+input[type="checkbox"] {
+  accent-color: var(--accept) !important;
+}
 
-  // Scrollbar
-  ::-webkit-scrollbar {
-    width: 0.5rem;
-    margin: 0.125rem 0;
-    height: 0.5rem;
-  }
+// Scrollbar
+::-webkit-scrollbar {
+  width: 0.5rem;
+  margin: 0.125rem 0;
+  height: 0.5rem;
+}
 
-  ::-webkit-scrollbar-track {
-    background: #f1f1f1;
-    border-radius: 10px;
-  }
+::-webkit-scrollbar-track {
+  background: #f1f1f1;
+  border-radius: 10px;
+}
 
-  ::-webkit-scrollbar-thumb {
-    background: #888;
-    border-radius: 10px;
-  }
+::-webkit-scrollbar-thumb {
+  background: #888;
+  border-radius: 10px;
+}
 
-  ::-webkit-scrollbar-thumb:hover {
-    background: #555;
-    border-radius: 10px;
-  }
+::-webkit-scrollbar-thumb:hover {
+  background: #555;
+  border-radius: 10px;
 }
 
 .suffix {
@@ -209,11 +140,29 @@
   box-shadow: var(--shadow);
   border-radius: 10px;
   height: 6rem;
-  @media (width > 1699px) {
+  width: 7rem;
+
+  @include width-small {
     width: 10rem;
   }
-  @media (width < 1640px) {
-    width: 7rem;
+}
+// // Responsive styles
+.responsive-small {
+  display: block;
+}
+
+.responsive-large {
+  display: none;
+}
+.responsive-large {
+  @include width-small {
+    display: block;
+  }
+}
+
+.responsive-small {
+  @include width-small {
+    display: none;
   }
 }