mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-02-21 18:06:38 +00:00
Saving edited text
This commit is contained in:
parent
fb14e657bb
commit
1b2206852b
@ -2,7 +2,6 @@ import { NgModule } from '@angular/core';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { MatMenuModule, MatIconModule } from '@angular/material';
|
import { MatMenuModule, MatIconModule } from '@angular/material';
|
||||||
|
|
||||||
import { DrawingResizingComponent } from './components/drawing-resizing/drawing-resizing.component';
|
|
||||||
import { CssFixer } from './helpers/css-fixer';
|
import { CssFixer } from './helpers/css-fixer';
|
||||||
import { FontFixer } from './helpers/font-fixer';
|
import { FontFixer } from './helpers/font-fixer';
|
||||||
import { MultiLinkCalculatorHelper } from './helpers/multi-link-calculator-helper';
|
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 { SelectionSelectComponent } from './components/selection-select/selection-select.component';
|
||||||
import { DraggableSelectionComponent } from './components/draggable-selection/draggable-selection.component';
|
import { DraggableSelectionComponent } from './components/draggable-selection/draggable-selection.component';
|
||||||
import { MapSettingsManager } from './managers/map-settings-manager';
|
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({
|
@NgModule({
|
||||||
@ -53,6 +54,7 @@ import { MapSettingsManager } from './managers/map-settings-manager';
|
|||||||
D3MapComponent,
|
D3MapComponent,
|
||||||
ExperimentalMapComponent,
|
ExperimentalMapComponent,
|
||||||
DrawingResizingComponent,
|
DrawingResizingComponent,
|
||||||
|
TextEditingComponent,
|
||||||
...ANGULAR_MAP_DECLARATIONS,
|
...ANGULAR_MAP_DECLARATIONS,
|
||||||
SelectionControlComponent,
|
SelectionControlComponent,
|
||||||
SelectionSelectComponent,
|
SelectionSelectComponent,
|
||||||
|
@ -11,4 +11,5 @@
|
|||||||
<app-drawing-resizing></app-drawing-resizing>
|
<app-drawing-resizing></app-drawing-resizing>
|
||||||
<app-selection-control></app-selection-control>
|
<app-selection-control></app-selection-control>
|
||||||
<app-selection-select></app-selection-select>
|
<app-selection-select></app-selection-select>
|
||||||
|
<app-text-editing></app-text-editing>
|
||||||
<app-draggable-selection [svg]="svg"></app-draggable-selection>
|
<app-draggable-selection [svg]="svg"></app-draggable-selection>
|
||||||
|
Before Width: | Height: | Size: 372 B After Width: | Height: | Size: 410 B |
@ -28,4 +28,4 @@ export class DrawingResizingComponent implements OnInit, OnDestroy{
|
|||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.resizingFinished.unsubscribe();
|
this.resizingFinished.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ import { DrawingsWidget } from './widgets/drawings';
|
|||||||
import { DrawingLineWidget } from './widgets/drawing-line';
|
import { DrawingLineWidget } from './widgets/drawing-line';
|
||||||
import { SelectionTool } from './tools/selection-tool';
|
import { SelectionTool } from './tools/selection-tool';
|
||||||
import { MovingTool } from './tools/moving-tool';
|
import { MovingTool } from './tools/moving-tool';
|
||||||
|
import {TextEditingTool} from './tools/text-editing-tool';
|
||||||
import { LayersWidget } from './widgets/layers';
|
import { LayersWidget } from './widgets/layers';
|
||||||
import { LinkWidget } from './widgets/link';
|
import { LinkWidget } from './widgets/link';
|
||||||
import { InterfaceStatusWidget } from './widgets/interface-status';
|
import { InterfaceStatusWidget } from './widgets/interface-status';
|
||||||
@ -28,6 +29,7 @@ export const D3_MAP_IMPORTS = [
|
|||||||
DrawingLineWidget,
|
DrawingLineWidget,
|
||||||
SelectionTool,
|
SelectionTool,
|
||||||
MovingTool,
|
MovingTool,
|
||||||
|
TextEditingTool,
|
||||||
LayersWidget,
|
LayersWidget,
|
||||||
LinkWidget,
|
LinkWidget,
|
||||||
InterfaceStatusWidget,
|
InterfaceStatusWidget,
|
||||||
|
@ -7,4 +7,5 @@ import { MapDrawing } from "../models/map/map-drawing";
|
|||||||
export class DrawingsEventSource {
|
export class DrawingsEventSource {
|
||||||
public dragged = new EventEmitter<DraggedDataEvent<MapDrawing>>();
|
public dragged = new EventEmitter<DraggedDataEvent<MapDrawing>>();
|
||||||
public resized = new EventEmitter<ResizedDataEvent<MapDrawing>>();
|
public resized = new EventEmitter<ResizedDataEvent<MapDrawing>>();
|
||||||
|
public textEdited = new EventEmitter<any>();
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { TextElement } from '../models/drawings/text-element';
|
||||||
|
|
||||||
export class DataEventSource<T> {
|
export class DataEventSource<T> {
|
||||||
constructor(
|
constructor(
|
||||||
public datum: T,
|
public datum: T,
|
||||||
@ -26,9 +28,10 @@ export class ClickedDataEvent<T> {
|
|||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EditedDataEvent {
|
export class TextEditedDataEvent {
|
||||||
constructor(
|
constructor(
|
||||||
public id: string,
|
public textDrawingId: string,
|
||||||
public editedText: string
|
public editedText: string,
|
||||||
|
public textElement: TextElement
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,7 @@ export class SelectionManager {
|
|||||||
|
|
||||||
this.selection = dictItems;
|
this.selection = dictItems;
|
||||||
|
|
||||||
if (selected.length > 0) {
|
if (selected.length > 0) {
|
||||||
//console.log(selected);
|
|
||||||
|
|
||||||
this.selected.emit(selected);
|
this.selected.emit(selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,8 +29,6 @@ export class SelectionTool {
|
|||||||
private activate(selection) {
|
private activate(selection) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
console.log("test!!!", selection);
|
|
||||||
|
|
||||||
selection.on("mousedown", function() {
|
selection.on("mousedown", function() {
|
||||||
const subject = select(window);
|
const subject = select(window);
|
||||||
const parent = this.parentElement;
|
const parent = this.parentElement;
|
||||||
|
@ -1,18 +1,78 @@
|
|||||||
import { Injectable, EventEmitter } from "@angular/core";
|
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()
|
@Injectable()
|
||||||
export class TextEditingTool {
|
export class TextEditingTool {
|
||||||
static readonly EDITING_CLASS = '.text-editing';
|
|
||||||
|
|
||||||
private enabled = true;
|
private enabled = true;
|
||||||
|
private editingDrawingId: string;
|
||||||
|
private editedElement: any;
|
||||||
public editingFinished = new EventEmitter<any>();
|
public editingFinished = new EventEmitter<any>();
|
||||||
|
|
||||||
public setEnabled(enabled) {
|
public setEnabled(enabled) {
|
||||||
this.enabled = 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();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,9 +90,7 @@ export class DrawingsWidget implements Widget {
|
|||||||
if ((datum.element.height + evt.dy) < 0) {
|
if ((datum.element.height + evt.dy) < 0) {
|
||||||
isReflectedVertical = true;
|
isReflectedVertical = true;
|
||||||
y = topEdge;
|
y = topEdge;
|
||||||
console.log(y);
|
|
||||||
datum.element.height = Math.abs(datum.element.height + evt.dy);
|
datum.element.height = Math.abs(datum.element.height + evt.dy);
|
||||||
console.log(datum.element.height);
|
|
||||||
} else {
|
} else {
|
||||||
datum.element.height += evt.dy;
|
datum.element.height += evt.dy;
|
||||||
|
|
||||||
@ -108,9 +106,7 @@ export class DrawingsWidget implements Widget {
|
|||||||
if ((datum.element.height + dy) < 0){
|
if ((datum.element.height + dy) < 0){
|
||||||
isReflectedVertical = false;
|
isReflectedVertical = false;
|
||||||
y = topEdge;
|
y = topEdge;
|
||||||
console.log(y);
|
|
||||||
datum.element.height = Math.abs(datum.element.height + evt.dy);
|
datum.element.height = Math.abs(datum.element.height + evt.dy);
|
||||||
console.log(datum.element.height);
|
|
||||||
} else {
|
} else {
|
||||||
datum.y = evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
|
datum.y = evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y;
|
||||||
datum.element.height += dy;
|
datum.element.height += dy;
|
||||||
|
@ -27,57 +27,7 @@ export class TextDrawingWidget implements DrawingShapeWidget {
|
|||||||
const drawing_enter = drawing
|
const drawing_enter = drawing
|
||||||
.enter()
|
.enter()
|
||||||
.append<SVGTextElement>('text')
|
.append<SVGTextElement>('text')
|
||||||
.attr('class', 'text_element noselect')
|
.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();
|
|
||||||
});
|
|
||||||
|
|
||||||
const merge = drawing.merge(drawing_enter);
|
const merge = drawing.merge(drawing_enter);
|
||||||
merge
|
merge
|
||||||
|
@ -8,6 +8,7 @@ import { MovingTool } from "../tools/moving-tool";
|
|||||||
import { LayersWidget } from "./layers";
|
import { LayersWidget } from "./layers";
|
||||||
import { LayersManager } from "../managers/layers-manager";
|
import { LayersManager } from "../managers/layers-manager";
|
||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
|
import { TextEditingTool } from '../tools/text-editing-tool';
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -17,6 +18,7 @@ export class GraphLayout implements Widget {
|
|||||||
private drawingLineTool: DrawingLineWidget,
|
private drawingLineTool: DrawingLineWidget,
|
||||||
private selectionTool: SelectionTool,
|
private selectionTool: SelectionTool,
|
||||||
private movingTool: MovingTool,
|
private movingTool: MovingTool,
|
||||||
|
private textEditingTool: TextEditingTool,
|
||||||
private layersWidget: LayersWidget,
|
private layersWidget: LayersWidget,
|
||||||
private layersManager: LayersManager
|
private layersManager: LayersManager
|
||||||
) {
|
) {
|
||||||
@ -67,6 +69,7 @@ export class GraphLayout implements Widget {
|
|||||||
this.drawingLineTool.draw(view, context);
|
this.drawingLineTool.draw(view, context);
|
||||||
this.selectionTool.draw(view, context);
|
this.selectionTool.draw(view, context);
|
||||||
this.movingTool.draw(view, context);
|
this.movingTool.draw(view, context);
|
||||||
|
this.textEditingTool.draw(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect(view: SVGSelection) {
|
disconnect(view: SVGSelection) {
|
||||||
|
@ -27,7 +27,7 @@ import { MapChangeDetectorRef } from '../../cartography/services/map-change-dete
|
|||||||
import { NodeContextMenu } from '../../cartography/events/nodes';
|
import { NodeContextMenu } from '../../cartography/events/nodes';
|
||||||
import { MapLinkCreated } from '../../cartography/events/links';
|
import { MapLinkCreated } from '../../cartography/events/links';
|
||||||
import { NodeWidget } from '../../cartography/widgets/node';
|
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 { DrawingService } from '../../services/drawing.service';
|
||||||
import { MapNodeToNodeConverter } from '../../cartography/converters/map/map-node-to-node-converter';
|
import { MapNodeToNodeConverter } from '../../cartography/converters/map/map-node-to-node-converter';
|
||||||
import { NodesEventSource } from '../../cartography/events/nodes-event-source';
|
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.drawingsEventSource.resized.subscribe((evt) => this.onDrawingResized(evt))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.subscriptions.push(
|
||||||
|
this.drawingsEventSource.textEdited.subscribe((evt) => this.onTextEdited(evt))
|
||||||
|
);
|
||||||
|
|
||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
this.linksEventSource.created.subscribe((evt) => this.onLinkCreated(evt))
|
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) {
|
public set readonly(value) {
|
||||||
|
@ -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> {
|
update(server: Server, drawing: Drawing): Observable<Drawing> {
|
||||||
return this.httpServer
|
return this.httpServer
|
||||||
.put<Drawing>(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`, {
|
.put<Drawing>(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`, {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user