box-annotations 5.2.1-beta.2 → 5.2.1-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/annotations.css +3 -4
- package/dist/annotations.js +1 -1
- package/dist/annotations.js.LICENSE.txt +3 -43
- package/i18n/en-US.js +4 -0
- package/i18n/en-US.properties +8 -0
- package/i18n/json/src/components/BoundingBoxHighlight/messages.json +1 -0
- package/i18n/json/src/components/Popups/messages.json +1 -1
- package/index.js +1 -1
- package/package.json +33 -15
- package/src/@types/api.ts +3 -2
- package/src/@types/events.ts +28 -7
- package/src/@types/global.ts +0 -9
- package/src/@types/model.ts +14 -0
- package/src/BoxAnnotations.ts +4 -1
- package/src/adapters/threadedAnnotationsAdapters.ts +164 -0
- package/src/api/APIFactory.ts +6 -1
- package/src/api/types.ts +50 -1
- package/src/boundingBoxHighlight/BoundingBoxHighlightContainer.tsx +33 -0
- package/src/boundingBoxHighlight/BoundingBoxHighlightManager.tsx +19 -0
- package/src/boundingBoxHighlight/index.ts +1 -0
- package/src/common/AnnotationCallbacksContext.ts +7 -0
- package/src/common/BaseAnnotator.scss +14 -1
- package/src/common/BaseAnnotator.ts +161 -14
- package/src/common/BaseManager.ts +2 -0
- package/src/common/DeselectListener.tsx +3 -1
- package/src/common/withProviders.tsx +6 -2
- package/src/components/BoundingBoxHighlight/BoundingBoxHighlightList.scss +8 -0
- package/src/components/BoundingBoxHighlight/BoundingBoxHighlightList.tsx +67 -0
- package/src/components/BoundingBoxHighlight/BoundingBoxHighlightNav.scss +73 -0
- package/src/components/BoundingBoxHighlight/BoundingBoxHighlightNav.tsx +68 -0
- package/src/components/BoundingBoxHighlight/BoundingBoxHighlightRect.scss +22 -0
- package/src/components/BoundingBoxHighlight/BoundingBoxHighlightRect.tsx +77 -0
- package/src/components/BoundingBoxHighlight/index.ts +4 -0
- package/src/components/BoundingBoxHighlight/messages.ts +14 -0
- package/src/components/Popups/PopupDrawingToolbar.tsx +0 -7
- package/src/components/Popups/PopupReply.tsx +8 -28
- package/src/components/Popups/PopupV2.scss +28 -0
- package/src/components/Popups/PopupV2.tsx +382 -0
- package/src/components/Popups/messages.ts +10 -0
- package/src/document/DocumentAnnotator.ts +79 -10
- package/src/document/docUtil.ts +2 -1
- package/src/drawing/DrawingAnnotations.tsx +23 -14
- package/src/drawing/DrawingAnnotationsContainer.tsx +3 -0
- package/src/drawing/DrawingCreator.tsx +9 -16
- package/src/drawing/DrawingManager.tsx +13 -2
- package/src/drawing/DrawingTarget.tsx +1 -0
- package/src/highlight/HighlightAnnotations.tsx +27 -14
- package/src/highlight/HighlightContainer.tsx +3 -0
- package/src/highlight/HighlightCreatorManager.ts +8 -5
- package/src/highlight/HighlightListener.ts +4 -2
- package/src/highlight/HighlightManager.tsx +13 -2
- package/src/highlight/HighlightTarget.tsx +1 -0
- package/src/icons/ChevronLeft.tsx +24 -0
- package/src/icons/ChevronRight.tsx +24 -0
- package/src/image/ImageAnnotator.ts +50 -3
- package/src/media/MediaAnnotator.ts +2 -1
- package/src/popup/PopupContainer.tsx +11 -1
- package/src/popup/PopupLayer.tsx +97 -22
- package/src/popup/PopupManager.tsx +24 -4
- package/src/region/RegionAnnotation.tsx +1 -0
- package/src/region/RegionCreation.tsx +4 -7
- package/src/region/RegionCreationContainer.tsx +2 -2
- package/src/region/RegionCreator.tsx +9 -17
- package/src/store/annotations/actions.ts +167 -3
- package/src/store/annotations/reducer.ts +71 -1
- package/src/store/boundingBoxHighlights/actions.ts +6 -0
- package/src/store/boundingBoxHighlights/index.ts +4 -0
- package/src/store/boundingBoxHighlights/reducer.ts +33 -0
- package/src/store/boundingBoxHighlights/selectors.ts +12 -0
- package/src/store/boundingBoxHighlights/types.ts +13 -0
- package/src/store/createRootReducer.ts +2 -0
- package/src/store/eventing/boundingBoxHighlightNav.ts +13 -0
- package/src/store/eventing/create.ts +3 -3
- package/src/store/eventing/delete.ts +27 -0
- package/src/store/eventing/middleware.ts +42 -3
- package/src/store/eventing/replyCreate.ts +34 -0
- package/src/store/eventing/replyDelete.ts +35 -0
- package/src/store/eventing/replyUpdate.ts +39 -0
- package/src/store/eventing/types.ts +4 -4
- package/src/store/eventing/update.ts +29 -0
- package/src/store/highlight/actions.ts +32 -3
- package/src/store/highlight/types.ts +1 -0
- package/src/store/index.ts +1 -0
- package/src/store/options/actions.ts +2 -0
- package/src/store/options/reducer.ts +9 -2
- package/src/store/options/selectors.ts +5 -1
- package/src/store/options/types.ts +7 -1
- package/src/store/types.ts +2 -0
- package/src/utils/rotate.ts +43 -0
- package/src/utils/scroll.ts +8 -3
- package/src/components/Popups/PopupReplyV2.tsx +0 -10
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { Unsubscribe } from 'redux';
|
|
2
2
|
import BaseAnnotator, { Options } from '../common/BaseAnnotator';
|
|
3
3
|
import PopupManager from '../popup/PopupManager';
|
|
4
|
+
import { BoundingBoxHighlightManager } from '../boundingBoxHighlight';
|
|
4
5
|
import { centerDrawing, DrawingManager, isDrawing } from '../drawing';
|
|
5
6
|
import { centerRegion, isRegion, RegionCreationManager, RegionManager } from '../region';
|
|
6
7
|
import { CreatorStatus, getCreatorStatus } from '../store/creator';
|
|
7
|
-
import { getAnnotation, getFileId, getIsCurrentFileVersion, getRotation } from '../store';
|
|
8
|
+
import { getAnnotation, getFileId, getIsCurrentFileVersion, getRotation, getViewMode } from '../store';
|
|
8
9
|
import { getRotatedPosition } from '../utils/rotate';
|
|
9
10
|
import { Manager } from '../common/BaseManager';
|
|
10
11
|
import { scrollToLocation } from '../utils/scroll';
|
|
11
12
|
import './ImageAnnotator.scss';
|
|
12
13
|
|
|
14
|
+
import type { ViewMode } from '../store/options/types';
|
|
15
|
+
|
|
13
16
|
export const CSS_IS_DRAWING_CLASS = 'ba-is-drawing';
|
|
14
17
|
|
|
15
18
|
export default class ImageAnnotator extends BaseAnnotator {
|
|
@@ -17,6 +20,9 @@ export default class ImageAnnotator extends BaseAnnotator {
|
|
|
17
20
|
|
|
18
21
|
managers: Set<Manager> = new Set();
|
|
19
22
|
|
|
23
|
+
/** Tracks which view mode the current managers were created for. Used to destroy/recreate when switching. */
|
|
24
|
+
private managersViewMode: ViewMode | null = null;
|
|
25
|
+
|
|
20
26
|
storeHandler?: Unsubscribe;
|
|
21
27
|
|
|
22
28
|
constructor(options: Options) {
|
|
@@ -41,10 +47,18 @@ export default class ImageAnnotator extends BaseAnnotator {
|
|
|
41
47
|
return this.containerEl?.querySelector('.bp-image');
|
|
42
48
|
}
|
|
43
49
|
|
|
50
|
+
/** Destroys all managers and clears the cache. Call when switching view modes. */
|
|
51
|
+
private clearManagers(): void {
|
|
52
|
+
this.managers.forEach(manager => manager.destroy());
|
|
53
|
+
this.managers.clear();
|
|
54
|
+
this.managersViewMode = null;
|
|
55
|
+
}
|
|
56
|
+
|
|
44
57
|
getManagers(parentEl: HTMLElement, referenceEl: HTMLElement): Set<Manager> {
|
|
45
58
|
const fileId = getFileId(this.store.getState());
|
|
46
59
|
const isCurrentFileVersion = getIsCurrentFileVersion(this.store.getState());
|
|
47
60
|
const resinTags = { fileid: fileId, iscurrent: isCurrentFileVersion };
|
|
61
|
+
const viewMode = getViewMode(this.store.getState());
|
|
48
62
|
|
|
49
63
|
this.managers.forEach(manager => {
|
|
50
64
|
if (!manager.exists(parentEl)) {
|
|
@@ -54,8 +68,13 @@ export default class ImageAnnotator extends BaseAnnotator {
|
|
|
54
68
|
});
|
|
55
69
|
|
|
56
70
|
if (this.managers.size === 0) {
|
|
57
|
-
|
|
58
|
-
|
|
71
|
+
if (viewMode === 'boundingBoxes') {
|
|
72
|
+
this.managers.add(new BoundingBoxHighlightManager({ location: 1, referenceEl, resinTags }));
|
|
73
|
+
return this.managers;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.managers.add(new PopupManager({ popupPortalEl: this.popupPortalEl, referenceEl, resinTags }));
|
|
77
|
+
this.managers.add(new DrawingManager({ popupPortalEl: this.popupPortalEl, referenceEl, resinTags }));
|
|
59
78
|
this.managers.add(new RegionManager({ referenceEl, resinTags }));
|
|
60
79
|
this.managers.add(new RegionCreationManager({ referenceEl, resinTags }));
|
|
61
80
|
}
|
|
@@ -84,11 +103,17 @@ export default class ImageAnnotator extends BaseAnnotator {
|
|
|
84
103
|
render(): void {
|
|
85
104
|
const referenceEl = this.getReference();
|
|
86
105
|
const rotation = getRotation(this.store.getState()) || 0;
|
|
106
|
+
const viewMode = getViewMode(this.store.getState());
|
|
87
107
|
|
|
88
108
|
if (!this.annotatedEl || !referenceEl) {
|
|
89
109
|
return;
|
|
90
110
|
}
|
|
91
111
|
|
|
112
|
+
if (this.managersViewMode !== null && this.managersViewMode !== viewMode) {
|
|
113
|
+
this.clearManagers();
|
|
114
|
+
}
|
|
115
|
+
this.managersViewMode = viewMode;
|
|
116
|
+
|
|
92
117
|
this.getManagers(this.annotatedEl, referenceEl).forEach(manager => {
|
|
93
118
|
manager.style({
|
|
94
119
|
height: `${referenceEl.offsetHeight}px`,
|
|
@@ -99,6 +124,7 @@ export default class ImageAnnotator extends BaseAnnotator {
|
|
|
99
124
|
});
|
|
100
125
|
|
|
101
126
|
manager.render({
|
|
127
|
+
callbacks: { onCopyLink: this.onCopyLink },
|
|
102
128
|
intl: this.intl,
|
|
103
129
|
store: this.store,
|
|
104
130
|
});
|
|
@@ -107,6 +133,27 @@ export default class ImageAnnotator extends BaseAnnotator {
|
|
|
107
133
|
this.postRender();
|
|
108
134
|
}
|
|
109
135
|
|
|
136
|
+
public postRender(): void {
|
|
137
|
+
// DeselectManager is only needed for annotation creation; skip in bounding box mode.
|
|
138
|
+
if (getViewMode(this.store.getState()) === 'boundingBoxes') {
|
|
139
|
+
if (this.deselectManager) {
|
|
140
|
+
this.deselectManager.destroy();
|
|
141
|
+
this.deselectManager = null;
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
super.postRender();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
protected getScrollReferenceForHighlight(): HTMLElement | null | undefined {
|
|
149
|
+
return this.getReference();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
protected getAdjustedScrollOffsets(offsets: { x: number; y: number }): { x: number; y: number } {
|
|
153
|
+
const rotation = getRotation(this.store.getState()) || 0;
|
|
154
|
+
return getRotatedPosition(offsets, rotation);
|
|
155
|
+
}
|
|
156
|
+
|
|
110
157
|
scrollToAnnotation(annotationId: string | null): void {
|
|
111
158
|
if (!annotationId) {
|
|
112
159
|
return;
|
|
@@ -46,7 +46,7 @@ export default class MediaAnnotator extends BaseAnnotator {
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
addManagers(referenceEl: HTMLVideoElement, resinTags: { fileid: string|null; iscurrent: boolean }): void {
|
|
49
|
-
this.managers.add(new PopupManager({ location: MEDIA_LOCATION_INDEX, referenceEl, resinTags, targetType: TARGET_TYPE.FRAME }));
|
|
49
|
+
this.managers.add(new PopupManager({ location: MEDIA_LOCATION_INDEX, popupPortalEl: this.popupPortalEl, referenceEl, resinTags, targetType: TARGET_TYPE.FRAME }));
|
|
50
50
|
this.managers.add(new DrawingManager({ location: MEDIA_LOCATION_INDEX, referenceEl, resinTags, targetType: TARGET_TYPE.FRAME }));
|
|
51
51
|
this.managers.add(new RegionManager({ location: MEDIA_LOCATION_INDEX, referenceEl, resinTags, targetType: TARGET_TYPE.FRAME }));
|
|
52
52
|
this.managers.add(new RegionCreationManager({ location: MEDIA_LOCATION_INDEX, referenceEl, resinTags, targetType: TARGET_TYPE.FRAME }));
|
|
@@ -108,6 +108,7 @@ export default class MediaAnnotator extends BaseAnnotator {
|
|
|
108
108
|
width: `${referenceEl.offsetWidth}px`,
|
|
109
109
|
});
|
|
110
110
|
manager.render({
|
|
111
|
+
callbacks: { onCopyLink: this.onCopyLink },
|
|
111
112
|
intl: this.intl,
|
|
112
113
|
store: this.store,
|
|
113
114
|
});
|
|
@@ -15,12 +15,21 @@ import {
|
|
|
15
15
|
setMessageAction,
|
|
16
16
|
getCreatorReferenceId,
|
|
17
17
|
} from '../store';
|
|
18
|
+
import { getActiveAnnotationId } from '../store/annotations/selectors';
|
|
18
19
|
import { isFeatureEnabled } from '../store/options';
|
|
19
20
|
import { createDrawingAction } from '../drawing/actions';
|
|
20
21
|
import { createHighlightAction } from '../highlight/actions';
|
|
21
22
|
import { createRegionAction } from '../region';
|
|
23
|
+
import { TARGET_TYPE } from '../constants';
|
|
24
|
+
|
|
25
|
+
export type OwnProps = {
|
|
26
|
+
location: number;
|
|
27
|
+
popupPortalEl?: HTMLElement | null;
|
|
28
|
+
targetType?: TARGET_TYPE;
|
|
29
|
+
};
|
|
22
30
|
|
|
23
31
|
export type Props = {
|
|
32
|
+
activeAnnotationId: string | null;
|
|
24
33
|
isPromoting: boolean;
|
|
25
34
|
isThreadedAnnotation?: boolean;
|
|
26
35
|
message: string;
|
|
@@ -30,8 +39,9 @@ export type Props = {
|
|
|
30
39
|
status: CreatorStatus;
|
|
31
40
|
};
|
|
32
41
|
|
|
33
|
-
export const mapStateToProps = (state: AppState, { location }:
|
|
42
|
+
export const mapStateToProps = (state: AppState, { location }: OwnProps): Props => {
|
|
34
43
|
return {
|
|
44
|
+
activeAnnotationId: getActiveAnnotationId(state),
|
|
35
45
|
isPromoting: getIsPromoting(state),
|
|
36
46
|
isThreadedAnnotation: isFeatureEnabled(state, 'isThreadedAnnotation'),
|
|
37
47
|
message: getCreatorMessage(state),
|
package/src/popup/PopupLayer.tsx
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import * as ReactDOM from 'react-dom';
|
|
2
3
|
import noop from 'lodash/noop';
|
|
3
4
|
import PopupReply from '../components/Popups/PopupReply';
|
|
5
|
+
import PopupV2 from '../components/Popups/PopupV2';
|
|
4
6
|
import { CreateArg as DrawingCreateArg } from '../drawing/actions';
|
|
5
7
|
import { CreateArg as HighlightCreateArg } from '../highlight/actions';
|
|
6
8
|
import { CreateArg as RegionCreateArg } from '../region/actions';
|
|
@@ -13,10 +15,13 @@ import {
|
|
|
13
15
|
Mode,
|
|
14
16
|
} from '../store';
|
|
15
17
|
import { PopupReference } from '../components/Popups/Popper';
|
|
16
|
-
|
|
18
|
+
|
|
17
19
|
import { TARGET_TYPE } from '../constants';
|
|
18
20
|
|
|
21
|
+
import './PopupLayer.scss';
|
|
22
|
+
|
|
19
23
|
export type Props = {
|
|
24
|
+
activeAnnotationId: string | null;
|
|
20
25
|
createDrawing?: (arg: DrawingCreateArg) => void;
|
|
21
26
|
createHighlight?: (arg: HighlightCreateArg) => void;
|
|
22
27
|
createRegion?: (arg: RegionCreateArg) => void;
|
|
@@ -25,6 +30,7 @@ export type Props = {
|
|
|
25
30
|
location: number;
|
|
26
31
|
message: string;
|
|
27
32
|
mode: Mode;
|
|
33
|
+
popupPortalEl?: HTMLElement | null;
|
|
28
34
|
referenceId: string | null;
|
|
29
35
|
resetCreator: () => void;
|
|
30
36
|
setMessage: (message: string) => void;
|
|
@@ -39,8 +45,11 @@ const modeStagedMap: { [M in Mode]?: (staged: CreatorItem | null) => boolean } =
|
|
|
39
45
|
[Mode.REGION]: isCreatorStagedRegion,
|
|
40
46
|
};
|
|
41
47
|
|
|
48
|
+
const ACTIVE_TARGET_OBSERVER_TIMEOUT_MS = 10000;
|
|
49
|
+
|
|
42
50
|
const PopupLayer = (props: Props): JSX.Element | null => {
|
|
43
51
|
const {
|
|
52
|
+
activeAnnotationId,
|
|
44
53
|
createDrawing = noop,
|
|
45
54
|
createHighlight = noop,
|
|
46
55
|
createRegion = noop,
|
|
@@ -48,6 +57,7 @@ const PopupLayer = (props: Props): JSX.Element | null => {
|
|
|
48
57
|
isThreadedAnnotation = false,
|
|
49
58
|
message,
|
|
50
59
|
mode,
|
|
60
|
+
popupPortalEl,
|
|
51
61
|
referenceId,
|
|
52
62
|
resetCreator,
|
|
53
63
|
setMessage,
|
|
@@ -57,6 +67,7 @@ const PopupLayer = (props: Props): JSX.Element | null => {
|
|
|
57
67
|
} = props;
|
|
58
68
|
|
|
59
69
|
const [reference, setReference] = React.useState<PopupReference | null>(null);
|
|
70
|
+
const [activeReference, setActiveReference] = React.useState<PopupReference | null>(null);
|
|
60
71
|
const canCreate = (modeStagedMap[mode]?.(staged ?? null) ?? false) || isPromoting;
|
|
61
72
|
const canReply = status !== CreatorStatus.started && status !== CreatorStatus.init;
|
|
62
73
|
const isPending = status === CreatorStatus.pending;
|
|
@@ -69,17 +80,19 @@ const PopupLayer = (props: Props): JSX.Element | null => {
|
|
|
69
80
|
setMessage(text);
|
|
70
81
|
};
|
|
71
82
|
|
|
72
|
-
const handleSubmit = (): void => {
|
|
83
|
+
const handleSubmit = (text?: string): void => {
|
|
73
84
|
if (!staged) {
|
|
74
85
|
return;
|
|
75
86
|
}
|
|
76
87
|
|
|
88
|
+
const submitMessage = text ?? message;
|
|
89
|
+
|
|
77
90
|
if (isCreatorStagedHighlight(staged)) {
|
|
78
|
-
createHighlight({ ...staged, message, targetType });
|
|
91
|
+
createHighlight({ ...staged, message: submitMessage, targetType });
|
|
79
92
|
} else if (isCreatorStagedRegion(staged)) {
|
|
80
|
-
createRegion({ ...staged, message, targetType });
|
|
93
|
+
createRegion({ ...staged, message: submitMessage, targetType });
|
|
81
94
|
} else if (isCreatorStagedDrawing(staged)) {
|
|
82
|
-
createDrawing({ ...staged, message, targetType });
|
|
95
|
+
createDrawing({ ...staged, message: submitMessage, targetType });
|
|
83
96
|
}
|
|
84
97
|
};
|
|
85
98
|
|
|
@@ -87,24 +100,86 @@ const PopupLayer = (props: Props): JSX.Element | null => {
|
|
|
87
100
|
setReference(referenceId ? document.querySelector(`[data-ba-reference-id="${referenceId}"]`) : null);
|
|
88
101
|
}, [referenceId]);
|
|
89
102
|
|
|
103
|
+
// activeAnnotationId can be set (e.g. via deep link) before the target DOM node mounts;
|
|
104
|
+
// observe until it appears, or give up after ACTIVE_TARGET_OBSERVER_TIMEOUT_MS.
|
|
105
|
+
React.useEffect(() => {
|
|
106
|
+
if (!activeAnnotationId || !isThreadedAnnotation) {
|
|
107
|
+
setActiveReference(null);
|
|
108
|
+
return noop;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const selector = `[data-ba-annotation-id="${CSS.escape(activeAnnotationId)}"]`;
|
|
112
|
+
const existing = document.querySelector(selector);
|
|
113
|
+
if (existing) {
|
|
114
|
+
setActiveReference(existing);
|
|
115
|
+
return noop;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
setActiveReference(null);
|
|
119
|
+
const observer = new MutationObserver(() => {
|
|
120
|
+
const el = document.querySelector(selector);
|
|
121
|
+
if (el) {
|
|
122
|
+
setActiveReference(el);
|
|
123
|
+
observer.disconnect();
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
127
|
+
|
|
128
|
+
const timeoutId = window.setTimeout(() => {
|
|
129
|
+
observer.disconnect();
|
|
130
|
+
}, ACTIVE_TARGET_OBSERVER_TIMEOUT_MS);
|
|
131
|
+
|
|
132
|
+
return () => {
|
|
133
|
+
observer.disconnect();
|
|
134
|
+
window.clearTimeout(timeoutId);
|
|
135
|
+
};
|
|
136
|
+
}, [activeAnnotationId, isThreadedAnnotation]);
|
|
137
|
+
|
|
138
|
+
const showCreator = canCreate && canReply && reference && staged;
|
|
139
|
+
|
|
140
|
+
if (showCreator && isThreadedAnnotation) {
|
|
141
|
+
return (
|
|
142
|
+
<div className="ba-PopupLayer-popup">
|
|
143
|
+
<PopupV2
|
|
144
|
+
onSubmit={handleSubmit}
|
|
145
|
+
popupPortalEl={popupPortalEl}
|
|
146
|
+
reference={reference}
|
|
147
|
+
/>
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (showCreator) {
|
|
153
|
+
if (!popupPortalEl) return null;
|
|
154
|
+
return ReactDOM.createPortal(
|
|
155
|
+
<div className="ba-PopupLayer-popup">
|
|
156
|
+
<PopupReply
|
|
157
|
+
isPending={isPending}
|
|
158
|
+
onCancel={handleCancel}
|
|
159
|
+
onChange={handleChange}
|
|
160
|
+
onSubmit={handleSubmit}
|
|
161
|
+
reference={reference}
|
|
162
|
+
value={message}
|
|
163
|
+
/>
|
|
164
|
+
</div>,
|
|
165
|
+
popupPortalEl,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (isThreadedAnnotation && activeAnnotationId && activeReference) {
|
|
170
|
+
return (
|
|
171
|
+
<div className="ba-PopupLayer-popup">
|
|
172
|
+
<PopupV2
|
|
173
|
+
annotationId={activeAnnotationId}
|
|
174
|
+
onSubmit={handleSubmit}
|
|
175
|
+
popupPortalEl={popupPortalEl}
|
|
176
|
+
reference={activeReference}
|
|
177
|
+
/>
|
|
178
|
+
</div>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
90
181
|
|
|
91
|
-
return
|
|
92
|
-
<>
|
|
93
|
-
{canCreate && canReply && reference && staged && (
|
|
94
|
-
<div className="ba-PopupLayer-popup">
|
|
95
|
-
<PopupReply
|
|
96
|
-
isPending={isPending}
|
|
97
|
-
isThreadedAnnotation={isThreadedAnnotation}
|
|
98
|
-
onCancel={handleCancel}
|
|
99
|
-
onChange={handleChange}
|
|
100
|
-
onSubmit={handleSubmit}
|
|
101
|
-
reference={reference}
|
|
102
|
-
value={message}
|
|
103
|
-
/>
|
|
104
|
-
</div>
|
|
105
|
-
)}
|
|
106
|
-
</>
|
|
107
|
-
);
|
|
182
|
+
return null;
|
|
108
183
|
};
|
|
109
184
|
|
|
110
185
|
export default PopupLayer;
|
|
@@ -1,19 +1,39 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import * as ReactDOM from 'react-dom/client';
|
|
3
|
-
import BaseManager, { Props } from '../common/BaseManager';
|
|
3
|
+
import BaseManager, { Options as BaseOptions, Props } from '../common/BaseManager';
|
|
4
4
|
import PopupContainer from './PopupContainer';
|
|
5
5
|
|
|
6
|
+
export type Options = BaseOptions & {
|
|
7
|
+
popupPortalEl?: HTMLElement | null;
|
|
8
|
+
};
|
|
9
|
+
|
|
6
10
|
export default class PopupManager extends BaseManager {
|
|
11
|
+
popupPortalEl?: HTMLElement | null;
|
|
12
|
+
|
|
13
|
+
constructor({ popupPortalEl, ...options }: Options) {
|
|
14
|
+
super(options);
|
|
15
|
+
this.popupPortalEl = popupPortalEl;
|
|
16
|
+
}
|
|
17
|
+
|
|
7
18
|
decorate(): void {
|
|
8
19
|
this.reactEl.classList.add('ba-Layer--popup');
|
|
9
20
|
this.reactEl.dataset.testid = 'ba-Layer--popup';
|
|
10
21
|
}
|
|
11
22
|
|
|
12
|
-
render(
|
|
23
|
+
render({ callbacks, intl, store }: Props): void {
|
|
13
24
|
if (!this.root) {
|
|
14
25
|
this.root = ReactDOM.createRoot(this.reactEl);
|
|
15
26
|
}
|
|
16
|
-
|
|
17
|
-
this.root.render(
|
|
27
|
+
|
|
28
|
+
this.root.render(
|
|
29
|
+
<PopupContainer
|
|
30
|
+
callbacks={callbacks}
|
|
31
|
+
intl={intl}
|
|
32
|
+
location={this.location}
|
|
33
|
+
popupPortalEl={this.popupPortalEl}
|
|
34
|
+
store={store}
|
|
35
|
+
targetType={this.targetType}
|
|
36
|
+
/>,
|
|
37
|
+
);
|
|
18
38
|
}
|
|
19
39
|
}
|
|
@@ -36,6 +36,7 @@ export const RegionAnnotation = (props: Props, ref: React.Ref<RegionAnnotationRe
|
|
|
36
36
|
<button
|
|
37
37
|
ref={ref}
|
|
38
38
|
className={classNames('ba-RegionAnnotation', className, { 'is-active': isActive })}
|
|
39
|
+
data-ba-annotation-id={annotationId}
|
|
39
40
|
data-resin-itemid={annotationId}
|
|
40
41
|
data-resin-target="highlightRegion"
|
|
41
42
|
data-testid={`ba-AnnotationTarget-${annotationId}`}
|
|
@@ -9,9 +9,9 @@ import { getVideoCurrentTimeInMilliseconds } from '../utils/useVideoTiming';
|
|
|
9
9
|
|
|
10
10
|
type Props = {
|
|
11
11
|
isCreating: boolean;
|
|
12
|
-
isRotated: boolean;
|
|
13
12
|
location: number;
|
|
14
13
|
resetCreator: () => void;
|
|
14
|
+
rotation: number;
|
|
15
15
|
setReferenceId: (uuid: string) => void;
|
|
16
16
|
setStaged: (staged: CreatorItemRegion | null) => void;
|
|
17
17
|
setStatus: (status: CreatorStatus) => void;
|
|
@@ -27,7 +27,7 @@ type State = {
|
|
|
27
27
|
export default class RegionCreation extends React.PureComponent<Props, State> {
|
|
28
28
|
static defaultProps = {
|
|
29
29
|
isCreating: false,
|
|
30
|
-
|
|
30
|
+
rotation: 0,
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
state: State = {};
|
|
@@ -57,11 +57,7 @@ export default class RegionCreation extends React.PureComponent<Props, State> {
|
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
render(): JSX.Element | null {
|
|
60
|
-
const { isCreating,
|
|
61
|
-
|
|
62
|
-
if (isRotated) {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
60
|
+
const { isCreating, rotation, staged } = this.props;
|
|
65
61
|
|
|
66
62
|
return (
|
|
67
63
|
<>
|
|
@@ -71,6 +67,7 @@ export default class RegionCreation extends React.PureComponent<Props, State> {
|
|
|
71
67
|
onAbort={this.handleAbort}
|
|
72
68
|
onStart={this.handleStart}
|
|
73
69
|
onStop={this.handleStop}
|
|
70
|
+
rotation={rotation}
|
|
74
71
|
/>
|
|
75
72
|
)}
|
|
76
73
|
|
|
@@ -19,7 +19,7 @@ import withProviders from '../common/withProviders';
|
|
|
19
19
|
|
|
20
20
|
export type Props = {
|
|
21
21
|
isCreating: boolean;
|
|
22
|
-
|
|
22
|
+
rotation: number;
|
|
23
23
|
staged: CreatorItemRegion | null;
|
|
24
24
|
};
|
|
25
25
|
|
|
@@ -28,7 +28,7 @@ export const mapStateToProps = (state: AppState, { location }: { location: numbe
|
|
|
28
28
|
|
|
29
29
|
return {
|
|
30
30
|
isCreating: getAnnotationMode(state) === Mode.REGION && getCreatorStatus(state) !== CreatorStatus.pending,
|
|
31
|
-
|
|
31
|
+
rotation: getRotation(state),
|
|
32
32
|
staged: isCreatorStagedRegion(staged) ? staged : null,
|
|
33
33
|
};
|
|
34
34
|
};
|
|
@@ -4,6 +4,7 @@ import PointerCapture, { Status as DrawingStatus } from '../components/PointerCa
|
|
|
4
4
|
import PopupCursor from '../components/Popups/PopupCursor';
|
|
5
5
|
import RegionRect, { RegionRectRef } from './RegionRect';
|
|
6
6
|
import useAutoScroll from '../common/useAutoScroll';
|
|
7
|
+
import { getElementLocalPosition } from '../utils/rotate';
|
|
7
8
|
import { Rect } from '../@types';
|
|
8
9
|
import { styleShape } from './regionUtil';
|
|
9
10
|
import './RegionCreator.scss';
|
|
@@ -13,6 +14,7 @@ type Props = {
|
|
|
13
14
|
onAbort: () => void;
|
|
14
15
|
onStart: () => void;
|
|
15
16
|
onStop: (shape: Rect) => void;
|
|
17
|
+
rotation?: number;
|
|
16
18
|
};
|
|
17
19
|
|
|
18
20
|
const MIN_X = 0; // Minimum region x position must be positive
|
|
@@ -22,7 +24,7 @@ const MIN_SIZE = 10; // Minimum region size must be large enough to be clickable
|
|
|
22
24
|
const isValid = (x1: number, y1: number, x2: number, y2: number): boolean =>
|
|
23
25
|
Math.abs(x2 - x1) >= MIN_SIZE || Math.abs(y2 - y1) >= MIN_SIZE;
|
|
24
26
|
|
|
25
|
-
export default function RegionCreator({ className, onAbort, onStart, onStop }: Props): JSX.Element {
|
|
27
|
+
export default function RegionCreator({ className, onAbort, onStart, onStop, rotation = 0 }: Props): JSX.Element {
|
|
26
28
|
const [drawingStatus, setDrawingStatus] = React.useState<DrawingStatus>(DrawingStatus.init);
|
|
27
29
|
const [isHovering, setIsHovering] = React.useState<boolean>(false);
|
|
28
30
|
const creatorElRef = React.useRef<HTMLDivElement>(null);
|
|
@@ -34,18 +36,6 @@ export default function RegionCreator({ className, onAbort, onStart, onStop }: P
|
|
|
34
36
|
const regionRectRef = React.useRef<RegionRectRef>(null);
|
|
35
37
|
const renderHandleRef = React.useRef<number | null>(null);
|
|
36
38
|
|
|
37
|
-
// Drawing Helpers
|
|
38
|
-
const getPosition = (x: number, y: number): [number, number] => {
|
|
39
|
-
const { current: creatorEl } = creatorElRef;
|
|
40
|
-
|
|
41
|
-
if (!creatorEl) {
|
|
42
|
-
return [x, y];
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Calculate the new position based on the mouse position less the page offset
|
|
46
|
-
const { left, top } = creatorEl.getBoundingClientRect();
|
|
47
|
-
return [x - left, y - top];
|
|
48
|
-
};
|
|
49
39
|
const getShape = (): Rect | null => {
|
|
50
40
|
const { current: creatorEl } = creatorElRef;
|
|
51
41
|
const { current: x1 } = positionX1Ref;
|
|
@@ -57,7 +47,9 @@ export default function RegionCreator({ className, onAbort, onStart, onStop }: P
|
|
|
57
47
|
return null;
|
|
58
48
|
}
|
|
59
49
|
|
|
60
|
-
|
|
50
|
+
// Get the element's dimensions (before any rotation is applied)
|
|
51
|
+
const width = creatorEl.offsetWidth;
|
|
52
|
+
const height = creatorEl.offsetHeight;
|
|
61
53
|
const MAX_HEIGHT = height - MIN_SIZE;
|
|
62
54
|
const MAX_WIDTH = width - MIN_SIZE;
|
|
63
55
|
const MAX_X = Math.max(0, width - MIN_X);
|
|
@@ -88,7 +80,7 @@ export default function RegionCreator({ className, onAbort, onStart, onStop }: P
|
|
|
88
80
|
|
|
89
81
|
// Drawing Lifecycle Callbacks
|
|
90
82
|
const startDraw = (x: number, y: number): void => {
|
|
91
|
-
const [x1, y1] =
|
|
83
|
+
const [x1, y1] = getElementLocalPosition(x, y, creatorElRef.current, rotation);
|
|
92
84
|
|
|
93
85
|
setDrawingStatus(DrawingStatus.dragging);
|
|
94
86
|
|
|
@@ -119,7 +111,7 @@ export default function RegionCreator({ className, onAbort, onStart, onStop }: P
|
|
|
119
111
|
|
|
120
112
|
const updateDraw = React.useCallback(
|
|
121
113
|
(x: number, y: number): void => {
|
|
122
|
-
const [x2, y2] =
|
|
114
|
+
const [x2, y2] = getElementLocalPosition(x, y, creatorElRef.current, rotation);
|
|
123
115
|
const { current: x1 } = positionX1Ref;
|
|
124
116
|
const { current: y1 } = positionY1Ref;
|
|
125
117
|
const { current: prevX2 } = positionX2Ref;
|
|
@@ -138,7 +130,7 @@ export default function RegionCreator({ className, onAbort, onStart, onStop }: P
|
|
|
138
130
|
onStart();
|
|
139
131
|
}
|
|
140
132
|
},
|
|
141
|
-
[drawingStatus, onStart, setDrawingStatus],
|
|
133
|
+
[drawingStatus, onStart, rotation, setDrawingStatus],
|
|
142
134
|
);
|
|
143
135
|
|
|
144
136
|
// Event Handlers
|