Skip to content
This repository was archived by the owner on May 30, 2023. It is now read-only.

Commit b74dc6c

Browse files
authored
Merge pull request #268 from daita-technologies/feat/annotation
feature: annotation
2 parents 1b53a53 + ec8adbc commit b74dc6c

93 files changed

Lines changed: 9751 additions & 80 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"@types/crypto-js": "^4.0.2",
4545
"@types/file-saver": "^2.0.4",
4646
"@types/lodash": "^4.14.177",
47-
"@types/node": "^12.20.14",
47+
"@types/node": "17.0.29",
4848
"@types/react": "^17.0.9",
4949
"@types/react-dom": "^17.0.6",
5050
"@types/react-google-recaptcha": "^2.1.3",
@@ -62,17 +62,20 @@
6262
"history": "^4.10.1",
6363
"js-base64": "^3.7.2",
6464
"jszip": "^3.9.1",
65+
"konva": "^8.3.10",
6566
"lodash": "^4.17.21",
6667
"moment": "^2.29.2",
6768
"p-limit": "^4.0.0",
6869
"react": "^17.0.2",
6970
"react-chartjs-2": "^4.1.0",
71+
"react-colorful": "^5.6.1",
7072
"react-dom": "^17.0.2",
7173
"react-dropzone": "^11.4.2",
7274
"react-google-recaptcha": "^2.1.0",
7375
"react-helmet": "^6.1.0",
7476
"react-hook-form": "^7.9.0",
7577
"react-infinite-scroll-component": "^6.1.0",
78+
"react-konva": "17.0.2-6",
7679
"react-markdown": "^8.0.3",
7780
"react-redux": "^7.2.4",
7881
"react-router-dom": "^5.2.0",
@@ -85,6 +88,7 @@
8588
"redux-saga": "^1.1.3",
8689
"styled-components": "^5.3.1",
8790
"typescript": "^4.3.2",
91+
"use-image": "^1.0.12",
8892
"uuid": "^8.3.2"
8993
},
9094
"devDependencies": {

src/App.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { Box } from "@mui/material";
22
import { Layout, PageLoading, ScrollToTop } from "components";
3+
import EmptyLayout from "components/Layout/EmptyLayout";
34
import GuestLayout from "components/Layout/GuestLayout";
45
import routeConfig, {
56
customLayoutRouteConfig,
67
CustomRoute,
8+
emptyLayoutRouteConfig,
79
} from "config/routeConfig";
810
import { TOKEN_NAME } from "constants/defaultValues";
911
import { Redirect, Route, Router, Switch } from "react-router-dom";
@@ -59,6 +61,23 @@ Apply here: contact@daita.tech
5961
</Switch>
6062
</GuestLayout>
6163
</Route>
64+
<Route path={emptyLayoutRouteConfig.map((t) => t.path)}>
65+
<EmptyLayout>
66+
<Switch>
67+
{emptyLayoutRouteConfig.map(
68+
({ path, exact, component, isPrivate }) => (
69+
<CustomRoute
70+
key={`route-${path}`}
71+
path={path}
72+
exact={exact}
73+
component={component}
74+
isPrivate={isPrivate}
75+
/>
76+
)
77+
)}
78+
</Switch>
79+
</EmptyLayout>
80+
</Route>
6281
<Route path={routeConfig.map((t) => t.path)}>
6382
<Layout>
6483
<Switch>
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import Konva from "konva";
2+
import { KonvaEventObject } from "konva/lib/Node";
3+
import { Box } from "konva/lib/shapes/Transformer";
4+
import { Vector2d } from "konva/lib/types";
5+
import React, { useEffect, useMemo, useState } from "react";
6+
import { Ellipse, Transformer } from "react-konva";
7+
import { useDispatch, useSelector } from "react-redux";
8+
import { updateDrawObject } from "reduxes/annotation/action";
9+
import {
10+
selectorCurrentDrawState,
11+
selectorDrawObject,
12+
selectorDrawObjectState,
13+
selectorSelectedEllipse,
14+
} from "reduxes/annotation/selector";
15+
import { DrawState } from "reduxes/annotation/type";
16+
import { CIRCLE_STYLE, CORNER_RADIUS } from "../const";
17+
import { EllipseCompProps, EllipseProps, EllipseSpec } from "../type";
18+
import useCommonShapeEvent from "../useCommonShapeEvent";
19+
20+
const EllipseComp = function ({
21+
spec,
22+
onMouseOverHandler,
23+
onMouseOutHandler,
24+
}: EllipseCompProps) {
25+
const shapeRef = React.useRef<Konva.Ellipse>(null);
26+
const trRef = React.useRef<any>(null);
27+
const dispatch = useDispatch();
28+
const currentShape = useSelector(selectorSelectedEllipse);
29+
const commonShapeEvent = useCommonShapeEvent({ drawObject: spec });
30+
const currentDrawState = useSelector(selectorCurrentDrawState);
31+
const drawObjectState = useSelector(selectorDrawObjectState(spec.id));
32+
33+
const [strokeWidth, setStrokeWidth] = useState<number>(
34+
spec.cssStyle.strokeWidth
35+
);
36+
37+
const isSelected = useMemo(() => {
38+
return currentShape != null && spec.id === currentShape.id;
39+
}, [currentShape?.id]);
40+
useEffect(() => {
41+
if (isSelected == true) {
42+
shapeRef.current?.moveToTop();
43+
}
44+
}, [isSelected]);
45+
const onChange = (rect: EllipseSpec) => {
46+
dispatch(
47+
updateDrawObject({
48+
data: { ...rect },
49+
})
50+
);
51+
};
52+
React.useEffect(() => {
53+
if (isSelected) {
54+
if (!trRef.current) return;
55+
trRef.current.nodes([shapeRef.current]);
56+
trRef.current.getLayer().batchDraw();
57+
}
58+
}, [isSelected]);
59+
const [stage, setStage] = useState<Konva.Stage | null>(null);
60+
61+
const groupDragBound = (pos: Vector2d) => {
62+
let { x, y } = pos;
63+
const sw = stage ? stage.width() : 0;
64+
const sh = stage ? stage.height() : 0;
65+
if (shapeRef && shapeRef.current) {
66+
const box = shapeRef.current.getClientRect();
67+
68+
if (y - box.height / 2 < 0) y = box.height / 2;
69+
if (x - box.width / 2 < 0) x = box.width / 2;
70+
if (y + box.height / 2 > sh) y = sh - box.height / 2;
71+
if (x + box.width / 2 > sw) x = sw - box.width / 2;
72+
return { x, y };
73+
}
74+
return { x: 0, y: 0 };
75+
};
76+
77+
const handleDragStart = (e: KonvaEventObject<DragEvent>) => {
78+
setStage(e.target.getStage());
79+
commonShapeEvent.handleDragStart(e);
80+
};
81+
const handleTransformEnd = (e: KonvaEventObject<Event>) => {
82+
const node = shapeRef.current;
83+
if (!node) return;
84+
85+
let radiusX = (node.width() * node.scaleX()) / 2.0;
86+
let radiusY = (node.height() * node.scaleY()) / 2.0;
87+
88+
onChange({
89+
...spec,
90+
x: node.x(),
91+
y: node.y(),
92+
rotation: node.attrs.rotation,
93+
radiusX: radiusX,
94+
radiusY: radiusY,
95+
});
96+
node.scale({ x: 1, y: 1 });
97+
commonShapeEvent.handleTransformEnd(e);
98+
};
99+
const handleDragEnd = (e: KonvaEventObject<DragEvent>) => {
100+
onChange({
101+
...spec,
102+
x: e.target.x(),
103+
y: e.target.y(),
104+
});
105+
commonShapeEvent.handleDragEnd(e);
106+
};
107+
const boundBoxFunc = (oldBox: Box, newBox: Box) => {
108+
if (newBox.width < 5 || newBox.height < 5) {
109+
return oldBox;
110+
}
111+
return newBox;
112+
};
113+
const onMouseOver = (e: KonvaEventObject<MouseEvent>) => {
114+
if (currentDrawState !== DrawState.DRAWING) {
115+
setStrokeWidth(spec.cssStyle.strokeWidth * 2);
116+
}
117+
onMouseOverHandler(e);
118+
};
119+
const onMouseOut = (e: KonvaEventObject<MouseEvent>) => {
120+
setStrokeWidth(spec.cssStyle.strokeWidth);
121+
onMouseOutHandler(e);
122+
};
123+
124+
return (
125+
<React.Fragment>
126+
<Ellipse
127+
ref={shapeRef}
128+
dragBoundFunc={groupDragBound}
129+
onClick={commonShapeEvent.handleCick}
130+
onMouseDown={commonShapeEvent.handleSelect}
131+
onTransformStart={commonShapeEvent.handleTransformStart}
132+
onMouseOver={onMouseOver}
133+
onMouseOut={onMouseOut}
134+
draggable={commonShapeEvent.isLock !== true}
135+
onDragEnd={handleDragEnd}
136+
onDragStart={handleDragStart}
137+
onTransformEnd={handleTransformEnd}
138+
strokeScaleEnabled={false}
139+
{...spec}
140+
{...spec.cssStyle}
141+
strokeWidth={strokeWidth}
142+
visible={drawObjectState ? !drawObjectState.isHidden : true}
143+
/>
144+
{isSelected && commonShapeEvent.isLock !== true && (
145+
<Transformer
146+
ref={trRef}
147+
anchorFill={CIRCLE_STYLE.fill}
148+
anchorCornerRadius={CORNER_RADIUS}
149+
anchorStrokeWidth={CIRCLE_STYLE.strokeWidth}
150+
anchorStroke={CIRCLE_STYLE.stroke}
151+
anchorSize={CORNER_RADIUS * 1.8}
152+
ignoreStroke={true}
153+
boundBoxFunc={boundBoxFunc}
154+
/>
155+
)}
156+
</React.Fragment>
157+
);
158+
};
159+
const EllipseShape = function ({
160+
id,
161+
onMouseOverHandler,
162+
onMouseOutHandler,
163+
}: EllipseProps) {
164+
const drawObject = useSelector(selectorDrawObject(id));
165+
if (drawObject) {
166+
return (
167+
<EllipseComp
168+
spec={drawObject.data as EllipseSpec}
169+
onMouseOutHandler={onMouseOutHandler}
170+
onMouseOverHandler={onMouseOverHandler}
171+
/>
172+
);
173+
}
174+
return <></>;
175+
};
176+
export default EllipseShape;

0 commit comments

Comments
 (0)