From cc549202669ed9b24e56051b0cab0c8fd5be7499 Mon Sep 17 00:00:00 2001
From: rhenck <richard.henck@iqb.hu-berlin.de>
Date: Thu, 11 Nov 2021 20:00:34 +0100
Subject: [PATCH] [editor] Refactor directory structure and separate dialog
 components

This structure moves closer to the structure template the team decided
on. Components and services are kept in dedicated folders instead of
following the logical code structure. This is still not the case here as
the unit-view component is still nested, but it's closer.
---
 projects/editor/src/app/app.component.ts      |   4 +-
 projects/editor/src/app/app.module.ts         |  58 ++-
 .../dialogs/confirmation-dialog.component.ts  |  18 +
 .../likert-column-edit-dialog.component.ts    |  48 +++
 .../likert-row-edit-dialog.component.ts       |  37 ++
 .../dialogs/player-edit-dialog.component.ts   | 109 ++++++
 .../rich-text-edit-dialog.component.ts        |  18 +
 .../dialogs/text-edit-dialog.component.ts     |  21 ++
 .../text-edit-multiline-dialog.component.ts   |  22 ++
 .../toolbar/toolbar.component.html            |   0
 .../toolbar/toolbar.component.ts              |   2 +-
 .../canvas/canvas-element-overlay.ts          |  10 +-
 .../page-view/canvas/canvas.component.html    |   0
 .../page-view/canvas/canvas.component.ts      |  10 +-
 .../dynamic-canvas-overlay.component.ts       |   2 +-
 .../canvas/section-dynamic.component.ts       |   6 +-
 .../canvas/section-menu.component.ts          |   8 +-
 .../canvas/section-static.component.ts        |   6 +-
 .../canvas/static-canvas-overlay.component.ts |   2 +-
 .../ui-element-toolbox.component.html         |   0
 .../ui-element-toolbox.component.ts           |   6 +-
 .../page-view/page-view.component.ts          |   2 +-
 .../element-properties.component.css          |   0
 .../element-properties.component.html         |   0
 .../element-properties.component.ts           |  16 +-
 .../element-sizing-properties.component.ts    |   6 +-
 .../element-style-properties.component.ts     |   2 +-
 .../unit-view/unit-view.component.html        |   0
 .../unit-view/unit-view.component.ts          |  12 +-
 projects/editor/src/app/dialog.service.ts     | 336 ------------------
 .../editor/src/app/services/dialog.service.ts |  84 +++++
 .../app/{ => services}/selection.service.ts   |   2 +-
 .../src/app/{ => services}/unit.service.ts    |  24 +-
 .../app/{ => services}/verona-api.service.ts  |   0
 34 files changed, 445 insertions(+), 426 deletions(-)
 create mode 100644 projects/editor/src/app/components/dialogs/confirmation-dialog.component.ts
 create mode 100644 projects/editor/src/app/components/dialogs/likert-column-edit-dialog.component.ts
 create mode 100644 projects/editor/src/app/components/dialogs/likert-row-edit-dialog.component.ts
 create mode 100644 projects/editor/src/app/components/dialogs/player-edit-dialog.component.ts
 create mode 100644 projects/editor/src/app/components/dialogs/rich-text-edit-dialog.component.ts
 create mode 100644 projects/editor/src/app/components/dialogs/text-edit-dialog.component.ts
 create mode 100644 projects/editor/src/app/components/dialogs/text-edit-multiline-dialog.component.ts
 rename projects/editor/src/app/{ => components}/toolbar/toolbar.component.html (100%)
 rename projects/editor/src/app/{ => components}/toolbar/toolbar.component.ts (88%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/canvas/canvas-element-overlay.ts (88%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/canvas/canvas.component.html (100%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/canvas/canvas.component.ts (93%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/canvas/dynamic-canvas-overlay.component.ts (97%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/canvas/section-dynamic.component.ts (96%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/canvas/section-menu.component.ts (97%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/canvas/section-static.component.ts (86%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/canvas/static-canvas-overlay.component.ts (97%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html (100%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.ts (79%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/page-view.component.ts (83%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/properties-panel/element-properties.component.css (100%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/properties-panel/element-properties.component.html (100%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/properties-panel/element-properties.component.ts (89%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/properties-panel/element-sizing-properties.component.ts (97%)
 rename projects/editor/src/app/{ => components}/unit-view/page-view/properties-panel/element-style-properties.component.ts (98%)
 rename projects/editor/src/app/{ => components}/unit-view/unit-view.component.html (100%)
 rename projects/editor/src/app/{ => components}/unit-view/unit-view.component.ts (90%)
 delete mode 100644 projects/editor/src/app/dialog.service.ts
 create mode 100644 projects/editor/src/app/services/dialog.service.ts
 rename projects/editor/src/app/{ => services}/selection.service.ts (94%)
 rename projects/editor/src/app/{ => services}/unit.service.ts (94%)
 rename projects/editor/src/app/{ => services}/verona-api.service.ts (100%)

diff --git a/projects/editor/src/app/app.component.ts b/projects/editor/src/app/app.component.ts
index 3f33e5ef4..ab2c3121c 100644
--- a/projects/editor/src/app/app.component.ts
+++ b/projects/editor/src/app/app.component.ts
@@ -1,7 +1,7 @@
 import { Component, OnInit } from '@angular/core';
 import { TranslateService } from '@ngx-translate/core';
-import { VeronaAPIService } from './verona-api.service';
-import { UnitService } from './unit.service';
+import { VeronaAPIService } from './services/verona-api.service';
+import { UnitService } from './services/unit.service';
 
 @Component({
   selector: 'editor-aspect',
diff --git a/projects/editor/src/app/app.module.ts b/projects/editor/src/app/app.module.ts
index 4674d2dd4..52b3aa13c 100644
--- a/projects/editor/src/app/app.module.ts
+++ b/projects/editor/src/app/app.module.ts
@@ -12,31 +12,29 @@ import { MatMenuModule } from '@angular/material/menu';
 import { MatSliderModule } from '@angular/material/slider';
 
 import { AppComponent } from './app.component';
-import { ToolbarComponent } from './toolbar/toolbar.component';
-import { UiElementToolboxComponent } from './unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component';
-import { UnitViewComponent } from './unit-view/unit-view.component';
-import { PageViewComponent } from './unit-view/page-view/page-view.component';
-import { CanvasComponent } from './unit-view/page-view/canvas/canvas.component';
-import { StaticCanvasOverlayComponent } from './unit-view/page-view/canvas/static-canvas-overlay.component';
-import { DynamicCanvasOverlayComponent } from './unit-view/page-view/canvas/dynamic-canvas-overlay.component';
+import { ToolbarComponent } from './components/toolbar/toolbar.component';
+import { UiElementToolboxComponent } from './components/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component';
+import { UnitViewComponent } from './components/unit-view/unit-view.component';
+import { PageViewComponent } from './components/unit-view/page-view/page-view.component';
+import { CanvasComponent } from './components/unit-view/page-view/canvas/canvas.component';
+import { StaticCanvasOverlayComponent } from './components/unit-view/page-view/canvas/static-canvas-overlay.component';
+import { DynamicCanvasOverlayComponent } from './components/unit-view/page-view/canvas/dynamic-canvas-overlay.component';
 import { SharedModule } from '../../../common/shared.module';
-import {
-  ConfirmationDialog,
-  TextEditDialog,
-  MultilineTextEditDialog,
-  RichTextEditDialog,
-  LikertColumnEditDialog,
-  LikertRowEditDialog,
-  PlayerEditDialog
-} from './dialog.service';
 import { EditorTranslateLoader } from './editor-translate-loader';
-import { ElementPropertiesComponent } from './unit-view/page-view/properties-panel/element-properties.component';
-import { SectionMenuComponent } from './unit-view/page-view/canvas/section-menu.component';
-import { SectionStaticComponent } from './unit-view/page-view/canvas/section-static.component';
-import { SectionDynamicComponent } from './unit-view/page-view/canvas/section-dynamic.component';
+import { ElementPropertiesComponent } from './components/unit-view/page-view/properties-panel/element-properties.component';
+import { SectionMenuComponent } from './components/unit-view/page-view/canvas/section-menu.component';
+import { SectionStaticComponent } from './components/unit-view/page-view/canvas/section-static.component';
+import { SectionDynamicComponent } from './components/unit-view/page-view/canvas/section-dynamic.component';
 import { RichTextEditorComponent } from './text-editor/rich-text-editor.component';
-import { ElementStylePropertiesComponent } from './unit-view/page-view/properties-panel/element-style-properties.component';
-import { ElementSizingPropertiesComponent } from './unit-view/page-view/properties-panel/element-sizing-properties.component';
+import { ElementStylePropertiesComponent } from './components/unit-view/page-view/properties-panel/element-style-properties.component';
+import { ElementSizingPropertiesComponent } from './components/unit-view/page-view/properties-panel/element-sizing-properties.component';
+import { ConfirmationDialogComponent } from './components/dialogs/confirmation-dialog.component';
+import { TextEditDialogComponent } from './components/dialogs/text-edit-dialog.component';
+import { TextEditMultilineDialogComponent } from './components/dialogs/text-edit-multiline-dialog.component';
+import { PlayerEditDialogComponent } from './components/dialogs/player-edit-dialog.component';
+import { LikertColumnEditDialogComponent } from './components/dialogs/likert-column-edit-dialog.component';
+import { LikertRowEditDialogComponent } from './components/dialogs/likert-row-edit-dialog.component';
+import { RichTextEditDialogComponent } from './components/dialogs/rich-text-edit-dialog.component';
 
 @NgModule({
   declarations: [
@@ -48,20 +46,20 @@ import { ElementSizingPropertiesComponent } from './unit-view/page-view/properti
     CanvasComponent,
     StaticCanvasOverlayComponent,
     DynamicCanvasOverlayComponent,
-    ConfirmationDialog,
-    TextEditDialog,
-    MultilineTextEditDialog,
-    RichTextEditDialog,
-    LikertColumnEditDialog,
-    LikertRowEditDialog,
-    PlayerEditDialog,
     ElementPropertiesComponent,
     SectionMenuComponent,
     SectionStaticComponent,
     SectionDynamicComponent,
     RichTextEditorComponent,
     ElementStylePropertiesComponent,
-    ElementSizingPropertiesComponent
+    ElementSizingPropertiesComponent,
+    ConfirmationDialogComponent,
+    TextEditDialogComponent,
+    TextEditMultilineDialogComponent,
+    PlayerEditDialogComponent,
+    LikertColumnEditDialogComponent,
+    LikertRowEditDialogComponent,
+    RichTextEditDialogComponent
   ],
   imports: [
     BrowserModule,
diff --git a/projects/editor/src/app/components/dialogs/confirmation-dialog.component.ts b/projects/editor/src/app/components/dialogs/confirmation-dialog.component.ts
new file mode 100644
index 000000000..9b0216b1d
--- /dev/null
+++ b/projects/editor/src/app/components/dialogs/confirmation-dialog.component.ts
@@ -0,0 +1,18 @@
+import { Component, Inject } from '@angular/core';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+
+@Component({
+  selector: 'app-confirmation-dialog',
+  template: `
+    <mat-dialog-content>
+        {{data.text}}
+    </mat-dialog-content>
+    <mat-dialog-actions>
+      <button mat-button [mat-dialog-close]="true">{{'save' | translate }}</button>
+      <button mat-button mat-dialog-close>{{'cancel' | translate }}</button>
+    </mat-dialog-actions>
+    `
+})
+export class ConfirmationDialogComponent {
+  constructor(@Inject(MAT_DIALOG_DATA) public data: { text: string }) { }
+}
diff --git a/projects/editor/src/app/components/dialogs/likert-column-edit-dialog.component.ts b/projects/editor/src/app/components/dialogs/likert-column-edit-dialog.component.ts
new file mode 100644
index 000000000..9b16ec040
--- /dev/null
+++ b/projects/editor/src/app/components/dialogs/likert-column-edit-dialog.component.ts
@@ -0,0 +1,48 @@
+import { Component, Inject } from '@angular/core';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { LikertColumn } from '../../../../../common/interfaces/UIElementInterfaces';
+import { FileService } from '../../../../../common/file.service';
+
+@Component({
+  selector: 'app-likert-column-edit-dialog',
+  template: `
+    <mat-dialog-content fxLayout="column">
+      <mat-form-field>
+        <mat-label>{{'text' | translate }}</mat-label>
+        <input #textInput matInput type="text" [value]="data.column.text">
+      </mat-form-field>
+      <input #imageUpload type="file" hidden (click)="loadImage()">
+      <button mat-raised-button (click)="imageUpload.click()">{{ 'loadImage' | translate }}</button>
+      <button mat-raised-button (click)="data.column.imgSrc = null">{{ 'removeImage' | translate }}</button>
+      <img [src]="data.column.imgSrc"
+           [style.object-fit]="'scale-down'"
+           [width]="200">
+      <mat-form-field appearance="fill">
+        <mat-label>{{'position' | translate }}</mat-label>
+        <mat-select [value]="data.column.position"
+                    (selectionChange)="this.data.column.position = $event.value">
+          <mat-option *ngFor="let option of ['above', 'below']"
+                      [value]="option">
+            {{ option | translate }}
+          </mat-option>
+        </mat-select>
+      </mat-form-field>
+    </mat-dialog-content>
+    <mat-dialog-actions>
+      <button mat-button [mat-dialog-close]="{
+                         text: textInput.value,
+                         imgSrc: data.column.imgSrc,
+                         position: data.column.position }">
+        {{'save' | translate }}
+      </button>
+      <button mat-button mat-dialog-close>{{'cancel' | translate }}</button>
+    </mat-dialog-actions>
+  `
+})
+export class LikertColumnEditDialogComponent {
+  constructor(@Inject(MAT_DIALOG_DATA) public data: { column: LikertColumn }) { }
+
+  async loadImage(): Promise<void> {
+    this.data.column.imgSrc = await FileService.loadImage();
+  }
+}
diff --git a/projects/editor/src/app/components/dialogs/likert-row-edit-dialog.component.ts b/projects/editor/src/app/components/dialogs/likert-row-edit-dialog.component.ts
new file mode 100644
index 000000000..8ce581e24
--- /dev/null
+++ b/projects/editor/src/app/components/dialogs/likert-row-edit-dialog.component.ts
@@ -0,0 +1,37 @@
+import { Component, Inject } from '@angular/core';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { LikertElementRow } from '../../../../../common/models/compound-elements/likert-element-row';
+import { LikertColumn } from '../../../../../common/interfaces/UIElementInterfaces';
+
+@Component({
+  selector: 'app-likert-row-edit-dialog',
+  template: `
+    <mat-dialog-content fxLayout="column">
+      <mat-form-field>
+        <mat-label>{{'text' | translate }}</mat-label>
+        <input #textField matInput type="text" [value]="data.row.text">
+      </mat-form-field>
+      <mat-form-field>
+        <mat-label>{{'id' | translate }}</mat-label>
+        <input #idField matInput type="text" [value]="data.row.id">
+      </mat-form-field>
+      {{'preset' | translate }}
+      <mat-select #valueField [value]="data.row.value">
+        <mat-option [value]="null">{{'propertiesPanel.undefined' | translate }}</mat-option>
+        <mat-option *ngFor="let column of data.columns; let i = index" [value]="i">
+          {{column.text}}
+        </mat-option>
+      </mat-select>
+    </mat-dialog-content>
+    <mat-dialog-actions>
+      <button mat-button
+              [mat-dialog-close]="{ text: textField.value, id: idField.value, value: valueField.value }">
+        {{'save' | translate }}
+      </button>
+      <button mat-button mat-dialog-close>{{'cancel' | translate }}</button>
+    </mat-dialog-actions>
+  `
+})
+export class LikertRowEditDialogComponent {
+  constructor(@Inject(MAT_DIALOG_DATA) public data: { row: LikertElementRow, columns: LikertColumn[] }) { }
+}
diff --git a/projects/editor/src/app/components/dialogs/player-edit-dialog.component.ts b/projects/editor/src/app/components/dialogs/player-edit-dialog.component.ts
new file mode 100644
index 000000000..a79fc6859
--- /dev/null
+++ b/projects/editor/src/app/components/dialogs/player-edit-dialog.component.ts
@@ -0,0 +1,109 @@
+import { Component, Inject } from '@angular/core';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+import { PlayerElement } from '../../../../../common/interfaces/UIElementInterfaces';
+
+@Component({
+  selector: 'app-player-edit-dialog',
+  template: `
+    <mat-dialog-content fxLayout="row">
+      <mat-tab-group>
+        <mat-tab label="{{ 'player.appearance' | translate }}">
+          <div fxLayout="column">
+            <mat-checkbox [checked]="newPlayerConfig.startControl || data.player.startControl"
+                          (change)="newPlayerConfig.startControl = $event.checked">
+              {{ 'player.startControl' | translate }}
+            </mat-checkbox>
+            <mat-checkbox [checked]="newPlayerConfig.pauseControl || data.player.pauseControl"
+                          (change)="newPlayerConfig.pauseControl = $event.checked">
+              {{ 'player.pauseControl' | translate }}
+            </mat-checkbox>
+            <mat-checkbox [checked]="newPlayerConfig.progressBar || data.player.progressBar"
+                          (change)="newPlayerConfig.progressBar = $event.checked">
+              {{ 'player.progressBar' | translate }}
+            </mat-checkbox>
+            <mat-checkbox [checked]="newPlayerConfig.interactiveProgressbar || data.player.interactiveProgressbar"
+                          (change)="newPlayerConfig.interactiveProgressbar = $event.checked">
+              {{ 'player.interactiveProgressbar' | translate }}
+            </mat-checkbox>
+            <mat-checkbox [checked]="newPlayerConfig.volumeControl || data.player.volumeControl"
+                          (change)="newPlayerConfig.volumeControl = $event.checked">
+              {{ 'player.volumeControl' | translate }}
+            </mat-checkbox>
+            <mat-checkbox [checked]="newPlayerConfig.showRestTime || data.player.showRestTime"
+                          (change)="newPlayerConfig.showRestTime = $event.checked">
+              {{ 'player.showRestTime' | translate }}
+            </mat-checkbox>
+            <mat-form-field appearance="fill">
+              <mat-label>{{ 'player.hintLabel' | translate }}</mat-label>
+              <input matInput type="text" [value]="newPlayerConfig.hintLabel || data.player.hintLabel"
+                     (input)="newPlayerConfig.hintLabel = $any($event.target).value">
+            </mat-form-field>
+            <mat-form-field *ngIf="newPlayerConfig.hintLabel || data.player.hintLabel"
+                            appearance="fill">
+              <mat-label>{{ 'player.hintLabelDelay' | translate }}</mat-label>
+              <input matInput type="number" step="1000" min="0"
+                     [ngModel]="newPlayerConfig.hintLabelDelay || data.player.hintLabelDelay"
+                     (ngModelChange)="newPlayerConfig.hintLabelDelay = $event">
+            </mat-form-field>
+          </div>
+        </mat-tab>
+        <mat-tab label="{{ 'player.behaviour' | translate }}">
+          <div fxLayout="column">
+            <mat-checkbox [checked]="newPlayerConfig.autostart || data.player.autostart"
+                          (change)="newPlayerConfig.autostart = $event.checked">
+              {{ 'player.autoStart' | translate }}
+            </mat-checkbox>
+            <mat-form-field *ngIf="newPlayerConfig.autostart || data.player.autostart" appearance="fill">
+              <mat-label>{{ 'player.autoStartDelay' | translate }}</mat-label>
+              <input matInput type="number" step="1000"
+                     [value]="newPlayerConfig.autostartDelay || data.player.autostartDelay"
+                     (input)="newPlayerConfig.autostartDelay = $any($event.target).value">
+            </mat-form-field>
+            <mat-checkbox [checked]="newPlayerConfig.loop || data.player.loop"
+                          (change)="newPlayerConfig.loop = $event.checked">
+              {{ 'player.loop' | translate }}
+            </mat-checkbox>
+            <mat-checkbox [checked]="newPlayerConfig.uninterruptible || data.player.uninterruptible"
+                          (change)="newPlayerConfig.uninterruptible = $event.checked">
+              {{ 'player.uninterruptible' | translate }}
+            </mat-checkbox>
+            <mat-checkbox [checked]="newPlayerConfig.hideOtherPages || data.player.hideOtherPages"
+                          (change)="newPlayerConfig.hideOtherPages = $event.checked">
+              {{ 'player.hideOtherPages' | translate }}
+            </mat-checkbox>
+            <mat-form-field appearance="fill">
+              <mat-label>{{ 'player.activeAfterID' | translate }}</mat-label>
+              <input matInput type="text" [value]="newPlayerConfig.activeAfterID || data.player.activeAfterID"
+                     (input)="newPlayerConfig.activeAfterID = $any($event.target).value">
+            </mat-form-field>
+            <mat-form-field appearance="fill">
+              <mat-label>{{ 'player.minRuns' | translate }}</mat-label>
+              <input matInput type="number" min="0"
+                     [ngModel]="newPlayerConfig.minRuns || data.player.minRuns"
+                     (ngModelChange)="newPlayerConfig.minRuns = $event">
+            </mat-form-field>
+            <mat-form-field appearance="fill">
+              <mat-label>{{ 'player.maxRuns' | translate }}</mat-label>
+              <input matInput type="number" min="0"
+                     [ngModel]="newPlayerConfig.maxRuns || data.player.maxRuns"
+                     (ngModelChange)="newPlayerConfig.maxRuns = $event">
+            </mat-form-field>
+            <mat-checkbox [checked]="newPlayerConfig.showRestRuns || data.player.showRestRuns"
+                          (change)="newPlayerConfig.showRestRuns = $event.checked">
+              {{ 'player.showRestRuns' | translate }}
+            </mat-checkbox>
+          </div>
+        </mat-tab>
+      </mat-tab-group>
+    </mat-dialog-content>
+    <mat-dialog-actions>
+      <button mat-button [mat-dialog-close]="newPlayerConfig">{{'save' | translate }}</button>
+      <button mat-button mat-dialog-close>{{'cancel' | translate }}</button>
+    </mat-dialog-actions>
+    `
+})
+export class PlayerEditDialogComponent {
+  newPlayerConfig: PlayerElement = {} as PlayerElement;
+  constructor(@Inject(MAT_DIALOG_DATA)public data: { player: PlayerElement }) {
+  }
+}
diff --git a/projects/editor/src/app/components/dialogs/rich-text-edit-dialog.component.ts b/projects/editor/src/app/components/dialogs/rich-text-edit-dialog.component.ts
new file mode 100644
index 000000000..4abdf7a3e
--- /dev/null
+++ b/projects/editor/src/app/components/dialogs/rich-text-edit-dialog.component.ts
@@ -0,0 +1,18 @@
+import { Component, Inject } from '@angular/core';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+
+@Component({
+  selector: 'app-rich-text-edit-dialog-tinymce',
+  template: `
+    <mat-dialog-content>
+      <app-rich-text-editor [(text)]="data.text"></app-rich-text-editor>
+    </mat-dialog-content>
+    <mat-dialog-actions>
+      <button mat-button [mat-dialog-close]="data.text">{{'save' | translate }}</button>
+      <button mat-button mat-dialog-close>{{'cancel' | translate }}</button>
+    </mat-dialog-actions>
+    `
+})
+export class RichTextEditDialogComponent {
+  constructor(@Inject(MAT_DIALOG_DATA) public data: { text: string }) { }
+}
diff --git a/projects/editor/src/app/components/dialogs/text-edit-dialog.component.ts b/projects/editor/src/app/components/dialogs/text-edit-dialog.component.ts
new file mode 100644
index 000000000..33ff28e15
--- /dev/null
+++ b/projects/editor/src/app/components/dialogs/text-edit-dialog.component.ts
@@ -0,0 +1,21 @@
+import { Component, Inject } from '@angular/core';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+
+@Component({
+  selector: 'app-text-edit-dialog',
+  template: `
+    <mat-dialog-content>
+      <mat-form-field>
+        <mat-label>{{'text' | translate }}</mat-label>
+        <input #inputElement matInput type="text" [value]="data.text">
+      </mat-form-field>
+    </mat-dialog-content>
+    <mat-dialog-actions>
+      <button mat-button [mat-dialog-close]="inputElement.value">{{'save' | translate }}</button>
+      <button mat-button mat-dialog-close>{{'cancel' | translate }}</button>
+    </mat-dialog-actions>
+    `
+})
+export class TextEditDialogComponent {
+  constructor(@Inject(MAT_DIALOG_DATA) public data: { text: string }) { }
+}
diff --git a/projects/editor/src/app/components/dialogs/text-edit-multiline-dialog.component.ts b/projects/editor/src/app/components/dialogs/text-edit-multiline-dialog.component.ts
new file mode 100644
index 000000000..e25d41cbd
--- /dev/null
+++ b/projects/editor/src/app/components/dialogs/text-edit-multiline-dialog.component.ts
@@ -0,0 +1,22 @@
+import { Component, Inject } from '@angular/core';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
+
+@Component({
+  selector: 'app-multiline-text-edit-dialog',
+  template: `
+    <mat-dialog-content>
+      <mat-form-field>
+        <mat-label>{{'text' | translate }}</mat-label>
+        <textarea #inputElement matInput type="text" [value]="data.text">
+        </textarea>
+      </mat-form-field>
+    </mat-dialog-content>
+    <mat-dialog-actions>
+      <button mat-button [mat-dialog-close]="inputElement.value">{{'save' | translate }}</button>
+      <button mat-button mat-dialog-close>{{'cancel' | translate }}</button>
+    </mat-dialog-actions>
+    `
+})
+export class TextEditMultilineDialogComponent {
+  constructor(@Inject(MAT_DIALOG_DATA) public data: { text: string }) { }
+}
diff --git a/projects/editor/src/app/toolbar/toolbar.component.html b/projects/editor/src/app/components/toolbar/toolbar.component.html
similarity index 100%
rename from projects/editor/src/app/toolbar/toolbar.component.html
rename to projects/editor/src/app/components/toolbar/toolbar.component.html
diff --git a/projects/editor/src/app/toolbar/toolbar.component.ts b/projects/editor/src/app/components/toolbar/toolbar.component.ts
similarity index 88%
rename from projects/editor/src/app/toolbar/toolbar.component.ts
rename to projects/editor/src/app/components/toolbar/toolbar.component.ts
index 2200af4a0..e9e91c3d4 100644
--- a/projects/editor/src/app/toolbar/toolbar.component.ts
+++ b/projects/editor/src/app/components/toolbar/toolbar.component.ts
@@ -1,5 +1,5 @@
 import { Component } from '@angular/core';
-import { UnitService } from '../unit.service';
+import { UnitService } from '../../services/unit.service';
 
 @Component({
   selector: 'app-toolbar',
diff --git a/projects/editor/src/app/unit-view/page-view/canvas/canvas-element-overlay.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/canvas-element-overlay.ts
similarity index 88%
rename from projects/editor/src/app/unit-view/page-view/canvas/canvas-element-overlay.ts
rename to projects/editor/src/app/components/unit-view/page-view/canvas/canvas-element-overlay.ts
index 043c98e14..f3ec24219 100644
--- a/projects/editor/src/app/unit-view/page-view/canvas/canvas-element-overlay.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/canvas-element-overlay.ts
@@ -4,11 +4,11 @@ import {
   ViewChild, ViewContainerRef, OnInit, OnDestroy, ChangeDetectorRef
 } from '@angular/core';
 import { Subject } from 'rxjs';
-import { UnitService } from '../../../unit.service';
-import * as ElementFactory from '../../../../../../common/util/element.factory';
-import { ElementComponent } from '../../../../../../common/element-component.directive';
-import { SelectionService } from '../../../selection.service';
-import { UIElement } from '../../../../../../common/models/uI-element';
+import { UnitService } from '../../../../services/unit.service';
+import * as ElementFactory from '../../../../../../../common/util/element.factory';
+import { ElementComponent } from '../../../../../../../common/element-component.directive';
+import { SelectionService } from '../../../../services/selection.service';
+import { UIElement } from '../../../../../../../common/models/uI-element';
 
 @Directive()
 export abstract class CanvasElementOverlay implements OnInit, OnDestroy {
diff --git a/projects/editor/src/app/unit-view/page-view/canvas/canvas.component.html b/projects/editor/src/app/components/unit-view/page-view/canvas/canvas.component.html
similarity index 100%
rename from projects/editor/src/app/unit-view/page-view/canvas/canvas.component.html
rename to projects/editor/src/app/components/unit-view/page-view/canvas/canvas.component.html
diff --git a/projects/editor/src/app/unit-view/page-view/canvas/canvas.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/canvas.component.ts
similarity index 93%
rename from projects/editor/src/app/unit-view/page-view/canvas/canvas.component.ts
rename to projects/editor/src/app/components/unit-view/page-view/canvas/canvas.component.ts
index 35671158a..1dd5084dc 100644
--- a/projects/editor/src/app/unit-view/page-view/canvas/canvas.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/canvas.component.ts
@@ -4,11 +4,11 @@ import {
 import { CdkDragDrop } from '@angular/cdk/drag-drop';
 import { Subject } from 'rxjs';
 import { takeUntil } from 'rxjs/operators';
-import { UnitService } from '../../../unit.service';
-import { SelectionService } from '../../../selection.service';
-import { Page } from '../../../../../../common/models/page';
-import { UIElement } from '../../../../../../common/models/uI-element';
-import { Section } from '../../../../../../common/models/section';
+import { UnitService } from '../../../../services/unit.service';
+import { SelectionService } from '../../../../services/selection.service';
+import { Page } from '../../../../../../../common/models/page';
+import { UIElement } from '../../../../../../../common/models/uI-element';
+import { Section } from '../../../../../../../common/models/section';
 
 @Component({
   selector: 'app-page-canvas',
diff --git a/projects/editor/src/app/unit-view/page-view/canvas/dynamic-canvas-overlay.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/dynamic-canvas-overlay.component.ts
similarity index 97%
rename from projects/editor/src/app/unit-view/page-view/canvas/dynamic-canvas-overlay.component.ts
rename to projects/editor/src/app/components/unit-view/page-view/canvas/dynamic-canvas-overlay.component.ts
index 9c43de68d..6b2db21d0 100644
--- a/projects/editor/src/app/unit-view/page-view/canvas/dynamic-canvas-overlay.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/dynamic-canvas-overlay.component.ts
@@ -3,7 +3,7 @@ import {
 } from '@angular/core';
 import { CdkDragMove } from '@angular/cdk/drag-drop';
 import { CanvasElementOverlay } from './canvas-element-overlay';
-import { UIElement } from '../../../../../../common/models/uI-element';
+import { UIElement } from '../../../../../../../common/models/uI-element';
 
 @Component({
   selector: 'app-dynamic-canvas-overlay',
diff --git a/projects/editor/src/app/unit-view/page-view/canvas/section-dynamic.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts
similarity index 96%
rename from projects/editor/src/app/unit-view/page-view/canvas/section-dynamic.component.ts
rename to projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts
index f0f54c04e..b643936d8 100644
--- a/projects/editor/src/app/unit-view/page-view/canvas/section-dynamic.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts
@@ -3,9 +3,9 @@ import {
 } from '@angular/core';
 import { CdkDragDrop } from '@angular/cdk/drag-drop/drag-events';
 import { DragItemData, DropListData } from './canvas.component';
-import { UnitService } from '../../../unit.service';
-import { Section } from '../../../../../../common/models/section';
-import { UIElementType } from '../../../../../../common/models/uI-element';
+import { UnitService } from '../../../../services/unit.service';
+import { Section } from '../../../../../../../common/models/section';
+import { UIElementType } from '../../../../../../../common/models/uI-element';
 
 @Component({
   selector: 'app-section-dynamic',
diff --git a/projects/editor/src/app/unit-view/page-view/canvas/section-menu.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/section-menu.component.ts
similarity index 97%
rename from projects/editor/src/app/unit-view/page-view/canvas/section-menu.component.ts
rename to projects/editor/src/app/components/unit-view/page-view/canvas/section-menu.component.ts
index 14acd9628..e173e81db 100644
--- a/projects/editor/src/app/unit-view/page-view/canvas/section-menu.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/section-menu.component.ts
@@ -4,10 +4,10 @@ import {
 } from '@angular/core';
 import { Subject } from 'rxjs';
 import { takeUntil } from 'rxjs/operators';
-import { UnitService } from '../../../unit.service';
-import { DialogService } from '../../../dialog.service';
-import { SelectionService } from '../../../selection.service';
-import { Section } from '../../../../../../common/models/section';
+import { UnitService } from '../../../../services/unit.service';
+import { DialogService } from '../../../../services/dialog.service';
+import { SelectionService } from '../../../../services/selection.service';
+import { Section } from '../../../../../../../common/models/section';
 
 @Component({
   selector: 'app-section-menu',
diff --git a/projects/editor/src/app/unit-view/page-view/canvas/section-static.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/section-static.component.ts
similarity index 86%
rename from projects/editor/src/app/unit-view/page-view/canvas/section-static.component.ts
rename to projects/editor/src/app/components/unit-view/page-view/canvas/section-static.component.ts
index 6fd07c3ee..56ba5d797 100644
--- a/projects/editor/src/app/unit-view/page-view/canvas/section-static.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/section-static.component.ts
@@ -1,9 +1,9 @@
 import {
   Component, ElementRef, Input, ViewChild
 } from '@angular/core';
-import { UnitService } from '../../../unit.service';
-import { Section } from '../../../../../../common/models/section';
-import { UIElementType } from '../../../../../../common/models/uI-element';
+import { UnitService } from '../../../../services/unit.service';
+import { Section } from '../../../../../../../common/models/section';
+import { UIElementType } from '../../../../../../../common/models/uI-element';
 
 @Component({
   selector: 'app-section-static',
diff --git a/projects/editor/src/app/unit-view/page-view/canvas/static-canvas-overlay.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/static-canvas-overlay.component.ts
similarity index 97%
rename from projects/editor/src/app/unit-view/page-view/canvas/static-canvas-overlay.component.ts
rename to projects/editor/src/app/components/unit-view/page-view/canvas/static-canvas-overlay.component.ts
index 19b988b47..ac98fea91 100644
--- a/projects/editor/src/app/unit-view/page-view/canvas/static-canvas-overlay.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/canvas/static-canvas-overlay.component.ts
@@ -2,7 +2,7 @@ import { Component } from '@angular/core';
 import { take } from 'rxjs/operators';
 import { CdkDragEnd, CdkDragMove } from '@angular/cdk/drag-drop';
 import { CanvasElementOverlay } from './canvas-element-overlay';
-import { UIElement } from '../../../../../../common/models/uI-element';
+import { UIElement } from '../../../../../../../common/models/uI-element';
 
 @Component({
   selector: 'app-static-canvas-overlay',
diff --git a/projects/editor/src/app/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html b/projects/editor/src/app/components/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html
similarity index 100%
rename from projects/editor/src/app/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html
rename to projects/editor/src/app/components/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.html
diff --git a/projects/editor/src/app/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.ts b/projects/editor/src/app/components/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.ts
similarity index 79%
rename from projects/editor/src/app/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.ts
rename to projects/editor/src/app/components/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.ts
index ce6cf9705..c2ed4f843 100644
--- a/projects/editor/src/app/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/new-ui-element-panel/ui-element-toolbox.component.ts
@@ -1,7 +1,7 @@
 import { Component } from '@angular/core';
-import { UnitService } from '../../../unit.service';
-import { SelectionService } from '../../../selection.service';
-import { UIElementType } from '../../../../../../common/models/uI-element';
+import { UnitService } from '../../../../services/unit.service';
+import { SelectionService } from '../../../../services/selection.service';
+import { UIElementType } from '../../../../../../../common/models/uI-element';
 
 @Component({
   selector: 'app-ui-element-toolbox',
diff --git a/projects/editor/src/app/unit-view/page-view/page-view.component.ts b/projects/editor/src/app/components/unit-view/page-view/page-view.component.ts
similarity index 83%
rename from projects/editor/src/app/unit-view/page-view/page-view.component.ts
rename to projects/editor/src/app/components/unit-view/page-view/page-view.component.ts
index 74b8a394e..7b0b9ce9c 100644
--- a/projects/editor/src/app/unit-view/page-view/page-view.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/page-view.component.ts
@@ -1,7 +1,7 @@
 import {
   Component, Input
 } from '@angular/core';
-import { Page } from '../../../../../common/models/page';
+import { Page } from '../../../../../../common/models/page';
 
 @Component({
   selector: 'app-page-view',
diff --git a/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.css b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties.component.css
similarity index 100%
rename from projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.css
rename to projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties.component.css
diff --git a/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.html b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties.component.html
similarity index 100%
rename from projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.html
rename to projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties.component.html
diff --git a/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties.component.ts
similarity index 89%
rename from projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.ts
rename to projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties.component.ts
index c7ed4680d..f26bda454 100644
--- a/projects/editor/src/app/unit-view/page-view/properties-panel/element-properties.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-properties.component.ts
@@ -7,14 +7,14 @@ import { takeUntil } from 'rxjs/operators';
 import { CdkDragDrop } from '@angular/cdk/drag-drop/drag-events';
 import { moveItemInArray } from '@angular/cdk/drag-drop';
 import { TranslateService } from '@ngx-translate/core';
-import { UnitService } from '../../../unit.service';
-import { SelectionService } from '../../../selection.service';
-import { MessageService } from '../../../../../../common/message.service';
-import { FileService } from '../../../../../../common/file.service';
-import { UIElement } from '../../../../../../common/models/uI-element';
-import { LikertElementRow } from '../../../../../../common/models/compound-elements/likert-element-row';
-import { LikertElement } from '../../../../../../common/models/compound-elements/likert-element';
-import { LikertColumn, LikertRow } from '../../../../../../common/interfaces/UIElementInterfaces';
+import { UnitService } from '../../../../services/unit.service';
+import { SelectionService } from '../../../../services/selection.service';
+import { MessageService } from '../../../../../../../common/message.service';
+import { FileService } from '../../../../../../../common/file.service';
+import { UIElement } from '../../../../../../../common/models/uI-element';
+import { LikertElementRow } from '../../../../../../../common/models/compound-elements/likert-element-row';
+import { LikertElement } from '../../../../../../../common/models/compound-elements/likert-element';
+import { LikertColumn, LikertRow } from '../../../../../../../common/interfaces/UIElementInterfaces';
 
 @Component({
   selector: 'app-element-properties',
diff --git a/projects/editor/src/app/unit-view/page-view/properties-panel/element-sizing-properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-sizing-properties.component.ts
similarity index 97%
rename from projects/editor/src/app/unit-view/page-view/properties-panel/element-sizing-properties.component.ts
rename to projects/editor/src/app/components/unit-view/page-view/properties-panel/element-sizing-properties.component.ts
index 2e80f919d..a07f50791 100644
--- a/projects/editor/src/app/unit-view/page-view/properties-panel/element-sizing-properties.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-sizing-properties.component.ts
@@ -1,9 +1,9 @@
 import {
   Component, Input, Output, EventEmitter
 } from '@angular/core';
-import { UnitService } from '../../../unit.service';
-import { SelectionService } from '../../../selection.service';
-import { UIElement } from '../../../../../../common/models/uI-element';
+import { UnitService } from '../../../../services/unit.service';
+import { SelectionService } from '../../../../services/selection.service';
+import { UIElement } from '../../../../../../../common/models/uI-element';
 
 @Component({
   selector: 'app-element-sizing-properties',
diff --git a/projects/editor/src/app/unit-view/page-view/properties-panel/element-style-properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-style-properties.component.ts
similarity index 98%
rename from projects/editor/src/app/unit-view/page-view/properties-panel/element-style-properties.component.ts
rename to projects/editor/src/app/components/unit-view/page-view/properties-panel/element-style-properties.component.ts
index aaff4480c..8448154ac 100644
--- a/projects/editor/src/app/unit-view/page-view/properties-panel/element-style-properties.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-view/properties-panel/element-style-properties.component.ts
@@ -1,7 +1,7 @@
 import {
   Component, EventEmitter, Input, Output
 } from '@angular/core';
-import { UIElement } from '../../../../../../common/models/uI-element';
+import { UIElement } from '../../../../../../../common/models/uI-element';
 
 @Component({
   selector: 'app-element-style-properties',
diff --git a/projects/editor/src/app/unit-view/unit-view.component.html b/projects/editor/src/app/components/unit-view/unit-view.component.html
similarity index 100%
rename from projects/editor/src/app/unit-view/unit-view.component.html
rename to projects/editor/src/app/components/unit-view/unit-view.component.html
diff --git a/projects/editor/src/app/unit-view/unit-view.component.ts b/projects/editor/src/app/components/unit-view/unit-view.component.ts
similarity index 90%
rename from projects/editor/src/app/unit-view/unit-view.component.ts
rename to projects/editor/src/app/components/unit-view/unit-view.component.ts
index 680a1d364..a65d7afcb 100644
--- a/projects/editor/src/app/unit-view/unit-view.component.ts
+++ b/projects/editor/src/app/components/unit-view/unit-view.component.ts
@@ -1,12 +1,12 @@
 import { Component, OnDestroy, OnInit } from '@angular/core';
 import { Subject } from 'rxjs';
 import { takeUntil } from 'rxjs/operators';
-import { UnitService } from '../unit.service';
-import { DialogService } from '../dialog.service';
-import { SelectionService } from '../selection.service';
-import { MessageService } from '../../../../common/message.service';
-import { Page } from '../../../../common/models/page';
-import { Unit } from '../../../../common/models/unit';
+import { UnitService } from '../../services/unit.service';
+import { DialogService } from '../../services/dialog.service';
+import { SelectionService } from '../../services/selection.service';
+import { MessageService } from '../../../../../common/message.service';
+import { Page } from '../../../../../common/models/page';
+import { Unit } from '../../../../../common/models/unit';
 
 @Component({
   selector: 'app-unit-view',
diff --git a/projects/editor/src/app/dialog.service.ts b/projects/editor/src/app/dialog.service.ts
deleted file mode 100644
index c92fa5fb6..000000000
--- a/projects/editor/src/app/dialog.service.ts
+++ /dev/null
@@ -1,336 +0,0 @@
-// eslint-disable-next-line max-classes-per-file
-import { Component, Inject, Injectable } from '@angular/core';
-import { Observable } from 'rxjs';
-import { MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog';
-import { PlayerElement, LikertColumn } from '../../../common/interfaces/UIElementInterfaces';
-import { FileService } from '../../../common/file.service';
-import { LikertElementRow } from '../../../common/models/compound-elements/likert-element-row';
-
-@Injectable({
-  providedIn: 'root'
-})
-export class DialogService {
-  constructor(private dialog: MatDialog) { }
-
-  showConfirmDialog(text: string): Observable<boolean> {
-    const dialogRef = this.dialog.open(ConfirmationDialog, {
-      data: {
-        text: text
-      }
-    });
-    return dialogRef.afterClosed();
-  }
-
-  showTextEditDialog(text: string): Observable<string> {
-    const dialogRef = this.dialog.open(TextEditDialog, {
-      data: {
-        text: text
-      }
-    });
-    return dialogRef.afterClosed();
-  }
-
-  showMultilineTextEditDialog(text: string): Observable<string> {
-    const dialogRef = this.dialog.open(MultilineTextEditDialog, {
-      data: {
-        text: text
-      }
-    });
-    return dialogRef.afterClosed();
-  }
-
-  showRichTextEditDialog(text: string): Observable<string> {
-    const dialogRef = this.dialog.open(RichTextEditDialog, {
-      data: {
-        text: text
-      },
-      autoFocus: false
-    });
-    return dialogRef.afterClosed();
-  }
-
-  showPlayerEditDialog(player: PlayerElement): Observable<PlayerElement> {
-    const dialogRef = this.dialog.open(PlayerEditDialog, {
-      data: {
-        player: player
-      },
-      autoFocus: false
-    });
-    return dialogRef.afterClosed();
-  }
-
-  showLikertColumnEditDialog(column: LikertColumn): Observable<LikertColumn> {
-    const dialogRef = this.dialog.open(LikertColumnEditDialog, {
-      data: {
-        column: column
-      },
-      autoFocus: false
-    });
-    return dialogRef.afterClosed();
-  }
-
-  showLikertRowEditDialog(row: LikertElementRow, columns: LikertColumn[]): Observable<LikertElementRow> {
-    const dialogRef = this.dialog.open(LikertRowEditDialog, {
-      data: {
-        row: row,
-        columns: columns
-      },
-      autoFocus: false
-    });
-    return dialogRef.afterClosed();
-  }
-}
-
-@Component({
-  selector: 'app-confirmation-dialog',
-  template: `
-    <mat-dialog-content>
-        {{data.text}}
-    </mat-dialog-content>
-    <mat-dialog-actions>
-      <button mat-button [mat-dialog-close]="true">{{'save' | translate }}</button>
-      <button mat-button mat-dialog-close>{{'cancel' | translate }}</button>
-    </mat-dialog-actions>
-    `
-})
-export class ConfirmationDialog {
-  constructor(@Inject(MAT_DIALOG_DATA) public data: { text: string }) { }
-}
-
-@Component({
-  selector: 'app-text-edit-dialog',
-  template: `
-    <mat-dialog-content>
-      <mat-form-field>
-        <mat-label>{{'text' | translate }}</mat-label>
-        <input #inputElement matInput type="text" [value]="data.text">
-      </mat-form-field>
-    </mat-dialog-content>
-    <mat-dialog-actions>
-      <button mat-button [mat-dialog-close]="inputElement.value">{{'save' | translate }}</button>
-      <button mat-button mat-dialog-close>{{'cancel' | translate }}</button>
-    </mat-dialog-actions>
-    `
-})
-export class TextEditDialog {
-  constructor(@Inject(MAT_DIALOG_DATA) public data: { text: string }) { }
-}
-
-@Component({
-  selector: 'app-multiline-text-edit-dialog',
-  template: `
-    <mat-dialog-content>
-      <mat-form-field>
-        <mat-label>{{'text' | translate }}</mat-label>
-        <textarea #inputElement matInput type="text" [value]="data.text">
-        </textarea>
-      </mat-form-field>
-    </mat-dialog-content>
-    <mat-dialog-actions>
-      <button mat-button [mat-dialog-close]="inputElement.value">{{'save' | translate }}</button>
-      <button mat-button mat-dialog-close>{{'cancel' | translate }}</button>
-    </mat-dialog-actions>
-    `
-})
-export class MultilineTextEditDialog {
-  constructor(@Inject(MAT_DIALOG_DATA) public data: { text: string }) { }
-}
-
-@Component({
-  selector: 'app-rich-text-edit-dialog-tinymce',
-  template: `
-    <mat-dialog-content>
-      <app-rich-text-editor [(text)]="data.text"></app-rich-text-editor>
-    </mat-dialog-content>
-    <mat-dialog-actions>
-      <button mat-button [mat-dialog-close]="data.text">{{'save' | translate }}</button>
-      <button mat-button mat-dialog-close>{{'cancel' | translate }}</button>
-    </mat-dialog-actions>
-    `
-})
-export class RichTextEditDialog {
-  constructor(@Inject(MAT_DIALOG_DATA) public data: { text: string }) { }
-}
-
-@Component({
-  selector: 'app-player-edit-dialog',
-  template: `
-    <mat-dialog-content fxLayout="row">
-      <mat-tab-group>
-        <mat-tab label="{{ 'player.appearance' | translate }}">
-          <div fxLayout="column">
-            <mat-checkbox [checked]="newPlayerConfig.startControl || data.player.startControl"
-                          (change)="newPlayerConfig.startControl = $event.checked">
-              {{ 'player.startControl' | translate }}
-            </mat-checkbox>
-            <mat-checkbox [checked]="newPlayerConfig.pauseControl || data.player.pauseControl"
-                          (change)="newPlayerConfig.pauseControl = $event.checked">
-              {{ 'player.pauseControl' | translate }}
-            </mat-checkbox>
-            <mat-checkbox [checked]="newPlayerConfig.progressBar || data.player.progressBar"
-                          (change)="newPlayerConfig.progressBar = $event.checked">
-              {{ 'player.progressBar' | translate }}
-            </mat-checkbox>
-            <mat-checkbox [checked]="newPlayerConfig.interactiveProgressbar || data.player.interactiveProgressbar"
-                          (change)="newPlayerConfig.interactiveProgressbar = $event.checked">
-              {{ 'player.interactiveProgressbar' | translate }}
-            </mat-checkbox>
-            <mat-checkbox [checked]="newPlayerConfig.volumeControl || data.player.volumeControl"
-                          (change)="newPlayerConfig.volumeControl = $event.checked">
-              {{ 'player.volumeControl' | translate }}
-            </mat-checkbox>
-            <mat-checkbox [checked]="newPlayerConfig.showRestTime || data.player.showRestTime"
-                          (change)="newPlayerConfig.showRestTime = $event.checked">
-              {{ 'player.showRestTime' | translate }}
-            </mat-checkbox>
-            <mat-form-field appearance="fill">
-              <mat-label>{{ 'player.hintLabel' | translate }}</mat-label>
-              <input matInput type="text" [value]="newPlayerConfig.hintLabel || data.player.hintLabel"
-                     (input)="newPlayerConfig.hintLabel = $any($event.target).value">
-            </mat-form-field>
-            <mat-form-field *ngIf="newPlayerConfig.hintLabel || data.player.hintLabel"
-                            appearance="fill">
-              <mat-label>{{ 'player.hintLabelDelay' | translate }}</mat-label>
-              <input matInput type="number" step="1000" min="0"
-                     [ngModel]="newPlayerConfig.hintLabelDelay || data.player.hintLabelDelay"
-                     (ngModelChange)="newPlayerConfig.hintLabelDelay = $event">
-            </mat-form-field>
-          </div>
-        </mat-tab>
-        <mat-tab label="{{ 'player.behaviour' | translate }}">
-          <div fxLayout="column">
-            <mat-checkbox [checked]="newPlayerConfig.autostart || data.player.autostart"
-                          (change)="newPlayerConfig.autostart = $event.checked">
-              {{ 'player.autoStart' | translate }}
-            </mat-checkbox>
-            <mat-form-field *ngIf="newPlayerConfig.autostart || data.player.autostart" appearance="fill">
-              <mat-label>{{ 'player.autoStartDelay' | translate }}</mat-label>
-              <input matInput type="number" step="1000"
-                     [value]="newPlayerConfig.autostartDelay || data.player.autostartDelay"
-                     (input)="newPlayerConfig.autostartDelay = $any($event.target).value">
-            </mat-form-field>
-            <mat-checkbox [checked]="newPlayerConfig.loop || data.player.loop"
-                          (change)="newPlayerConfig.loop = $event.checked">
-              {{ 'player.loop' | translate }}
-            </mat-checkbox>
-            <mat-checkbox [checked]="newPlayerConfig.uninterruptible || data.player.uninterruptible"
-                          (change)="newPlayerConfig.uninterruptible = $event.checked">
-              {{ 'player.uninterruptible' | translate }}
-            </mat-checkbox>
-            <mat-checkbox [checked]="newPlayerConfig.hideOtherPages || data.player.hideOtherPages"
-                          (change)="newPlayerConfig.hideOtherPages = $event.checked">
-              {{ 'player.hideOtherPages' | translate }}
-            </mat-checkbox>
-            <mat-form-field appearance="fill">
-              <mat-label>{{ 'player.activeAfterID' | translate }}</mat-label>
-              <input matInput type="text" [value]="newPlayerConfig.activeAfterID || data.player.activeAfterID"
-                     (input)="newPlayerConfig.activeAfterID = $any($event.target).value">
-            </mat-form-field>
-            <mat-form-field appearance="fill">
-              <mat-label>{{ 'player.minRuns' | translate }}</mat-label>
-              <input matInput type="number" min="0"
-                     [ngModel]="newPlayerConfig.minRuns || data.player.minRuns"
-                     (ngModelChange)="newPlayerConfig.minRuns = $event">
-            </mat-form-field>
-            <mat-form-field appearance="fill">
-              <mat-label>{{ 'player.maxRuns' | translate }}</mat-label>
-              <input matInput type="number" min="0"
-                     [ngModel]="newPlayerConfig.maxRuns || data.player.maxRuns"
-                     (ngModelChange)="newPlayerConfig.maxRuns = $event">
-            </mat-form-field>
-            <mat-checkbox [checked]="newPlayerConfig.showRestRuns || data.player.showRestRuns"
-                          (change)="newPlayerConfig.showRestRuns = $event.checked">
-              {{ 'player.showRestRuns' | translate }}
-            </mat-checkbox>
-          </div>
-        </mat-tab>
-      </mat-tab-group>
-    </mat-dialog-content>
-    <mat-dialog-actions>
-      <button mat-button [mat-dialog-close]="newPlayerConfig">{{'save' | translate }}</button>
-      <button mat-button mat-dialog-close>{{'cancel' | translate }}</button>
-    </mat-dialog-actions>
-    `
-})
-export class PlayerEditDialog {
-  newPlayerConfig: PlayerElement = {} as PlayerElement;
-  constructor(@Inject(MAT_DIALOG_DATA)public data: { player: PlayerElement }) {
-  }
-}
-
-@Component({
-  selector: 'app-likert-column-edit-dialog',
-  template: `
-    <mat-dialog-content fxLayout="column">
-      <mat-form-field>
-        <mat-label>{{'text' | translate }}</mat-label>
-        <input #textInput matInput type="text" [value]="data.column.text">
-      </mat-form-field>
-      <input #imageUpload type="file" hidden (click)="loadImage()">
-      <button mat-raised-button (click)="imageUpload.click()">{{ 'loadImage' | translate }}</button>
-      <button mat-raised-button (click)="data.column.imgSrc = null">{{ 'removeImage' | translate }}</button>
-      <img [src]="data.column.imgSrc"
-           [style.object-fit]="'scale-down'"
-           [width]="200">
-      <mat-form-field appearance="fill">
-        <mat-label>{{'position' | translate }}</mat-label>
-        <mat-select [value]="data.column.position"
-                    (selectionChange)="this.data.column.position = $event.value">
-          <mat-option *ngFor="let option of ['above', 'below']"
-                      [value]="option">
-            {{ option | translate }}
-          </mat-option>
-        </mat-select>
-      </mat-form-field>
-    </mat-dialog-content>
-    <mat-dialog-actions>
-      <button mat-button [mat-dialog-close]="{
-                         text: textInput.value,
-                         imgSrc: data.column.imgSrc,
-                         position: data.column.position }">
-        {{'save' | translate }}
-      </button>
-      <button mat-button mat-dialog-close>{{'cancel' | translate }}</button>
-    </mat-dialog-actions>
-  `
-})
-export class LikertColumnEditDialog {
-  constructor(@Inject(MAT_DIALOG_DATA) public data: { column: LikertColumn }) { }
-
-  async loadImage(): Promise<void> {
-    this.data.column.imgSrc = await FileService.loadImage();
-  }
-}
-
-@Component({
-  selector: 'app-likert-row-edit-dialog',
-  template: `
-    <mat-dialog-content fxLayout="column">
-      <mat-form-field>
-        <mat-label>{{'text' | translate }}</mat-label>
-        <input #textField matInput type="text" [value]="data.row.text">
-      </mat-form-field>
-      <mat-form-field>
-        <mat-label>{{'id' | translate }}</mat-label>
-        <input #idField matInput type="text" [value]="data.row.id">
-      </mat-form-field>
-      {{'preset' | translate }}
-      <mat-select #valueField [value]="data.row.value">
-        <mat-option [value]="null">{{'propertiesPanel.undefined' | translate }}</mat-option>
-        <mat-option *ngFor="let column of data.columns; let i = index" [value]="i">
-          {{column.text}}
-        </mat-option>
-      </mat-select>
-    </mat-dialog-content>
-    <mat-dialog-actions>
-      <button mat-button
-              [mat-dialog-close]="{ text: textField.value, id: idField.value, value: valueField.value }">
-        {{'save' | translate }}
-      </button>
-      <button mat-button mat-dialog-close>{{'cancel' | translate }}</button>
-    </mat-dialog-actions>
-  `
-})
-export class LikertRowEditDialog {
-  constructor(@Inject(MAT_DIALOG_DATA) public data: { row: LikertElementRow, columns: LikertColumn[] }) { }
-}
diff --git a/projects/editor/src/app/services/dialog.service.ts b/projects/editor/src/app/services/dialog.service.ts
new file mode 100644
index 000000000..8b633a8b9
--- /dev/null
+++ b/projects/editor/src/app/services/dialog.service.ts
@@ -0,0 +1,84 @@
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+import { MatDialog } from '@angular/material/dialog';
+import { PlayerElement, LikertColumn } from '../../../../common/interfaces/UIElementInterfaces';
+import { LikertElementRow } from '../../../../common/models/compound-elements/likert-element-row';
+import { ConfirmationDialogComponent } from '../components/dialogs/confirmation-dialog.component';
+import { TextEditDialogComponent } from '../components/dialogs/text-edit-dialog.component';
+import { TextEditMultilineDialogComponent } from '../components/dialogs/text-edit-multiline-dialog.component';
+import { RichTextEditDialogComponent } from '../components/dialogs/rich-text-edit-dialog.component';
+import { PlayerEditDialogComponent } from '../components/dialogs/player-edit-dialog.component';
+import { LikertColumnEditDialogComponent } from '../components/dialogs/likert-column-edit-dialog.component';
+import { LikertRowEditDialogComponent } from '../components/dialogs/likert-row-edit-dialog.component';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class DialogService {
+  constructor(private dialog: MatDialog) { }
+
+  showConfirmDialog(text: string): Observable<boolean> {
+    const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
+      data: {
+        text: text
+      }
+    });
+    return dialogRef.afterClosed();
+  }
+
+  showTextEditDialog(text: string): Observable<string> {
+    const dialogRef = this.dialog.open(TextEditDialogComponent, {
+      data: {
+        text: text
+      }
+    });
+    return dialogRef.afterClosed();
+  }
+
+  showMultilineTextEditDialog(text: string): Observable<string> {
+    const dialogRef = this.dialog.open(TextEditMultilineDialogComponent, {
+      data: {
+        text: text
+      }
+    });
+    return dialogRef.afterClosed();
+  }
+
+  showRichTextEditDialog(text: string): Observable<string> {
+    const dialogRef = this.dialog.open(RichTextEditDialogComponent, {
+      data: {
+        text: text
+      },
+      autoFocus: false
+    });
+    return dialogRef.afterClosed();
+  }
+
+  showPlayerEditDialog(player: PlayerElement): Observable<PlayerElement> {
+    const dialogRef = this.dialog.open(PlayerEditDialogComponent, {
+      data: {
+        player: player
+      }
+    });
+    return dialogRef.afterClosed();
+  }
+
+  showLikertColumnEditDialog(column: LikertColumn): Observable<LikertColumn> {
+    const dialogRef = this.dialog.open(LikertColumnEditDialogComponent, {
+      data: {
+        column: column
+      }
+    });
+    return dialogRef.afterClosed();
+  }
+
+  showLikertRowEditDialog(row: LikertElementRow, columns: LikertColumn[]): Observable<LikertElementRow> {
+    const dialogRef = this.dialog.open(LikertRowEditDialogComponent, {
+      data: {
+        row: row,
+        columns: columns
+      }
+    });
+    return dialogRef.afterClosed();
+  }
+}
diff --git a/projects/editor/src/app/selection.service.ts b/projects/editor/src/app/services/selection.service.ts
similarity index 94%
rename from projects/editor/src/app/selection.service.ts
rename to projects/editor/src/app/services/selection.service.ts
index 1f6ae279b..2b0d69545 100644
--- a/projects/editor/src/app/selection.service.ts
+++ b/projects/editor/src/app/services/selection.service.ts
@@ -1,6 +1,6 @@
 import { Injectable } from '@angular/core';
 import { BehaviorSubject, Observable } from 'rxjs';
-import { UIElement } from '../../../common/models/uI-element';
+import { UIElement } from '../../../../common/models/uI-element';
 
 @Injectable({
   providedIn: 'root'
diff --git a/projects/editor/src/app/unit.service.ts b/projects/editor/src/app/services/unit.service.ts
similarity index 94%
rename from projects/editor/src/app/unit.service.ts
rename to projects/editor/src/app/services/unit.service.ts
index 3c846c783..8c5bbccef 100644
--- a/projects/editor/src/app/unit.service.ts
+++ b/projects/editor/src/app/services/unit.service.ts
@@ -2,21 +2,21 @@ import { Injectable } from '@angular/core';
 import { DomSanitizer } from '@angular/platform-browser';
 import { BehaviorSubject, Observable, Subject } from 'rxjs';
 import { TranslateService } from '@ngx-translate/core';
-import { FileService } from '../../../common/file.service';
-import { MessageService } from '../../../common/message.service';
-import { IdService } from '../../../common/id.service';
+import { FileService } from '../../../../common/file.service';
+import { MessageService } from '../../../../common/message.service';
+import { IdService } from '../../../../common/id.service';
 import { DialogService } from './dialog.service';
 import { VeronaAPIService } from './verona-api.service';
-import { Unit } from '../../../common/models/unit';
-import { Page } from '../../../common/models/page';
-import { Section } from '../../../common/models/section';
-import { InputElement, UIElement, UIElementType } from '../../../common/models/uI-element';
-import { TextElement } from '../../../common/models/text-element';
-import { LikertElement } from '../../../common/models/compound-elements/likert-element';
-import { LikertElementRow } from '../../../common/models/compound-elements/likert-element-row';
-import { LikertColumn, LikertRow, PlayerElement } from '../../../common/interfaces/UIElementInterfaces';
+import { Unit } from '../../../../common/models/unit';
+import { Page } from '../../../../common/models/page';
+import { Section } from '../../../../common/models/section';
+import { InputElement, UIElement, UIElementType } from '../../../../common/models/uI-element';
+import { TextElement } from '../../../../common/models/text-element';
+import { LikertElement } from '../../../../common/models/compound-elements/likert-element';
+import { LikertElementRow } from '../../../../common/models/compound-elements/likert-element-row';
+import { LikertColumn, LikertRow, PlayerElement } from '../../../../common/interfaces/UIElementInterfaces';
 import { SelectionService } from './selection.service';
-import * as ElementFactory from '../../../common/util/element.factory';
+import * as ElementFactory from '../../../../common/util/element.factory';
 
 @Injectable({
   providedIn: 'root'
diff --git a/projects/editor/src/app/verona-api.service.ts b/projects/editor/src/app/services/verona-api.service.ts
similarity index 100%
rename from projects/editor/src/app/verona-api.service.ts
rename to projects/editor/src/app/services/verona-api.service.ts
-- 
GitLab