example of CSV file
-
-`label_name,rect_left,rect_top,rect_width,rect_height,image_name,image_width,image_height`
+
+**Schema:**
+
+`label_name,rect_left,rect_top,rect_width,rect_height,image_name,image_width,image_height`
+
+**Where:**
+
+`label_name` - selected label name
+`rect_left` - absolute horizontal distance between the left edge of the image and the left edge of the rect in pixels
+`rect_top` - absolute vertical distance between the top edge of the image and the top edge of the rect in pixels
+`rect_width` - absolute rect width in pixels
+`rect_height` - absolute rect height in pixels
+`image_width` - absolute image width in pixels
+`image_height` - absolute image height in pixels
+
+**Example:**
```
banana,491,164,530,614,000000.jpg,1280,960
@@ -92,3 +210,4 @@ Copyright (c) 2019-present, Piotr Skalski
[1]: http://makesense.ai
[2]: ./LICENSE
[3]: https://twitter.com/PiotrSkalski92
+[4]: https://github.com/SkalskiP/make-sense/issues/16
diff --git a/public/ico/minus.png b/public/ico/minus.png
deleted file mode 100644
index 1232daee..00000000
Binary files a/public/ico/minus.png and /dev/null differ
diff --git a/public/ico/plus.png b/public/ico/plus.png
index 4ce1b81c..11c7cb50 100644
Binary files a/public/ico/plus.png and b/public/ico/plus.png differ
diff --git a/src/data/EditorFeatureData.ts b/src/data/EditorFeatureData.ts
index a5e502d7..17ff5632 100644
--- a/src/data/EditorFeatureData.ts
+++ b/src/data/EditorFeatureData.ts
@@ -26,7 +26,7 @@ export const EditorFeatureData: IEditorFeature[] = [
imageAlt: "labels",
},
{
- displayText: "Support output file formats like YOLO, CSV",
+ displayText: "Support output file formats like YOLO, VOC XML, CSV",
imageSrc: "img/file.png",
imageAlt: "file",
},
diff --git a/src/data/ExportFormatType.ts b/src/data/ExportFormatType.ts
index 53d713cf..48614e34 100644
--- a/src/data/ExportFormatType.ts
+++ b/src/data/ExportFormatType.ts
@@ -1,5 +1,6 @@
export enum ExportFormatType {
YOLO = "YOLO",
COCO = "COCO",
- CSV = "CSV"
+ CSV = "CSV",
+ VOC = "VOC"
}
\ No newline at end of file
diff --git a/src/data/RectExportFormatData.ts b/src/data/RectExportFormatData.ts
index 91675702..1b814222 100644
--- a/src/data/RectExportFormatData.ts
+++ b/src/data/RectExportFormatData.ts
@@ -6,6 +6,10 @@ export const RectExportFormatData: IExportFormat[] = [
type: ExportFormatType.YOLO,
label: "A .zip package containing files in YOLO format."
},
+ {
+ type: ExportFormatType.VOC,
+ label: "A .zip package containing files in VOC XML format."
+ },
{
type: ExportFormatType.CSV,
label: "Single CSV file."
diff --git a/src/logic/export/PointLabelsExport.ts b/src/logic/export/PointLabelsExport.ts
index 059f7d85..5fd87845 100644
--- a/src/logic/export/PointLabelsExport.ts
+++ b/src/logic/export/PointLabelsExport.ts
@@ -1,9 +1,9 @@
import {ExportFormatType} from "../../data/ExportFormatType";
-import {store} from "../../index";
import {ImageData, LabelPoint} from "../../store/editor/types";
import {saveAs} from "file-saver";
import {ImageRepository} from "../imageRepository/ImageRepository";
import moment from 'moment';
+import {EditorSelector} from "../../store/selectors/EditorSelector";
export class PointLabelsExporter {
public static export(exportFormatType: ExportFormatType): void {
@@ -17,16 +17,22 @@ export class PointLabelsExporter {
}
private static exportAsCSV(): void {
- const content: string = store.getState().editor.imagesData
+ const content: string = EditorSelector.getImagesData()
.map((imageData: ImageData) => {
return PointLabelsExporter.wrapRectLabelsIntoCSV(imageData)})
.filter((imageLabelData: string) => {
return !!imageLabelData})
.join("\n");
+ const projectName: string = EditorSelector.getProjectName();
const date: string = moment().format('YYYYMMDDhhmmss');
const blob = new Blob([content], {type: "text/plain;charset=utf-8"});
- saveAs(blob, "labels_" + date + ".csv");
+ try {
+ saveAs(blob, `labels_${projectName}_${date}.csv`);
+ } catch (error) {
+ // TODO
+ throw new Error(error);
+ }
}
private static wrapRectLabelsIntoCSV(imageData: ImageData): string {
@@ -34,7 +40,7 @@ export class PointLabelsExporter {
return null;
const image: HTMLImageElement = ImageRepository.getById(imageData.id);
- const labelNamesList: string[] = store.getState().editor.labelNames;
+ const labelNamesList: string[] = EditorSelector.getLabelNames();
const labelRectsString: string[] = imageData.labelPoints.map((labelPoint: LabelPoint) => {
const labelFields = [
labelNamesList[labelPoint.labelIndex],
diff --git a/src/logic/export/RectLabelsExporter.ts b/src/logic/export/RectLabelsExporter.ts
index f9e4d3be..71460d3c 100644
--- a/src/logic/export/RectLabelsExporter.ts
+++ b/src/logic/export/RectLabelsExporter.ts
@@ -1,10 +1,11 @@
import {ExportFormatType} from "../../data/ExportFormatType";
import {ImageData, LabelRect} from "../../store/editor/types";
import {ImageRepository} from "../imageRepository/ImageRepository";
-import {store} from "../..";
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
import moment from 'moment';
+import {EditorSelector} from "../../store/selectors/EditorSelector";
+import {XMLSanitizerUtil} from "../../utils/XMLSanitizerUtil";
export class RectLabelsExporter {
public static export(exportFormatType: ExportFormatType): void {
@@ -12,6 +13,9 @@ export class RectLabelsExporter {
case ExportFormatType.YOLO:
RectLabelsExporter.exportAsYOLO();
break;
+ case ExportFormatType.VOC:
+ RectLabelsExporter.exportAsVOC();
+ break;
case ExportFormatType.CSV:
RectLabelsExporter.exportAsCSV();
break;
@@ -22,18 +26,33 @@ export class RectLabelsExporter {
private static exportAsYOLO(): void {
let zip = new JSZip();
- store.getState().editor.imagesData.forEach((imageData: ImageData) => {
- const fileContent: string = RectLabelsExporter.wrapRectLabelsIntoYOLO(imageData);
- if (fileContent) {
- const fileName : string = imageData.fileData.name.replace(/\.[^/.]+$/, ".txt");
- zip.file(fileName, fileContent);
- }
- });
- const date: string = moment().format('YYYYMMDDhhmmss');
- zip.generateAsync({type:"blob"})
- .then(function(content) {
- saveAs(content, "labels_yolo_" + date + ".zip");
+ EditorSelector.getImagesData()
+ .forEach((imageData: ImageData) => {
+ const fileContent: string = RectLabelsExporter.wrapRectLabelsIntoYOLO(imageData);
+ if (fileContent) {
+ const fileName : string = imageData.fileData.name.replace(/\.[^/.]+$/, ".txt");
+ try {
+ zip.file(fileName, fileContent);
+ } catch (error) {
+ // TODO
+ throw new Error(error);
+ }
+ }
});
+
+ const projectName: string = EditorSelector.getProjectName();
+ const date: string = moment().format('YYYYMMDDhhmmss');
+
+ try {
+ zip.generateAsync({type:"blob"})
+ .then(function(content) {
+ saveAs(content, `labels_${projectName}_${date}.zip`);
+ });
+ } catch (error) {
+ // TODO
+ throw new Error(error);
+ }
+
}
private static wrapRectLabelsIntoYOLO(imageData: ImageData): string {
@@ -54,17 +73,105 @@ export class RectLabelsExporter {
return labelRectsString.join("\n");
}
+ private static exportAsVOC(): void {
+ let zip = new JSZip();
+ EditorSelector.getImagesData().forEach((imageData: ImageData) => {
+ const fileContent: string = RectLabelsExporter.wrapImageIntoVOC(imageData);
+ if (fileContent) {
+ const fileName : string = imageData.fileData.name.replace(/\.[^/.]+$/, ".xml");
+ try {
+ zip.file(fileName, fileContent);
+ } catch (error) {
+ // TODO
+ throw new Error(error);
+ }
+ }
+ });
+
+ const projectName: string = EditorSelector.getProjectName();
+ const date: string = moment().format('YYYYMMDDhhmmss');
+
+ try {
+ zip.generateAsync({type:"blob"})
+ .then(function(content) {
+ saveAs(content, `labels_${projectName}_${date}.zip`);
+ });
+ } catch (error) {
+ // TODO
+ throw new Error(error);
+ }
+ }
+
+ private static wrapRectLabelsIntoVOC(imageData: ImageData): string {
+ if (imageData.labelRects.length === 0 || !imageData.loadStatus)
+ return null;
+
+ const labelNamesList: string[] = EditorSelector.getLabelNames();
+ const labelRectsString: string[] = imageData.labelRects.map((labelRect: LabelRect) => {
+ const labelFields = [
+ `\t`,
+ `\t\t${labelNamesList[labelRect.labelIndex]} `,
+ `\t\tUnspecified `,
+ `\t\tUnspecified `,
+ `\t\tUnspecified `,
+ `\t\t`,
+ `\t\t\t${Math.round(labelRect.rect.x)} `,
+ `\t\t\t${Math.round(labelRect.rect.y)} `,
+ `\t\t\t${Math.round(labelRect.rect.x + labelRect.rect.width)} `,
+ `\t\t\t${Math.round(labelRect.rect.y + labelRect.rect.height)} `,
+ `\t\t `,
+ `\t `
+ ];
+ return labelFields.join("\n")
+ });
+ return labelRectsString.join("\n");
+ }
+
+ private static wrapImageIntoVOC(imageData: ImageData): string {
+ const labels: string = RectLabelsExporter.wrapRectLabelsIntoVOC(imageData);
+ const projectName: string = XMLSanitizerUtil.sanitize(EditorSelector.getProjectName());
+
+ if (labels) {
+ const image: HTMLImageElement = ImageRepository.getById(imageData.id);
+ return [
+ ``,
+ `\t${projectName} `,
+ `\t${imageData.fileData.name} `,
+ `\t/${projectName}/${imageData.fileData.name} `,
+ `\t`,
+ `\t\tUnspecified `,
+ `\t `,
+ `\t`,
+ `\t\t${image.width} `,
+ `\t\t${image.height} `,
+ `\t\t3 `,
+ `\t `,
+ labels,
+ ` `
+ ].join("\n");
+ }
+ return null;
+ }
+
+
private static exportAsCSV(): void {
- const content: string = store.getState().editor.imagesData
+ const content: string = EditorSelector.getImagesData()
.map((imageData: ImageData) => {
return RectLabelsExporter.wrapRectLabelsIntoCSV(imageData)})
.filter((imageLabelData: string) => {
return !!imageLabelData})
.join("\n");
+ const projectName: string = EditorSelector.getProjectName();
const date: string = moment().format('YYYYMMDDhhmmss');
const blob = new Blob([content], {type: "text/plain;charset=utf-8"});
- saveAs(blob, "labels_" + date + ".csv");
+
+ try {
+ saveAs(blob, `labels_${projectName}_${date}.csv`);
+ } catch (error) {
+ // TODO
+ throw new Error(error);
+ }
}
private static wrapRectLabelsIntoCSV(imageData: ImageData): string {
@@ -72,7 +179,7 @@ export class RectLabelsExporter {
return null;
const image: HTMLImageElement = ImageRepository.getById(imageData.id);
- const labelNamesList: string[] = store.getState().editor.labelNames;
+ const labelNamesList: string[] = EditorSelector.getLabelNames();
const labelRectsString: string[] = imageData.labelRects.map((labelRect: LabelRect) => {
const labelFields = [
labelNamesList[labelRect.labelIndex],
diff --git a/src/logic/render/BaseSuportRenderEngine.ts b/src/logic/render/BaseSuportRenderEngine.ts
new file mode 100644
index 00000000..5d2f905d
--- /dev/null
+++ b/src/logic/render/BaseSuportRenderEngine.ts
@@ -0,0 +1,10 @@
+import {IRect} from "../../interfaces/IRect";
+import {BaseRenderEngine} from "./BaseRenderEngine";
+
+export abstract class BaseSuportRenderEngine extends BaseRenderEngine{
+ public constructor(canvas: HTMLCanvasElement, imageRect: IRect) {
+ super(canvas, imageRect);
+ }
+
+ abstract isInProgress(): boolean;
+}
\ No newline at end of file
diff --git a/src/logic/render/PointRenderEngine.ts b/src/logic/render/PointRenderEngine.ts
index ce99ee3e..35b23c3c 100644
--- a/src/logic/render/PointRenderEngine.ts
+++ b/src/logic/render/PointRenderEngine.ts
@@ -1,4 +1,3 @@
-import {BaseRenderEngine} from "./BaseRenderEngine";
import {IRect} from "../../interfaces/IRect";
import {RenderEngineConfig} from "../../settings/RenderEngineConfig";
import {IPoint} from "../../interfaces/IPoint";
@@ -18,8 +17,10 @@ import {DrawUtil} from "../../utils/DrawUtil";
import {PointUtil} from "../../utils/PointUtil";
import {updateCustomcursorStyle} from "../../store/general/actionCreators";
import {CustomCursorStyle} from "../../data/CustomCursorStyle";
+import {BaseSuportRenderEngine} from "./BaseSuportRenderEngine";
+import {NumberUtil} from "../../utils/NumberUtil";
-export class PointRenderEngine extends BaseRenderEngine {
+export class PointRenderEngine extends BaseSuportRenderEngine {
private config: RenderEngineConfig = new RenderEngineConfig();
// =================================================================================================================
@@ -41,7 +42,10 @@ export class PointRenderEngine extends BaseRenderEngine {
public mouseDownHandler(event: MouseEvent): void {
const mousePosition: IPoint = CanvasUtil.getMousePositionOnCanvasFromEvent(event, this.canvas);
const isMouseOverImage: boolean = RectUtil.isPointInside(this.imageRectOnCanvas, mousePosition);
- if (isMouseOverImage) {
+ const isMouseOverCanvas: boolean = RectUtil.isPointInside({x: 0, y: 0, ...CanvasUtil.getSize(this.canvas)},
+ this.mousePosition);
+
+ if (isMouseOverCanvas) {
const labelPoint: LabelPoint = this.getLabelPointUnderMouse();
if (!!labelPoint) {
const pointOnImage: IPoint = this.calculatePointRelativeToActiveImage(labelPoint.point);
@@ -61,7 +65,7 @@ export class PointRenderEngine extends BaseRenderEngine {
};
this.addPointLabel(point);
}
- } else {
+ } else if (isMouseOverImage) {
const scale = this.scale;
const point: IPoint = {
x: (mousePosition.x - this.imageRectOnCanvas.x) * scale,
@@ -74,14 +78,14 @@ export class PointRenderEngine extends BaseRenderEngine {
public mouseUpHandler(event: MouseEvent): void {
const mousePosition: IPoint = CanvasUtil.getMousePositionOnCanvasFromEvent(event, this.canvas);
- const isOverImage: boolean = RectUtil.isPointInside(this.imageRectOnCanvas, mousePosition);
- if (isOverImage && this.transformInProgress) {
+ if (this.transformInProgress) {
const scale = this.scale;
const activeLabelPoint: LabelPoint = this.getActivePointLabel();
+ const snappedPoint: IPoint = this.snapPointToImage(mousePosition);
const scaledPoint: IPoint = PointRenderEngine.scalePoint({
- x: mousePosition.x - this.imageRectOnCanvas.x,
- y: mousePosition.y - this.imageRectOnCanvas.y,
+ x: snappedPoint.x - this.imageRectOnCanvas.x,
+ y: snappedPoint.y - this.imageRectOnCanvas.y,
}, scale);
const imageData = this.getActiveImage();
@@ -128,7 +132,8 @@ export class PointRenderEngine extends BaseRenderEngine {
imageData.labelPoints.forEach((labelPoint: LabelPoint) => {
if (labelPoint.id === activeLabelId) {
if (this.transformInProgress) {
- const pointBetweenPixels = DrawUtil.setPointBetweenPixels(this.mousePosition);
+ const pointSnapped: IPoint = this.snapPointToImage(this.mousePosition);
+ const pointBetweenPixels: IPoint = DrawUtil.setPointBetweenPixels(pointSnapped);
const handleRect: IRect = RectUtil.getRectWithCenterAndSize(pointBetweenPixels, this.config.anchorSize);
DrawUtil.drawRectWithFill(this.canvas, handleRect, this.config.activeAnchorColor);
} else {
@@ -168,7 +173,7 @@ export class PointRenderEngine extends BaseRenderEngine {
return;
}
- if (RectUtil.isPointInside(this.imageRectOnCanvas, this.mousePosition)) {
+ if (RectUtil.isPointInside({x: 0, y: 0, ...CanvasUtil.getSize(this.canvas)}, this.mousePosition)) {
store.dispatch(updateCustomcursorStyle(CustomCursorStyle.DEFAULT));
this.canvas.style.cursor = "none";
} else {
@@ -186,6 +191,10 @@ export class PointRenderEngine extends BaseRenderEngine {
this.scale = this.getActiveImageScale();
}
+ public isInProgress(): boolean {
+ return !!this.transformInProgress;
+ }
+
private static scalePoint(inputPoint:IPoint, scale: number): IPoint {
return {
x: inputPoint.x * scale,
@@ -229,4 +238,14 @@ export class PointRenderEngine extends BaseRenderEngine {
store.dispatch(updateFirstLabelCreatedFlag(true));
store.dispatch(updateActiveLabelId(labelPoint.id));
};
+
+ private snapPointToImage(point: IPoint): IPoint {
+ if (RectUtil.isPointInside(this.imageRectOnCanvas, point))
+ return point;
+
+ return {
+ x: NumberUtil.snapValueToRange(point.x, this.imageRectOnCanvas.x, this.imageRectOnCanvas.x + this.imageRectOnCanvas.width),
+ y: NumberUtil.snapValueToRange(point.y, this.imageRectOnCanvas.y, this.imageRectOnCanvas.y + this.imageRectOnCanvas.height)
+ }
+ }
}
\ No newline at end of file
diff --git a/src/logic/render/PolygonRenderEngine.ts b/src/logic/render/PolygonRenderEngine.ts
index 487e3b98..34382e37 100644
--- a/src/logic/render/PolygonRenderEngine.ts
+++ b/src/logic/render/PolygonRenderEngine.ts
@@ -1,4 +1,3 @@
-import {BaseRenderEngine} from "./BaseRenderEngine";
import {IRect} from "../../interfaces/IRect";
import {RenderEngineConfig} from "../../settings/RenderEngineConfig";
import {IPoint} from "../../interfaces/IPoint";
@@ -7,8 +6,9 @@ import {store} from "../../index";
import {RectUtil} from "../../utils/RectUtil";
import {updateCustomcursorStyle} from "../../store/general/actionCreators";
import {CustomCursorStyle} from "../../data/CustomCursorStyle";
+import {BaseSuportRenderEngine} from "./BaseSuportRenderEngine";
-export class PolygonRenderEngine extends BaseRenderEngine {
+export class PolygonRenderEngine extends BaseSuportRenderEngine {
private config: RenderEngineConfig = new RenderEngineConfig();
// =================================================================================================================
@@ -63,4 +63,8 @@ export class PolygonRenderEngine extends BaseRenderEngine {
public updateImageRect(imageRect: IRect): void {
this.imageRectOnCanvas = imageRect;
}
+
+ public isInProgress(): boolean {
+ return false;
+ }
}
\ No newline at end of file
diff --git a/src/logic/render/RectRenderEngine.ts b/src/logic/render/RectRenderEngine.ts
index 89d386ef..98a37a40 100644
--- a/src/logic/render/RectRenderEngine.ts
+++ b/src/logic/render/RectRenderEngine.ts
@@ -2,7 +2,6 @@ import {IPoint} from "../../interfaces/IPoint";
import {IRect} from "../../interfaces/IRect";
import {RectUtil} from "../../utils/RectUtil";
import {DrawUtil} from "../../utils/DrawUtil";
-import {BaseRenderEngine} from "./BaseRenderEngine";
import {store} from "../..";
import {ImageData, LabelRect} from "../../store/editor/types";
import uuidv1 from 'uuid/v1';
@@ -19,8 +18,10 @@ import {RenderEngineConfig} from "../../settings/RenderEngineConfig";
import {CanvasUtil} from "../../utils/CanvasUtil";
import {updateCustomcursorStyle} from "../../store/general/actionCreators";
import {CustomCursorStyle} from "../../data/CustomCursorStyle";
+import {BaseSuportRenderEngine} from "./BaseSuportRenderEngine";
+import {NumberUtil} from "../../utils/NumberUtil";
-export class RectRenderEngine extends BaseRenderEngine {
+export class RectRenderEngine extends BaseSuportRenderEngine {
private config: RenderEngineConfig = new RenderEngineConfig();
// =================================================================================================================
@@ -41,9 +42,12 @@ export class RectRenderEngine extends BaseRenderEngine {
// =================================================================================================================
public mouseDownHandler = (event: MouseEvent) => {
- const mousePosition: IPoint = CanvasUtil.getMousePositionOnCanvasFromEvent(event, this.canvas);
- const isMouseOverImage: boolean = RectUtil.isPointInside(this.imageRectOnCanvas, mousePosition);
- if (isMouseOverImage) {
+ this.mousePosition = CanvasUtil.getMousePositionOnCanvasFromEvent(event, this.canvas);
+ const isMouseOverImage: boolean = RectUtil.isPointInside(this.imageRectOnCanvas, this.mousePosition);
+ const isMouseOverCanvas: boolean = RectUtil.isPointInside({x: 0, y: 0, ...CanvasUtil.getSize(this.canvas)},
+ this.mousePosition);
+
+ if (isMouseOverCanvas) {
const rectUnderMouse: LabelRect = this.getRectUnderMouse();
if (!!rectUnderMouse) {
const rect: IRect = this.calculateRectRelativeToActiveImage(rectUnderMouse.rect);
@@ -52,26 +56,26 @@ export class RectRenderEngine extends BaseRenderEngine {
store.dispatch(updateActiveLabelId(rectUnderMouse.id));
this.startRectResize(anchorUnderMouse);
} else {
- this.startRectCreation(mousePosition);
+ this.startRectCreation(this.mousePosition);
}
- } else {
- this.startRectCreation(mousePosition);
+ } else if (isMouseOverImage) {
+ this.startRectCreation(this.mousePosition);
}
}
};
public mouseUpHandler = (event: MouseEvent) => {
if (!!this.imageRectOnCanvas) {
- const mousePosition: IPoint = CanvasUtil.getMousePositionOnCanvasFromEvent(event, this.canvas);
- const isOverImage: boolean = RectUtil.isPointInside(this.imageRectOnCanvas, mousePosition);
+ this.mousePosition = CanvasUtil.getMousePositionOnCanvasFromEvent(event, this.canvas);
+ const mousePositionSnapped: IPoint = this.snapPointToImage(this.mousePosition);
- if (isOverImage && !!this.startCreateRectPoint && !PointUtil.equals(this.startCreateRectPoint, this.mousePosition)) {
+ if (!!this.startCreateRectPoint && !PointUtil.equals(this.startCreateRectPoint, mousePositionSnapped)) {
const scale = this.scale;
- const minX: number = Math.min(this.startCreateRectPoint.x, this.mousePosition.x);
- const minY: number = Math.min(this.startCreateRectPoint.y, this.mousePosition.y);
- const maxX: number = Math.max(this.startCreateRectPoint.x, this.mousePosition.x);
- const maxY: number = Math.max(this.startCreateRectPoint.y, this.mousePosition.y);
+ const minX: number = Math.min(this.startCreateRectPoint.x, mousePositionSnapped.x);
+ const minY: number = Math.min(this.startCreateRectPoint.y, mousePositionSnapped.y);
+ const maxX: number = Math.max(this.startCreateRectPoint.x, mousePositionSnapped.x);
+ const maxY: number = Math.max(this.startCreateRectPoint.y, mousePositionSnapped.y);
const rect: IRect = {
x: (minX - this.imageRectOnCanvas.x) * scale,
@@ -82,7 +86,7 @@ export class RectRenderEngine extends BaseRenderEngine {
this.addRectLabel(rect);
}
- if (isOverImage && !!this.startResizeRectAnchor) {
+ if (!!this.startResizeRectAnchor) {
const activeLabelRect: LabelRect = this.getActiveRectLabel();
const rect: IRect = this.calculateRectRelativeToActiveImage(activeLabelRect.rect);
const startAnchorPosition = {
@@ -90,12 +94,12 @@ export class RectRenderEngine extends BaseRenderEngine {
y: this.startResizeRectAnchor.middlePosition.y + this.imageRectOnCanvas.y
};
const delta = {
- x: this.mousePosition.x - startAnchorPosition.x,
- y: this.mousePosition.y - startAnchorPosition.y
+ x: mousePositionSnapped.x - startAnchorPosition.x,
+ y: mousePositionSnapped.y - startAnchorPosition.y
};
- const resizedRect: IRect = RectUtil.resizeRect(rect, this.startResizeRectAnchor.type, delta);
+ const resizeRect: IRect = RectUtil.resizeRect(rect, this.startResizeRectAnchor.type, delta);
const scale = this.scale;
- const scaledRect: IRect = RectRenderEngine.scaleRect(resizedRect, scale);
+ const scaledRect: IRect = RectRenderEngine.scaleRect(resizeRect, scale);
const imageData = this.getActiveImage();
imageData.labelRects = imageData.labelRects.map((labelRect: LabelRect) => {
@@ -117,7 +121,7 @@ export class RectRenderEngine extends BaseRenderEngine {
this.mousePosition = CanvasUtil.getMousePositionOnCanvasFromEvent(event, this.canvas);
if (!!this.imageRectOnCanvas) {
const isOverImage: boolean = RectUtil.isPointInside(this.imageRectOnCanvas, this.mousePosition);
- if (isOverImage) {
+ if (isOverImage && !this.startResizeRectAnchor) {
const labelRect: LabelRect = this.getRectUnderMouse();
if (!!labelRect) {
if (store.getState().editor.highlightedLabelId !== labelRect.id) {
@@ -151,11 +155,12 @@ export class RectRenderEngine extends BaseRenderEngine {
private drawCurrentlyCreatedRect() {
if (!!this.startCreateRectPoint) {
+ const mousePositionSnapped: IPoint = this.snapPointToImage(this.mousePosition);
const activeRect: IRect = {
x: this.startCreateRectPoint.x,
y: this.startCreateRectPoint.y,
- width: this.mousePosition.x - this.startCreateRectPoint.x,
- height: this.mousePosition.y - this.startCreateRectPoint.y
+ width: mousePositionSnapped.x - this.startCreateRectPoint.x,
+ height: mousePositionSnapped.y - this.startCreateRectPoint.y
};
const activeRectBetweenPixels = DrawUtil.setRectBetweenPixels(activeRect);
DrawUtil.drawRect(this.canvas, activeRectBetweenPixels, this.config.rectActiveColor, this.config.rectThickness);
@@ -171,13 +176,14 @@ export class RectRenderEngine extends BaseRenderEngine {
private drawActiveRect(labelRect: LabelRect) {
let rect: IRect = this.calculateRectRelativeToActiveImage(labelRect.rect);
if (!!this.startResizeRectAnchor) {
- const startAnchorPosition = {
+ const startAnchorPosition: IPoint = {
x: this.startResizeRectAnchor.middlePosition.x + this.imageRectOnCanvas.x,
y: this.startResizeRectAnchor.middlePosition.y + this.imageRectOnCanvas.y
};
+ const endAnchorPositionSnapped: IPoint = this.snapPointToImage(this.mousePosition);
const delta = {
- x: this.mousePosition.x - startAnchorPosition.x,
- y: this.mousePosition.y - startAnchorPosition.y
+ x: endAnchorPositionSnapped.x - startAnchorPosition.x,
+ y: endAnchorPositionSnapped.y - startAnchorPosition.y
};
rect = RectUtil.resizeRect(rect, this.startResizeRectAnchor.type, delta);
}
@@ -206,8 +212,12 @@ export class RectRenderEngine extends BaseRenderEngine {
store.dispatch(updateCustomcursorStyle(CustomCursorStyle.MOVE));
return;
}
- if (RectUtil.isPointInside(this.imageRectOnCanvas, this.mousePosition)) {
- store.dispatch(updateCustomcursorStyle(CustomCursorStyle.DEFAULT));
+ if (RectUtil.isPointInside({x: 0, y: 0, ...CanvasUtil.getSize(this.canvas)}, this.mousePosition)) {
+ if (!RectUtil.isPointInside(this.imageRectOnCanvas, this.mousePosition) && !!this.startCreateRectPoint)
+ store.dispatch(updateCustomcursorStyle(CustomCursorStyle.MOVE));
+ else
+ store.dispatch(updateCustomcursorStyle(CustomCursorStyle.DEFAULT));
+
this.canvas.style.cursor = "none";
} else {
this.canvas.style.cursor = "default";
@@ -219,6 +229,15 @@ export class RectRenderEngine extends BaseRenderEngine {
// HELPERS
// =================================================================================================================
+ public updateImageRect(imageRect: IRect): void {
+ this.imageRectOnCanvas = imageRect;
+ this.scale = this.getActiveImageScale();
+ }
+
+ public isInProgress(): boolean {
+ return !!this.startCreateRectPoint || !!this.startResizeRectAnchor;
+ }
+
private static scaleRect(inputRect:IRect, scale: number): IRect {
return {
x: inputRect.x * scale,
@@ -233,11 +252,6 @@ export class RectRenderEngine extends BaseRenderEngine {
return RectRenderEngine.scaleRect(rect, 1/scale);
}
- public updateImageRect(imageRect: IRect): void {
- this.imageRectOnCanvas = imageRect;
- this.scale = this.getActiveImageScale();
- }
-
private addRectLabel = (rect: IRect) => {
const activeImageIndex = store.getState().editor.activeImageIndex;
const activeLabelIndex = store.getState().editor.activeLabelNameIndex;
@@ -259,15 +273,40 @@ export class RectRenderEngine extends BaseRenderEngine {
}
private getRectUnderMouse(): LabelRect {
+ const activeRectLabel: LabelRect = this.getActiveRectLabel();
+ if (!!activeRectLabel && this.isMouseOverRectEdges(activeRectLabel.rect)) {
+ return activeRectLabel;
+ }
+
const labelRects: LabelRect[] = this.getActiveImage().labelRects;
for (let i = 0; i < labelRects.length; i++) {
- const rect: IRect = this.calculateRectRelativeToActiveImage(labelRects[i].rect);
- const rectAnchor = this.getAnchorUnderMouseByRect(rect);
- if (!!rectAnchor) return labelRects[i];
+ if (this.isMouseOverRectEdges(labelRects[i].rect)) {
+ return labelRects[i];
+ }
}
return null;
}
+ private isMouseOverRectEdges(rect: IRect): boolean {
+ const rectOnImage: IRect = RectUtil.translate(
+ this.calculateRectRelativeToActiveImage(rect), this.imageRectOnCanvas);
+
+ const outerRectDelta: IPoint = {
+ x: this.config.anchorHoverSize.width / 2,
+ y: this.config.anchorHoverSize.height / 2
+ };
+ const outerRect: IRect = RectUtil.expand(rectOnImage, outerRectDelta);
+
+ const innerRectDelta: IPoint = {
+ x: - this.config.anchorHoverSize.width / 2,
+ y: - this.config.anchorHoverSize.height / 2
+ };
+ const innerRect: IRect = RectUtil.expand(rectOnImage, innerRectDelta);
+
+ return (RectUtil.isPointInside(outerRect, this.mousePosition) &&
+ !RectUtil.isPointInside(innerRect, this.mousePosition));
+ }
+
private getAnchorUnderMouseByRect(rect: IRect): RectAnchor {
const rectAnchors: RectAnchor[] = RectUtil.mapRectToAnchors(rect);
for (let i = 0; i < rectAnchors.length; i++) {
@@ -314,4 +353,14 @@ export class RectRenderEngine extends BaseRenderEngine {
y: scaledRect.y + this.imageRectOnCanvas.y
}
}
+
+ private snapPointToImage(point: IPoint): IPoint {
+ if (RectUtil.isPointInside(this.imageRectOnCanvas, point))
+ return point;
+
+ return {
+ x: NumberUtil.snapValueToRange(point.x, this.imageRectOnCanvas.x, this.imageRectOnCanvas.x + this.imageRectOnCanvas.width),
+ y: NumberUtil.snapValueToRange(point.y, this.imageRectOnCanvas.y, this.imageRectOnCanvas.y + this.imageRectOnCanvas.height)
+ }
+ }
}
\ No newline at end of file
diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts
index 975072e3..a67caa6c 100644
--- a/src/settings/Settings.ts
+++ b/src/settings/Settings.ts
@@ -19,7 +19,7 @@ export class Settings {
public static readonly DARK_THEME_SECOND_COLOR: string = "#282828";
public static readonly DARK_THEME_THIRD_COLOR: string = "#4c4c4c";
- public static readonly CANVAS_PADDING_WIDTH_PX: number = 10;
+ public static readonly CANVAS_PADDING_WIDTH_PX: number = 20;
public static readonly CROSS_HAIR_THICKNESS_PX: number = 1;
public static readonly CROSS_HAIR_COLOR: string = "#fff";
diff --git a/src/store/Actions.ts b/src/store/Actions.ts
index 749ffec0..15f738d4 100644
--- a/src/store/Actions.ts
+++ b/src/store/Actions.ts
@@ -1,5 +1,6 @@
export enum Action {
UPDATE_PROJECT_TYPE = '@@UPDATE_PROJECT_TYPE',
+ UPDATE_PROJECT_NAME = '@@UPDATE_PROJECT_NAME',
UPDATE_ACTIVE_IMAGE_INDEX = '@@UPDATE_ACTIVE_IMAGE_INDEX',
UPDATE_IMAGE_DATA_BY_ID = '@@UPDATE_IMAGE_DATA_BY_ID',
ADD_IMAGES_DATA = '@@ADD_IMAGES_DATA',
diff --git a/src/store/editor/actionCreators.ts b/src/store/editor/actionCreators.ts
index 35ac88a8..6ea08d0d 100644
--- a/src/store/editor/actionCreators.ts
+++ b/src/store/editor/actionCreators.ts
@@ -12,6 +12,15 @@ export function updateProjectType(projectType: ProjectType): EditorActionTypes {
};
}
+export function updateProjectName(projectName: string): EditorActionTypes {
+ return {
+ type: Action.UPDATE_PROJECT_NAME,
+ payload: {
+ projectName,
+ },
+ };
+}
+
export function updateActiveImageIndex(activeImageIndex: number): EditorActionTypes {
return {
type: Action.UPDATE_ACTIVE_IMAGE_INDEX,
diff --git a/src/store/editor/reducer.ts b/src/store/editor/reducer.ts
index c4dbbf19..804e0227 100644
--- a/src/store/editor/reducer.ts
+++ b/src/store/editor/reducer.ts
@@ -8,6 +8,7 @@ const initialState: EditorState = {
activeLabelId: null,
highlightedLabelId: null,
projectType: null,
+ projectName: "my-project-name",
imagesData: [],
labelNames: [],
firstLabelCreatedFlag: false
@@ -24,6 +25,12 @@ export function editorReducer(
projectType: action.payload.projectType
}
}
+ case Action.UPDATE_PROJECT_NAME: {
+ return {
+ ...state,
+ projectName: action.payload.projectName
+ }
+ }
case Action.UPDATE_ACTIVE_IMAGE_INDEX: {
return {
...state,
diff --git a/src/store/editor/types.ts b/src/store/editor/types.ts
index 780e5a89..1c971e0a 100644
--- a/src/store/editor/types.ts
+++ b/src/store/editor/types.ts
@@ -31,6 +31,7 @@ export type EditorState = {
activeLabelId: string;
highlightedLabelId: string;
projectType: ProjectType;
+ projectName: string,
imagesData: ImageData[];
labelNames: string[];
firstLabelCreatedFlag: boolean;
@@ -43,6 +44,13 @@ interface UpdateProjectType {
}
}
+interface UpdateProjectName {
+ type: typeof Action.UPDATE_PROJECT_NAME;
+ payload: {
+ projectName: string;
+ }
+}
+
interface UpdateActiveImageIndex {
type: typeof Action.UPDATE_ACTIVE_IMAGE_INDEX;
payload: {
@@ -115,6 +123,7 @@ interface UpdateFirstLabelCreatedFlag {
}
export type EditorActionTypes = UpdateProjectType
+ | UpdateProjectName
| UpdateActiveImageIndex
| UpdateActiveLabelNameIndex
| UpdateActiveLabelType
diff --git a/src/store/selectors/EditorSelector.ts b/src/store/selectors/EditorSelector.ts
new file mode 100644
index 00000000..70332eca
--- /dev/null
+++ b/src/store/selectors/EditorSelector.ts
@@ -0,0 +1,16 @@
+import {store} from "../..";
+import {ImageData} from "../editor/types";
+
+export class EditorSelector {
+ public static getProjectName(): string {
+ return store.getState().editor.projectName;
+ }
+
+ public static getLabelNames(): string[] {
+ return store.getState().editor.labelNames;
+ }
+
+ public static getImagesData(): ImageData[] {
+ return store.getState().editor.imagesData;
+ }
+}
\ No newline at end of file
diff --git a/src/utils/CanvasUtil.ts b/src/utils/CanvasUtil.ts
index 5aa590b2..d1f06416 100644
--- a/src/utils/CanvasUtil.ts
+++ b/src/utils/CanvasUtil.ts
@@ -1,5 +1,7 @@
import React from "react";
import {IPoint} from "../interfaces/IPoint";
+import {IRect} from "../interfaces/IRect";
+import {ISize} from "../interfaces/ISize";
export class CanvasUtil {
public static getMousePositionOnCanvasFromEvent(event: React.MouseEvent | MouseEvent, canvas: HTMLCanvasElement): IPoint {
@@ -12,4 +14,28 @@ export class CanvasUtil {
}
return null;
}
+
+ public static getClientRect(canvas: HTMLCanvasElement): IRect {
+ if (!!canvas) {
+ const canvasRect: ClientRect | DOMRect = canvas.getBoundingClientRect();
+ return {
+ x: canvasRect.left,
+ y: canvasRect.top,
+ width: canvasRect.width,
+ height: canvasRect.height
+ }
+ }
+ return null;
+ }
+
+ public static getSize(canvas: HTMLCanvasElement): ISize {
+ if (!!canvas) {
+ const canvasRect: ClientRect | DOMRect = canvas.getBoundingClientRect();
+ return {
+ width: canvasRect.width,
+ height: canvasRect.height
+ }
+ }
+ return null;
+ }
}
\ No newline at end of file
diff --git a/src/utils/NumberUtil.ts b/src/utils/NumberUtil.ts
new file mode 100644
index 00000000..e5ab948d
--- /dev/null
+++ b/src/utils/NumberUtil.ts
@@ -0,0 +1,10 @@
+export class NumberUtil {
+ public static snapValueToRange(value: number, min: number, max: number): number {
+ if (value < min)
+ return min;
+ if (value > max)
+ return max;
+
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/src/utils/RectUtil.ts b/src/utils/RectUtil.ts
index dccb508c..8f0ddae1 100644
--- a/src/utils/RectUtil.ts
+++ b/src/utils/RectUtil.ts
@@ -117,6 +117,15 @@ export class RectUtil {
}
}
+ public static expand(rect: IRect, delta: IPoint): IRect {
+ return {
+ x: rect.x - delta.x,
+ y: rect.y - delta.y,
+ width: rect.width + 2 * delta.x,
+ height: rect.height + 2 * delta.y
+ }
+ }
+
public static mapRectToAnchors(rect: IRect): RectAnchor[] {
return [
{type: AnchorType.TOP_LEFT, middlePosition: {x: rect.x, y: rect.y}},
diff --git a/src/utils/XMLSanitizerUtil.ts b/src/utils/XMLSanitizerUtil.ts
new file mode 100644
index 00000000..e05b7aea
--- /dev/null
+++ b/src/utils/XMLSanitizerUtil.ts
@@ -0,0 +1,10 @@
+export class XMLSanitizerUtil {
+ public static sanitize(input: string): string {
+ return input
+ .replace('<', '<')
+ .replace('>', '>')
+ .replace('&', '&')
+ .replace("'", ''')
+ .replace("/", '/')
+ }
+}
\ No newline at end of file
diff --git a/src/views/Common/ImageButton/ImageButton.scss b/src/views/Common/ImageButton/ImageButton.scss
index 0c21d381..8f21ac21 100644
--- a/src/views/Common/ImageButton/ImageButton.scss
+++ b/src/views/Common/ImageButton/ImageButton.scss
@@ -10,6 +10,10 @@
transition: background-color 0.7s ease;
margin: 5px 2px;
+ > img {
+ user-select: none;
+ }
+
&:hover ~ .Cursor {
width: 20px;
height: 20px;
diff --git a/src/views/Common/TextInput/TextInput.tsx b/src/views/Common/TextInput/TextInput.tsx
index 32bdb73a..db3fa70f 100644
--- a/src/views/Common/TextInput/TextInput.tsx
+++ b/src/views/Common/TextInput/TextInput.tsx
@@ -5,7 +5,8 @@ interface IProps {
key: string;
label?: string;
isPassword: boolean;
- onChange: (value: string) => any;
+ onChange?: (event: React.ChangeEvent) => any;
+ onFocus?: (event: React.FocusEvent) => any;
inputStyle?: React.CSSProperties;
labelStyle?: React.CSSProperties;
barStyle?: React.CSSProperties;
@@ -19,9 +20,11 @@ const TextInput = (props: IProps) => {
label,
isPassword,
onChange,
+ onFocus,
inputStyle,
labelStyle,
- barStyle
+ barStyle,
+ value
} = props;
const getInputType = () => {
@@ -31,11 +34,12 @@ const TextInput = (props: IProps) => {
return (
onChange(event.target.value)}
+ onChange={onChange ? onChange : undefined}
+ onFocus={onFocus ? onFocus : undefined}
/>
{!!label &&
{
private mousePositionIndicator: HTMLDivElement;
private cursor: HTMLDivElement;
private primaryRenderingEngine: PrimaryEditorRenderEngine;
- private supportRenderingEngine: BaseRenderEngine;
+ private supportRenderingEngine: BaseSuportRenderEngine;
private imageRectOnCanvas: IRect;
private isLoading: boolean = false;
@@ -158,23 +158,38 @@ class Editor extends React.Component {
private updateMousePositionIndicator = (event: React.MouseEvent | MouseEvent) => {
const image = this.state.image;
+
+ if (!image || !this.imageRectOnCanvas || !this.canvas) {
+ this.mousePositionIndicator.style.display = "none";
+ this.cursor.style.display = "none";
+ return;
+ }
+
const mousePositionOnCanvas: IPoint = CanvasUtil.getMousePositionOnCanvasFromEvent(event, this.canvas);
+ const canvasRect: IRect = {x: 0, y: 0, ...CanvasUtil.getSize(this.canvas)};
+ const isOverCanvas: boolean = RectUtil.isPointInside(canvasRect, mousePositionOnCanvas);
- if (!image || !this.imageRectOnCanvas || !RectUtil.isPointInside(this.imageRectOnCanvas, mousePositionOnCanvas)) {
+ if (!isOverCanvas) {
this.mousePositionIndicator.style.display = "none";
this.cursor.style.display = "none";
return;
}
- const scale = image.width / this.imageRectOnCanvas.width;
- const x: number = Math.round((mousePositionOnCanvas.x - this.imageRectOnCanvas.x) * scale);
- const y: number = Math.round((mousePositionOnCanvas.y - this.imageRectOnCanvas.y) * scale);
- const text: string = "x: " + x + ", y: " + y;
+ const isOverImage: boolean = RectUtil.isPointInside(this.imageRectOnCanvas, mousePositionOnCanvas);
+
+ if (isOverImage) {
+ const scale = image.width / this.imageRectOnCanvas.width;
+ const x: number = Math.round((mousePositionOnCanvas.x - this.imageRectOnCanvas.x) * scale);
+ const y: number = Math.round((mousePositionOnCanvas.y - this.imageRectOnCanvas.y) * scale);
+ const text: string = "x: " + x + ", y: " + y;
- this.mousePositionIndicator.innerHTML = text;
- this.mousePositionIndicator.style.left = (mousePositionOnCanvas.x + 15) + "px";
- this.mousePositionIndicator.style.top = (mousePositionOnCanvas.y + 15) + "px";
- this.mousePositionIndicator.style.display = "block";
+ this.mousePositionIndicator.innerHTML = text;
+ this.mousePositionIndicator.style.left = (mousePositionOnCanvas.x + 15) + "px";
+ this.mousePositionIndicator.style.top = (mousePositionOnCanvas.y + 15) + "px";
+ this.mousePositionIndicator.style.display = "block";
+ } else {
+ this.mousePositionIndicator.style.display = "none";
+ }
this.cursor.style.left = mousePositionOnCanvas.x + "px";
this.cursor.style.top = mousePositionOnCanvas.y + "px";
@@ -250,6 +265,7 @@ class Editor extends React.Component {
this.canvas = ref}
+ onContextMenu={(event: React.MouseEvent) => event.preventDefault()}
/>
{
input {
+ padding-top: 3.5px;
+
+ &:focus {
+ outline: none;
+ color: $secondaryColor;
+
+ ~ .Bar {
+ background-color: $secondaryColor;
+ }
+ }
+ }
+
+ .Bar {
+ height: 0;
+ }
+ }
+
.Header {
align-self: stretch;
display: flex;
diff --git a/src/views/EditorView/TopNavigationBar/TopNavigationBar.tsx b/src/views/EditorView/TopNavigationBar/TopNavigationBar.tsx
index a680f639..815584f7 100644
--- a/src/views/EditorView/TopNavigationBar/TopNavigationBar.tsx
+++ b/src/views/EditorView/TopNavigationBar/TopNavigationBar.tsx
@@ -6,17 +6,32 @@ import {PopupWindowType} from "../../../data/PopupWindowType";
import {AppState} from "../../../store";
import {connect} from "react-redux";
import {updateActivePopupType} from "../../../store/general/actionCreators";
+import TextInput from "../../Common/TextInput/TextInput";
+import {updateProjectName} from "../../../store/editor/actionCreators";
interface IProps {
updateActivePopupType: (activePopupType: PopupWindowType) => any;
+ updateProjectName: (projectName: string) => any;
+ projectName: string;
}
-const TopNavigationBar: React.FC = ({updateActivePopupType}) => {
+const TopNavigationBar: React.FC = ({updateActivePopupType, updateProjectName, projectName}) => {
+ const onFocus = (event: React.FocusEvent) => {
+ event.target.setSelectionRange(0, event.target.value.length);
+ };
+
+ const onChange = (event: React.ChangeEvent) => {
+ const value = event.target.value
+ .toLowerCase()
+ .replace(' ', '-');
+ updateProjectName(value)
+ };
+
return (
-
+
updateActivePopupType(PopupWindowType.EXIT_PROJECT)}
@@ -30,11 +45,16 @@ const TopNavigationBar: React.FC = ({updateActivePopupType}) => {
- {/*
updateActivePopupType(PopupWindowType.LOAD_LABELS)}*/}
- {/*/>*/}
+ Project Name:
+
+
+
= ({updateActivePopupType}) => {
};
const mapDispatchToProps = {
- updateActivePopupType
+ updateActivePopupType,
+ updateProjectName
};
-const mapStateToProps = (state: AppState) => ({});
+const mapStateToProps = (state: AppState) => ({
+ projectName: state.editor.projectName
+});
export default connect(
mapStateToProps,
diff --git a/src/views/PopupView/ExportLabelsPopup/ExportLabelPopup.scss b/src/views/PopupView/ExportLabelsPopup/ExportLabelPopup.scss
index 600bb954..d35a63be 100644
--- a/src/views/PopupView/ExportLabelsPopup/ExportLabelPopup.scss
+++ b/src/views/PopupView/ExportLabelsPopup/ExportLabelPopup.scss
@@ -22,7 +22,7 @@
justify-content: flex-start;
align-items: center;
align-content: flex-start;
- padding: 30px 0;
+ padding: 3px 0;
.ImageButton {
transition: transform 0.3s;
@@ -32,13 +32,13 @@
}
&:hover {
- background-color: transparent;
- filter: brightness(35%) sepia(100%) hue-rotate(172deg) saturate(1000%);
+ border-radius: 3px;
+ background-color: rgba(0, 0, 0, 0.1);
}
&.active {
- background-color: transparent;
- filter: brightness(35%) sepia(100%) hue-rotate(172deg) saturate(1000%);
+ border-radius: 3px;
+ background-color: rgba(0, 0, 0, 0.1);
}
}
}
diff --git a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.scss b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.scss
index 99a63a9d..f189ab2b 100644
--- a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.scss
+++ b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.scss
@@ -23,7 +23,7 @@
justify-content: flex-start;
align-items: center;
align-content: flex-start;
- padding: 30px 0;
+ padding: 3px 0;
.ImageButton {
transition: transform 0.3s;
@@ -33,13 +33,13 @@
}
&:hover {
- background-color: transparent;
- filter: brightness(35%) sepia(100%) hue-rotate(172deg) saturate(1000%);
+ border-radius: 3px;
+ background-color: rgba(0, 0, 0, 0.1);
}
&.active {
- background-color: transparent;
- filter: brightness(35%) sepia(100%) hue-rotate(172deg) saturate(1000%);
+ border-radius: 3px;
+ background-color: rgba(0, 0, 0, 0.1);
}
}
}
diff --git a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx
index cd2d595b..99612321 100644
--- a/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx
+++ b/src/views/PopupView/InsertLabelNamesPopup/InsertLabelNamesPopup.tsx
@@ -36,7 +36,7 @@ const InsertLabelNamesPopup: React.FC = ({updateActiveLabelNameIndex, up
onChange(key, value)}
+ onChange={(event: React.ChangeEvent) => onChange(key, event.target.value)}
label={"Inset label"}
/>
= ({updateActiveLabelNameIndex, up
image={"ico/plus.png"}
imageAlt={"plus"}
size={{width: 40, height: 40}}
+ padding={25}
onClick={addHandle}
/>