diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8566a4ba..dec67b93 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -85,6 +85,9 @@ import { InterfaceLabelDraggedComponent } from './components/drawings-listeners/ import { ToolsService } from './services/tools.service'; import { TextAddedComponent } from './components/drawings-listeners/text-added/text-added.component'; import { DrawingAddedComponent } from './components/drawings-listeners/drawing-added/drawing-added.component'; +import { StyleEditorDialogComponent } from './components/project-map/drawings-editors/style-editor/style-editor.component'; +import { EditTextActionComponent } from './components/project-map/context-menu/actions/edit-text-action/edit-text-action.component'; +import { TextEditorDialogComponent } from './components/project-map/drawings-editors/text-editor/text-editor.component'; if (environment.production) { @@ -122,6 +125,7 @@ if (environment.production) { MoveLayerDownActionComponent, MoveLayerUpActionComponent, EditStyleActionComponent, + EditTextActionComponent, ProjectMapShortcutsComponent, SettingsComponent, LocalServerComponent, @@ -137,7 +141,9 @@ if (environment.production) { NodeLabelDraggedComponent, DrawingDraggedComponent, LinkCreatedComponent, - InterfaceLabelDraggedComponent + InterfaceLabelDraggedComponent, + StyleEditorDialogComponent, + TextEditorDialogComponent ], imports: [ BrowserModule, @@ -192,7 +198,9 @@ if (environment.production) { TemplateListDialogComponent, AddBlankProjectDialogComponent, ImportProjectDialogComponent, - ConfirmationDialogComponent + ConfirmationDialogComponent, + StyleEditorDialogComponent, + TextEditorDialogComponent ], bootstrap: [ AppComponent ] }) diff --git a/src/app/cartography/components/text-editor/text-editor.component.ts b/src/app/cartography/components/text-editor/text-editor.component.ts index 4517666b..56a3cf3f 100644 --- a/src/app/cartography/components/text-editor/text-editor.component.ts +++ b/src/app/cartography/components/text-editor/text-editor.component.ts @@ -92,6 +92,11 @@ export class TextEditorComponent implements OnInit, OnDestroy { this.leftPosition = x.toString() + 'px'; this.topPosition = y.toString() + 'px'; this.temporaryTextElement.nativeElement.innerText = elem.text; + + this.renderer.setStyle(this.temporaryTextElement.nativeElement, 'color', elem.fill); + this.renderer.setStyle(this.temporaryTextElement.nativeElement, 'font-family', elem.font_family); + this.renderer.setStyle(this.temporaryTextElement.nativeElement, 'font-size', `${elem.font_size}pt`); + this.renderer.setStyle(this.temporaryTextElement.nativeElement, 'font-weight', elem.font_weight); let listener = () => { let innerText = this.temporaryTextElement.nativeElement.innerText; @@ -105,6 +110,7 @@ export class TextEditorComponent implements OnInit, OnDestroy { this.temporaryTextElement.nativeElement.innerText = ''; this.temporaryTextElement.nativeElement.removeEventListener("focusout", this.textListener); + this.clearStyle(); this.renderer.setStyle(this.temporaryTextElement.nativeElement, 'display', 'none'); }; this.textListener = listener; @@ -116,4 +122,11 @@ export class TextEditorComponent implements OnInit, OnDestroy { ngOnDestroy(){ this.textAddingSubscription.unsubscribe(); } + + clearStyle(){ + this.renderer.setStyle(this.temporaryTextElement.nativeElement, 'color', '#000000'); + this.renderer.setStyle(this.temporaryTextElement.nativeElement, 'font-family', 'Noto Sans'); + this.renderer.setStyle(this.temporaryTextElement.nativeElement, 'font-size', '11pt'); + this.renderer.setStyle(this.temporaryTextElement.nativeElement, 'font-weight', 'bold'); + } } diff --git a/src/app/cartography/converters/map/map-drawing-to-drawing-converter.ts b/src/app/cartography/converters/map/map-drawing-to-drawing-converter.ts index 04347c0e..1ed35d30 100644 --- a/src/app/cartography/converters/map/map-drawing-to-drawing-converter.ts +++ b/src/app/cartography/converters/map/map-drawing-to-drawing-converter.ts @@ -19,6 +19,7 @@ export class MapDrawingToDrawingConverter implements Converter { convert(mapDrawing: MapDrawing) { let elem = ``; - + if (mapDrawing.element instanceof RectElement) { elem = ``; } else if (mapDrawing.element instanceof EllipseElement) { diff --git a/src/app/components/project-map/context-menu/actions/edit-style-action/edit-style-action.component.ts b/src/app/components/project-map/context-menu/actions/edit-style-action/edit-style-action.component.ts index 54b1a542..d112eb2f 100644 --- a/src/app/components/project-map/context-menu/actions/edit-style-action/edit-style-action.component.ts +++ b/src/app/components/project-map/context-menu/actions/edit-style-action/edit-style-action.component.ts @@ -1,8 +1,9 @@ import { Component, OnInit, Input } from "@angular/core"; import { Drawing } from '../../../../../cartography/models/drawing'; import { Server } from '../../../../../models/server'; -import { DrawingsDataSource } from '../../../../../cartography/datasources/drawings-datasource'; -import { DrawingService } from '../../../../../services/drawing.service'; +import { MatDialog } from '@angular/material'; +import { Project } from '../../../../../models/project'; +import { StyleEditorDialogComponent } from '../../../drawings-editors/style-editor/style-editor.component'; @Component({ @@ -11,14 +12,22 @@ import { DrawingService } from '../../../../../services/drawing.service'; }) export class EditStyleActionComponent implements OnInit { @Input() server: Server; + @Input() project: Project; @Input() drawing: Drawing; constructor( - private drawingsDataSource: DrawingsDataSource, - private drawingService: DrawingService + private dialog: MatDialog ) {} ngOnInit() {} - editStyle(){} + editStyle() { + const dialogRef = this.dialog.open(StyleEditorDialogComponent, { + width: '450px', + }); + let instance = dialogRef.componentInstance; + instance.server = this.server; + instance.project = this.project; + instance.drawing = this.drawing; + } } diff --git a/src/app/components/project-map/context-menu/actions/edit-text-action/edit-text-action.component.html b/src/app/components/project-map/context-menu/actions/edit-text-action/edit-text-action.component.html new file mode 100644 index 00000000..5a76b054 --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/edit-text-action/edit-text-action.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/components/project-map/context-menu/actions/edit-text-action/edit-text-action.component.spec.ts b/src/app/components/project-map/context-menu/actions/edit-text-action/edit-text-action.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/context-menu/actions/edit-text-action/edit-text-action.component.ts b/src/app/components/project-map/context-menu/actions/edit-text-action/edit-text-action.component.ts new file mode 100644 index 00000000..b69cd4f7 --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/edit-text-action/edit-text-action.component.ts @@ -0,0 +1,33 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Server } from '../../../../../models/server'; +import { Project } from '../../../../../models/project'; +import { Drawing } from '../../../../../cartography/models/drawing'; +import { MatDialog } from '@angular/material'; +import { TextEditorDialogComponent } from '../../../drawings-editors/text-editor/text-editor.component'; + + +@Component({ + selector: 'app-edit-text-action', + templateUrl: './edit-text-action.component.html' +}) +export class EditTextActionComponent implements OnInit { + @Input() server: Server; + @Input() project: Project; + @Input() drawing: Drawing; + + constructor( + private dialog: MatDialog + ) {} + + ngOnInit() {} + + editText() { + const dialogRef = this.dialog.open(TextEditorDialogComponent, { + width: '450px', + }); + let instance = dialogRef.componentInstance; + instance.server = this.server; + instance.project = this.project; + instance.drawing = this.drawing; + } +} diff --git a/src/app/components/project-map/context-menu/context-menu.component.html b/src/app/components/project-map/context-menu/context-menu.component.html index 396b659b..1eeb6c12 100644 --- a/src/app/components/project-map/context-menu/context-menu.component.html +++ b/src/app/components/project-map/context-menu/context-menu.component.html @@ -3,7 +3,8 @@ - + + diff --git a/src/app/components/project-map/context-menu/context-menu.component.spec.ts b/src/app/components/project-map/context-menu/context-menu.component.spec.ts index 74f643c0..58f77366 100644 --- a/src/app/components/project-map/context-menu/context-menu.component.spec.ts +++ b/src/app/components/project-map/context-menu/context-menu.component.spec.ts @@ -1,14 +1,30 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - import { ContextMenuComponent } from './context-menu.component'; +import { BrowserModule } from '@angular/platform-browser'; +import { ChangeDetectorRef, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ProjectService } from '../../../services/project.service'; +import { MockedProjectService } from '../../projects/add-blank-project-dialog/add-blank-project-dialog.component.spec'; +import { MatMenuModule, MatMenuTrigger } from '@angular/material'; +import { Drawing } from '../../../cartography/models/drawing'; +import { RectElement } from '../../../cartography/models/drawings/rect-element'; +import { TextElement } from '../../../cartography/models/drawings/text-element'; -describe('NodeContextMenuComponent', () => { +describe('ContextMenuComponent', () => { let component: ContextMenuComponent; let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ ContextMenuComponent ] + imports: [ + MatMenuModule, + BrowserModule + ], + providers: [ + { provide: ChangeDetectorRef }, + { provide: ProjectService, useClass: MockedProjectService } + ], + declarations: [ ContextMenuComponent ], + schemas: [ NO_ERRORS_SCHEMA ] }) .compileComponents(); })); @@ -19,7 +35,38 @@ describe('NodeContextMenuComponent', () => { fixture.detectChanges(); }); - // it('should create', () => { - // expect(component).toBeTruthy(); - // }); + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should reset capabilities while opening menu for node', () => { + component.contextMenu = { openMenu(){} } as MatMenuTrigger; + var spy = spyOn(component, "resetCapabilities"); + + component.openMenuForNode(null, 0, 0); + + expect(spy.calls.any()).toBeTruthy(); + }); + + it('should reset capabilities while opening menu for drawing', () => { + component.contextMenu = { openMenu(){} } as MatMenuTrigger; + let drawing = {} as Drawing; + drawing.element = new RectElement(); + var spy = spyOn(component, "resetCapabilities"); + spyOn(component, 'setPosition').and.callFake(() => {}); + component.openMenuForDrawing(drawing, 0, 0); + + expect(spy.calls.any()).toBeTruthy(); + }); + + it('should set correct flag while drawing is text element', () => { + component.contextMenu = { openMenu(){} } as MatMenuTrigger; + let drawing = {} as Drawing; + drawing.element = new TextElement(); + var spy = spyOn(component, "resetCapabilities"); + spyOn(component, 'setPosition').and.callFake(() => {}); + component.openMenuForDrawing(drawing, 0, 0); + + expect(spy.calls.any()).toBeTruthy(); + }); }); diff --git a/src/app/components/project-map/context-menu/context-menu.component.ts b/src/app/components/project-map/context-menu/context-menu.component.ts index 883a3e57..76ed7794 100644 --- a/src/app/components/project-map/context-menu/context-menu.component.ts +++ b/src/app/components/project-map/context-menu/context-menu.component.ts @@ -6,6 +6,7 @@ import { Server } from "../../../models/server"; import { Project } from "../../../models/project"; import { ProjectService } from "../../../services/project.service"; import { Drawing } from '../../../cartography/models/drawing'; +import { TextElement } from '../../../cartography/models/drawings/text-element'; @Component({ @@ -25,6 +26,7 @@ export class ContextMenuComponent implements OnInit { drawing: Drawing; private hasNodeCapabilities: boolean = false; private hasDrawingCapabilities: boolean = false; + private isTextElement: boolean = false; constructor( private sanitizer: DomSanitizer, @@ -54,6 +56,7 @@ export class ContextMenuComponent implements OnInit { public openMenuForDrawing(drawing: Drawing, top: number, left: number) { this.resetCapabilities(); this.hasDrawingCapabilities = true; + this.isTextElement = drawing.element instanceof TextElement; this.drawing = drawing; this.setPosition(top, left); @@ -66,5 +69,6 @@ export class ContextMenuComponent implements OnInit { this.drawing = null; this.hasDrawingCapabilities = false; this.hasNodeCapabilities = false; + this.isTextElement = false; } } diff --git a/src/app/components/project-map/drawings-editors/style-editor/style-editor.component.html b/src/app/components/project-map/drawings-editors/style-editor/style-editor.component.html new file mode 100644 index 00000000..00d7c0d9 --- /dev/null +++ b/src/app/components/project-map/drawings-editors/style-editor/style-editor.component.html @@ -0,0 +1,25 @@ +

Style editor

+ +
Fill color:
+ +
+ +
Border color:
+ +
+ +
Border width:
+ +
+ +
Border style:
+ +
+ +
Rotation:
+ +
+
+ + +
diff --git a/src/app/components/project-map/drawings-editors/style-editor/style-editor.component.scss b/src/app/components/project-map/drawings-editors/style-editor/style-editor.component.scss new file mode 100644 index 00000000..bcd46d6d --- /dev/null +++ b/src/app/components/project-map/drawings-editors/style-editor/style-editor.component.scss @@ -0,0 +1,29 @@ +.item { + display: flex; + height: 25px; + font-size:10pt; + margin-bottom: 10px; +} + +.item-name { + width: 30%; +} + +.item-value { + width: 70%; + margin: 3px; + margin-right: 4px; + margin-left: 4px; +} + +.input-color { + padding: 0px; + border-width: 0px; + width: 70%; + background-color: transparent; + outline: none; +} + +input:focus { + outline: none; +} diff --git a/src/app/components/project-map/drawings-editors/style-editor/style-editor.component.spec.ts b/src/app/components/project-map/drawings-editors/style-editor/style-editor.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/drawings-editors/style-editor/style-editor.component.ts b/src/app/components/project-map/drawings-editors/style-editor/style-editor.component.ts new file mode 100644 index 00000000..2fc2eb16 --- /dev/null +++ b/src/app/components/project-map/drawings-editors/style-editor/style-editor.component.ts @@ -0,0 +1,52 @@ +import { Component, OnInit } from "@angular/core"; +import { Server } from '../../../../models/server'; +import { Project } from '../../../../models/project'; +import { Drawing } from '../../../../cartography/models/drawing'; +import { MatDialogRef } from '@angular/material'; +import { DrawingToMapDrawingConverter } from '../../../../cartography/converters/map/drawing-to-map-drawing-converter'; +import { MapDrawingToSvgConverter } from '../../../../cartography/converters/map/map-drawing-to-svg-converter'; +import { DrawingService } from '../../../../services/drawing.service'; +import { DrawingsDataSource } from '../../../../cartography/datasources/drawings-datasource'; + + +@Component({ + selector: 'app-style-editor', + templateUrl: './style-editor.component.html', + styleUrls: ['./style-editor.component.scss'] +}) +export class StyleEditorDialogComponent implements OnInit { + server: Server; + project: Project; + drawing: Drawing; + rotation: string; + + constructor( + public dialogRef: MatDialogRef, + private drawingToMapDrawingConverter: DrawingToMapDrawingConverter, + private mapDrawingToSvgConverter: MapDrawingToSvgConverter, + private drawingService: DrawingService, + private drawingsDataSource: DrawingsDataSource + ){} + + ngOnInit() { + this.rotation = this.drawing.rotation.toString(); + } + + onNoClick() { + this.dialogRef.close(); + } + + onYesClick() { + this.drawing.rotation = +this.rotation; + let mapDrawing = this.drawingToMapDrawingConverter.convert(this.drawing); + mapDrawing.element = this.drawing.element; + + this.drawing.svg = this.mapDrawingToSvgConverter.convert(mapDrawing); + + this.drawingService + .update(this.server, this.drawing).subscribe((serverDrawing: Drawing) => { + this.drawingsDataSource.update(serverDrawing); + this.dialogRef.close(); + }); + } +} diff --git a/src/app/components/project-map/drawings-editors/text-editor/text-editor.component.html b/src/app/components/project-map/drawings-editors/text-editor/text-editor.component.html new file mode 100644 index 00000000..4cd2e728 --- /dev/null +++ b/src/app/components/project-map/drawings-editors/text-editor/text-editor.component.html @@ -0,0 +1,17 @@ +

Text editor

+ +
Fill color:
+ +
+ +
Rotation:
+ +
+ + + +
+ + +
diff --git a/src/app/components/project-map/drawings-editors/text-editor/text-editor.component.scss b/src/app/components/project-map/drawings-editors/text-editor/text-editor.component.scss new file mode 100644 index 00000000..52238a07 --- /dev/null +++ b/src/app/components/project-map/drawings-editors/text-editor/text-editor.component.scss @@ -0,0 +1,33 @@ +.item { + display: flex; + height: 25px; + font-size:10pt; + margin-bottom: 10px; +} + +.item-name { + width: 30%; +} + +.item-value { + width: 70%; + margin-top: 3px; + margin-bottom: 3px; +} + +.input-color { + padding: 0px; + border-width: 0px; + width: 70%; + background-color: transparent; + outline: none; +} + +input:focus { + outline: none; +} + +.text { + width: 100%; + height: 150px; +} diff --git a/src/app/components/project-map/drawings-editors/text-editor/text-editor.component.spec.ts b/src/app/components/project-map/drawings-editors/text-editor/text-editor.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/drawings-editors/text-editor/text-editor.component.ts b/src/app/components/project-map/drawings-editors/text-editor/text-editor.component.ts new file mode 100644 index 00000000..749d374f --- /dev/null +++ b/src/app/components/project-map/drawings-editors/text-editor/text-editor.component.ts @@ -0,0 +1,66 @@ +import { Component, OnInit, ViewChild, ElementRef, Renderer2 } from "@angular/core"; +import { Project } from '../../../../models/project'; +import { Drawing } from '../../../../cartography/models/drawing'; +import { Server } from '../../../../models/server'; +import { MatDialogRef } from '@angular/material'; +import { DrawingToMapDrawingConverter } from '../../../../cartography/converters/map/drawing-to-map-drawing-converter'; +import { MapDrawingToSvgConverter } from '../../../../cartography/converters/map/map-drawing-to-svg-converter'; +import { DrawingService } from '../../../../services/drawing.service'; +import { DrawingsDataSource } from '../../../../cartography/datasources/drawings-datasource'; +import { TextElement } from '../../../../cartography/models/drawings/text-element'; + + +@Component({ + selector: 'app-text-editor', + templateUrl: './text-editor.component.html', + styleUrls: ['./text-editor.component.scss'] +}) +export class TextEditorDialogComponent implements OnInit { + @ViewChild('textArea') textArea : ElementRef; + + server: Server; + project: Project; + drawing: Drawing; + rotation: string; + + constructor( + private dialogRef: MatDialogRef, + private drawingToMapDrawingConverter: DrawingToMapDrawingConverter, + private mapDrawingToSvgConverter: MapDrawingToSvgConverter, + private drawingService: DrawingService, + private drawingsDataSource: DrawingsDataSource, + private renderer: Renderer2 + ){} + + ngOnInit() { + this.rotation = this.drawing.rotation.toString(); + + let textElement = this.drawing.element as TextElement; + this.renderer.setStyle(this.textArea.nativeElement, 'color', textElement.fill); + this.renderer.setStyle(this.textArea.nativeElement, 'font-family', textElement.font_family); + this.renderer.setStyle(this.textArea.nativeElement, 'font-size', `${textElement.font_size}pt`); + this.renderer.setStyle(this.textArea.nativeElement, 'font-weight', textElement.font_weight); + } + + onNoClick() { + this.dialogRef.close(); + } + + onYesClick() { + this.drawing.rotation = +this.rotation; + let mapDrawing = this.drawingToMapDrawingConverter.convert(this.drawing); + mapDrawing.element = this.drawing.element; + + this.drawing.svg = this.mapDrawingToSvgConverter.convert(mapDrawing); + + this.drawingService + .update(this.server, this.drawing).subscribe((serverDrawing: Drawing) => { + this.drawingsDataSource.update(serverDrawing); + this.dialogRef.close(); + }); + } + + changeTextColor(changedColor) { + this.renderer.setStyle(this.textArea.nativeElement, 'color', changedColor); + } +} diff --git a/src/app/components/project-map/project-map.component.ts b/src/app/components/project-map/project-map.component.ts index 9e0a3f80..d5787c6d 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -313,10 +313,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { } public showMenu() { - setTimeout(() => { - this.drawTools.visibility = true; - }, - 200); + this.drawTools.visibility = true; } public ngOnDestroy() { diff --git a/src/app/services/drawing.service.spec.ts b/src/app/services/drawing.service.spec.ts index 70db98f8..48c118ea 100644 --- a/src/app/services/drawing.service.spec.ts +++ b/src/app/services/drawing.service.spec.ts @@ -117,6 +117,8 @@ describe('DrawingService', () => { drawing.x = 10; drawing.y = 20; drawing.z = 30; + drawing.rotation = 0; + drawing.svg = ''; service.update(server, drawing).subscribe(); @@ -126,7 +128,9 @@ describe('DrawingService', () => { expect(req.request.body).toEqual({ 'x': 10, 'y': 20, - 'z': 30 + 'z': 30, + 'rotation': 0, + 'svg': '' }); })); diff --git a/src/app/services/drawing.service.ts b/src/app/services/drawing.service.ts index e0cf9d8b..d93a97ac 100644 --- a/src/app/services/drawing.service.ts +++ b/src/app/services/drawing.service.ts @@ -52,6 +52,8 @@ export class DrawingService { update(server: Server, drawing: Drawing): Observable { return this.httpServer .put(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`, { + 'svg': drawing.svg, + 'rotation': drawing.rotation, 'x': Math.round(drawing.x), 'y': Math.round(drawing.y), 'z': drawing.z