First Approach to Edit Template Gamesystems from Characters perspective
All checks were successful
E2E Testing / test (push) Successful in 1m33s

This commit is contained in:
Sebastian Böckelmann 2024-03-22 17:06:19 +01:00
parent a54a652358
commit b7ccfe5201
16 changed files with 416 additions and 12 deletions

View File

@ -70,6 +70,18 @@ import {
} from "./editor/gamesystem-editor/scriptaccount-condition-editor/scriptaccount-condition-editor.component";
import {CharacterOverviewComponent} from "./side-overviews/character-overview/character-overview.component";
import {CharacterEditorComponent} from "./editor/character-editor/character-editor.component";
import {
TemplateGamesystemEditorComponent
} from "./editor/gamesystem-editor/template-gamesystem-editor/template-gamesystem-editor.component";
import {
TemplateStateEditorComponent
} from "./editor/gamesystem-editor/template-gamesystem-editor/template-state-editor/template-state-editor.component";
import {
MatAccordion,
MatExpansionPanel,
MatExpansionPanelHeader,
MatExpansionPanelTitle
} from "@angular/material/expansion";
// AoT requires an exported function for factories
const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new TranslateHttpLoader(http, './assets/i18n/', '.json');
@ -93,7 +105,9 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl
ScriptaccountActionEditorComponent,
ScriptaccountConditionEditorComponent,
CharacterOverviewComponent,
CharacterEditorComponent
CharacterEditorComponent,
TemplateGamesystemEditorComponent,
TemplateStateEditorComponent
],
imports: [
BrowserModule,
@ -150,9 +164,16 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl
MatHint,
MatTooltip,
MatCard,
MatCardContent
MatCardContent,
MatAccordion,
MatExpansionPanel,
MatExpansionPanelTitle,
MatExpansionPanelHeader
],
providers: [],
exports: [
ProductGamesystemEditorComponent
],
bootstrap: [AppComponent]
})
export class AppModule {}

View File

@ -1 +1,8 @@
<p>character-editor works!</p>
<mat-accordion>
<mat-expansion-panel *ngFor="let templateGamesystem of templateSimpleGamesystems">
<mat-expansion-panel-header>
<mat-panel-title>{{templateGamesystem.referenceGamesystem.componentName}}</mat-panel-title>
</mat-expansion-panel-header>
<app-template-gamesystem-editor [gamesystem]="templateGamesystem"></app-template-gamesystem-editor>
</mat-expansion-panel>
</mat-accordion>

View File

@ -1,10 +1,31 @@
import { Component } from '@angular/core';
import {Component, Input, OnInit} from '@angular/core';
import {Character} from "../../project/game-model/characters/Character";
import {Gamesystem} from "../../project/game-model/gamesystems/Gamesystem";
import {TemplateGamesystem} from "../../project/game-model/gamesystems/TemplateGamesystem";
import {ProductGamesystem} from "../../project/game-model/gamesystems/ProductGamesystem";
import {SimpleGamesystem} from "../../project/game-model/gamesystems/SimpleGamesystem";
@Component({
selector: 'app-character-editor',
templateUrl: './character-editor.component.html',
styleUrl: './character-editor.component.scss'
})
export class CharacterEditorComponent {
export class CharacterEditorComponent implements OnInit{
@Input() character: Character | undefined
@Input() referenceGamesystems: Gamesystem<any, any>[] = []
templateSimpleGamesystems: TemplateGamesystem[] = []
templateProductGamesystems: ProductGamesystem[] = []
ngOnInit() {
this.referenceGamesystems.forEach(referenceGamesystem => {
if(referenceGamesystem instanceof SimpleGamesystem) {
this.templateSimpleGamesystems.push(new TemplateGamesystem(referenceGamesystem))
} else {
this.templateProductGamesystems.push(referenceGamesystem as ProductGamesystem)
}
})
}
}

View File

@ -14,7 +14,9 @@
[gamesystem]="convertModelComponentToGamesystem(modelComponent)"
(onOpenGamesystemEditor)="openGameModelComponent($event)"
[scriptAccounts]="gameModel!.scriptAccounts"></app-gamesystem-editor>
<app-character-editor *ngIf="modelComponent.type === ModelComponentType.CHARACTER">
<app-character-editor *ngIf="modelComponent.type === ModelComponentType.CHARACTER"
[referenceGamesystems]="loadCharacterSpecificGamesystems()"
[character]="convertModelComponentToCharacter(modelComponent)">
</app-character-editor>
</mat-tab>

View File

@ -6,6 +6,9 @@ import {Gamesystem} from "../project/game-model/gamesystems/Gamesystem";
import {State} from "../project/game-model/gamesystems/states/State";
import {Transition} from "../project/game-model/gamesystems/transitions/Transition";
import {ModelComponentType} from "../project/game-model/ModelComponentType";
import {Character} from "../project/game-model/characters/Character";
import {TemplateType} from "../project/game-model/gamesystems/TemplateType";
import {load} from "@angular-devkit/build-angular/src/utils/server-rendering/esm-in-memory-loader/loader-hooks";
@Component({
@ -44,10 +47,20 @@ export class EditorComponent {
}
}
convertModelComponentToCharacter(modelComponent: ModelComponent) {
if(modelComponent instanceof Character) {
return modelComponent as Character
}
}
onModelNameUpdate() {
this.onModelNameUpdateEmitter.emit(true);
}
loadCharacterSpecificGamesystems() {
return this.gameModel!.gamesystems.filter(gamesystem => gamesystem.template == TemplateType.CHARACTER);
}
protected readonly ModelComponentType = ModelComponentType;
}

View File

@ -1,4 +1,4 @@
import {Component, Input, OnInit} from '@angular/core';
import {Component, EventEmitter, Input, OnChanges, OnInit, Output} from '@angular/core';
import {MatTableDataSource} from "@angular/material/table";
import {animate, state, style, transition, trigger} from "@angular/animations";
import {MatSnackBar} from "@angular/material/snack-bar";
@ -6,6 +6,7 @@ import {SimpleState} from "../../../../project/game-model/gamesystems/states/Sim
import {SimpleGamesystem} from "../../../../project/game-model/gamesystems/SimpleGamesystem";
import {ScriptAccount} from "../../../../project/game-model/scriptAccounts/ScriptAccount";
import {ScriptAccountCondition} from "../../../../project/game-model/gamesystems/conditions/ScriptAccountCondition";
import {TemplateGamesystem} from "../../../../project/game-model/gamesystems/TemplateGamesystem";
@Component({
selector: 'app-simple-state-editor',
@ -19,11 +20,12 @@ import {ScriptAccountCondition} from "../../../../project/game-model/gamesystems
]),
],
})
export class SimpleStateEditorComponent implements OnInit{
export class SimpleStateEditorComponent implements OnInit, OnChanges{
@Input() states: SimpleState[] = [];
@Input() gamesystem: SimpleGamesystem | undefined
@Input() scriptAccounts: ScriptAccount[] = []
@Output() onExtractReferenceState = new EventEmitter<SimpleState>()
dataSource = new MatTableDataSource<SimpleState>();
displayedColumns = ["name", "initial", "edit", "delete"];
columnsToDisplayWithExpand = [...this.displayedColumns, 'expand'];
@ -40,15 +42,32 @@ export class SimpleStateEditorComponent implements OnInit{
this.dataSource.filterPredicate = (data: SimpleState, filter: string) => {
return data.stateLabel.toLowerCase().includes(filter);
}
if(this.gamesystem == undefined) {
this.displayedColumns = this.displayedColumns.slice(0, -1);
this.columnsToDisplayWithExpand = [... this.displayedColumns, 'expand']
}
}
ngOnChanges() {
this.dataSource.data = this.states;
this.dataSource.filterPredicate = (data: SimpleState, filter: string) => {
return data.stateLabel.toLowerCase().includes(filter);
}
console.log(this.states.length)
}
editState(state: SimpleState) {
if(this.gamesystem == undefined) {
this.onExtractReferenceState.emit(state)
} else {
if(this.editedElement === state) {
this.editedElement = null;
} else {
this.editedElement = state;
}
}
}
finishEditing() {
if(this.isEditedStateValid()) {

View File

@ -0,0 +1 @@
<app-template-state-editor [templateGamesystem]="gamesystem"></app-template-state-editor>

View File

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

View File

@ -0,0 +1,17 @@
import {Component, Input} from '@angular/core';
import {SimpleGamesystem} from "../../../project/game-model/gamesystems/SimpleGamesystem";
import {ScriptAccount} from "../../../project/game-model/scriptAccounts/ScriptAccount";
import {Gamesystem} from "../../../project/game-model/gamesystems/Gamesystem";
import {ProductGamesystem} from "../../../project/game-model/gamesystems/ProductGamesystem";
import {TemplateGamesystem} from "../../../project/game-model/gamesystems/TemplateGamesystem";
@Component({
selector: 'app-template-gamesystem-editor',
templateUrl: './template-gamesystem-editor.component.html',
styleUrl: './template-gamesystem-editor.component.scss'
})
export class TemplateGamesystemEditorComponent {
@Input() gamesystem: TemplateGamesystem | undefined
@Input() scriptAccunts: ScriptAccount[] = []
}

View File

@ -0,0 +1,91 @@
<div class="state-editor-container">
<div class="template-editor">
<mat-card>
<mat-card-content>
<mat-form-field appearance="fill" class="long-form">
<mat-label>Filter</mat-label>
<input matInput (keyup)="applyFilter($event)" placeholder="Filter" #input>
</mat-form-field>
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" multiTemplateDataRows>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>Label</th>
<td mat-cell *matCellDef="let state">
{{state.stateLabel}}
</td>
</ng-container>
<!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->
<ng-container matColumnDef="expandedDetail">
<td mat-cell *matCellDef="let element" [attr.colspan]="columnsToDisplayWithExpand.length">
<div class="example-element-detail" [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'">
<p>{{element.stateDescription}}</p>
<div class="long-form">
<app-scriptaccount-condition-editor [conditions]="element.conditions" [scriptAccounts]="scriptAccounts" [enableEditiong]="true"
(onCreateCondition)="onCreateCondition(element, $event)" (onDeleteCondition)="deleteCondition(element, $event)"></app-scriptaccount-condition-editor>
</div>
</div>
</td>
</ng-container>
<ng-container matColumnDef="initial">
<th mat-header-cell *matHeaderCellDef>Initial</th>
<td mat-cell *matCellDef="let state">
<div *ngIf="editedElement !== state">
<mat-icon *ngIf="state.initial">done</mat-icon>
<mat-icon *ngIf="!state.initial">close</mat-icon>
</div>
<mat-checkbox *ngIf="editedElement === state" [(ngModel)]="state.initial"></mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let state">
<button mat-icon-button class="icon-btn-primary" *ngIf="editedElement !== state" [disabled]="editedElement !== null" (click)="onEditState(state)"><mat-icon>edit</mat-icon></button>
<button mat-icon-button class="icon-btn-primary" *ngIf="editedElement === state" (click)="onEditState(state)"><mat-icon>done</mat-icon></button>
</td>
</ng-container>
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let state">
<button mat-icon-button color="warn" (click)="onDeleteState(state)"><mat-icon>delete</mat-icon></button>
</td>
</ng-container>
<ng-container matColumnDef="expand">
<th mat-header-cell *matHeaderCellDef aria-label="row actions">
</th>
<td mat-cell *matCellDef="let element">
<button mat-icon-button aria-label="expand row" (click)="(expandedElement = expandedElement === element ? null : element); $event.stopPropagation()">
@if (expandedElement === element) {
<mat-icon>keyboard_arrow_up</mat-icon>
} @else {
<mat-icon>keyboard_arrow_down</mat-icon>
}
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columnsToDisplayWithExpand"></tr>
<tr mat-row *matRowDef="let element; columns: columnsToDisplayWithExpand;"
class="example-element-row"
[class.example-expanded-row]="expandedElement === element"
(click)="expandedElement = expandedElement === element ? null : element">
</tr>
<tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="example-detail-row"></tr>
</table>
</mat-card-content>
</mat-card>
</div>
<div class="reference-editor">
<app-simple-state-editor [states]="templateGamesystem!.referenceGamesystem.states"
(onExtractReferenceState)="onExtractReferenceState($event)"></app-simple-state-editor>
</div>
</div>

View File

@ -0,0 +1,65 @@
.state-editor-container {
display: flex;
}
table {
width: 100%;
}
tr.example-detail-row {
height: 0;
}
tr.example-element-row:not(.example-expanded-row):hover {
background: #545456
}
tr.example-element-row:not(.example-expanded-row):active {
background: #545456;
}
.example-element-row td {
border-bottom-width: 0;
}
.example-element-diagram {
min-width: 80px;
border: 2px solid black;
padding: 8px;
font-weight: lighter;
margin: 8px 0;
height: 104px;
}
.example-element-symbol {
font-weight: bold;
font-size: 40px;
line-height: normal;
}
.example-element-description {
padding: 16px;
}
.example-element-description-attribution {
opacity: 0.5;
}
.mat-column-edit, .mat-column-delete, .mat-column-expand {
width: 32px;
}
.long-form {
width: 100%;
}
.mat-error {
color: red;
}
.warning-icon {
margin-right: 5px;
}
.reference-editor, .template-editor {
width: 50%;
}

View File

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

View File

@ -0,0 +1,65 @@
import {Component, Input, ViewChild} from '@angular/core';
import {SimpleState} from "../../../../project/game-model/gamesystems/states/SimpleState";
import {TemplateGamesystem} from "../../../../project/game-model/gamesystems/TemplateGamesystem";
import {SimpleStateEditorComponent} from "../../state-editor/simple-state-editor/simple-state-editor.component";
import {animate, state, style, transition, trigger} from "@angular/animations";
import {ScriptAccount} from "../../../../project/game-model/scriptAccounts/ScriptAccount";
import {MatTableDataSource} from "@angular/material/table";
import {ScriptAccountCondition} from "../../../../project/game-model/gamesystems/conditions/ScriptAccountCondition";
@Component({
selector: 'app-template-state-editor',
templateUrl: './template-state-editor.component.html',
styleUrl: './template-state-editor.component.scss',
animations: [
trigger('detailExpand', [
state('collapsed,void', style({height: '0px', minHeight: '0'})),
state('expanded', style({height: '*'})),
transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
]),
],
})
export class TemplateStateEditorComponent {
@Input() templateGamesystem: TemplateGamesystem | undefined
@Input() scriptAccounts: ScriptAccount[] = []
dataSource = new MatTableDataSource<SimpleState>();
displayedColumns = ["name", "initial", "edit", "delete"];
columnsToDisplayWithExpand = [...this.displayedColumns, 'expand'];
expandedElement: SimpleState | null = null;
editedElement: SimpleState | null = null;
editedStateLabelError: boolean = false;
onExtractReferenceState(state: SimpleState) {
this.templateGamesystem!.addReferenceState(state)
this.dataSource.data = this.templateGamesystem!.templateStates
}
applyFilter(event: KeyboardEvent) {
const filterValue = (event.target as HTMLInputElement).value;
this.dataSource.filter = filterValue.trim().toLowerCase();
}
onCreateCondition(state: SimpleState, $event: ScriptAccountCondition) {
}
onEditState(state: SimpleState) {
if(this.editedElement == null) {
this.editedElement = state
} else {
this.editedElement = null
}
}
onDeleteState(state: SimpleState) {
this.templateGamesystem!.templateStates = this.templateGamesystem!.templateStates.filter(templateState => templateState.stateLabel !== state.stateLabel)
this.dataSource.data = this.templateGamesystem!.templateStates
}
deleteCondition(state: SimpleState, $event: ScriptAccountCondition) {
}
}

View File

@ -0,0 +1,22 @@
import {SimpleGamesystem} from "./SimpleGamesystem";
import {TemplateState} from "./states/TemplateState";
import {SimpleState} from "./states/SimpleState";
import {ScriptAccountCondition} from "./conditions/ScriptAccountCondition";
export class TemplateGamesystem {
referenceGamesystem: SimpleGamesystem
templateStates: SimpleState[] = []
constructor(referenceGamesystem: SimpleGamesystem) {
this.referenceGamesystem = referenceGamesystem;
}
addReferenceState(referenceState: SimpleState) {
const templateState = new SimpleState(referenceState.stateLabel, referenceState.stateDescription);
templateState.conditions = referenceState.conditions.map(condition =>
ScriptAccountCondition.constructScriptAccountCondition(condition.scriptAccount, condition.minValue, condition.maxValue)!)
this.templateStates.push(templateState)
}
}

View File

@ -0,0 +1,14 @@
import {Transition} from "../transitions/Transition";
import {SimpleState} from "./SimpleState";
import {ScriptAccountCondition} from "../conditions/ScriptAccountCondition";
export class TemplateState {
referenceState: SimpleState
conditions: ScriptAccountCondition[]
constructor(referenceState: SimpleState, conditions: ScriptAccountCondition[]) {
this.referenceState = referenceState;
this.conditions = conditions;
}
}