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.
Files changed (208) hide show
  1. package/README.md +7 -57
  2. package/dist/annotations.css +1 -1
  3. package/dist/annotations.js +1 -1
  4. package/i18n/bn-IN.js +6 -0
  5. package/i18n/bn-IN.properties +12 -0
  6. package/i18n/da-DK.js +6 -0
  7. package/i18n/da-DK.properties +12 -0
  8. package/i18n/de-DE.js +6 -0
  9. package/i18n/de-DE.properties +12 -0
  10. package/i18n/en-AU.js +6 -0
  11. package/i18n/en-AU.properties +12 -0
  12. package/i18n/en-CA.js +6 -0
  13. package/i18n/en-CA.properties +12 -0
  14. package/i18n/en-GB.js +6 -0
  15. package/i18n/en-GB.properties +12 -0
  16. package/i18n/en-US.js +6 -0
  17. package/i18n/en-US.properties +12 -0
  18. package/i18n/en-x-pseudo.js +16 -9
  19. package/i18n/en-x-pseudo.properties +23 -9
  20. package/i18n/es-419.js +6 -0
  21. package/i18n/es-419.properties +12 -0
  22. package/i18n/es-ES.js +6 -0
  23. package/i18n/es-ES.properties +12 -0
  24. package/i18n/fi-FI.js +6 -0
  25. package/i18n/fi-FI.properties +12 -0
  26. package/i18n/fr-CA.js +6 -0
  27. package/i18n/fr-CA.properties +12 -0
  28. package/i18n/fr-FR.js +6 -0
  29. package/i18n/fr-FR.properties +12 -0
  30. package/i18n/hi-IN.js +6 -0
  31. package/i18n/hi-IN.properties +12 -0
  32. package/i18n/it-IT.js +6 -0
  33. package/i18n/it-IT.properties +12 -0
  34. package/i18n/ja-JP.js +6 -0
  35. package/i18n/ja-JP.properties +12 -0
  36. package/i18n/json/src/components/Popups/messages.json +1 -1
  37. package/i18n/ko-KR.js +6 -0
  38. package/i18n/ko-KR.properties +12 -0
  39. package/i18n/nb-NO.js +6 -0
  40. package/i18n/nb-NO.properties +12 -0
  41. package/i18n/nl-NL.js +6 -0
  42. package/i18n/nl-NL.properties +12 -0
  43. package/i18n/pl-PL.js +6 -0
  44. package/i18n/pl-PL.properties +12 -0
  45. package/i18n/pt-BR.js +6 -0
  46. package/i18n/pt-BR.properties +12 -0
  47. package/i18n/ru-RU.js +6 -0
  48. package/i18n/ru-RU.properties +12 -0
  49. package/i18n/sv-SE.js +6 -0
  50. package/i18n/sv-SE.properties +12 -0
  51. package/i18n/tr-TR.js +6 -0
  52. package/i18n/tr-TR.properties +12 -0
  53. package/i18n/zh-CN.js +6 -0
  54. package/i18n/zh-CN.properties +12 -0
  55. package/i18n/zh-TW.js +6 -0
  56. package/i18n/zh-TW.properties +12 -0
  57. package/package.json +29 -29
  58. package/src/@types/events.ts +8 -2
  59. package/src/@types/global.ts +1 -0
  60. package/src/@types/model.ts +20 -7
  61. package/src/BoxAnnotations.ts +16 -2
  62. package/src/common/BaseAnnotator.scss +0 -1
  63. package/src/common/BaseAnnotator.ts +125 -19
  64. package/src/common/BaseManager.ts +67 -4
  65. package/src/common/DeselectListener.tsx +21 -0
  66. package/src/common/DeselectManager.tsx +30 -0
  67. package/src/common/useIsListInteractive.ts +10 -0
  68. package/src/common/useMountId.ts +14 -0
  69. package/src/common/useOutsideEvent.ts +9 -4
  70. package/src/common/usePrevious.ts +13 -0
  71. package/src/components/ItemList/ItemList.scss +7 -2
  72. package/src/components/ItemList/ItemList.tsx +45 -26
  73. package/src/components/ItemList/ItemRow.scss +19 -7
  74. package/src/components/ItemList/ItemRow.tsx +33 -12
  75. package/src/components/PointerCapture/PointerCapture.tsx +116 -0
  76. package/src/components/PointerCapture/index.ts +2 -0
  77. package/src/components/Popups/Popper.ts +1 -0
  78. package/src/components/Popups/PopupArrow.scss +91 -0
  79. package/src/components/Popups/PopupBase.scss +12 -81
  80. package/src/components/Popups/PopupBase.tsx +0 -1
  81. package/src/components/Popups/PopupCursor.tsx +1 -1
  82. package/src/components/Popups/PopupDrawingToolbar.scss +76 -0
  83. package/src/components/Popups/PopupDrawingToolbar.tsx +109 -0
  84. package/src/components/Popups/PopupHighlight.scss +39 -0
  85. package/src/components/Popups/PopupHighlight.tsx +97 -0
  86. package/src/components/Popups/PopupHighlightError.scss +23 -0
  87. package/src/components/Popups/PopupHighlightError.tsx +74 -0
  88. package/src/components/Popups/PopupList.scss +1 -4
  89. package/src/components/Popups/PopupList.tsx +2 -2
  90. package/src/components/Popups/PopupReply.tsx +51 -6
  91. package/src/components/Popups/messages.ts +30 -0
  92. package/src/components/ReplyField/ReplyField.scss +6 -1
  93. package/src/components/ReplyField/ReplyField.tsx +43 -42
  94. package/src/components/ReplyField/ReplyFieldContainer.tsx +2 -1
  95. package/src/components/ReplyForm/ReplyButton.tsx +38 -0
  96. package/src/components/ReplyForm/ReplyForm.scss +3 -0
  97. package/src/components/ReplyForm/ReplyForm.tsx +49 -12
  98. package/src/components/ReplyForm/ReplyFormContainer.tsx +6 -4
  99. package/src/constants.js +1 -8
  100. package/src/document/DocumentAnnotator.scss +15 -0
  101. package/src/document/DocumentAnnotator.ts +110 -53
  102. package/src/document/docUtil.ts +61 -0
  103. package/src/drawing/DecoratedDrawingPath.scss +9 -0
  104. package/src/drawing/DecoratedDrawingPath.tsx +43 -0
  105. package/src/drawing/DrawingAnnotations.scss +39 -0
  106. package/src/drawing/DrawingAnnotations.tsx +157 -0
  107. package/src/drawing/DrawingAnnotationsContainer.tsx +66 -0
  108. package/src/drawing/DrawingCreator.scss +9 -0
  109. package/src/drawing/DrawingCreator.tsx +183 -0
  110. package/src/drawing/DrawingCursor.ts +23 -0
  111. package/src/drawing/DrawingList.tsx +60 -0
  112. package/src/drawing/DrawingManager.tsx +15 -0
  113. package/src/drawing/DrawingPath.tsx +17 -0
  114. package/src/drawing/DrawingPathGroup.tsx +52 -0
  115. package/src/drawing/DrawingSVG.tsx +34 -0
  116. package/src/drawing/DrawingSVGGroup.tsx +23 -0
  117. package/src/drawing/DrawingTarget.scss +11 -0
  118. package/src/drawing/DrawingTarget.tsx +100 -0
  119. package/src/drawing/SVGFilterContext.js +5 -0
  120. package/src/drawing/actions.ts +48 -0
  121. package/src/drawing/drawingUtil.ts +106 -0
  122. package/src/drawing/index.ts +2 -0
  123. package/src/highlight/HighlightAnnotations.scss +12 -0
  124. package/src/highlight/HighlightAnnotations.tsx +144 -0
  125. package/src/highlight/HighlightCanvas.scss +9 -0
  126. package/src/highlight/HighlightCanvas.tsx +109 -0
  127. package/src/highlight/HighlightContainer.tsx +64 -0
  128. package/src/highlight/HighlightCreator.scss +12 -0
  129. package/src/highlight/HighlightCreatorManager.ts +79 -0
  130. package/src/highlight/HighlightList.scss +13 -0
  131. package/src/highlight/HighlightList.tsx +92 -0
  132. package/src/highlight/HighlightListener.ts +37 -0
  133. package/src/highlight/HighlightManager.tsx +15 -0
  134. package/src/highlight/HighlightSvg.scss +7 -0
  135. package/src/highlight/HighlightSvg.tsx +18 -0
  136. package/src/highlight/HighlightTarget.scss +5 -0
  137. package/src/highlight/HighlightTarget.tsx +106 -0
  138. package/src/highlight/actions.ts +36 -0
  139. package/src/highlight/highlightUtil.ts +143 -0
  140. package/src/highlight/index.ts +4 -0
  141. package/src/image/ImageAnnotator.scss +20 -0
  142. package/src/image/ImageAnnotator.ts +137 -0
  143. package/src/polyfill.ts +19 -0
  144. package/src/popup/PopupContainer.tsx +50 -0
  145. package/src/popup/PopupLayer.scss +3 -0
  146. package/src/popup/PopupLayer.tsx +103 -0
  147. package/src/popup/PopupManager.tsx +15 -0
  148. package/src/region/RegionAnnotation.scss +4 -0
  149. package/src/region/RegionAnnotation.tsx +15 -16
  150. package/src/region/RegionAnnotations.scss +1 -15
  151. package/src/region/RegionAnnotations.tsx +14 -115
  152. package/src/region/RegionAnnotationsContainer.tsx +24 -0
  153. package/src/region/RegionCreation.scss +17 -0
  154. package/src/region/RegionCreation.tsx +79 -0
  155. package/src/region/RegionCreationContainer.tsx +43 -0
  156. package/src/region/RegionCreationManager.tsx +15 -0
  157. package/src/region/RegionCreator.scss +9 -2
  158. package/src/region/RegionCreator.tsx +50 -71
  159. package/src/region/RegionList.tsx +8 -12
  160. package/src/region/RegionManager.tsx +7 -39
  161. package/src/region/RegionRect.scss +41 -9
  162. package/src/region/RegionRect.tsx +9 -3
  163. package/src/region/index.ts +1 -0
  164. package/src/region/regionUtil.ts +1 -8
  165. package/src/region/transformUtil.ts +28 -0
  166. package/src/store/annotations/actions.ts +1 -0
  167. package/src/store/annotations/reducer.ts +9 -2
  168. package/src/store/annotations/selectors.ts +1 -0
  169. package/src/store/annotations/types.ts +1 -0
  170. package/src/store/common/actions.ts +1 -0
  171. package/src/store/common/reducer.ts +16 -10
  172. package/src/store/common/selectors.ts +1 -0
  173. package/src/store/common/types.ts +3 -0
  174. package/src/store/createRootReducer.ts +4 -0
  175. package/src/store/creator/actions.ts +2 -0
  176. package/src/store/creator/reducer.ts +22 -3
  177. package/src/store/creator/selectors.ts +9 -1
  178. package/src/store/creator/types.ts +18 -3
  179. package/src/store/drawing/actions.ts +10 -0
  180. package/src/store/drawing/index.ts +4 -0
  181. package/src/store/drawing/reducer.ts +50 -0
  182. package/src/store/drawing/selectors.ts +9 -0
  183. package/src/store/drawing/types.ts +7 -0
  184. package/src/store/eventing/active.ts +4 -2
  185. package/src/store/eventing/fetch.ts +8 -0
  186. package/src/store/eventing/init.ts +14 -0
  187. package/src/store/eventing/middleware.ts +21 -3
  188. package/src/store/eventing/mode.ts +7 -0
  189. package/src/store/eventing/staged.ts +56 -0
  190. package/src/store/eventing/status.ts +19 -0
  191. package/src/store/highlight/actions.ts +106 -0
  192. package/src/store/highlight/index.ts +4 -0
  193. package/src/store/highlight/reducer.ts +37 -0
  194. package/src/store/highlight/selectors.ts +12 -0
  195. package/src/store/highlight/types.ts +14 -0
  196. package/src/store/index.ts +2 -0
  197. package/src/store/options/actions.ts +2 -0
  198. package/src/store/options/reducer.ts +17 -1
  199. package/src/store/options/selectors.ts +8 -0
  200. package/src/store/options/types.ts +5 -0
  201. package/src/store/types.ts +5 -1
  202. package/src/store/users/actions.ts +3 -2
  203. package/src/utils/resin.ts +22 -0
  204. package/src/utils/rotate.ts +131 -0
  205. package/src/utils/scroll.ts +45 -0
  206. package/src/utils/util.ts +3 -0
  207. package/src/region/RegionContainer.tsx +0 -49
  208. 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
+ }
@@ -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,3 @@
1
+ .ba-PopupLayer-popup {
2
+ pointer-events: auto;
3
+ }
@@ -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
+ }
@@ -15,4 +15,8 @@
15
15
  &:focus {
16
16
  outline: none;
17
17
  }
18
+
19
+ &.is-active {
20
+ @include ba-RegionRect-callout;
21
+ }
18
22
  }
@@ -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 { Shape, styleShape } from './regionUtil';
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 cancelEvent = (event: React.SyntheticEvent): void => {
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 handleFocus = (event: React.SyntheticEvent): void => {
33
- cancelEvent(event);
34
- onSelect(annotationId);
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-creator,
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 RegionRect, { RegionRectRef } from './RegionRect';
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
- type State = {
27
- rectRef?: RegionRectRef;
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
- handleCancel = (): void => {
45
- const { setMessage, setStaged, setStatus } = this.props;
46
- setMessage('');
47
- setStaged(null);
48
- setStatus(CreatorStatus.init);
49
- };
50
-
51
- handleChange = (text?: string): void => {
52
- const { setMessage } = this.props;
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
- {/* Layer 3b: Staged (unsaved) annotation description popup, if 3a is ready */}
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
+ }