Saving edited text

This commit is contained in:
Piotr Pekala 2018-12-10 05:17:40 -08:00
parent fb14e657bb
commit 1b2206852b
18 changed files with 140 additions and 70 deletions

View File

@ -2,7 +2,6 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatMenuModule, MatIconModule } from '@angular/material';
import { DrawingResizingComponent } from './components/drawing-resizing/drawing-resizing.component';
import { CssFixer } from './helpers/css-fixer';
import { FontFixer } from './helpers/font-fixer';
import { MultiLinkCalculatorHelper } from './helpers/multi-link-calculator-helper';
@ -41,6 +40,8 @@ import { SelectionControlComponent } from './components/selection-control/select
import { SelectionSelectComponent } from './components/selection-select/selection-select.component';
import { DraggableSelectionComponent } from './components/draggable-selection/draggable-selection.component';
import { MapSettingsManager } from './managers/map-settings-manager';
import { DrawingResizingComponent } from './components/drawing-resizing/drawing-resizing.component';
import { TextEditingComponent } from './components/text-editing/text-editing.component';
@NgModule({
@ -53,6 +54,7 @@ import { MapSettingsManager } from './managers/map-settings-manager';
D3MapComponent,
ExperimentalMapComponent,
DrawingResizingComponent,
TextEditingComponent,
...ANGULAR_MAP_DECLARATIONS,
SelectionControlComponent,
SelectionSelectComponent,

View File

@ -11,4 +11,5 @@
<app-drawing-resizing></app-drawing-resizing>
<app-selection-control></app-selection-control>
<app-selection-select></app-selection-select>
<app-text-editing></app-text-editing>
<app-draggable-selection [svg]="svg"></app-draggable-selection>

Before

Width:  |  Height:  |  Size: 372 B

After

Width:  |  Height:  |  Size: 410 B

View File

@ -28,4 +28,4 @@ export class DrawingResizingComponent implements OnInit, OnDestroy{
ngOnDestroy() {
this.resizingFinished.unsubscribe();
}
}
}

View File

@ -0,0 +1,29 @@
import { Component, OnDestroy, OnInit } from "@angular/core";
import { Subscription } from 'rxjs';
import { TextEditingTool } from '../../tools/text-editing-tool';
import { DrawingsEventSource } from '../../events/drawings-event-source';
import { TextEditedDataEvent } from '../../events/event-source';
@Component({
selector: 'app-text-editing',
template: `<ng-content></ng-content>`,
styleUrls: ['./text-editing.component.scss']
})
export class TextEditingComponent implements OnInit, OnDestroy{
textEditingFinished: Subscription;
constructor(
private textEditingTool: TextEditingTool,
private drawingEventSource: DrawingsEventSource
) {}
ngOnInit() {
this.textEditingFinished = this.textEditingTool.editingFinished.subscribe((evt: TextEditedDataEvent) => {
this.drawingEventSource.textEdited.emit(evt);
});
}
ngOnDestroy() {
this.textEditingFinished.unsubscribe();
}
}

View File

@ -5,6 +5,7 @@ import { DrawingsWidget } from './widgets/drawings';
import { DrawingLineWidget } from './widgets/drawing-line';
import { SelectionTool } from './tools/selection-tool';
import { MovingTool } from './tools/moving-tool';
import {TextEditingTool} from './tools/text-editing-tool';
import { LayersWidget } from './widgets/layers';
import { LinkWidget } from './widgets/link';
import { InterfaceStatusWidget } from './widgets/interface-status';
@ -28,6 +29,7 @@ export const D3_MAP_IMPORTS = [
DrawingLineWidget,
SelectionTool,
MovingTool,
TextEditingTool,
LayersWidget,
LinkWidget,
InterfaceStatusWidget,

View File

@ -7,4 +7,5 @@ import { MapDrawing } from "../models/map/map-drawing";
export class DrawingsEventSource {
public dragged = new EventEmitter<DraggedDataEvent<MapDrawing>>();
public resized = new EventEmitter<ResizedDataEvent<MapDrawing>>();
public textEdited = new EventEmitter<any>();
}

View File

@ -1,3 +1,5 @@
import { TextElement } from '../models/drawings/text-element';
export class DataEventSource<T> {
constructor(
public datum: T,
@ -26,9 +28,10 @@ export class ClickedDataEvent<T> {
) {}
}
export class EditedDataEvent {
export class TextEditedDataEvent {
constructor(
public id: string,
public editedText: string
public textDrawingId: string,
public editedText: string,
public textElement: TextElement
) {}
}

View File

@ -23,9 +23,7 @@ export class SelectionManager {
this.selection = dictItems;
if (selected.length > 0) {
//console.log(selected);
if (selected.length > 0) {
this.selected.emit(selected);
}

View File

@ -29,8 +29,6 @@ export class SelectionTool {
private activate(selection) {
const self = this;
console.log("test!!!", selection);
selection.on("mousedown", function() {
const subject = select(window);
const parent = this.parentElement;

View File

@ -1,18 +1,78 @@
import { Injectable, EventEmitter } from "@angular/core";
import { SVGSelection } from '../models/types';
import { select } from 'd3-selection';
import { TextElement } from '../models/drawings/text-element';
import { TextEditedDataEvent } from '../events/event-source';
@Injectable()
export class TextEditingTool {
static readonly EDITING_CLASS = '.text-editing';
private enabled = true;
private editingDrawingId: string;
private editedElement: any;
public editingFinished = new EventEmitter<any>();
public setEnabled(enabled) {
this.enabled = enabled;
}
public activate(){
public draw(selection: SVGSelection){
if (!this.enabled){
return;
}
selection.selectAll<SVGTextElement, TextElement>('text.text_element')
.on("dblclick", (elem, index, textElements) => {
this.editedElement = elem;
select(textElements[index])
.attr("visibility", "hidden");
select(textElements[index])
.classed("editingMode", true);
this.editingDrawingId = textElements[index].parentElement.parentElement.getAttribute("drawing_id");
//simply get canvas
select(textElements[index].parentElement.parentElement.parentElement)
.append("foreignObject")
.attr("width", '1000px')
.attr("min-width", 'fit-content')
.attr("height", '100px')
.attr("id", "temporaryText")
.attr("transform", textElements[index].parentElement.getAttribute("transform"))
.append("xhtml:div")
.attr("width", "fit-content")
.attr("height", "fit-content")
.attr("class", "temporaryTextInside")
.attr('style', () => {
const styles: string[] = [];
styles.push(`white-space: pre-line`)
styles.push(`outline: 0px solid transparent`)
styles.push(`font-family: ${elem.font_family}`)
styles.push(`font-size: ${elem.font_size}pt!important`);
styles.push(`font-weight: ${elem.font_weight}`)
styles.push(`color: ${elem.fill}`);
return styles.join("; ");
})
.attr('text-decoration', elem.text_decoration)
.attr('contenteditable', 'true')
.text(elem.text)
.on("focusout", () => {
let temporaryText = document.getElementsByClassName("temporaryTextInside")[0] as HTMLElement;
let savedText = temporaryText.innerText;
this.editingFinished.emit(new TextEditedDataEvent(this.editingDrawingId, savedText, this.editedElement));
var temporaryElement = document.getElementById("temporaryText") as HTMLElement;
temporaryElement.remove();
selection.selectAll<SVGTextElement, TextElement>('text.editingMode')
.attr("visibility", "visible")
.classed("editingMode", false);
});
var txtInside = document.getElementsByClassName("temporaryTextInside")[0] as HTMLElement;
txtInside.focus();
});
}
}

View File

@ -90,9 +90,7 @@ export class DrawingsWidget implements Widget {
if ((datum.element.height + evt.dy) < 0) {
isReflectedVertical = true;
y = topEdge;
console.log(y);
datum.element.height = Math.abs(datum.element.height + evt.dy);
console.log(datum.element.height);
} else {
datum.element.height += evt.dy;
@ -108,9 +106,7 @@ export class DrawingsWidget implements Widget {
if ((datum.element.height + dy) < 0){
isReflectedVertical = false;
y = topEdge;
console.log(y);
datum.element.height = Math.abs(datum.element.height + evt.dy);
console.log(datum.element.height);
} else {
datum.y = evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
datum.element.height += dy;

View File

@ -27,57 +27,7 @@ export class TextDrawingWidget implements DrawingShapeWidget {
const drawing_enter = drawing
.enter()
.append<SVGTextElement>('text')
.attr('class', 'text_element noselect')
.on("dblclick", (elem, index, textElements) => {
console.log("Id: ", textElements[index].parentElement.parentElement.getAttribute("drawing_id"));
select(textElements[index])
.attr("visibility", "hidden");
select(textElements[index])
.classed("editingMode", true);
select(textElements[index].parentElement.parentElement.parentElement)
.append("foreignObject")
.attr("width", '1000px')
.attr("min-width", 'fit-content')
.attr("height", '100px')
.attr("id", "temporaryText")
.attr("transform", textElements[index].parentElement.getAttribute("transform"))
.append("xhtml:div")
.attr("width", "fit-content")
.attr("height", "fit-content")
.attr("class", "temporaryTextInside")
.attr('style', () => {
const styles: string[] = [];
styles.push(`white-space: pre-line`)
styles.push(`outline: 0px solid transparent`)
styles.push(`font-family: ${elem.font_family}`)
styles.push(`font-size: ${elem.font_size}pt!important`);
styles.push(`font-weight: ${elem.font_weight}`)
styles.push(`color: ${elem.fill}`);
return styles.join("; ");
})
.attr('text-decoration', elem.text_decoration)
.attr('contenteditable', 'true')
.text(elem.text)
.on("focusout", (elem, index, textElements) => {
let temporaryText = document.getElementsByClassName("temporaryTextInside")[0] as HTMLElement;
let savedText = temporaryText.innerText;
//let splittedText = savedText.split(/\r?\n/);
var temporaryElement = document.getElementById("temporaryText") as HTMLElement;
temporaryElement.remove();
view.selectAll<SVGTextElement, TextElement>('text.editingMode')
.attr("visibility", "visible")
.classed("editingMode", false)
.text(savedText);
});
var txtInside = document.getElementsByClassName("temporaryTextInside")[0] as HTMLElement;
txtInside.focus();
});
.attr('class', 'text_element noselect');
const merge = drawing.merge(drawing_enter);
merge

View File

@ -8,6 +8,7 @@ import { MovingTool } from "../tools/moving-tool";
import { LayersWidget } from "./layers";
import { LayersManager } from "../managers/layers-manager";
import { Injectable } from "@angular/core";
import { TextEditingTool } from '../tools/text-editing-tool';
@Injectable()
@ -17,6 +18,7 @@ export class GraphLayout implements Widget {
private drawingLineTool: DrawingLineWidget,
private selectionTool: SelectionTool,
private movingTool: MovingTool,
private textEditingTool: TextEditingTool,
private layersWidget: LayersWidget,
private layersManager: LayersManager
) {
@ -67,6 +69,7 @@ export class GraphLayout implements Widget {
this.drawingLineTool.draw(view, context);
this.selectionTool.draw(view, context);
this.movingTool.draw(view, context);
this.textEditingTool.draw(view);
}
disconnect(view: SVGSelection) {

View File

@ -27,7 +27,7 @@ import { MapChangeDetectorRef } from '../../cartography/services/map-change-dete
import { NodeContextMenu } from '../../cartography/events/nodes';
import { MapLinkCreated } from '../../cartography/events/links';
import { NodeWidget } from '../../cartography/widgets/node';
import { DraggedDataEvent, ResizedDataEvent, EditedDataEvent } from '../../cartography/events/event-source';
import { DraggedDataEvent, ResizedDataEvent, EditedDataEvent, TextEditedDataEvent } from '../../cartography/events/event-source';
import { DrawingService } from '../../services/drawing.service';
import { MapNodeToNodeConverter } from '../../cartography/converters/map/map-node-to-node-converter';
import { NodesEventSource } from '../../cartography/events/nodes-event-source';
@ -195,6 +195,10 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.drawingsEventSource.resized.subscribe((evt) => this.onDrawingResized(evt))
);
this.subscriptions.push(
this.drawingsEventSource.textEdited.subscribe((evt) => this.onTextEdited(evt))
);
this.subscriptions.push(
this.linksEventSource.created.subscribe((evt) => this.onLinkCreated(evt))
);
@ -348,8 +352,21 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
});
}
public onDrawingEdited(editedEvent: EditedDataEvent){
public onTextEdited(evt: TextEditedDataEvent){
//<svg height=\"100\" width=\"100\"><text fill=\"#000000\" fill-opacity=\"0\" font-family=\"Noto Sans\" font-size=\"11\" font-weight=\"bold\">this is one another text\nto save\n</text></svg>
//let splittedText = evt.editedText.split(/\r?\n/);
let mapDrawing: MapDrawing = new MapDrawing();
mapDrawing.element = evt.textElement;
(mapDrawing.element as TextElement).text = evt.editedText;
let svgString = this.mapDrawingToSvgConverter.convert(mapDrawing);
let drawing = this.drawingsDataSource.get(evt.textDrawingId);
this.drawingService
.updateText(this.server, drawing, svgString)
.subscribe((serverDrawing: Drawing) => {
this.drawingsDataSource.update(serverDrawing);
});
}
public set readonly(value) {

View File

@ -38,6 +38,16 @@ export class DrawingService {
})
}
updateText(server: Server, drawing: Drawing, svg: string): Observable<Drawing> {
return this.httpServer
.put<Drawing>(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`, {
'svg': svg,
'x': drawing.x,
'y': drawing.y,
'z': drawing.z
});
}
update(server: Server, drawing: Drawing): Observable<Drawing> {
return this.httpServer
.put<Drawing>(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`, {