Explorar o código

Finished attribute and save details components. Next is skills details

Christopher Giese hai 1 ano
pai
achega
50ef2401a5
Modificáronse 19 ficheiros con 330 adicións e 67 borrados
  1. 75 0
      .nx/cache/d/daemon.log
  2. 1 1
      .nx/cache/d/server-process.json
  3. 1 0
      src/app/journal/journal-home/details-panel/details-panel.component.scss
  4. 30 48
      src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-details/attribute-details.component.html
  5. 32 4
      src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-details/attribute-details.component.scss
  6. 21 0
      src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-details/attribute-details.component.ts
  7. 2 1
      src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-field.component.scss
  8. 1 2
      src/app/journal/journal-stats/attribute-skill-container/attribute-panel/attribute-field/attribute-field.component.ts
  9. 16 0
      src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-details/save-throw-details.component.html
  10. 36 0
      src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-details/save-throw-details.component.scss
  11. 23 0
      src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-details/save-throw-details.component.spec.ts
  12. 11 0
      src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-details/save-throw-details.component.ts
  13. 8 4
      src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-field/save-throw-field.component.html
  14. 63 3
      src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-field/save-throw-field.component.ts
  15. 1 1
      src/app/journal/journal-stats/attribute-skill-container/save-throw-panel/save-throw-panel.component.html
  16. 1 1
      src/app/journal/journal-stats/attribute-skill-container/skill-panel/skill-field/skill-field.component.ts
  17. 2 0
      src/app/journal/journal.module.ts
  18. 1 1
      src/interfaces/attribute.ts
  19. 5 1
      src/styles.scss

+ 75 - 0
.nx/cache/d/daemon.log

@@ -459745,3 +459745,78 @@ To fix this, set a unique name for each project in a project.json inside the pro
 [NX Daemon Server] - 2023-11-19T09:40:23.437Z - [WATCHER]: Processing file changes in outputs
 [NX Daemon Server] - 2023-11-19T09:40:23.438Z - [WATCHER]: .nx/cache/d/daemon.log was modified
 [NX Daemon Server] - 2023-11-19T09:40:23.438Z - Done responding to the client null
+[NX Daemon Server] - 2023-11-20T17:45:45.792Z - Started listening on: \\.\pipe\nx\C:\Users\chris\AppData\Local\Temp\e6a479e02f1047051806\d.sock
+[NX Daemon Server] - 2023-11-20T17:45:45.800Z - [WATCHER]: Subscribed to changes within: c:\Users\chris\Softwareprojekte\Test\DnDTools (native)
+[NX Daemon Server] - 2023-11-20T17:45:45.802Z - Established a connection. Number of open connections: 1
+[NX Daemon Server] - 2023-11-20T17:45:45.804Z - Closed a connection. Number of open connections: 0
+[NX Daemon Server] - 2023-11-20T17:45:45.804Z - Established a connection. Number of open connections: 1
+[NX Daemon Server] - 2023-11-20T17:45:45.806Z - [REQUEST]: Client Request for Project Graph Received
+[NX Daemon Server] - 2023-11-20T17:45:46.398Z - Error detected when recomputing project file map: The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+[NX Daemon Server] - 2023-11-20T17:45:46.398Z - [REQUEST]: Responding to the client with an error. Error when preparing serialized project graph. The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+Error: The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+    at readProjectConfigurationsFromRootMap (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\project-graph\utils\project-configuration-utils.js:97:15)
+    at buildProjectsConfigurationsFromProjectPathsAndPlugins (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\project-graph\utils\project-configuration-utils.js:70:19)
+    at createProjectConfigurations (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:131:129)
+    at WorkspaceContext.<anonymous> (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:82:39)
+    at getProjectConfigurationsFromContext (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\utils\workspace-context.js:26:29)
+    at _retrieveProjectConfigurations (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:81:72)
+    at retrieveProjectConfigurations (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:58:12)
+    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
+    at async processCollectedUpdatedAndDeletedFiles (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\daemon\server\project-graph-incremental-recomputation.js:102:34)
+    at async processFilesAndCreateAndSerializeProjectGraph (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\daemon\server\project-graph-incremental-recomputation.js:138:17)
+[NX Daemon Server] - 2023-11-20T17:45:46.399Z - Time taken for 'hash changed files from watcher' 169.283999979496ms
+[NX Daemon Server] - 2023-11-20T17:45:46.400Z - [WATCHER]: Processing file changes in outputs
+[NX Daemon Server] - 2023-11-20T17:45:46.401Z - [WATCHER]: .nx/cache/d/daemon.log was modified
+[NX Daemon Server] - 2023-11-20T17:45:46.401Z - Done responding to the client null
+[NX Daemon Server] - 2023-11-20T18:05:08.380Z - Started listening on: \\.\pipe\nx\C:\Users\chris\AppData\Local\Temp\e6a479e02f1047051806\d.sock
+[NX Daemon Server] - 2023-11-20T18:05:08.386Z - [WATCHER]: Subscribed to changes within: c:\Users\chris\Softwareprojekte\Test\DnDTools (native)
+[NX Daemon Server] - 2023-11-20T18:05:08.387Z - Established a connection. Number of open connections: 1
+[NX Daemon Server] - 2023-11-20T18:05:08.388Z - Established a connection. Number of open connections: 2
+[NX Daemon Server] - 2023-11-20T18:05:08.389Z - Closed a connection. Number of open connections: 1
+[NX Daemon Server] - 2023-11-20T18:05:08.390Z - [REQUEST]: Client Request for Project Graph Received
+[NX Daemon Server] - 2023-11-20T18:05:08.805Z - Error detected when recomputing project file map: The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+[NX Daemon Server] - 2023-11-20T18:05:08.805Z - [REQUEST]: Responding to the client with an error. Error when preparing serialized project graph. The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+Error: The following projects are defined in multiple locations:
+- DnDTools: 
+  - 
+  - .
+
+To fix this, set a unique name for each project in a project.json inside the project's root. If the project does not currently have a project.json, you can create one that contains only a name.
+    at readProjectConfigurationsFromRootMap (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\project-graph\utils\project-configuration-utils.js:97:15)
+    at buildProjectsConfigurationsFromProjectPathsAndPlugins (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\project-graph\utils\project-configuration-utils.js:70:19)
+    at createProjectConfigurations (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:131:129)
+    at WorkspaceContext.<anonymous> (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:82:39)
+    at getProjectConfigurationsFromContext (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\utils\workspace-context.js:26:29)
+    at _retrieveProjectConfigurations (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:81:72)
+    at retrieveProjectConfigurations (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\project-graph\utils\retrieve-workspace-files.js:58:12)
+    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
+    at async processCollectedUpdatedAndDeletedFiles (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\daemon\server\project-graph-incremental-recomputation.js:102:34)
+    at async processFilesAndCreateAndSerializeProjectGraph (c:\Users\chris\Softwareprojekte\Test\DnDTools\node_modules\nx\src\daemon\server\project-graph-incremental-recomputation.js:138:17)
+[NX Daemon Server] - 2023-11-20T18:05:08.806Z - Time taken for 'hash changed files from watcher' 22.765500009059906ms
+[NX Daemon Server] - 2023-11-20T18:05:08.807Z - [WATCHER]: Processing file changes in outputs
+[NX Daemon Server] - 2023-11-20T18:05:08.807Z - Done responding to the client null

+ 1 - 1
.nx/cache/d/server-process.json

@@ -1 +1 @@
-{"processId":10560}
+{"processId":15616}

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

@@ -23,6 +23,7 @@
     width: 25vw;
     background-color: var(--background-color);
     z-index: 999;
+    padding: 1.5rem;
     transform: translateX(100%);
     transition: transform 0.3s ease-in-out;
 }

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

@@ -14,8 +14,16 @@
   @default{ Leider gab es einen Fehler bei der Datenübertragung. Bitte öffne ein
   Issue auf Github. } }
 </div>
-
-<div class="attribute-value">{{ attribute.value }} => {{ modifier }}</div>
+<div class="attribute-container">
+  <div>
+    <div class="attribute-value">{{ attribute.value }}</div>
+    <div class="attribute-label">Attributwert</div>
+  </div>
+  <div>
+    <div class="attribute-value">{{ modifier }}</div>
+    <div class="attribute-label">Modifikator</div>
+  </div>
+</div>
 <div class="modifier-table">
   <table class="table table-striped">
     <thead>
@@ -72,42 +80,40 @@
     </tbody>
   </table>
 </div>
+@if(attribute.name !== 'constitution'){
+<div class="details-subheading">Beeinflusste Fähigkeiten:</div>
+<div class="skills">
+  @for(skill of skillsTable[attribute.name]; track skill){
+  <div class="skill-item">{{ skill }}</div>
+  }
+</div>
+}
 
 <ng-template #strengthTemplate>
-  <div class="attribute-name">Stärke</div>
-  <div class="attribute-description">
+  <div class="details-title">Stärke</div>
+  <div class="details-content">
     Stärke misst die physische Kraft und Muskulatur deines Charakters. Ein hoher
     Stärkewert deutet auf körperliche Robustheit, Muskelkraft und
     Durchsetzungsvermögen hin. Charaktere mit hoher Stärke sind oft besser in
     der Lage, schwere Lasten zu tragen, mächtige Waffen zu schwingen und
     physische Herausforderungen zu bewältigen.
   </div>
-  <div class="skills">
-    Beeinflusste Fähigkeiten:
-    <div class="skill-item">Athletik</div>
-  </div>
 </ng-template>
 
 <ng-template #dexterityTemplate>
-  <div class="attribute-name">Geschicklichkeit</div>
-  <div class="attribute-description">
+  <div class="details-title">Geschicklichkeit</div>
+  <div class="details-content">
     Geschicklichkeit repräsentiert die Beweglichkeit, Reflexe und allgemeine
     Körperbeherrschung deines Charakters. Ein hoher Geschicklichkeitswert deutet
     auf schnelle Reaktionen, geschickte Bewegungen und eine gute
     Hand-Auge-Koordination hin. Charaktere mit hoher Geschicklichkeit sind oft
     geschickte Diebe, Bogenschützen oder Akrobaten.
   </div>
-  Beeinflusste Fähigkeiten:
-  <div class="skills">
-    <div class="skill-item">Akrobatik</div>
-    <div class="skill-item">Fingerfertigkeit</div>
-    <div class="skill-item">Heimlichkeit</div>
-  </div>
 </ng-template>
 
 <ng-template #constitutionTemplate>
-  <div class="attribute-name">Konstitution</div>
-  <div class="attribute-description">
+  <div class="details-title">Konstitution</div>
+  <div class="details-content">
     Konstitution steht für die Widerstandsfähigkeit, Gesundheit und Ausdauer
     deines Charakters. Ein hoher Konstitutionswert bedeutet, dass dein Charakter
     robust ist und gut mit physischem Stress umgehen kann. Charaktere mit hoher
@@ -117,45 +123,28 @@
 </ng-template>
 
 <ng-template #intelligenceTemplate>
-  <div class="attribute-name">Intelligenz</div>
-  <div class="attribute-description">
+  <div class="details-title">Intelligenz</div>
+  <div class="details-content">
     Intelligenz misst die geistige Schärfe, das logische Denkvermögen und die
     Lernfähigkeit deines Charakters. Ein hoher Intelligenzwert deutet auf eine
     gute Allgemeinbildung, Wissensdurst und analytische Fähigkeiten hin.
     Charaktere mit hoher Intelligenz sind oft Magier, Forscher oder Strategen.
   </div>
-  Beeinflusste Fähigkeiten:
-  <div class="skills">
-    <div class="skill-item">Arkana</div>
-    <div class="skill-item">Geschichte</div>
-    <div class="skill-item">Nachforschung</div>
-    <div class="skill-item">Natur</div>
-    <div class="skill-item">Religion</div>
-  </div>
 </ng-template>
 
 <ng-template #wisdomTemplate>
-  <div class="attribute-name">Weisheit</div>
-  <div class="attribute-description">
+  <div class="details-title">Weisheit</div>
+  <div class="details-content">
     Beschreibung: Weisheit repräsentiert die Wahrnehmung, Intuition und
     emotionale Intelligenz deines Charakters. Ein hoher Weisheitswert deutet auf
     eine gute Urteilsfähigkeit, Einsicht und innere Stärke hin. Charaktere mit
     hoher Weisheit sind oft Kleriker, Druiden oder Weise.
   </div>
-  Beeinflusste Fähigkeiten:
-  <div class="skills">
-    <div class="skill-item">Tierkunde</div>
-    <div class="skill-item">Geschichte</div>
-    <div class="skill-item">Motiv erkennen</div>
-    <div class="skill-item">Heilkunde</div>
-    <div class="skill-item">Wahrnehmung</div>
-    <div class="skill-item">Überlebenskunst</div>
-  </div>
 </ng-template>
 
 <ng-template #charismaTemplate>
-  <div class="attribute-name">Charisma</div>
-  <div class="attribute-description">
+  <div class="details-title">Charisma</div>
+  <div class="details-content">
     Charisma spiegelt die Ausstrahlung, Überzeugungskraft und soziale
     Fähigkeiten deines Charakters wider. Ein hoher Charismawert bedeutet, dass
     dein Charakter charmant, einnehmend und überzeugend ist. Charaktere mit
@@ -163,11 +152,4 @@
     Führungsaufgaben übernehmen. Charisma ist oft wichtig für Barden, Anführer
     oder Diplomaten.
   </div>
-  Beeinflusste Fähigkeiten:
-  <div class="skills">
-    <div class="skill-item">Täuschung</div>
-    <div class="skill-item">Einschütern</div>
-    <div class="skill-item">Auftreten</div>
-    <div class="skill-item">Überzeugen</div>
-  </div>
 </ng-template>

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

@@ -1,18 +1,46 @@
+.attribute-container{
+    display: flex;
+    justify-content: space-around;
+    margin-top: 2rem;
+}
 
+.attribute-value{
+    width: 4rem;
+    height: 3rem;
+    font-size: 1.5rem;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin:auto;
+    border-radius: 10px;
+    background-color: white;
+    box-shadow: var(--shadow-small);
+}
 
-
+.attribute-label{
+    margin-top: 0.5rem;
+    font-weight: 500;
+}
 
 .skills{
     display: flex;
-    gap: 0.5rem;
     flex-wrap: wrap;
-    margin-left: 1rem;
-
+    gap: 0.5rem;
+    margin-top: 0.5rem;
 }
 
 .skill-item{
     background-color: var(--primary-color);
     border-radius: 1rem;
+    font-weight: 500;
     padding: 0.25rem 0.5rem;
     box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.75);
+}
+
+.modifier-table{
+    margin-top: 1.5rem;
+}
+
+td{
+    padding: .375rem 1rem
 }

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

@@ -11,4 +11,25 @@ export class AttributeDetailsComponent {
   public constructor(public detailsService: DetailsService) {}
   @Input() public attribute!: Attribute;
   @Input() public modifier!: string;
+
+  public skillsTable: any = {
+    strength: ['Athletik'],
+    dexterity: ['Akrobatik', 'Fingerfertigkeit', 'Heimlichkeit'],
+    constitution: [],
+    intelligence: [
+      'Arkana',
+      'Geschichte',
+      'Nachforschung',
+      'Naturkunde',
+      'Religion',
+    ],
+    wisdom: [
+      'Tierkunde',
+      'Motiv erkennen',
+      'Heilkunde',
+      'Wahrnehmung',
+      'Überlebenskunde',
+    ],
+    charisma: ['Täuschung', 'Einschüchterung', 'Auftreten', 'Überzeugung'],
+  };
 }

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

@@ -1,9 +1,10 @@
 .attribute-box{
     border: solid 1px black;
     text-align: center;
+    cursor: pointer;
 
     .attribute-name{
-        // font-size: 1.5rem;
+        cursor: pointer;
         font-weight: 600;
     }
 

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

@@ -12,7 +12,7 @@ import { AttributeDetailsComponent } from './attribute-details/attribute-details
 })
 export class AttributeFieldComponent {
   @Input() attributeName: string = '';
-  public attribute: Attribute = { name: '', value: 0 };
+  public attribute: Attribute = { name: '', value: 0, proficiency: false };
   public attributeModifier: string = '0';
   public saveModifier: string = '0';
   public proficiencyBonus: number = 0;
@@ -32,7 +32,6 @@ export class AttributeFieldComponent {
   ) {}
 
   ngOnInit(): void {
-    this.initProficiencySubscription();
     this.initAttributeSubscription();
   }
 

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

@@ -0,0 +1,16 @@
+<div class="details-title">{{ attributeName }}</div>
+<div class="save-subheading">Rettungswurf</div>
+
+<div class="details-content">
+  Ein Rettungswurf wird immer dann verlangt, wenn überprüft werden soll, ob ein
+  Spieler einem Angriff oder äußeren Einfluss widerstehen kann. Durch das
+  bestehen eines Rettungswurfes kann der Effekt verhindert, oder zumindest
+  abgeschwächt werden.
+</div>
+
+<div class="value-container">
+  <div class="save-value">
+    {{ saveModifier }}
+  </div>
+  <div class="save-label">Modifikator</div>
+</div>

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

@@ -0,0 +1,36 @@
+.save-subheading{
+    font-size: 1.25rem;
+    text-align: center;
+    font-weight: bold;
+}
+
+.value-container{
+    display: flex;
+    flex-direction: column;
+    margin-top: 2rem;
+}
+
+.save-value{
+    width: 3.5rem;
+    height: 3rem;
+    font-size: 1.5rem;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin:auto;
+    border-radius: 10px;
+    background-color: white;
+    box-shadow: var(--shadow-small);
+}
+
+.save-label{
+    margin-top: 0.5rem;
+    font-weight: 500;
+    text-align: center;
+}
+
+.save-calculation-heading{
+    font-size: 1.25rem;
+    font-weight: 500;
+}
+

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

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

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

@@ -0,0 +1,11 @@
+import { Component, Input } from '@angular/core';
+
+@Component({
+  selector: 'app-save-throw-details',
+  templateUrl: './save-throw-details.component.html',
+  styleUrl: './save-throw-details.component.scss',
+})
+export class SaveThrowDetailsComponent {
+  @Input() attributeName: string = '';
+  @Input() saveModifier: string = '0';
+}

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

@@ -1,7 +1,11 @@
-<div class="save-throw-field">
-  <input class="save-throw-field__input" type="checkbox" />
+<div class="save-throw-field" (click)="openDetails()">
+  <input
+    class="save-throw-field__input"
+    type="checkbox"
+    [(ngModel)]="attribute.proficiency"
+  />
 
-  <div class="save-throw-field__name">{{ nameTranslator[attribute] }}</div>
+  <div class="save-throw-field__name">{{ nameTranslator[attributeName] }}</div>
 
-  <div class="save-throw-field__value">{{ modifier }}</div>
+  <div class="save-throw-field__value">{{ saveModifier }}</div>
 </div>

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

@@ -1,5 +1,9 @@
 import { Component, Input } from '@angular/core';
-import { FormControl } from '@angular/forms';
+import { DataService } from 'src/services/data/data.service';
+import { Observable } from 'rxjs';
+import { Attribute } from 'src/interfaces/attribute';
+import { DetailsService } from 'src/services/details/details.service';
+import { SaveThrowDetailsComponent } from '../save-throw-details/save-throw-details.component';
 
 @Component({
   selector: 'save-throw-field',
@@ -7,9 +11,18 @@ import { FormControl } from '@angular/forms';
   styleUrls: ['./save-throw-field.component.scss'],
 })
 export class SaveThrowFieldComponent {
-  @Input() attribute: string = '';
+  constructor(
+    public dataAccessor: DataService,
+    public detailsAccessor: DetailsService
+  ) {}
 
-  public modifier: string = '+5';
+  @Input() attributeName: string = '';
+
+  public attribute: Attribute = { name: '', value: 0, proficiency: false };
+
+  private proficiencyBonus: number = 0;
+  private attributeModifier: number = 0;
+  public saveModifier: string = '0';
 
   public nameTranslator: any = {
     strength: 'Stärke',
@@ -19,4 +32,51 @@ export class SaveThrowFieldComponent {
     wisdom: 'Weisheit',
     charisma: 'Charisma',
   };
+
+  public ngOnInit(): void {
+    this.initAttributeSubscription();
+    this.initProficiencySubscription();
+  }
+
+  private initAttributeSubscription(): void {
+    const observable: Observable<Attribute> = eval(
+      `this.dataAccessor.${this.attributeName}$`
+    );
+    observable.subscribe((newValue: Attribute) => {
+      this.attribute = newValue;
+      this.attributeModifier = this.calculateAttributeModifier();
+      this.saveModifier = this.calculateSaveModifier();
+    });
+  }
+
+  private initProficiencySubscription(): void {
+    this.dataAccessor.proficiency$.subscribe((newValue: any) => {
+      this.proficiencyBonus = newValue.value;
+      this.attributeModifier = this.calculateAttributeModifier();
+      this.saveModifier = this.calculateSaveModifier();
+    });
+  }
+
+  private calculateAttributeModifier(): number {
+    return Math.floor((this.attribute.value - 10) / 2);
+  }
+
+  public calculateSaveModifier(): string {
+    let mod = this.attributeModifier;
+    if (this.attribute.proficiency) {
+      mod += this.proficiencyBonus;
+    }
+    if (mod > 0) {
+      return '+' + mod;
+    } else {
+      return mod.toString();
+    }
+  }
+
+  public openDetails(): void {
+    this.detailsAccessor.openPanel(SaveThrowDetailsComponent, {
+      attributeName: this.nameTranslator[this.attributeName],
+      saveModifier: this.saveModifier,
+    });
+  }
 }

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

@@ -1,5 +1,5 @@
 <div class="save-throw-panel">
   <ng-container *ngFor="let attribute of attributes">
-    <save-throw-field [attribute]="attribute"></save-throw-field>
+    <save-throw-field [attributeName]="attribute"></save-throw-field>
   </ng-container>
 </div>

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

@@ -12,7 +12,7 @@ import { Attribute } from 'src/interfaces/attribute';
 export class SkillFieldComponent {
   @Input() skillName: string = '';
   public skill: Skill = { name: '', proficiency: false };
-  private attribute: Attribute = { name: '', value: 0 };
+  private attribute: Attribute = { name: '', value: 0, proficiency: false };
   public skillModifier: string = '0';
   public skillProficiency: boolean = false;
   private proficiencyBonus: number = 0;

+ 2 - 0
src/app/journal/journal.module.ts

@@ -57,6 +57,7 @@ import { SkillPanelComponent } from './journal-stats/attribute-skill-container/s
 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 { 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';
 
 @NgModule({
   declarations: [
@@ -108,6 +109,7 @@ import { ProficiencyFieldComponent } from './journal-stats/info-row/proficiency/
     InfoRowComponent,
     ConditionsComponent,
     AttributeDetailsComponent,
+    SaveThrowDetailsComponent,
   ],
   imports: [
     CommonModule,

+ 1 - 1
src/interfaces/attribute.ts

@@ -1,5 +1,5 @@
 export interface Attribute {
   name: string;
   value: number;
-  proficiency?: boolean;
+  proficiency: boolean;
 }

+ 5 - 1
src/styles.scss

@@ -122,9 +122,13 @@ $dialog-position-right: 20%;
     margin-top: 1.5rem;
 }
 
+.details-bold{
+    font-weight: 500;
+}
+
 .details-content{
     font-size: 1rem;
-    margin-top: .5rem;
+    margin-top: 1.5rem;
     font-weight: 400;
 }