Merge pull request 'issue-2-scriptAccounts' (#4) from issue-2-scriptAccounts into main
All checks were successful
E2E Testing / test (push) Successful in 1m24s
All checks were successful
E2E Testing / test (push) Successful in 1m24s
Reviewed-on: #4
This commit is contained in:
commit
0dd87fe81c
@ -30,13 +30,13 @@
|
|||||||
"main": "src/main.ts",
|
"main": "src/main.ts",
|
||||||
"tsConfig": "src/tsconfig.app.json",
|
"tsConfig": "src/tsconfig.app.json",
|
||||||
"polyfills": "src/polyfills.ts",
|
"polyfills": "src/polyfills.ts",
|
||||||
"inlineStyleLanguage": "scss",
|
"inlineStyleLanguage": "css",
|
||||||
"assets": [
|
"assets": [
|
||||||
"src/favicon.ico",
|
"src/favicon.ico",
|
||||||
"src/assets"
|
"src/assets"
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.scss"
|
"src/styles.css"
|
||||||
],
|
],
|
||||||
"scripts": [],
|
"scripts": [],
|
||||||
"customWebpackConfig": {
|
"customWebpackConfig": {
|
||||||
|
43
app/main.ts
43
app/main.ts
@ -1,4 +1,4 @@
|
|||||||
import {app, BrowserWindow, screen} from 'electron';
|
import {app, BrowserWindow, screen, Menu, ipcMain} from 'electron';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
|
||||||
@ -50,6 +50,47 @@ function createWindow(): BrowserWindow {
|
|||||||
win = null;
|
win = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const contextMenuTemplate = [
|
||||||
|
{
|
||||||
|
label: 'New',
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
label: "Gamesystem",
|
||||||
|
click: () => {
|
||||||
|
win!.webContents.send('context-menu', "new-location");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "ScriptAccount",
|
||||||
|
click: () => {
|
||||||
|
win!.webContents.send('context-menu', "new-scriptaccount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Edit...',
|
||||||
|
click: () => {
|
||||||
|
win!.webContents.send('context-menu', "edit");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Delete...',
|
||||||
|
click: () => {
|
||||||
|
win!.webContents.send('context-menu', "delete");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const contextMenu = Menu.buildFromTemplate(contextMenuTemplate);
|
||||||
|
|
||||||
|
win.webContents.on('context-menu', (e, params) => {
|
||||||
|
console.log("Electron: Context menu showing")
|
||||||
|
contextMenu.popup({ window: win!, x: params.x, y: params.y });
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
return win;
|
return win;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
71
e2e/game-model/scriptAccounts/ScriptAccountTest.spec.ts
Normal file
71
e2e/game-model/scriptAccounts/ScriptAccountTest.spec.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { BrowserContext, ElectronApplication, Page, _electron as electron } from 'playwright';
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import * as PATH from 'path';
|
||||||
|
import {GameModel} from "../../../src/app/game-model/GameModel";
|
||||||
|
import {Gamesystem} from "../../src/app/game-model/gamesystems/Gamesystem";
|
||||||
|
import {ScriptAccount} from "../../../src/app/game-model/scriptAccounts/ScriptAccount";
|
||||||
|
import {ModelComponentType} from "../../../src/app/game-model/ModelComponentType";
|
||||||
|
|
||||||
|
test.describe('Test ScriptAccounts', () => {
|
||||||
|
|
||||||
|
test("Test ScriptAccount Creation", async () => {
|
||||||
|
const scriptAccunt = new ScriptAccount("ScriptAccount", "Description");
|
||||||
|
|
||||||
|
expect(scriptAccunt.componentName).toEqual("ScriptAccount");
|
||||||
|
expect(scriptAccunt.componentDescription).toEqual("Description");
|
||||||
|
expect(scriptAccunt.type).toEqual(ModelComponentType.SCRIPTACCOUNT);
|
||||||
|
expect(scriptAccunt.minValue).toEqual(0);
|
||||||
|
expect(scriptAccunt.maxValue).toEqual(100);
|
||||||
|
})
|
||||||
|
|
||||||
|
test("Test Adding ScriptAccounts", async () => {
|
||||||
|
const gameModel: GameModel = new GameModel("GameModel");
|
||||||
|
|
||||||
|
let scriptAccount =gameModel.addScriptAccount("ScriptAccount");
|
||||||
|
expect(scriptAccount).toBeDefined();
|
||||||
|
expect(gameModel.scriptAccounts.length).toEqual(1);
|
||||||
|
expect(gameModel.scriptAccounts.includes(scriptAccount)).toBeTruthy();
|
||||||
|
|
||||||
|
//Test adding scriptAccount with already existing name
|
||||||
|
const scriptAccount2 = gameModel.addScriptAccount("ScriptAccount")
|
||||||
|
expect(scriptAccount2).toBeUndefined();
|
||||||
|
expect(gameModel.scriptAccounts.length).toEqual(1);
|
||||||
|
|
||||||
|
//Test for adding invalid names as scriptaccount names (null/undefined/empty)
|
||||||
|
let result = gameModel.addScriptAccount(null);
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
expect(gameModel.scriptAccounts.length).toEqual(1);
|
||||||
|
result = gameModel.addScriptAccount(undefined);
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
expect(gameModel.scriptAccounts.length).toEqual(1);
|
||||||
|
result = gameModel.addScriptAccount("");
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
expect(gameModel.scriptAccounts.length).toEqual(1);
|
||||||
|
})
|
||||||
|
|
||||||
|
test("test Removing ScriptAccounts", async () => {
|
||||||
|
const gameModel: GameModel = new GameModel("GameModel");
|
||||||
|
let scriptAccount = new ScriptAccount("test", "")
|
||||||
|
|
||||||
|
gameModel.removeScriptAccount(scriptAccount);
|
||||||
|
expect(gameModel.scriptAccounts.length).toEqual(0);
|
||||||
|
|
||||||
|
scriptAccount = gameModel.addScriptAccount("ScriptAccount");
|
||||||
|
gameModel.removeScriptAccount(scriptAccount);
|
||||||
|
expect(gameModel.scriptAccounts.length).toEqual(0);
|
||||||
|
|
||||||
|
gameModel.removeScriptAccount(undefined);
|
||||||
|
expect(gameModel.scriptAccounts.length).toEqual(0);
|
||||||
|
|
||||||
|
gameModel.removeScriptAccount(null);
|
||||||
|
expect(gameModel.scriptAccounts.length).toEqual(0);
|
||||||
|
|
||||||
|
scriptAccount = gameModel.addScriptAccount(scriptAccount);
|
||||||
|
let scriptAccount2 = gameModel.addScriptAccount("ScriptAccount 2");
|
||||||
|
|
||||||
|
gameModel.removeScriptAccount(scriptAccount);
|
||||||
|
expect(gameModel.scriptAccounts.length).toEqual(1);
|
||||||
|
expect(gameModel.scriptAccounts.includes(scriptAccount2)).toBeTruthy();
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
@ -1,3 +1,103 @@
|
|||||||
.background {
|
.full-height-container {
|
||||||
|
height: 99vh;
|
||||||
|
background-color: #2b2d30;
|
||||||
|
width: 40px;
|
||||||
|
|
||||||
|
border-right: #b9b9b9;
|
||||||
|
border-right-style: solid;
|
||||||
|
border-right-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-icon-button {
|
||||||
|
width: 28px !important;
|
||||||
|
height: 28px !important;
|
||||||
|
padding: 0px !important;
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
& > *[role=img] {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
font-size: 20px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-button-touch-target {
|
||||||
|
width: 24px !important;
|
||||||
|
height: 24px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-icon-button mat-icon {
|
||||||
|
color: whitesmoke;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
background-color: #4e5157;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-sidenav-content {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-sidenav {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust the vertical alignment of the icon within the button */
|
||||||
|
.mat-button-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenav-header {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-sidenav-btn {
|
||||||
|
margin-top: 5px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenav-header:hover + .close-sidenav-btn {
|
||||||
|
display: block;
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app-editor {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-form {
|
||||||
|
min-width: 150px;
|
||||||
|
max-width: 500px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
@ -1 +1,31 @@
|
|||||||
<mat-icon>add</mat-icon>
|
<div class="container">
|
||||||
|
<div class="full-height-container">
|
||||||
|
<button mat-icon-button class="small-icon-button" [ngClass]="openContent === ModelComponentType.SCRIPTACCOUNT ? 'selected':''"
|
||||||
|
(click)="openScriptAccountsOverview()"><mat-icon>inventory_2</mat-icon></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<mat-drawer-container class="example-container" autosize>
|
||||||
|
<mat-drawer #drawer class="example-sidenav" mode="side">
|
||||||
|
<div class="sidenav-header">
|
||||||
|
<button mat-button [matMenuTriggerFor]="contentMenu">
|
||||||
|
<span class="mat-button-wrapper">{{ModelComponentTypeUtillities.toString(openContent)}}
|
||||||
|
<mat-icon class="mat-icon">expand_more</mat-icon>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-icon-button class="small-icon-button close-sidenav-btn" (click)="closeContentOverview()"><mat-icon>close</mat-icon></button>
|
||||||
|
<mat-menu #contentMenu="matMenu">
|
||||||
|
<button mat-menu-item (click)="openScriptAccountsOverview()">{{ModelComponentTypeUtillities.toString(ModelComponentType.SCRIPTACCOUNT)}}</button>
|
||||||
|
</mat-menu>
|
||||||
|
</div>
|
||||||
|
<app-script-account-overview #scriptAccountOverview [gameModel]="gameModel" (onOpenScriptAccount)="openModelComponent($event)"></app-script-account-overview>
|
||||||
|
</mat-drawer>
|
||||||
|
|
||||||
|
<div class="example-sidenav-content">
|
||||||
|
<app-editor #editor></app-editor>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</mat-drawer-container>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
@ -1,15 +1,36 @@
|
|||||||
import { Component } from '@angular/core';
|
import {Component, NgZone, OnInit, ViewChild} from '@angular/core';
|
||||||
import { ElectronService } from './core/services';
|
import {ElectronService} from './core/services';
|
||||||
import { APP_CONFIG } from '../environments/environment';
|
import {APP_CONFIG} from '../environments/environment';
|
||||||
|
import {ModelComponentType} from "./game-model/ModelComponentType";
|
||||||
|
import {MatDrawerContainer} from "@angular/material/sidenav";
|
||||||
|
import {ModelComponentTypeUtillities} from "./game-model/ModelComponentTypeUtillities";
|
||||||
|
import {GameModel} from "./game-model/GameModel";
|
||||||
|
import {EditorComponent} from "./editor/editor.component";
|
||||||
|
import {ModelComponent} from "./game-model/ModelComponent";
|
||||||
|
import {
|
||||||
|
ScriptAccountOverviewComponent
|
||||||
|
} from "./side-overviews/script-account-overview/script-account-overview.component";
|
||||||
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
|
import {DeleteConfirmationDialogComponent} from "./delete-confirmation-dialog/delete-confirmation-dialog.component";
|
||||||
|
import {ScriptAccount} from "./game-model/scriptAccounts/ScriptAccount";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrls: ['./app.component.css']
|
styleUrls: ['./app.component.css']
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent implements OnInit{
|
||||||
constructor(
|
|
||||||
private electronService: ElectronService,
|
openContent : ModelComponentType | undefined = undefined;
|
||||||
|
@ViewChild('drawer') drawer: MatDrawerContainer|undefined
|
||||||
|
@ViewChild('editor') editor: EditorComponent|undefined
|
||||||
|
@ViewChild('scriptAccountOverview') scriptAccountOverview: ScriptAccountOverviewComponent | undefined
|
||||||
|
|
||||||
|
gameModel: GameModel | undefined
|
||||||
|
|
||||||
|
constructor(private electronService: ElectronService,
|
||||||
|
private zone: NgZone,
|
||||||
|
private dialog: MatDialog
|
||||||
) {
|
) {
|
||||||
console.log('APP_CONFIG', APP_CONFIG);
|
console.log('APP_CONFIG', APP_CONFIG);
|
||||||
|
|
||||||
@ -18,8 +39,69 @@ export class AppComponent {
|
|||||||
console.log('Run in electron');
|
console.log('Run in electron');
|
||||||
console.log('Electron ipcRenderer', this.electronService.ipcRenderer);
|
console.log('Electron ipcRenderer', this.electronService.ipcRenderer);
|
||||||
console.log('NodeJS childProcess', this.electronService.childProcess);
|
console.log('NodeJS childProcess', this.electronService.childProcess);
|
||||||
|
|
||||||
|
electronService.ipcRenderer.on('context-menu', (event: any, message: string) => {
|
||||||
|
this.zone.run(() => {
|
||||||
|
if(message == "edit") {
|
||||||
|
if(this.openContent == ModelComponentType.SCRIPTACCOUNT && this.scriptAccountOverview != undefined && this.scriptAccountOverview.selectedScriptAccount != undefined) {
|
||||||
|
this.editor!.openGameModelComponent(this.scriptAccountOverview.selectedScriptAccount!);
|
||||||
|
}
|
||||||
|
} else if(message == "delete") {
|
||||||
|
const affectedModelComponent = this.getSelectedModelComponent();
|
||||||
|
const dialogRef = this.dialog.open(DeleteConfirmationDialogComponent, {data: affectedModelComponent, minWidth: "400px"});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(res => {
|
||||||
|
if(res != undefined && res) {
|
||||||
|
if(affectedModelComponent instanceof ScriptAccount) {
|
||||||
|
this.gameModel!.removeScriptAccount(affectedModelComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
console.log('Run in browser');
|
console.log('Run in browser');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getSelectedModelComponent(): ModelComponent | undefined {
|
||||||
|
if(this.openContent == ModelComponentType.SCRIPTACCOUNT) {
|
||||||
|
if(this.scriptAccountOverview != undefined) {
|
||||||
|
return this.scriptAccountOverview!.selectedScriptAccount;
|
||||||
|
} else {
|
||||||
|
console.log("[WARN] [App.component] ScriptAccountOverview is undefined")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.gameModel = new GameModel("No More");
|
||||||
|
this.gameModel.addScriptAccount("Temperature");
|
||||||
|
this.gameModel.addScriptAccount("Luftfeuchtigkeit");
|
||||||
|
}
|
||||||
|
|
||||||
|
openScriptAccountsOverview() {
|
||||||
|
this.openContent = ModelComponentType.SCRIPTACCOUNT
|
||||||
|
this.drawer!.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly ModelComponentType = ModelComponentType;
|
||||||
|
|
||||||
|
closeContentOverview() {
|
||||||
|
this.drawer!.close();
|
||||||
|
this.openContent = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly ModelComponentTypeUtillities = ModelComponentTypeUtillities;
|
||||||
|
|
||||||
|
openModelComponent(modelComponent: ModelComponent) {
|
||||||
|
if(this.editor != undefined) {
|
||||||
|
this.editor.openGameModelComponent(modelComponent);
|
||||||
|
} else {
|
||||||
|
console.log("[WARN] [App.Component] Editor is undefined")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||||
import { HttpClientModule, HttpClient } from '@angular/common/http';
|
import { HttpClientModule, HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
// NG Translate
|
// NG Translate
|
||||||
@ -12,12 +12,35 @@ import {CoreModule} from "./core/core.module";
|
|||||||
import {SharedModule} from "./shared/shared.module";
|
import {SharedModule} from "./shared/shared.module";
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import {MatIcon} from "@angular/material/icon";
|
import {MatIcon} from "@angular/material/icon";
|
||||||
|
import {MatToolbar} from "@angular/material/toolbar";
|
||||||
|
import {MatButton, MatIconButton, MatMiniFabButton} from "@angular/material/button";
|
||||||
|
import {MatError, MatFormField, MatLabel} from "@angular/material/form-field";
|
||||||
|
import {MatInput} from "@angular/material/input";
|
||||||
|
import {MatDrawer, MatDrawerContainer} from "@angular/material/sidenav";
|
||||||
|
import {MatMenu, MatMenuItem, MatMenuTrigger} from "@angular/material/menu";
|
||||||
|
import {
|
||||||
|
ScriptAccountOverviewComponent
|
||||||
|
} from "./side-overviews/script-account-overview/script-account-overview.component";
|
||||||
|
import {MatActionList, MatListItem} from "@angular/material/list";
|
||||||
|
import {EditorComponent} from "./editor/editor.component";
|
||||||
|
import {MatTab, MatTabGroup, MatTabLabel} from "@angular/material/tabs";
|
||||||
|
import {ScriptAccountEditorComponent} from "./editor/script-account-editor/script-account-editor.component";
|
||||||
|
import {ModelComponentEditorComponent} from "./editor/model-component-editor/model-component-editor.component";
|
||||||
|
import {DeleteConfirmationDialogComponent} from "./delete-confirmation-dialog/delete-confirmation-dialog.component";
|
||||||
|
import {MatDialogActions, MatDialogContent, MatDialogTitle} from "@angular/material/dialog";
|
||||||
|
|
||||||
// AoT requires an exported function for factories
|
// AoT requires an exported function for factories
|
||||||
const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new TranslateHttpLoader(http, './assets/i18n/', '.json');
|
const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new TranslateHttpLoader(http, './assets/i18n/', '.json');
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [AppComponent],
|
declarations: [
|
||||||
|
AppComponent,
|
||||||
|
ScriptAccountOverviewComponent,
|
||||||
|
EditorComponent,
|
||||||
|
ScriptAccountEditorComponent,
|
||||||
|
ModelComponentEditorComponent,
|
||||||
|
DeleteConfirmationDialogComponent
|
||||||
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
@ -32,7 +55,30 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
MatIcon
|
MatIcon,
|
||||||
|
MatToolbar,
|
||||||
|
MatButton,
|
||||||
|
MatFormField,
|
||||||
|
MatInput,
|
||||||
|
MatDrawerContainer,
|
||||||
|
MatDrawer,
|
||||||
|
MatIconButton,
|
||||||
|
MatMenuTrigger,
|
||||||
|
MatMenu,
|
||||||
|
MatMenuItem,
|
||||||
|
MatListItem,
|
||||||
|
MatActionList,
|
||||||
|
MatTabGroup,
|
||||||
|
MatTab,
|
||||||
|
MatTabLabel,
|
||||||
|
MatLabel,
|
||||||
|
MatFormField,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
MatError,
|
||||||
|
MatDialogTitle,
|
||||||
|
MatDialogContent,
|
||||||
|
MatDialogActions,
|
||||||
|
MatMiniFabButton,
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
<h3 mat-dialog-title class="dialog-title">
|
||||||
|
<span style="margin-top: 10px">Delete</span>
|
||||||
|
<button style="margin-top: 10px" mat-mini-fab class="small-icon-button" (click)="cancel()"><mat-icon>close</mat-icon></button>
|
||||||
|
</h3>
|
||||||
|
<div mat-dialog-content>
|
||||||
|
<p>Delete {{ModelComponentTypeUtillities.toSingleString(deleteModelComponent.type)}}"{{deleteModelComponent.componentName}}"?</p>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions align="end">
|
||||||
|
<button mat-raised-button class="btn-primary" (click)="confirmDelete()">Ok</button>
|
||||||
|
<button mat-raised-button class="btn-secondary" (click)="cancel()">Cancel</button>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,41 @@
|
|||||||
|
.dialog-title {
|
||||||
|
background-color: #272727;
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 50px !important;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-icon-button {
|
||||||
|
width: 28px !important;
|
||||||
|
height: 28px !important;
|
||||||
|
padding: 0px !important;
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
background-color: black;
|
||||||
|
|
||||||
|
& > *[role=img] {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
font-size: 20px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-button-touch-target {
|
||||||
|
width: 24px !important;
|
||||||
|
height: 24px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-icon-button mat-icon {
|
||||||
|
color: whitesmoke;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { DeleteConfirmationDialogComponent } from './delete-confirmation-dialog.component';
|
||||||
|
|
||||||
|
describe('DeleteConfirmationDialogComponent', () => {
|
||||||
|
let component: DeleteConfirmationDialogComponent;
|
||||||
|
let fixture: ComponentFixture<DeleteConfirmationDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [DeleteConfirmationDialogComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(DeleteConfirmationDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,25 @@
|
|||||||
|
import {Component, Inject} from '@angular/core';
|
||||||
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
|
import {ModelComponentTypeUtillities} from "../game-model/ModelComponentTypeUtillities";
|
||||||
|
import {ModelComponent} from "../game-model/ModelComponent";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-delete-confirmation-dialog',
|
||||||
|
templateUrl: './delete-confirmation-dialog.component.html',
|
||||||
|
styleUrl: './delete-confirmation-dialog.component.scss'
|
||||||
|
})
|
||||||
|
export class DeleteConfirmationDialogComponent {
|
||||||
|
|
||||||
|
constructor(private dialogRef: MatDialogRef<DeleteConfirmationDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public deleteModelComponent: ModelComponent) {
|
||||||
|
}
|
||||||
|
protected readonly ModelComponentTypeUtillities = ModelComponentTypeUtillities;
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
this.dialogRef.close(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmDelete() {
|
||||||
|
this.dialogRef.close(true);
|
||||||
|
}
|
||||||
|
}
|
13
src/app/editor/editor.component.html
Normal file
13
src/app/editor/editor.component.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<mat-tab-group>
|
||||||
|
<mat-tab *ngFor="let modelComponent of gameModelComponents">
|
||||||
|
<ng-template mat-tab-label>
|
||||||
|
<mat-icon class="example-tab-icon unsaved" [ngClass]="modelComponent.unsaved? 'unsaved':'saved'">inventory_2</mat-icon>
|
||||||
|
<span [ngClass]="modelComponent.unsaved? 'unsaved':'saved'">{{modelComponent.componentName}}</span>
|
||||||
|
<button class="content-label close-btn" mat-icon-button (click)="closeGameModelComponent(modelComponent)"><mat-icon>close</mat-icon></button>
|
||||||
|
</ng-template>
|
||||||
|
<app-model-component-editor [modelComponent]="modelComponent"></app-model-component-editor>
|
||||||
|
<app-script-account-editor *ngIf="modelComponent.type === ModelComponentType.SCRIPTACCOUNT"
|
||||||
|
[scriptAccount]="convertModelComponentToScriptAccount(modelComponent)"></app-script-account-editor>
|
||||||
|
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
11
src/app/editor/editor.component.scss
Normal file
11
src/app/editor/editor.component.scss
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
.unsaved {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saved {
|
||||||
|
color: #3c95f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
color: white
|
||||||
|
}
|
60
src/app/editor/editor.component.spec.ts
Normal file
60
src/app/editor/editor.component.spec.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { EditorComponent } from './editor.component';
|
||||||
|
import {ScriptAccount} from "../game-model/scriptAccounts/ScriptAccount";
|
||||||
|
import {TranslateModule} from "@ngx-translate/core";
|
||||||
|
import {RouterTestingModule} from "@angular/router/testing";
|
||||||
|
|
||||||
|
describe('EditorComponent', () => {
|
||||||
|
let component: EditorComponent;
|
||||||
|
let fixture: ComponentFixture<EditorComponent>;
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
void TestBed.configureTestingModule({
|
||||||
|
declarations: [EditorComponent],
|
||||||
|
imports: [TranslateModule.forRoot(), RouterTestingModule]
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(EditorComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("Test open ScriptAccount", waitForAsync (() => {
|
||||||
|
const scriptAccount = new ScriptAccount("ScriptAccount", "");
|
||||||
|
component.openGameModelComponent(scriptAccount);
|
||||||
|
|
||||||
|
expect(component.gameModelComponents.includes(scriptAccount));
|
||||||
|
expect(component.gameModelComponents.length).toEqual(1);
|
||||||
|
|
||||||
|
component.openGameModelComponent(scriptAccount);
|
||||||
|
expect(component.gameModelComponents.length).toEqual(1);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("Test close ScriptAccount", waitForAsync(() => {
|
||||||
|
const scriptAccount = new ScriptAccount("ScriptAccount", "");
|
||||||
|
const scriptAccount2 = new ScriptAccount("ScriptAccount 2", "");
|
||||||
|
component.openGameModelComponent(scriptAccount);
|
||||||
|
|
||||||
|
component.closeGameModelComponent(scriptAccount);
|
||||||
|
expect(component.gameModelComponents.length).toEqual(0);
|
||||||
|
|
||||||
|
component.openGameModelComponent(scriptAccount);
|
||||||
|
component.openGameModelComponent(scriptAccount2);
|
||||||
|
component.closeGameModelComponent(scriptAccount);
|
||||||
|
expect(component.gameModelComponents.length).toEqual(1);
|
||||||
|
expect(component.gameModelComponents.includes(scriptAccount2));
|
||||||
|
}))
|
||||||
|
|
||||||
|
it("Test convert ModelComponent to ScriptAccount", waitForAsync(() => {
|
||||||
|
const modelComponent = new ScriptAccount("test", "");
|
||||||
|
const scriptAccount = component.convertModelComponentToScriptAccount(modelComponent);
|
||||||
|
|
||||||
|
expect(scriptAccount).toBeInstanceOf(ScriptAccount);
|
||||||
|
}))
|
||||||
|
});
|
32
src/app/editor/editor.component.ts
Normal file
32
src/app/editor/editor.component.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {GameModel} from "../game-model/GameModel";
|
||||||
|
import {ModelComponent} from "../game-model/ModelComponent";
|
||||||
|
import {ModelComponentType} from "../game-model/ModelComponentType";
|
||||||
|
import {ScriptAccount} from "../game-model/scriptAccounts/ScriptAccount";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-editor',
|
||||||
|
templateUrl: './editor.component.html',
|
||||||
|
styleUrl: './editor.component.scss'
|
||||||
|
})
|
||||||
|
export class EditorComponent {
|
||||||
|
gameModelComponents: ModelComponent[] = [];
|
||||||
|
|
||||||
|
openGameModelComponent(gameModelComponent: ModelComponent) {
|
||||||
|
if(!this.gameModelComponents.includes(gameModelComponent)) {
|
||||||
|
this.gameModelComponents.push(gameModelComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closeGameModelComponent(gameModelComponent: ModelComponent) {
|
||||||
|
this.gameModelComponents = this.gameModelComponents.filter(modelComponent => modelComponent !== gameModelComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
convertModelComponentToScriptAccount(modelComponent: ModelComponent) {
|
||||||
|
if(modelComponent instanceof ScriptAccount) {
|
||||||
|
return modelComponent as ScriptAccount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly ModelComponentType = ModelComponentType;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
<mat-form-field appearance="fill" class="long-form">
|
||||||
|
<mat-label>Name</mat-label>
|
||||||
|
<input matInput [formControl]="nameCtrl" (change)="onUpdateName()">
|
||||||
|
<mat-error *ngIf="nameCtrl.hasError('required')">Please enter a valid number!</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="fill" class="long-form">
|
||||||
|
<mat-label>Description</mat-label>
|
||||||
|
<textarea matInput [formControl]="descriptionCtrl" (change)="onUpdateDescription()" rows="3"></textarea>
|
||||||
|
</mat-form-field>
|
@ -0,0 +1,3 @@
|
|||||||
|
.long-form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ModelComponentEditorComponent } from './model-component-editor.component';
|
||||||
|
|
||||||
|
describe('ModelComponentEditorComponent', () => {
|
||||||
|
let component: ModelComponentEditorComponent;
|
||||||
|
let fixture: ComponentFixture<ModelComponentEditorComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ModelComponentEditorComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ModelComponentEditorComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,32 @@
|
|||||||
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
|
import {ModelComponent} from "../../game-model/ModelComponent";
|
||||||
|
import {FormControl, Validators} from "@angular/forms";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-model-component-editor',
|
||||||
|
templateUrl: './model-component-editor.component.html',
|
||||||
|
styleUrl: './model-component-editor.component.scss'
|
||||||
|
})
|
||||||
|
export class ModelComponentEditorComponent implements OnInit{
|
||||||
|
@Input('modelComponent') modelComponent: ModelComponent | undefined
|
||||||
|
|
||||||
|
nameCtrl: FormControl = new FormControl('', [Validators.required]);
|
||||||
|
descriptionCtrl: FormControl = new FormControl('' );
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.nameCtrl.setValue(this.modelComponent!.componentName);
|
||||||
|
this.descriptionCtrl.setValue(this.modelComponent!.componentDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdateName() {
|
||||||
|
this.modelComponent!.componentName = this.nameCtrl.value;
|
||||||
|
this.modelComponent!.onModifyContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdateDescription() {
|
||||||
|
this.modelComponent!.componentDescription = this.descriptionCtrl.value;
|
||||||
|
this.modelComponent!.onModifyContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly name = name;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
<div *ngIf="scriptAccount != undefined">
|
||||||
|
<mat-form-field class="example-full-width">
|
||||||
|
<mat-label>MinValue</mat-label>
|
||||||
|
<input matInput [formControl]="minCtrl" type="number" (keypress)="onKeyPress($event)" (change)="onUpdateMinValue()">
|
||||||
|
<mat-error *ngIf="minCtrl.hasError('required')">Please enter a valid number!</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field class="example-full-width">
|
||||||
|
<mat-label>MaxValue</mat-label>
|
||||||
|
<input matInput type="number" [formControl]="maxCtrl" (keypress)="onKeyPress($event)" (change)="onUpdateMaxValue()">
|
||||||
|
<mat-error *ngIf="maxCtrl.hasError('required')">Please enter a valid number!</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
@ -0,0 +1,7 @@
|
|||||||
|
.example-form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ScriptAccountEditorComponent } from './script-account-editor.component';
|
||||||
|
|
||||||
|
describe('ScriptAccountEditorComponent', () => {
|
||||||
|
let component: ScriptAccountEditorComponent;
|
||||||
|
let fixture: ComponentFixture<ScriptAccountEditorComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ScriptAccountEditorComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ScriptAccountEditorComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,49 @@
|
|||||||
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
|
import {ScriptAccount} from "../../game-model/scriptAccounts/ScriptAccount";
|
||||||
|
import {ModelComponent} from "../../game-model/ModelComponent";
|
||||||
|
import {MatFormField} from "@angular/material/form-field";
|
||||||
|
import {MatInput} from "@angular/material/input";
|
||||||
|
import {FormControl, FormGroupDirective, FormsModule, NgForm, Validators} from "@angular/forms";
|
||||||
|
import {NgIf} from "@angular/common";
|
||||||
|
import {ErrorStateMatcher} from "@angular/material/core";
|
||||||
|
|
||||||
|
export class MyErrorStateMatcher implements ErrorStateMatcher {
|
||||||
|
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
|
||||||
|
const isSubmitted = form && form.submitted;
|
||||||
|
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Component({
|
||||||
|
selector: 'app-script-account-editor',
|
||||||
|
templateUrl: './script-account-editor.component.html',
|
||||||
|
styleUrl: './script-account-editor.component.scss'
|
||||||
|
})
|
||||||
|
export class ScriptAccountEditorComponent implements OnInit{
|
||||||
|
@Input("scriptAccount") scriptAccount : ScriptAccount | undefined
|
||||||
|
|
||||||
|
minCtrl: FormControl = new FormControl(0, [Validators.required, Validators.pattern('^[0-9]*$')]);
|
||||||
|
maxCtrl: FormControl = new FormControl(100, [Validators.required, Validators.pattern('^[0-9]*$')]);
|
||||||
|
matcher = new MyErrorStateMatcher();
|
||||||
|
ngOnInit() {
|
||||||
|
this.minCtrl.setValue(this.scriptAccount!.minValue);
|
||||||
|
this.maxCtrl.setValue(this.scriptAccount!.maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyPress(event: KeyboardEvent) {
|
||||||
|
const input = event.key;
|
||||||
|
const isDigit = /^\d+$/.test(input);
|
||||||
|
if (!isDigit) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdateMinValue() {
|
||||||
|
this.scriptAccount!.minValue = Number(this.minCtrl.value);
|
||||||
|
this.scriptAccount!.onModifyContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdateMaxValue() {
|
||||||
|
this.scriptAccount!.maxValue = Number(this.maxCtrl.value);
|
||||||
|
this.scriptAccount!.onModifyContent();
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,11 @@
|
|||||||
import {Gamesystem} from "./gamesystems/Gamesystem";
|
import {Gamesystem} from "./gamesystems/Gamesystem";
|
||||||
|
import {ScriptAccount} from "./scriptAccounts/ScriptAccount";
|
||||||
|
|
||||||
export class GameModel {
|
export class GameModel {
|
||||||
private readonly _gameModelName: string
|
private readonly _gameModelName: string
|
||||||
|
|
||||||
private _gamesystems: Gamesystem[] = [];
|
private _gamesystems: Gamesystem[] = [];
|
||||||
|
private _scriptAccounts: ScriptAccount[] = [];
|
||||||
|
|
||||||
constructor(gameModelName: string) {
|
constructor(gameModelName: string) {
|
||||||
this._gameModelName = gameModelName;
|
this._gameModelName = gameModelName;
|
||||||
@ -16,6 +18,9 @@ export class GameModel {
|
|||||||
get gamesystems(): Gamesystem[] {
|
get gamesystems(): Gamesystem[] {
|
||||||
return this._gamesystems;
|
return this._gamesystems;
|
||||||
}
|
}
|
||||||
|
get scriptAccounts(): ScriptAccount[] {
|
||||||
|
return this._scriptAccounts;
|
||||||
|
}
|
||||||
|
|
||||||
addGamesystem(gamesystem: Gamesystem) {
|
addGamesystem(gamesystem: Gamesystem) {
|
||||||
if(!this.gamesystems.includes(gamesystem)) {
|
if(!this.gamesystems.includes(gamesystem)) {
|
||||||
@ -26,4 +31,23 @@ export class GameModel {
|
|||||||
removeGamesystem(gamesystem : Gamesystem) {
|
removeGamesystem(gamesystem : Gamesystem) {
|
||||||
this._gamesystems = this._gamesystems.filter(g => g !== gamesystem);
|
this._gamesystems = this._gamesystems.filter(g => g !== gamesystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addScriptAccount(scriptAccountName: string) {
|
||||||
|
if(scriptAccountName != undefined && scriptAccountName.length > 0) {
|
||||||
|
const scriptAccount = new ScriptAccount(scriptAccountName, "");
|
||||||
|
const searchedScriptAccount = this.scriptAccounts.find(s => s.componentName === scriptAccount.componentName);
|
||||||
|
if(searchedScriptAccount == undefined) {
|
||||||
|
this.scriptAccounts.push(scriptAccount);
|
||||||
|
return scriptAccount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeScriptAccount(scriptAccount: ScriptAccount) {
|
||||||
|
if(scriptAccount != undefined) {
|
||||||
|
this._scriptAccounts = this.scriptAccounts.filter(s => s != scriptAccount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
14
src/app/game-model/ModelComponent.ts
Normal file
14
src/app/game-model/ModelComponent.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {ModelComponentType} from "./ModelComponentType";
|
||||||
|
import {SaveComponent} from "./SaveComponent";
|
||||||
|
|
||||||
|
export abstract class ModelComponent extends SaveComponent{
|
||||||
|
componentName: string
|
||||||
|
componentDescription: string
|
||||||
|
type: ModelComponentType
|
||||||
|
constructor(componentName: string, componentDescription: string, type: ModelComponentType) {
|
||||||
|
super();
|
||||||
|
this.componentName = componentName;
|
||||||
|
this.componentDescription = componentDescription;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
5
src/app/game-model/ModelComponentType.ts
Normal file
5
src/app/game-model/ModelComponentType.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export enum ModelComponentType {
|
||||||
|
SCRIPTACCOUNT
|
||||||
|
|
||||||
|
|
||||||
|
}
|
18
src/app/game-model/ModelComponentTypeUtillities.ts
Normal file
18
src/app/game-model/ModelComponentTypeUtillities.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import {ModelComponentType} from "./ModelComponentType";
|
||||||
|
import {ModelComponent} from "./ModelComponent";
|
||||||
|
|
||||||
|
export class ModelComponentTypeUtillities {
|
||||||
|
static toString(modelComponentType: ModelComponentType | undefined): string {
|
||||||
|
switch (modelComponentType) {
|
||||||
|
case ModelComponentType.SCRIPTACCOUNT: return "ScriptAccounts";
|
||||||
|
default: return "Undefined";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static toSingleString(modelComponentType: ModelComponentType | undefined): string {
|
||||||
|
switch (modelComponentType) {
|
||||||
|
case ModelComponentType.SCRIPTACCOUNT: return "ScriptAccount";
|
||||||
|
default: return "Undefined";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
src/app/game-model/SaveComponent.ts
Normal file
13
src/app/game-model/SaveComponent.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export abstract class SaveComponent {
|
||||||
|
unsaved: boolean = false;
|
||||||
|
|
||||||
|
onModifyContent() {
|
||||||
|
this.unsaved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSaveContent() {
|
||||||
|
this.unsaved = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract save(): void;
|
||||||
|
}
|
14
src/app/game-model/scriptAccounts/ScriptAccount.ts
Normal file
14
src/app/game-model/scriptAccounts/ScriptAccount.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {ModelComponent} from "../ModelComponent";
|
||||||
|
import {ModelComponentType} from "../ModelComponentType";
|
||||||
|
|
||||||
|
export class ScriptAccount extends ModelComponent{
|
||||||
|
minValue: number = 0;
|
||||||
|
maxValue: number = 100;
|
||||||
|
constructor(componentName: string, componentDescription: string) {
|
||||||
|
super(componentName, componentDescription, ModelComponentType.SCRIPTACCOUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
save(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
.scriptAccount-item {
|
||||||
|
min-height: 1.8em !important;
|
||||||
|
height: 1.8em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scriptAccount-icon {
|
||||||
|
margin-right: 10px;
|
||||||
|
color: #ccffff;
|
||||||
|
align-content: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-item {
|
||||||
|
background-color: #8696b6;
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
<mat-action-list>
|
||||||
|
<mat-list-item class="scriptAccount-item" *ngFor="let scriptAccount of gameModel!.scriptAccounts"
|
||||||
|
(dblclick)="onOpenScriptAccount(scriptAccount)" (click)="selectScriptAccount(scriptAccount)"
|
||||||
|
[ngClass]="selectedScriptAccount === scriptAccount ?'selected-item':''"
|
||||||
|
(contextmenu)="selectScriptAccount(scriptAccount)">
|
||||||
|
<mat-icon class="scriptAccount-icon">inventory_2</mat-icon>{{scriptAccount.componentName}}
|
||||||
|
</mat-list-item>
|
||||||
|
</mat-action-list>
|
@ -0,0 +1,46 @@
|
|||||||
|
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {TranslateModule} from "@ngx-translate/core";
|
||||||
|
import {RouterTestingModule} from "@angular/router/testing";
|
||||||
|
import {ScriptAccountOverviewComponent} from "./script-account-overview.component";
|
||||||
|
import exp from "node:constants";
|
||||||
|
import {GameModel} from "../../game-model/GameModel";
|
||||||
|
|
||||||
|
describe('ScriptAccountOverview', () => {
|
||||||
|
let component: ScriptAccountOverviewComponent;
|
||||||
|
let fixture: ComponentFixture<ScriptAccountOverviewComponent>;
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
void TestBed.configureTestingModule({
|
||||||
|
declarations: [ScriptAccountOverviewComponent],
|
||||||
|
imports: [TranslateModule.forRoot(), RouterTestingModule]
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ScriptAccountOverviewComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.gameModel = new GameModel("GameModel")
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Test ScriptAccount Creation", waitForAsync(() => {
|
||||||
|
component.onCreateNewScriptAccount();
|
||||||
|
expect(component.gameModel!.scriptAccounts.length).toEqual(1)
|
||||||
|
component.gameModel!.removeScriptAccount(component.gameModel!.scriptAccounts[0]);
|
||||||
|
|
||||||
|
jest.spyOn(component.openScriptAccountEmitter, 'emit');
|
||||||
|
component.onCreateNewScriptAccount();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component.openScriptAccountEmitter.emit).toHaveBeenCalledWith(component.gameModel!.scriptAccounts[0]);
|
||||||
|
|
||||||
|
component.onCreateNewScriptAccount();
|
||||||
|
fixture.detectChanges()
|
||||||
|
expect(component.openScriptAccountEmitter.emit).toBeCalledTimes(1);
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,46 @@
|
|||||||
|
import {Component, EventEmitter, Input, NgZone, Output} from '@angular/core';
|
||||||
|
import {GameModel} from "../../game-model/GameModel";
|
||||||
|
import {ScriptAccount} from "../../game-model/scriptAccounts/ScriptAccount";
|
||||||
|
import {ElectronService} from "../../core/services";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-script-account-overview',
|
||||||
|
templateUrl: './script-account-overview.component.html',
|
||||||
|
styleUrl: './script-account-overview.component.css'
|
||||||
|
})
|
||||||
|
export class ScriptAccountOverviewComponent {
|
||||||
|
@Input("gameModel") gameModel: GameModel | undefined
|
||||||
|
@Output("onOpenScriptAccount") openScriptAccountEmitter: EventEmitter<ScriptAccount> = new EventEmitter<ScriptAccount>();
|
||||||
|
|
||||||
|
selectedScriptAccount: ScriptAccount | undefined
|
||||||
|
|
||||||
|
constructor(private electronService: ElectronService,
|
||||||
|
private zone: NgZone) {
|
||||||
|
if(electronService.isElectron) {
|
||||||
|
this.electronService.ipcRenderer.on('context-menu', (event: any, message: string) => {
|
||||||
|
this.zone.run(() => {
|
||||||
|
if(message == "new-scriptaccount") {
|
||||||
|
this.onCreateNewScriptAccount()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpenScriptAccount(scriptAccount: ScriptAccount) {
|
||||||
|
console.log("onOpenScriptAccount (overview)")
|
||||||
|
this.openScriptAccountEmitter.emit(scriptAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCreateNewScriptAccount() {
|
||||||
|
const scriptAccount = this.gameModel!.addScriptAccount("New ScriptAccount");
|
||||||
|
if(scriptAccount != undefined) {
|
||||||
|
this.openScriptAccountEmitter.emit(scriptAccount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
selectScriptAccount(scriptAccount: ScriptAccount) {
|
||||||
|
this.selectedScriptAccount = scriptAccount;
|
||||||
|
}
|
||||||
|
}
|
19
src/styles.css
Normal file
19
src/styles.css
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/* You can add global styles to this file, and also import other style files */
|
||||||
|
@import "@angular/material/prebuilt-themes/purple-green.css";
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body { height: 100%; margin: 0 }
|
||||||
|
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: #3c95f8 !important;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background-color: #4f5459 !important;
|
||||||
|
color: white;
|
||||||
|
}
|
@ -1,50 +0,0 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
|
||||||
html, body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
height: 100%;
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CAN (MUST) BE REMOVED ! Sample Global style */
|
|
||||||
.container {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
background: url(./assets/background.jpg) no-repeat center fixed;
|
|
||||||
-webkit-background-size: cover; /* pour anciens Chrome et Safari */
|
|
||||||
background-size: cover; /* version standardisée */
|
|
||||||
|
|
||||||
.title {
|
|
||||||
color: white;
|
|
||||||
margin: 0;
|
|
||||||
padding: 50px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #fff !important;
|
|
||||||
text-transform: uppercase;
|
|
||||||
text-decoration: none;
|
|
||||||
background: #ed3330;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 5px;
|
|
||||||
display: inline-block;
|
|
||||||
border: none;
|
|
||||||
transition: all 0.4s ease 0s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #fff;
|
|
||||||
color: #ed3330 !important;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
-webkit-box-shadow: 0px 5px 40px -10px rgba(0,0,0,0.57);
|
|
||||||
-moz-box-shadow: 0px 5px 40px -10px rgba(0,0,0,0.57);
|
|
||||||
box-shadow: 5px 40px -10px rgba(0,0,0,0.57);
|
|
||||||
transition: all 0.4s ease 0s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user