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
package/src/BoxAnnotations.ts
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
|
+
import './polyfill';
|
|
1
2
|
import getProp from 'lodash/get';
|
|
3
|
+
import BaseAnnotator from './common/BaseAnnotator';
|
|
4
|
+
import ImageAnnotator from './image/ImageAnnotator';
|
|
2
5
|
import DocumentAnnotator from './document/DocumentAnnotator';
|
|
3
6
|
import { IntlOptions, Permissions, PERMISSIONS, Type } from './@types';
|
|
4
7
|
|
|
5
8
|
type Annotator = {
|
|
6
|
-
CONSTRUCTOR: typeof
|
|
9
|
+
CONSTRUCTOR: typeof BaseAnnotator;
|
|
7
10
|
NAME: string;
|
|
8
11
|
TYPES: string[];
|
|
9
12
|
VIEWERS: string[];
|
|
10
13
|
};
|
|
11
14
|
|
|
12
15
|
type AnnotationsOptions = {
|
|
16
|
+
features: Features;
|
|
13
17
|
intl: IntlOptions;
|
|
14
18
|
};
|
|
15
19
|
|
|
20
|
+
export type Features = {
|
|
21
|
+
[key: string]: boolean;
|
|
22
|
+
};
|
|
23
|
+
|
|
16
24
|
type PreviewOptions = {
|
|
17
25
|
file?: {
|
|
18
26
|
permissions?: Permissions;
|
|
@@ -43,8 +51,14 @@ const ANNOTATORS: Annotator[] = [
|
|
|
43
51
|
{
|
|
44
52
|
CONSTRUCTOR: DocumentAnnotator,
|
|
45
53
|
NAME: 'Document',
|
|
54
|
+
TYPES: [Type.highlight, Type.region],
|
|
55
|
+
VIEWERS: ['AutoCAD', 'Document', 'Presentation'],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
CONSTRUCTOR: ImageAnnotator,
|
|
59
|
+
NAME: 'Image',
|
|
46
60
|
TYPES: [Type.region],
|
|
47
|
-
VIEWERS: ['
|
|
61
|
+
VIEWERS: ['Image'],
|
|
48
62
|
},
|
|
49
63
|
];
|
|
50
64
|
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
src: local('Lato Bold'), local('Lato-Bold'), url('https://cdn01.boxcdn.net/fonts/1.0.2/lato/Lato-Bold.woff2') format('woff2'), url('https://cdn01.boxcdn.net/fonts/1.0.2/lato/Lato-Bold.woff') format('woff');
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
@import '~box-ui-elements/es/styles/reset';
|
|
16
15
|
@import '~box-ui-elements/es/styles/variables';
|
|
17
16
|
|
|
18
17
|
.ba {
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import getProp from 'lodash/get';
|
|
1
2
|
import { IntlShape } from 'react-intl';
|
|
2
3
|
import * as store from '../store';
|
|
3
4
|
import API from '../api';
|
|
5
|
+
import DeselectManager from './DeselectManager';
|
|
4
6
|
import EventEmitter from './EventEmitter';
|
|
5
7
|
import i18n from '../utils/i18n';
|
|
6
8
|
import messages from '../messages';
|
|
7
9
|
import { Event, IntlOptions, LegacyEvent, Permissions } from '../@types';
|
|
10
|
+
import { Features } from '../BoxAnnotations';
|
|
8
11
|
import './BaseAnnotator.scss';
|
|
9
12
|
|
|
10
13
|
export type Container = string | HTMLElement;
|
|
@@ -14,12 +17,15 @@ export type FileOptions = {
|
|
|
14
17
|
annotations?: {
|
|
15
18
|
activeId?: string;
|
|
16
19
|
};
|
|
20
|
+
currentFileVersionId?: string;
|
|
21
|
+
fileVersionId?: string;
|
|
17
22
|
};
|
|
18
23
|
};
|
|
19
24
|
|
|
20
25
|
export type Options = {
|
|
21
26
|
apiHost: string;
|
|
22
27
|
container: Container;
|
|
28
|
+
features?: Features;
|
|
23
29
|
file: {
|
|
24
30
|
id: string;
|
|
25
31
|
file_version: {
|
|
@@ -29,37 +35,69 @@ export type Options = {
|
|
|
29
35
|
};
|
|
30
36
|
fileOptions?: FileOptions;
|
|
31
37
|
hasTouch?: boolean;
|
|
38
|
+
initialColor?: string;
|
|
39
|
+
initialMode?: store.Mode;
|
|
32
40
|
intl: IntlOptions;
|
|
33
41
|
locale?: string;
|
|
34
42
|
token: string;
|
|
35
43
|
};
|
|
36
44
|
|
|
45
|
+
export const CSS_CONTAINER_CLASS = 'ba';
|
|
46
|
+
export const CSS_LOADED_CLASS = 'ba-annotations-loaded';
|
|
47
|
+
export const ANNOTATION_CLASSES: { [M in store.Mode]?: string } = {
|
|
48
|
+
[store.Mode.HIGHLIGHT]: 'ba-is-create--highlight',
|
|
49
|
+
[store.Mode.REGION]: 'ba-is-create--region',
|
|
50
|
+
};
|
|
51
|
+
|
|
37
52
|
export default class BaseAnnotator extends EventEmitter {
|
|
53
|
+
annotatedEl?: HTMLElement | null;
|
|
54
|
+
|
|
55
|
+
containerEl?: HTMLElement | null;
|
|
56
|
+
|
|
38
57
|
container: Container;
|
|
39
58
|
|
|
40
|
-
|
|
59
|
+
deselectManager: DeselectManager | null = null;
|
|
41
60
|
|
|
42
|
-
|
|
61
|
+
features: Features;
|
|
43
62
|
|
|
44
|
-
|
|
63
|
+
intl: IntlShape;
|
|
45
64
|
|
|
46
65
|
store: store.AppStore;
|
|
47
66
|
|
|
48
|
-
constructor({
|
|
67
|
+
constructor({
|
|
68
|
+
apiHost,
|
|
69
|
+
container,
|
|
70
|
+
features = {},
|
|
71
|
+
file,
|
|
72
|
+
fileOptions,
|
|
73
|
+
initialColor,
|
|
74
|
+
initialMode,
|
|
75
|
+
intl,
|
|
76
|
+
token,
|
|
77
|
+
}: Options) {
|
|
49
78
|
super();
|
|
50
79
|
|
|
80
|
+
const fileOptionsValue = fileOptions?.[file.id];
|
|
81
|
+
const currentFileVersionId = fileOptionsValue?.currentFileVersionId;
|
|
82
|
+
const fileOptionsVersionId = fileOptionsValue?.fileVersionId;
|
|
83
|
+
const fileVersionId = file.file_version.id; // This is always the currently-previewed version id, not current version id
|
|
84
|
+
|
|
51
85
|
const initialState = {
|
|
52
86
|
annotations: {
|
|
53
|
-
activeId:
|
|
87
|
+
activeId: fileOptionsValue?.annotations?.activeId ?? null,
|
|
54
88
|
},
|
|
89
|
+
common: { color: initialColor, mode: initialMode },
|
|
55
90
|
options: {
|
|
91
|
+
features,
|
|
56
92
|
fileId: file.id,
|
|
57
|
-
fileVersionId:
|
|
93
|
+
fileVersionId: fileOptionsVersionId ?? fileVersionId,
|
|
94
|
+
isCurrentFileVersion: !fileOptionsVersionId || fileOptionsVersionId === currentFileVersionId,
|
|
58
95
|
permissions: file.permissions,
|
|
59
96
|
},
|
|
60
97
|
};
|
|
61
98
|
|
|
62
99
|
this.container = container;
|
|
100
|
+
this.features = features;
|
|
63
101
|
this.intl = i18n.createIntlProvider(intl);
|
|
64
102
|
this.store = store.createStore(initialState, {
|
|
65
103
|
api: new API({ apiHost, token }),
|
|
@@ -69,6 +107,7 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
69
107
|
this.addListener(LegacyEvent.SCALE, this.handleScale);
|
|
70
108
|
this.addListener(Event.ACTIVE_SET, this.handleSetActive);
|
|
71
109
|
this.addListener(Event.ANNOTATION_REMOVE, this.handleRemove);
|
|
110
|
+
this.addListener(Event.COLOR_SET, this.handleSetColor);
|
|
72
111
|
this.addListener(Event.VISIBLE_SET, this.handleSetVisible);
|
|
73
112
|
|
|
74
113
|
// Load any required data at startup
|
|
@@ -76,26 +115,51 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
76
115
|
}
|
|
77
116
|
|
|
78
117
|
public destroy(): void {
|
|
79
|
-
if (this.
|
|
80
|
-
this.
|
|
118
|
+
if (this.containerEl) {
|
|
119
|
+
this.containerEl.classList.remove(CSS_CONTAINER_CLASS);
|
|
81
120
|
}
|
|
82
121
|
|
|
122
|
+
if (this.annotatedEl) {
|
|
123
|
+
this.annotatedEl.classList.remove(CSS_LOADED_CLASS);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (this.deselectManager) {
|
|
127
|
+
this.deselectManager.destroy();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
this.removeAnnotationClasses();
|
|
131
|
+
|
|
83
132
|
this.removeListener(LegacyEvent.SCALE, this.handleScale);
|
|
84
133
|
this.removeListener(Event.ACTIVE_SET, this.handleSetActive);
|
|
85
134
|
this.removeListener(Event.ANNOTATION_REMOVE, this.handleRemove);
|
|
135
|
+
this.removeListener(Event.COLOR_SET, this.handleSetColor);
|
|
86
136
|
this.removeListener(Event.VISIBLE_SET, this.handleSetVisible);
|
|
87
137
|
}
|
|
88
138
|
|
|
89
|
-
public init(scale
|
|
90
|
-
this.
|
|
91
|
-
this.
|
|
139
|
+
public init(scale = 1, rotation = 0): void {
|
|
140
|
+
this.containerEl = this.getElement(this.container);
|
|
141
|
+
this.annotatedEl = this.getAnnotatedElement();
|
|
92
142
|
|
|
93
|
-
if (!this.
|
|
143
|
+
if (!this.annotatedEl || !this.containerEl) {
|
|
94
144
|
this.emit(LegacyEvent.ERROR, this.intl.formatMessage(messages.annotationsLoadError));
|
|
95
145
|
return;
|
|
96
146
|
}
|
|
97
147
|
|
|
98
|
-
|
|
148
|
+
// Add classes to the parent elements to support CSS scoping
|
|
149
|
+
this.annotatedEl.classList.add(CSS_LOADED_CLASS);
|
|
150
|
+
this.containerEl.classList.add(CSS_CONTAINER_CLASS);
|
|
151
|
+
|
|
152
|
+
// Defer to the child class to render annotations
|
|
153
|
+
this.render();
|
|
154
|
+
|
|
155
|
+
// Update the store now that annotations have been rendered
|
|
156
|
+
this.store.dispatch(store.setRotationAction(rotation));
|
|
157
|
+
this.store.dispatch(store.setScaleAction(scale));
|
|
158
|
+
this.store.dispatch(store.setIsInitialized());
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
public isFeatureEnabled(feature = ''): boolean {
|
|
162
|
+
return getProp(this.features, feature, false);
|
|
99
163
|
}
|
|
100
164
|
|
|
101
165
|
public removeAnnotation = (annotationId: string): void => {
|
|
@@ -111,14 +175,19 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
111
175
|
this.store.dispatch(store.setActiveAnnotationIdAction(annotationId));
|
|
112
176
|
}
|
|
113
177
|
|
|
178
|
+
public setColor(color: string): void {
|
|
179
|
+
this.store.dispatch(store.setColorAction(color));
|
|
180
|
+
}
|
|
181
|
+
|
|
114
182
|
public setVisibility(visibility: boolean): void {
|
|
115
|
-
if (!this.
|
|
183
|
+
if (!this.containerEl) {
|
|
116
184
|
return;
|
|
117
185
|
}
|
|
186
|
+
|
|
118
187
|
if (visibility) {
|
|
119
|
-
this.
|
|
188
|
+
this.containerEl.classList.remove('is-hidden');
|
|
120
189
|
} else {
|
|
121
|
-
this.
|
|
190
|
+
this.containerEl.classList.add('is-hidden');
|
|
122
191
|
}
|
|
123
192
|
}
|
|
124
193
|
|
|
@@ -126,6 +195,10 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
126
195
|
this.store.dispatch(store.toggleAnnotationModeAction(mode));
|
|
127
196
|
}
|
|
128
197
|
|
|
198
|
+
protected getAnnotatedElement(): HTMLElement | null | undefined {
|
|
199
|
+
return undefined; // Must be implemented in child class
|
|
200
|
+
}
|
|
201
|
+
|
|
129
202
|
protected getElement(selector: HTMLElement | string): HTMLElement | null {
|
|
130
203
|
return typeof selector === 'string' ? document.querySelector(selector) : selector;
|
|
131
204
|
}
|
|
@@ -134,8 +207,8 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
134
207
|
this.removeAnnotation(annotationId);
|
|
135
208
|
};
|
|
136
209
|
|
|
137
|
-
protected handleScale = ({ scale }: { scale: number }): void => {
|
|
138
|
-
this.init(scale);
|
|
210
|
+
protected handleScale = ({ rotationAngle, scale }: { rotationAngle: number; scale: number }): void => {
|
|
211
|
+
this.init(scale, rotationAngle);
|
|
139
212
|
};
|
|
140
213
|
|
|
141
214
|
protected handleSetActive = (annotationId: string | null): void => {
|
|
@@ -146,9 +219,42 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
146
219
|
this.setVisibility(visibility);
|
|
147
220
|
};
|
|
148
221
|
|
|
222
|
+
protected handleSetColor = (color: string): void => {
|
|
223
|
+
this.setColor(color);
|
|
224
|
+
};
|
|
225
|
+
|
|
149
226
|
protected hydrate(): void {
|
|
150
227
|
// Redux dispatch method signature doesn't seem to like async actions
|
|
151
228
|
this.store.dispatch<any>(store.fetchAnnotationsAction()); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
152
|
-
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
protected removeAnnotationClasses = (): void => {
|
|
232
|
+
if (!this.annotatedEl) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const annotatedElement = this.annotatedEl;
|
|
237
|
+
Object.values(ANNOTATION_CLASSES).forEach(className => {
|
|
238
|
+
if (className) {
|
|
239
|
+
annotatedElement.classList.remove(className);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
protected render(): void {
|
|
245
|
+
// Must be implemented in child class
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
public postRender(): void {
|
|
249
|
+
if (!this.annotatedEl) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (this.deselectManager) {
|
|
254
|
+
this.deselectManager.destroy();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
this.deselectManager = new DeselectManager({ referenceEl: this.annotatedEl });
|
|
258
|
+
this.deselectManager.render({ store: this.store });
|
|
153
259
|
}
|
|
154
260
|
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import * as ReactDOM from 'react-dom';
|
|
1
2
|
import { IntlShape } from 'react-intl';
|
|
2
3
|
import { Store } from 'redux';
|
|
4
|
+
import { applyResinTags } from '../utils/resin';
|
|
5
|
+
|
|
6
|
+
export type ResinTags = Record<string, unknown>;
|
|
3
7
|
|
|
4
8
|
export type Options = {
|
|
5
|
-
|
|
6
|
-
pageEl: HTMLElement;
|
|
9
|
+
location?: number;
|
|
7
10
|
referenceEl: HTMLElement;
|
|
11
|
+
resinTags?: ResinTags;
|
|
8
12
|
};
|
|
9
13
|
|
|
10
14
|
export type Props = {
|
|
@@ -12,8 +16,67 @@ export type Props = {
|
|
|
12
16
|
store: Store;
|
|
13
17
|
};
|
|
14
18
|
|
|
15
|
-
export
|
|
19
|
+
export interface Manager {
|
|
16
20
|
destroy(): void;
|
|
17
|
-
exists(
|
|
21
|
+
exists(parentEl: HTMLElement): boolean;
|
|
18
22
|
render(props: Props): void;
|
|
23
|
+
style(styles: Partial<CSSStyleDeclaration>): CSSStyleDeclaration;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default class BaseManager implements Manager {
|
|
27
|
+
location: number;
|
|
28
|
+
|
|
29
|
+
reactEl: HTMLElement;
|
|
30
|
+
|
|
31
|
+
constructor({ location = 1, referenceEl, resinTags = {} }: Options) {
|
|
32
|
+
this.location = location;
|
|
33
|
+
this.reactEl = this.insert(referenceEl, {
|
|
34
|
+
...resinTags,
|
|
35
|
+
feature: 'annotations',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
this.decorate();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
decorate(): void {
|
|
42
|
+
throw new Error('Method not implemented.');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
destroy(): void {
|
|
46
|
+
ReactDOM.unmountComponentAtNode(this.reactEl);
|
|
47
|
+
|
|
48
|
+
const parentEl = this.reactEl.parentNode;
|
|
49
|
+
if (parentEl) {
|
|
50
|
+
parentEl.removeChild(this.reactEl);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
exists(parentEl: HTMLElement): boolean {
|
|
55
|
+
return parentEl.contains(this.reactEl);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
insert(referenceEl: HTMLElement, resinTags: ResinTags = {}): HTMLElement {
|
|
59
|
+
// Find the nearest applicable reference and document elements
|
|
60
|
+
const documentEl = referenceEl.ownerDocument || document;
|
|
61
|
+
const parentEl = referenceEl.parentNode || documentEl;
|
|
62
|
+
|
|
63
|
+
// Construct a layer element where we can inject a root React component
|
|
64
|
+
const rootLayerEl = documentEl.createElement('div');
|
|
65
|
+
rootLayerEl.classList.add('ba-Layer');
|
|
66
|
+
|
|
67
|
+
// Apply any resin tags
|
|
68
|
+
applyResinTags(rootLayerEl, resinTags);
|
|
69
|
+
|
|
70
|
+
// Insert the new layer element immediately after the reference element
|
|
71
|
+
return parentEl.insertBefore(rootLayerEl, referenceEl.nextSibling);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
75
|
+
render(props: Props): void {
|
|
76
|
+
throw new Error('Method not implemented.');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
style(styles: Partial<CSSStyleDeclaration>): CSSStyleDeclaration {
|
|
80
|
+
return Object.assign(this.reactEl.style, styles);
|
|
81
|
+
}
|
|
19
82
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useDispatch } from 'react-redux';
|
|
3
|
+
import { setActiveAnnotationIdAction } from '../store/annotations';
|
|
4
|
+
|
|
5
|
+
export default function DeselectListener(): null {
|
|
6
|
+
const dispatch = useDispatch();
|
|
7
|
+
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
const handleMouseDown = (): void => {
|
|
10
|
+
dispatch(setActiveAnnotationIdAction(null));
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
document.addEventListener('mousedown', handleMouseDown);
|
|
14
|
+
|
|
15
|
+
return () => {
|
|
16
|
+
document.removeEventListener('mousedown', handleMouseDown);
|
|
17
|
+
};
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as ReactDOM from 'react-dom';
|
|
3
|
+
import { Provider } from 'react-redux';
|
|
4
|
+
import { Store } from 'redux';
|
|
5
|
+
import BaseManager from './BaseManager';
|
|
6
|
+
import DeselectListener from './DeselectListener';
|
|
7
|
+
|
|
8
|
+
export type Options = {
|
|
9
|
+
referenceEl: HTMLElement;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type Props = {
|
|
13
|
+
store: Store;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default class DeselectManager extends BaseManager {
|
|
17
|
+
decorate(): void {
|
|
18
|
+
this.reactEl.classList.add('ba-Layer--deselect');
|
|
19
|
+
this.reactEl.dataset.testid = 'ba-Layer--deselect';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
render({ store }: Props): void {
|
|
23
|
+
ReactDOM.render(
|
|
24
|
+
<Provider store={store}>
|
|
25
|
+
<DeselectListener />
|
|
26
|
+
</Provider>,
|
|
27
|
+
this.reactEl,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as ReactRedux from 'react-redux';
|
|
2
|
+
import { getCreatorStatus, getIsSelecting, CreatorStatus } from '../store';
|
|
3
|
+
|
|
4
|
+
// Returns whether rendered annotations should be interactive
|
|
5
|
+
export default function useIsListInteractive(): boolean {
|
|
6
|
+
const status = ReactRedux.useSelector(getCreatorStatus);
|
|
7
|
+
const isSelecting = ReactRedux.useSelector(getIsSelecting);
|
|
8
|
+
|
|
9
|
+
return status === CreatorStatus.init && !isSelecting;
|
|
10
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import * as uuid from 'uuid';
|
|
3
|
+
|
|
4
|
+
// Returns a generated uuid, then calls the provided callback via an effect
|
|
5
|
+
export default function useMountId(callback: (uuid: string) => void): string {
|
|
6
|
+
const uuidRef = React.useRef<string>(uuid.v4());
|
|
7
|
+
|
|
8
|
+
// The callback is only invoked after the parent component has rendered
|
|
9
|
+
React.useEffect(() => {
|
|
10
|
+
callback(uuidRef.current);
|
|
11
|
+
}, [callback]);
|
|
12
|
+
|
|
13
|
+
return uuidRef.current;
|
|
14
|
+
}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
3
|
// Utilizes the useEffect hook to handle when an event is fired on the document, but outside of the specified element
|
|
4
|
-
export default function useOutsideEvent(
|
|
4
|
+
export default function useOutsideEvent(
|
|
5
|
+
type: string,
|
|
6
|
+
ref?: React.RefObject<Element> | Element | null,
|
|
7
|
+
callback?: EventListener,
|
|
8
|
+
): void {
|
|
5
9
|
React.useEffect(() => {
|
|
6
|
-
function handleEvent(event:
|
|
7
|
-
const
|
|
10
|
+
function handleEvent<T extends Event>(event: T): void {
|
|
11
|
+
const element = ref instanceof Element ? ref : ref?.current;
|
|
12
|
+
const containsEventTarget = !!element?.contains(event.target as Node);
|
|
8
13
|
|
|
9
14
|
if (callback && !containsEventTarget) {
|
|
10
|
-
callback();
|
|
15
|
+
callback(event);
|
|
11
16
|
}
|
|
12
17
|
}
|
|
13
18
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
// Returns the last provided value, then caches the new value via an effect
|
|
4
|
+
export default function usePrevious<T>(value: T): T | undefined {
|
|
5
|
+
const ref = React.useRef<T>();
|
|
6
|
+
|
|
7
|
+
// The useEffect callback is only invoked after the parent component has rendered
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
ref.current = value;
|
|
10
|
+
}, [value]);
|
|
11
|
+
|
|
12
|
+
return ref.current;
|
|
13
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import noop from 'lodash/noop';
|
|
3
|
-
import
|
|
3
|
+
import scrollIntoView from 'scroll-into-view-if-needed';
|
|
4
|
+
import ItemRow, { ItemRowRef } from './ItemRow';
|
|
4
5
|
import './ItemList.scss';
|
|
5
6
|
|
|
6
7
|
export type Props<T extends { id: string }> = {
|
|
7
8
|
activeItemIndex?: number;
|
|
8
|
-
|
|
9
|
+
autoScroll?: boolean;
|
|
10
|
+
itemRowAs?: React.ElementType;
|
|
9
11
|
items: T[];
|
|
10
12
|
onActivate?: (index: number) => void;
|
|
11
13
|
onSelect: (index: number, event: React.SyntheticEvent) => void;
|
|
@@ -13,32 +15,49 @@ export type Props<T extends { id: string }> = {
|
|
|
13
15
|
|
|
14
16
|
const ItemList = <T extends { id: string }>({
|
|
15
17
|
activeItemIndex = 0,
|
|
16
|
-
|
|
18
|
+
autoScroll = false,
|
|
19
|
+
itemRowAs: ItemRowComponent = ItemRow,
|
|
17
20
|
items,
|
|
18
21
|
onActivate = noop,
|
|
19
22
|
onSelect,
|
|
20
23
|
...rest
|
|
21
|
-
}: Props<T>): JSX.Element =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
24
|
+
}: Props<T>): JSX.Element => {
|
|
25
|
+
const activeRef = React.useCallback(
|
|
26
|
+
(activeEl: ItemRowRef) => {
|
|
27
|
+
if (!autoScroll || !activeEl) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
scrollIntoView(activeEl, {
|
|
32
|
+
scrollMode: 'if-needed',
|
|
33
|
+
block: 'nearest',
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
[autoScroll],
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<ul className="ba-ItemList" data-testid="ba-ItemList" role="listbox" {...rest}>
|
|
41
|
+
{items.map((item, index) => (
|
|
42
|
+
<ItemRowComponent
|
|
43
|
+
key={item.id}
|
|
44
|
+
ref={index === activeItemIndex ? activeRef : null}
|
|
45
|
+
className="ba-ItemList-row"
|
|
46
|
+
isActive={index === activeItemIndex}
|
|
47
|
+
item={item}
|
|
48
|
+
onClick={(event: React.SyntheticEvent) => {
|
|
49
|
+
onSelect(index, event);
|
|
50
|
+
}}
|
|
51
|
+
onMouseDown={(event: React.SyntheticEvent) => {
|
|
52
|
+
event.preventDefault(); // preventDefault on mousedown so blur doesn't happen before click
|
|
53
|
+
}}
|
|
54
|
+
onMouseEnter={() => {
|
|
55
|
+
onActivate(index);
|
|
56
|
+
}}
|
|
57
|
+
/>
|
|
58
|
+
))}
|
|
59
|
+
</ul>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
43
62
|
|
|
44
63
|
export default ItemList;
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
@import '~box-ui-elements/es/styles/variables';
|
|
2
2
|
|
|
3
|
-
.
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
// increase specificity to override .bp styles
|
|
4
|
+
.ba {
|
|
5
|
+
.ba-ItemRow {
|
|
6
|
+
padding: 5px 30px 5px 15px;
|
|
7
|
+
|
|
8
|
+
&.is-active {
|
|
9
|
+
background-color: fade-out($bdl-gray, .95);
|
|
10
|
+
cursor: pointer;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.ba-ItemRow-name {
|
|
15
|
+
line-height: 15px;
|
|
16
|
+
}
|
|
6
17
|
|
|
7
|
-
.ba-ItemRow-email {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
18
|
+
.ba-ItemRow-email {
|
|
19
|
+
color: $bdl-gray-62;
|
|
20
|
+
font-size: 11px;
|
|
21
|
+
line-height: 15px;
|
|
22
|
+
}
|
|
11
23
|
}
|