box-annotations 4.0.0-beta.8 → 4.0.1-beta.1
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/README.md +7 -57
- package/dist/annotations.css +1 -1
- package/dist/annotations.js +1 -1
- package/i18n/bn-IN.js +6 -0
- package/i18n/bn-IN.properties +12 -0
- package/i18n/da-DK.js +6 -0
- package/i18n/da-DK.properties +12 -0
- package/i18n/de-DE.js +6 -0
- package/i18n/de-DE.properties +12 -0
- package/i18n/en-AU.js +6 -0
- package/i18n/en-AU.properties +12 -0
- package/i18n/en-CA.js +6 -0
- package/i18n/en-CA.properties +12 -0
- package/i18n/en-GB.js +6 -0
- package/i18n/en-GB.properties +12 -0
- package/i18n/en-US.js +6 -0
- package/i18n/en-US.properties +12 -0
- package/i18n/en-x-pseudo.js +16 -9
- package/i18n/en-x-pseudo.properties +23 -9
- package/i18n/es-419.js +6 -0
- package/i18n/es-419.properties +12 -0
- package/i18n/es-ES.js +6 -0
- package/i18n/es-ES.properties +12 -0
- package/i18n/fi-FI.js +6 -0
- package/i18n/fi-FI.properties +12 -0
- package/i18n/fr-CA.js +6 -0
- package/i18n/fr-CA.properties +12 -0
- package/i18n/fr-FR.js +6 -0
- package/i18n/fr-FR.properties +12 -0
- package/i18n/hi-IN.js +6 -0
- package/i18n/hi-IN.properties +12 -0
- package/i18n/it-IT.js +6 -0
- package/i18n/it-IT.properties +12 -0
- package/i18n/ja-JP.js +6 -0
- package/i18n/ja-JP.properties +12 -0
- package/i18n/json/src/components/Popups/messages.json +1 -1
- package/i18n/ko-KR.js +6 -0
- package/i18n/ko-KR.properties +12 -0
- package/i18n/nb-NO.js +6 -0
- package/i18n/nb-NO.properties +12 -0
- package/i18n/nl-NL.js +6 -0
- package/i18n/nl-NL.properties +12 -0
- package/i18n/pl-PL.js +6 -0
- package/i18n/pl-PL.properties +12 -0
- package/i18n/pt-BR.js +6 -0
- package/i18n/pt-BR.properties +12 -0
- package/i18n/ru-RU.js +6 -0
- package/i18n/ru-RU.properties +12 -0
- package/i18n/sv-SE.js +6 -0
- package/i18n/sv-SE.properties +12 -0
- package/i18n/tr-TR.js +6 -0
- package/i18n/tr-TR.properties +12 -0
- package/i18n/zh-CN.js +6 -0
- package/i18n/zh-CN.properties +12 -0
- package/i18n/zh-TW.js +6 -0
- package/i18n/zh-TW.properties +12 -0
- package/package.json +29 -29
- package/src/@types/events.ts +8 -2
- package/src/@types/global.ts +1 -0
- package/src/@types/model.ts +20 -7
- package/src/BoxAnnotations.ts +16 -2
- package/src/common/BaseAnnotator.scss +0 -1
- package/src/common/BaseAnnotator.ts +125 -19
- package/src/common/BaseManager.ts +67 -4
- package/src/common/DeselectListener.tsx +21 -0
- package/src/common/DeselectManager.tsx +30 -0
- package/src/common/useIsListInteractive.ts +10 -0
- package/src/common/useMountId.ts +14 -0
- package/src/common/useOutsideEvent.ts +9 -4
- package/src/common/usePrevious.ts +13 -0
- package/src/components/ItemList/ItemList.scss +7 -2
- package/src/components/ItemList/ItemList.tsx +45 -26
- package/src/components/ItemList/ItemRow.scss +19 -7
- package/src/components/ItemList/ItemRow.tsx +33 -12
- package/src/components/PointerCapture/PointerCapture.tsx +116 -0
- package/src/components/PointerCapture/index.ts +2 -0
- package/src/components/Popups/Popper.ts +1 -0
- package/src/components/Popups/PopupArrow.scss +91 -0
- package/src/components/Popups/PopupBase.scss +12 -81
- package/src/components/Popups/PopupBase.tsx +0 -1
- package/src/components/Popups/PopupCursor.tsx +1 -1
- package/src/components/Popups/PopupDrawingToolbar.scss +76 -0
- package/src/components/Popups/PopupDrawingToolbar.tsx +109 -0
- package/src/components/Popups/PopupHighlight.scss +39 -0
- package/src/components/Popups/PopupHighlight.tsx +97 -0
- package/src/components/Popups/PopupHighlightError.scss +23 -0
- package/src/components/Popups/PopupHighlightError.tsx +74 -0
- package/src/components/Popups/PopupList.scss +1 -4
- package/src/components/Popups/PopupList.tsx +2 -2
- package/src/components/Popups/PopupReply.tsx +51 -6
- package/src/components/Popups/messages.ts +30 -0
- package/src/components/ReplyField/ReplyField.scss +6 -1
- package/src/components/ReplyField/ReplyField.tsx +43 -42
- package/src/components/ReplyField/ReplyFieldContainer.tsx +2 -1
- package/src/components/ReplyForm/ReplyButton.tsx +38 -0
- package/src/components/ReplyForm/ReplyForm.scss +3 -0
- package/src/components/ReplyForm/ReplyForm.tsx +49 -12
- package/src/components/ReplyForm/ReplyFormContainer.tsx +6 -4
- package/src/constants.js +1 -8
- package/src/document/DocumentAnnotator.scss +15 -0
- package/src/document/DocumentAnnotator.ts +110 -53
- package/src/document/docUtil.ts +61 -0
- package/src/drawing/DecoratedDrawingPath.scss +9 -0
- package/src/drawing/DecoratedDrawingPath.tsx +43 -0
- package/src/drawing/DrawingAnnotations.scss +39 -0
- package/src/drawing/DrawingAnnotations.tsx +157 -0
- package/src/drawing/DrawingAnnotationsContainer.tsx +66 -0
- package/src/drawing/DrawingCreator.scss +9 -0
- package/src/drawing/DrawingCreator.tsx +183 -0
- package/src/drawing/DrawingCursor.ts +23 -0
- package/src/drawing/DrawingList.tsx +60 -0
- package/src/drawing/DrawingManager.tsx +15 -0
- package/src/drawing/DrawingPath.tsx +17 -0
- package/src/drawing/DrawingPathGroup.tsx +52 -0
- package/src/drawing/DrawingSVG.tsx +34 -0
- package/src/drawing/DrawingSVGGroup.tsx +23 -0
- package/src/drawing/DrawingTarget.scss +11 -0
- package/src/drawing/DrawingTarget.tsx +100 -0
- package/src/drawing/SVGFilterContext.js +5 -0
- package/src/drawing/actions.ts +48 -0
- package/src/drawing/drawingUtil.ts +106 -0
- package/src/drawing/index.ts +2 -0
- package/src/highlight/HighlightAnnotations.scss +12 -0
- package/src/highlight/HighlightAnnotations.tsx +144 -0
- package/src/highlight/HighlightCanvas.scss +9 -0
- package/src/highlight/HighlightCanvas.tsx +109 -0
- package/src/highlight/HighlightContainer.tsx +64 -0
- package/src/highlight/HighlightCreator.scss +12 -0
- package/src/highlight/HighlightCreatorManager.ts +79 -0
- package/src/highlight/HighlightList.scss +13 -0
- package/src/highlight/HighlightList.tsx +92 -0
- package/src/highlight/HighlightListener.ts +37 -0
- package/src/highlight/HighlightManager.tsx +15 -0
- package/src/highlight/HighlightSvg.scss +7 -0
- package/src/highlight/HighlightSvg.tsx +18 -0
- package/src/highlight/HighlightTarget.scss +5 -0
- package/src/highlight/HighlightTarget.tsx +106 -0
- package/src/highlight/actions.ts +36 -0
- package/src/highlight/highlightUtil.ts +143 -0
- package/src/highlight/index.ts +4 -0
- package/src/image/ImageAnnotator.scss +20 -0
- package/src/image/ImageAnnotator.ts +137 -0
- package/src/polyfill.ts +19 -0
- package/src/popup/PopupContainer.tsx +50 -0
- package/src/popup/PopupLayer.scss +3 -0
- package/src/popup/PopupLayer.tsx +103 -0
- package/src/popup/PopupManager.tsx +15 -0
- package/src/region/RegionAnnotation.scss +4 -0
- package/src/region/RegionAnnotation.tsx +15 -16
- package/src/region/RegionAnnotations.scss +1 -15
- package/src/region/RegionAnnotations.tsx +14 -115
- package/src/region/RegionAnnotationsContainer.tsx +24 -0
- package/src/region/RegionCreation.scss +17 -0
- package/src/region/RegionCreation.tsx +79 -0
- package/src/region/RegionCreationContainer.tsx +43 -0
- package/src/region/RegionCreationManager.tsx +15 -0
- package/src/region/RegionCreator.scss +9 -2
- package/src/region/RegionCreator.tsx +50 -71
- package/src/region/RegionList.tsx +8 -12
- package/src/region/RegionManager.tsx +7 -39
- package/src/region/RegionRect.scss +41 -9
- package/src/region/RegionRect.tsx +9 -3
- package/src/region/index.ts +1 -0
- package/src/region/regionUtil.ts +1 -8
- package/src/region/transformUtil.ts +28 -0
- package/src/store/annotations/actions.ts +1 -0
- package/src/store/annotations/reducer.ts +9 -2
- package/src/store/annotations/selectors.ts +1 -0
- package/src/store/annotations/types.ts +1 -0
- package/src/store/common/actions.ts +1 -0
- package/src/store/common/reducer.ts +16 -10
- package/src/store/common/selectors.ts +1 -0
- package/src/store/common/types.ts +3 -0
- package/src/store/createRootReducer.ts +4 -0
- package/src/store/creator/actions.ts +2 -0
- package/src/store/creator/reducer.ts +22 -3
- package/src/store/creator/selectors.ts +9 -1
- package/src/store/creator/types.ts +18 -3
- package/src/store/drawing/actions.ts +10 -0
- package/src/store/drawing/index.ts +4 -0
- package/src/store/drawing/reducer.ts +50 -0
- package/src/store/drawing/selectors.ts +9 -0
- package/src/store/drawing/types.ts +7 -0
- package/src/store/eventing/active.ts +4 -2
- package/src/store/eventing/fetch.ts +8 -0
- package/src/store/eventing/init.ts +14 -0
- package/src/store/eventing/middleware.ts +21 -3
- package/src/store/eventing/mode.ts +7 -0
- package/src/store/eventing/staged.ts +56 -0
- package/src/store/eventing/status.ts +19 -0
- package/src/store/highlight/actions.ts +106 -0
- package/src/store/highlight/index.ts +4 -0
- package/src/store/highlight/reducer.ts +37 -0
- package/src/store/highlight/selectors.ts +12 -0
- package/src/store/highlight/types.ts +14 -0
- package/src/store/index.ts +2 -0
- package/src/store/options/actions.ts +2 -0
- package/src/store/options/reducer.ts +17 -1
- package/src/store/options/selectors.ts +8 -0
- package/src/store/options/types.ts +5 -0
- package/src/store/types.ts +5 -1
- package/src/store/users/actions.ts +3 -2
- package/src/utils/resin.ts +22 -0
- package/src/utils/rotate.ts +131 -0
- package/src/utils/scroll.ts +45 -0
- package/src/utils/util.ts +3 -0
- package/src/region/RegionContainer.tsx +0 -49
- package/src/utils/polyfill.js +0 -161
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Unsubscribe } from 'redux';
|
|
2
|
+
import BaseAnnotator, { Options } from '../common/BaseAnnotator';
|
|
3
|
+
import PopupManager from '../popup/PopupManager';
|
|
4
|
+
import { centerDrawing, DrawingManager, isDrawing } from '../drawing';
|
|
5
|
+
import { centerRegion, isRegion, RegionCreationManager, RegionManager } from '../region';
|
|
6
|
+
import { CreatorStatus, getCreatorStatus } from '../store/creator';
|
|
7
|
+
import { getAnnotation, getFileId, getIsCurrentFileVersion, getRotation } from '../store';
|
|
8
|
+
import { getRotatedPosition } from '../utils/rotate';
|
|
9
|
+
import { Manager } from '../common/BaseManager';
|
|
10
|
+
import { scrollToLocation } from '../utils/scroll';
|
|
11
|
+
import './ImageAnnotator.scss';
|
|
12
|
+
|
|
13
|
+
export const CSS_IS_DRAWING_CLASS = 'ba-is-drawing';
|
|
14
|
+
|
|
15
|
+
export default class ImageAnnotator extends BaseAnnotator {
|
|
16
|
+
annotatedEl?: HTMLElement;
|
|
17
|
+
|
|
18
|
+
managers: Set<Manager> = new Set();
|
|
19
|
+
|
|
20
|
+
storeHandler?: Unsubscribe;
|
|
21
|
+
|
|
22
|
+
constructor(options: Options) {
|
|
23
|
+
super(options);
|
|
24
|
+
|
|
25
|
+
this.storeHandler = this.store.subscribe(this.handleStore);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
destroy(): void {
|
|
29
|
+
if (this.storeHandler) {
|
|
30
|
+
this.storeHandler();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (this.managers) {
|
|
34
|
+
this.managers.forEach(manager => manager.destroy());
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
super.destroy();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
getAnnotatedElement(): HTMLElement | null | undefined {
|
|
41
|
+
return this.containerEl?.querySelector('.bp-image');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getManagers(parentEl: HTMLElement, referenceEl: HTMLElement): Set<Manager> {
|
|
45
|
+
const fileId = getFileId(this.store.getState());
|
|
46
|
+
const isCurrentFileVersion = getIsCurrentFileVersion(this.store.getState());
|
|
47
|
+
const resinTags = { fileid: fileId, iscurrent: isCurrentFileVersion };
|
|
48
|
+
|
|
49
|
+
this.managers.forEach(manager => {
|
|
50
|
+
if (!manager.exists(parentEl)) {
|
|
51
|
+
manager.destroy();
|
|
52
|
+
this.managers.delete(manager);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (this.managers.size === 0) {
|
|
57
|
+
this.managers.add(new PopupManager({ referenceEl, resinTags }));
|
|
58
|
+
this.managers.add(new DrawingManager({ referenceEl, resinTags }));
|
|
59
|
+
this.managers.add(new RegionManager({ referenceEl, resinTags }));
|
|
60
|
+
this.managers.add(new RegionCreationManager({ referenceEl, resinTags }));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return this.managers;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getReference(): HTMLElement | null | undefined {
|
|
67
|
+
return this.annotatedEl?.querySelector('img');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
handleStore = (): void => {
|
|
71
|
+
const referenceEl = this.getReference();
|
|
72
|
+
|
|
73
|
+
if (!referenceEl) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (getCreatorStatus(this.store.getState()) === CreatorStatus.started) {
|
|
78
|
+
referenceEl.classList.add(CSS_IS_DRAWING_CLASS);
|
|
79
|
+
} else {
|
|
80
|
+
referenceEl.classList.remove(CSS_IS_DRAWING_CLASS);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
render(): void {
|
|
85
|
+
const referenceEl = this.getReference();
|
|
86
|
+
const rotation = getRotation(this.store.getState()) || 0;
|
|
87
|
+
|
|
88
|
+
if (!this.annotatedEl || !referenceEl) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
this.getManagers(this.annotatedEl, referenceEl).forEach(manager => {
|
|
93
|
+
manager.style({
|
|
94
|
+
height: `${referenceEl.offsetHeight}px`,
|
|
95
|
+
left: `${referenceEl.offsetLeft}px`,
|
|
96
|
+
top: `${referenceEl.offsetTop}px`,
|
|
97
|
+
transform: `rotate(${rotation}deg)`,
|
|
98
|
+
width: `${referenceEl.offsetWidth}px`,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
manager.render({
|
|
102
|
+
intl: this.intl,
|
|
103
|
+
store: this.store,
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
this.postRender();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
scrollToAnnotation(annotationId: string | null): void {
|
|
111
|
+
if (!annotationId) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const annotation = getAnnotation(this.store.getState(), annotationId);
|
|
116
|
+
const annotationLocation = annotation?.target.location.value ?? 1;
|
|
117
|
+
const referenceEl = this.getReference();
|
|
118
|
+
const rotation = getRotation(this.store.getState()) || 0;
|
|
119
|
+
|
|
120
|
+
if (!annotation || !annotationLocation || !referenceEl || !this.annotatedEl) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let offsets = null;
|
|
125
|
+
if (isRegion(annotation)) {
|
|
126
|
+
offsets = centerRegion(annotation.target.shape);
|
|
127
|
+
} else if (isDrawing(annotation)) {
|
|
128
|
+
offsets = centerDrawing(annotation.target.path_groups);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (offsets) {
|
|
132
|
+
scrollToLocation(this.annotatedEl, referenceEl, {
|
|
133
|
+
offsets: getRotatedPosition(offsets, rotation),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
package/src/polyfill.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Add support for SVGElement.contains in IE11
|
|
2
|
+
if (!SVGElement.prototype.contains) {
|
|
3
|
+
Object.defineProperty(SVGElement.prototype, 'contains', {
|
|
4
|
+
configurable: true,
|
|
5
|
+
enumerable: false,
|
|
6
|
+
writable: true,
|
|
7
|
+
value: function contains(node: Node) {
|
|
8
|
+
let n: Node | null = node;
|
|
9
|
+
do {
|
|
10
|
+
if (this === n) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
n = n && n.parentNode;
|
|
14
|
+
} while (n);
|
|
15
|
+
|
|
16
|
+
return false;
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { connect } from 'react-redux';
|
|
2
|
+
import PopupLayer from './PopupLayer';
|
|
3
|
+
import withProviders from '../common/withProviders';
|
|
4
|
+
import {
|
|
5
|
+
AppState,
|
|
6
|
+
CreatorItem,
|
|
7
|
+
CreatorStatus,
|
|
8
|
+
getAnnotationMode,
|
|
9
|
+
getCreatorMessage,
|
|
10
|
+
getCreatorStagedForLocation,
|
|
11
|
+
getCreatorStatus,
|
|
12
|
+
getIsPromoting,
|
|
13
|
+
Mode,
|
|
14
|
+
resetCreatorAction,
|
|
15
|
+
setMessageAction,
|
|
16
|
+
getCreatorReferenceId,
|
|
17
|
+
} from '../store';
|
|
18
|
+
import { createDrawingAction } from '../drawing/actions';
|
|
19
|
+
import { createHighlightAction } from '../highlight/actions';
|
|
20
|
+
import { createRegionAction } from '../region';
|
|
21
|
+
|
|
22
|
+
export type Props = {
|
|
23
|
+
isPromoting: boolean;
|
|
24
|
+
message: string;
|
|
25
|
+
mode: Mode;
|
|
26
|
+
referenceId: string | null;
|
|
27
|
+
staged: CreatorItem | null;
|
|
28
|
+
status: CreatorStatus;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const mapStateToProps = (state: AppState, { location }: { location: number }): Props => {
|
|
32
|
+
return {
|
|
33
|
+
isPromoting: getIsPromoting(state),
|
|
34
|
+
message: getCreatorMessage(state),
|
|
35
|
+
mode: getAnnotationMode(state),
|
|
36
|
+
referenceId: getCreatorReferenceId(state),
|
|
37
|
+
staged: getCreatorStagedForLocation(state, location),
|
|
38
|
+
status: getCreatorStatus(state),
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const mapDispatchToProps = {
|
|
43
|
+
createDrawing: createDrawingAction,
|
|
44
|
+
createHighlight: createHighlightAction,
|
|
45
|
+
createRegion: createRegionAction,
|
|
46
|
+
resetCreator: resetCreatorAction,
|
|
47
|
+
setMessage: setMessageAction,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default connect(mapStateToProps, mapDispatchToProps)(withProviders(PopupLayer));
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import noop from 'lodash/noop';
|
|
3
|
+
import PopupReply from '../components/Popups/PopupReply';
|
|
4
|
+
import { CreateArg as DrawingCreateArg } from '../drawing/actions';
|
|
5
|
+
import { CreateArg as HighlightCreateArg } from '../highlight/actions';
|
|
6
|
+
import { CreateArg as RegionCreateArg } from '../region/actions';
|
|
7
|
+
import {
|
|
8
|
+
CreatorItem,
|
|
9
|
+
CreatorStatus,
|
|
10
|
+
isCreatorStagedDrawing,
|
|
11
|
+
isCreatorStagedHighlight,
|
|
12
|
+
isCreatorStagedRegion,
|
|
13
|
+
Mode,
|
|
14
|
+
} from '../store';
|
|
15
|
+
import { PopupReference } from '../components/Popups/Popper';
|
|
16
|
+
import './PopupLayer.scss';
|
|
17
|
+
|
|
18
|
+
export type Props = {
|
|
19
|
+
createDrawing?: (arg: DrawingCreateArg) => void;
|
|
20
|
+
createHighlight?: (arg: HighlightCreateArg) => void;
|
|
21
|
+
createRegion?: (arg: RegionCreateArg) => void;
|
|
22
|
+
isPromoting: boolean;
|
|
23
|
+
location: number;
|
|
24
|
+
message: string;
|
|
25
|
+
mode: Mode;
|
|
26
|
+
referenceId: string | null;
|
|
27
|
+
resetCreator: () => void;
|
|
28
|
+
setMessage: (message: string) => void;
|
|
29
|
+
staged?: CreatorItem | null;
|
|
30
|
+
status: CreatorStatus;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const modeStagedMap: { [M in Mode]?: Function } = {
|
|
34
|
+
[Mode.DRAWING]: isCreatorStagedDrawing,
|
|
35
|
+
[Mode.HIGHLIGHT]: isCreatorStagedHighlight,
|
|
36
|
+
[Mode.REGION]: isCreatorStagedRegion,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const PopupLayer = (props: Props): JSX.Element | null => {
|
|
40
|
+
const {
|
|
41
|
+
createDrawing = noop,
|
|
42
|
+
createHighlight = noop,
|
|
43
|
+
createRegion = noop,
|
|
44
|
+
isPromoting = false,
|
|
45
|
+
message,
|
|
46
|
+
mode,
|
|
47
|
+
referenceId,
|
|
48
|
+
resetCreator,
|
|
49
|
+
setMessage,
|
|
50
|
+
staged,
|
|
51
|
+
status,
|
|
52
|
+
} = props;
|
|
53
|
+
|
|
54
|
+
const [reference, setReference] = React.useState<PopupReference | null>(null);
|
|
55
|
+
const canCreate = (modeStagedMap[mode] || noop)(staged) || isPromoting;
|
|
56
|
+
const canReply = status !== CreatorStatus.started && status !== CreatorStatus.init;
|
|
57
|
+
const isPending = status === CreatorStatus.pending;
|
|
58
|
+
|
|
59
|
+
const handleCancel = (): void => {
|
|
60
|
+
resetCreator();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const handleChange = (text = ''): void => {
|
|
64
|
+
setMessage(text);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const handleSubmit = (): void => {
|
|
68
|
+
if (!staged) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (isCreatorStagedHighlight(staged)) {
|
|
73
|
+
createHighlight({ ...staged, message });
|
|
74
|
+
} else if (isCreatorStagedRegion(staged)) {
|
|
75
|
+
createRegion({ ...staged, message });
|
|
76
|
+
} else if (isCreatorStagedDrawing(staged)) {
|
|
77
|
+
createDrawing({ ...staged, message });
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
React.useEffect(() => {
|
|
82
|
+
setReference(referenceId ? document.querySelector(`[data-ba-reference-id="${referenceId}"]`) : null);
|
|
83
|
+
}, [referenceId]);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<>
|
|
87
|
+
{canCreate && canReply && reference && staged && (
|
|
88
|
+
<div className="ba-PopupLayer-popup">
|
|
89
|
+
<PopupReply
|
|
90
|
+
isPending={isPending}
|
|
91
|
+
onCancel={handleCancel}
|
|
92
|
+
onChange={handleChange}
|
|
93
|
+
onSubmit={handleSubmit}
|
|
94
|
+
reference={reference}
|
|
95
|
+
value={message}
|
|
96
|
+
/>
|
|
97
|
+
</div>
|
|
98
|
+
)}
|
|
99
|
+
</>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export default PopupLayer;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as ReactDOM from 'react-dom';
|
|
3
|
+
import BaseManager, { Props } from '../common/BaseManager';
|
|
4
|
+
import PopupContainer from './PopupContainer';
|
|
5
|
+
|
|
6
|
+
export default class PopupManager extends BaseManager {
|
|
7
|
+
decorate(): void {
|
|
8
|
+
this.reactEl.classList.add('ba-Layer--popup');
|
|
9
|
+
this.reactEl.dataset.testid = 'ba-Layer--popup';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
render(props: Props): void {
|
|
13
|
+
ReactDOM.render(<PopupContainer location={this.location} {...props} />, this.reactEl);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import noop from 'lodash/noop';
|
|
4
|
-
import {
|
|
4
|
+
import { MOUSE_PRIMARY } from '../constants';
|
|
5
|
+
import { Shape } from '../@types';
|
|
6
|
+
import { styleShape } from './regionUtil';
|
|
5
7
|
import './RegionAnnotation.scss';
|
|
6
8
|
|
|
7
9
|
type Props = {
|
|
@@ -17,31 +19,28 @@ export type RegionAnnotationRef = HTMLButtonElement;
|
|
|
17
19
|
export const RegionAnnotation = (props: Props, ref: React.Ref<RegionAnnotationRef>): JSX.Element => {
|
|
18
20
|
const { annotationId, className, isActive, onSelect = noop, shape } = props;
|
|
19
21
|
|
|
20
|
-
const
|
|
21
|
-
event.preventDefault();
|
|
22
|
-
event.stopPropagation();
|
|
23
|
-
event.nativeEvent.stopImmediatePropagation(); // Prevents document event handlers from executing
|
|
24
|
-
};
|
|
25
|
-
const handleBlur = (event: React.SyntheticEvent): void => {
|
|
26
|
-
cancelEvent(event);
|
|
27
|
-
};
|
|
28
|
-
const handleClick = (event: React.MouseEvent): void => {
|
|
29
|
-
cancelEvent(event);
|
|
22
|
+
const handleFocus = (): void => {
|
|
30
23
|
onSelect(annotationId);
|
|
31
24
|
};
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
25
|
+
const handleMouseDown = (event: React.MouseEvent<RegionAnnotationRef>): void => {
|
|
26
|
+
if (event.buttons !== MOUSE_PRIMARY) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
event.preventDefault(); // Prevents focus from leaving the button immediately in some browsers
|
|
31
|
+
event.stopPropagation(); // Prevents document-level deselect event handlers from executing
|
|
32
|
+
event.currentTarget.focus(); // Buttons do not receive focus in Firefox and Safari on MacOS; triggers handleFocus
|
|
35
33
|
};
|
|
36
34
|
|
|
37
35
|
return (
|
|
38
36
|
<button
|
|
39
37
|
ref={ref}
|
|
40
38
|
className={classNames('ba-RegionAnnotation', className, { 'is-active': isActive })}
|
|
39
|
+
data-resin-itemid={annotationId}
|
|
40
|
+
data-resin-target="highlightRegion"
|
|
41
41
|
data-testid={`ba-AnnotationTarget-${annotationId}`}
|
|
42
|
-
onBlur={handleBlur}
|
|
43
|
-
onClick={handleClick}
|
|
44
42
|
onFocus={handleFocus}
|
|
43
|
+
onMouseDown={handleMouseDown}
|
|
45
44
|
style={styleShape(shape)}
|
|
46
45
|
type="button"
|
|
47
46
|
/>
|
|
@@ -1,19 +1,9 @@
|
|
|
1
|
-
.ba-RegionAnnotations-
|
|
2
|
-
.ba-RegionAnnotations-list,
|
|
3
|
-
.ba-RegionAnnotations-target {
|
|
1
|
+
.ba-RegionAnnotations-list {
|
|
4
2
|
position: absolute;
|
|
5
3
|
top: 0;
|
|
6
4
|
left: 0;
|
|
7
5
|
width: 100%;
|
|
8
6
|
height: 100%;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
.ba-RegionAnnotations-creator {
|
|
12
|
-
pointer-events: auto;
|
|
13
|
-
touch-action: none;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.ba-RegionAnnotations-list {
|
|
17
7
|
pointer-events: none;
|
|
18
8
|
|
|
19
9
|
&.is-listening {
|
|
@@ -22,7 +12,3 @@
|
|
|
22
12
|
}
|
|
23
13
|
}
|
|
24
14
|
}
|
|
25
|
-
|
|
26
|
-
.ba-RegionAnnotations-popup {
|
|
27
|
-
pointer-events: auto; // Swallow all pointer events that occur within the popup
|
|
28
|
-
}
|
|
@@ -1,130 +1,29 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import PopupReply from '../components/Popups/PopupReply';
|
|
3
|
-
import RegionCreator from './RegionCreator';
|
|
4
2
|
import RegionList from './RegionList';
|
|
5
|
-
import
|
|
6
|
-
import { AnnotationRegion, Rect } from '../@types';
|
|
7
|
-
import { CreateArg } from './actions';
|
|
8
|
-
import { CreatorItem, CreatorStatus } from '../store/creator';
|
|
3
|
+
import { AnnotationRegion } from '../@types';
|
|
9
4
|
import './RegionAnnotations.scss';
|
|
10
5
|
|
|
11
6
|
type Props = {
|
|
12
7
|
activeAnnotationId: string | null;
|
|
13
8
|
annotations: AnnotationRegion[];
|
|
14
|
-
createRegion: (arg: CreateArg) => void;
|
|
15
|
-
isCreating: boolean;
|
|
16
|
-
message: string;
|
|
17
|
-
page: number;
|
|
18
9
|
setActiveAnnotationId: (annotationId: string | null) => void;
|
|
19
|
-
setMessage: (message: string) => void;
|
|
20
|
-
setStaged: (staged: CreatorItem | null) => void;
|
|
21
|
-
setStatus: (status: CreatorStatus) => void;
|
|
22
|
-
staged?: CreatorItem | null;
|
|
23
|
-
status: CreatorStatus;
|
|
24
10
|
};
|
|
25
11
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export default class RegionAnnotations extends React.PureComponent<Props, State> {
|
|
31
|
-
static defaultProps = {
|
|
32
|
-
annotations: [],
|
|
33
|
-
isCreating: false,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
state: State = {};
|
|
37
|
-
|
|
38
|
-
handleAnnotationActive = (annotationId: string | null): void => {
|
|
39
|
-
const { setActiveAnnotationId } = this.props;
|
|
12
|
+
const RegionAnnotations = (props: Props): JSX.Element => {
|
|
13
|
+
const { activeAnnotationId, annotations, setActiveAnnotationId } = props;
|
|
40
14
|
|
|
15
|
+
const handleAnnotationActive = (annotationId: string | null): void => {
|
|
41
16
|
setActiveAnnotationId(annotationId);
|
|
42
17
|
};
|
|
43
18
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
setMessage(text || '');
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
handleStart = (): void => {
|
|
57
|
-
const { setStaged, setStatus } = this.props;
|
|
58
|
-
setStaged(null);
|
|
59
|
-
setStatus(CreatorStatus.init);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
handleStop = (shape: Rect): void => {
|
|
63
|
-
const { page, setStaged, setStatus } = this.props;
|
|
64
|
-
setStaged({ location: page, shape });
|
|
65
|
-
setStatus(CreatorStatus.staged);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
handleSubmit = (): void => {
|
|
69
|
-
const { createRegion, message, staged } = this.props;
|
|
70
|
-
|
|
71
|
-
if (!staged) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
createRegion({ ...staged, message });
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
setRectRef = (rectRef: RegionRectRef): void => {
|
|
79
|
-
this.setState({ rectRef });
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
render(): JSX.Element {
|
|
83
|
-
const { activeAnnotationId, annotations, isCreating, message, staged, status } = this.props;
|
|
84
|
-
const { rectRef } = this.state;
|
|
85
|
-
const canReply = status !== CreatorStatus.init;
|
|
86
|
-
const isPending = status === CreatorStatus.pending;
|
|
87
|
-
|
|
88
|
-
return (
|
|
89
|
-
<>
|
|
90
|
-
{/* Layer 1: Saved annotations */}
|
|
91
|
-
<RegionList
|
|
92
|
-
activeId={activeAnnotationId}
|
|
93
|
-
annotations={annotations}
|
|
94
|
-
className="ba-RegionAnnotations-list"
|
|
95
|
-
onSelect={this.handleAnnotationActive}
|
|
96
|
-
/>
|
|
97
|
-
|
|
98
|
-
{/* Layer 2: Drawn (unsaved) incomplete annotation target, if any */}
|
|
99
|
-
{isCreating && (
|
|
100
|
-
<RegionCreator
|
|
101
|
-
className="ba-RegionAnnotations-creator"
|
|
102
|
-
onStart={this.handleStart}
|
|
103
|
-
onStop={this.handleStop}
|
|
104
|
-
/>
|
|
105
|
-
)}
|
|
106
|
-
|
|
107
|
-
{/* Layer 3a: Staged (unsaved) annotation target, if any */}
|
|
108
|
-
{isCreating && staged && (
|
|
109
|
-
<div className="ba-RegionAnnotations-target">
|
|
110
|
-
<RegionRect ref={this.setRectRef} isActive shape={staged.shape} />
|
|
111
|
-
</div>
|
|
112
|
-
)}
|
|
19
|
+
return (
|
|
20
|
+
<RegionList
|
|
21
|
+
activeId={activeAnnotationId}
|
|
22
|
+
annotations={annotations}
|
|
23
|
+
className="ba-RegionAnnotations-list"
|
|
24
|
+
onSelect={handleAnnotationActive}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
113
28
|
|
|
114
|
-
|
|
115
|
-
{isCreating && staged && canReply && rectRef && (
|
|
116
|
-
<div className="ba-RegionAnnotations-popup">
|
|
117
|
-
<PopupReply
|
|
118
|
-
isPending={isPending}
|
|
119
|
-
onCancel={this.handleCancel}
|
|
120
|
-
onChange={this.handleChange}
|
|
121
|
-
onSubmit={this.handleSubmit}
|
|
122
|
-
reference={rectRef}
|
|
123
|
-
value={message}
|
|
124
|
-
/>
|
|
125
|
-
</div>
|
|
126
|
-
)}
|
|
127
|
-
</>
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
29
|
+
export default RegionAnnotations;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { connect } from 'react-redux';
|
|
2
|
+
import RegionAnnotations from './RegionAnnotations';
|
|
3
|
+
import withProviders from '../common/withProviders';
|
|
4
|
+
import { AnnotationRegion } from '../@types';
|
|
5
|
+
import { AppState, getActiveAnnotationId, getAnnotationsForLocation, setActiveAnnotationIdAction } from '../store';
|
|
6
|
+
import { isRegion } from './regionUtil';
|
|
7
|
+
|
|
8
|
+
export type Props = {
|
|
9
|
+
activeAnnotationId: string | null;
|
|
10
|
+
annotations: AnnotationRegion[];
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const mapStateToProps = (state: AppState, { location }: { location: number }): Props => {
|
|
14
|
+
return {
|
|
15
|
+
activeAnnotationId: getActiveAnnotationId(state),
|
|
16
|
+
annotations: getAnnotationsForLocation(state, location).filter(isRegion),
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const mapDispatchToProps = {
|
|
21
|
+
setActiveAnnotationId: setActiveAnnotationIdAction,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default connect(mapStateToProps, mapDispatchToProps)(withProviders(RegionAnnotations));
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.ba-RegionCreation-creator,
|
|
2
|
+
.ba-RegionCreation-target {
|
|
3
|
+
position: absolute;
|
|
4
|
+
top: 0;
|
|
5
|
+
left: 0;
|
|
6
|
+
width: 100%;
|
|
7
|
+
height: 100%;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.ba-RegionCreation-creator {
|
|
11
|
+
pointer-events: auto;
|
|
12
|
+
touch-action: none;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.ba-RegionCreation-target {
|
|
16
|
+
pointer-events: none;
|
|
17
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import RegionCreator from './RegionCreator';
|
|
3
|
+
import RegionRect, { RegionRectRef } from './RegionRect';
|
|
4
|
+
import { Rect } from '../@types';
|
|
5
|
+
import { CreatorItemRegion, CreatorStatus } from '../store/creator';
|
|
6
|
+
import './RegionCreation.scss';
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
isCreating: boolean;
|
|
10
|
+
isRotated: boolean;
|
|
11
|
+
location: number;
|
|
12
|
+
resetCreator: () => void;
|
|
13
|
+
setReferenceId: (uuid: string) => void;
|
|
14
|
+
setStaged: (staged: CreatorItemRegion | null) => void;
|
|
15
|
+
setStatus: (status: CreatorStatus) => void;
|
|
16
|
+
staged?: CreatorItemRegion | null;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type State = {
|
|
20
|
+
rectRef?: RegionRectRef;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default class RegionCreation extends React.PureComponent<Props, State> {
|
|
24
|
+
static defaultProps = {
|
|
25
|
+
isCreating: false,
|
|
26
|
+
isRotated: false,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
state: State = {};
|
|
30
|
+
|
|
31
|
+
handleAbort = (): void => {
|
|
32
|
+
const { resetCreator } = this.props;
|
|
33
|
+
resetCreator();
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
handleStart = (): void => {
|
|
37
|
+
const { setStaged, setStatus } = this.props;
|
|
38
|
+
setStaged(null);
|
|
39
|
+
setStatus(CreatorStatus.started);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
handleStagedMount = (uuid: string): void => {
|
|
43
|
+
const { setReferenceId } = this.props;
|
|
44
|
+
setReferenceId(uuid);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
handleStop = (shape: Rect): void => {
|
|
48
|
+
const { location, setStaged, setStatus } = this.props;
|
|
49
|
+
setStaged({ location, shape });
|
|
50
|
+
setStatus(CreatorStatus.staged);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
render(): JSX.Element | null {
|
|
54
|
+
const { isCreating, isRotated, staged } = this.props;
|
|
55
|
+
|
|
56
|
+
if (isRotated) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<>
|
|
62
|
+
{isCreating && (
|
|
63
|
+
<RegionCreator
|
|
64
|
+
className="ba-RegionCreation-creator"
|
|
65
|
+
onAbort={this.handleAbort}
|
|
66
|
+
onStart={this.handleStart}
|
|
67
|
+
onStop={this.handleStop}
|
|
68
|
+
/>
|
|
69
|
+
)}
|
|
70
|
+
|
|
71
|
+
{staged && (
|
|
72
|
+
<div className="ba-RegionCreation-target">
|
|
73
|
+
<RegionRect isActive onMount={this.handleStagedMount} shape={staged.shape} />
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
</>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|