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,43 @@
1
+ import { connect } from 'react-redux';
2
+ import {
3
+ AppState,
4
+ CreatorItemRegion,
5
+ CreatorStatus,
6
+ getAnnotationMode,
7
+ getCreatorStagedForLocation,
8
+ getCreatorStatus,
9
+ getRotation,
10
+ isCreatorStagedRegion,
11
+ Mode,
12
+ resetCreatorAction,
13
+ setReferenceIdAction,
14
+ setStagedAction,
15
+ setStatusAction,
16
+ } from '../store';
17
+ import RegionCreation from './RegionCreation';
18
+ import withProviders from '../common/withProviders';
19
+
20
+ export type Props = {
21
+ isCreating: boolean;
22
+ isRotated: boolean;
23
+ staged: CreatorItemRegion | null;
24
+ };
25
+
26
+ export const mapStateToProps = (state: AppState, { location }: { location: number }): Props => {
27
+ const staged = getCreatorStagedForLocation(state, location);
28
+
29
+ return {
30
+ isCreating: getAnnotationMode(state) === Mode.REGION && getCreatorStatus(state) !== CreatorStatus.pending,
31
+ isRotated: !!getRotation(state),
32
+ staged: isCreatorStagedRegion(staged) ? staged : null,
33
+ };
34
+ };
35
+
36
+ export const mapDispatchToProps = {
37
+ resetCreator: resetCreatorAction,
38
+ setReferenceId: setReferenceIdAction,
39
+ setStaged: setStagedAction,
40
+ setStatus: setStatusAction,
41
+ };
42
+
43
+ export default connect(mapStateToProps, mapDispatchToProps)(withProviders(RegionCreation));
@@ -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 RegionCreationContainer from './RegionCreationContainer';
5
+
6
+ export default class RegionManager extends BaseManager {
7
+ decorate(): void {
8
+ this.reactEl.classList.add('ba-Layer--regionCreation');
9
+ this.reactEl.dataset.testid = 'ba-Layer--regionCreation';
10
+ }
11
+
12
+ render(props: Props): void {
13
+ ReactDOM.render(<RegionCreationContainer location={this.location} {...props} />, this.reactEl);
14
+ }
15
+ }
@@ -2,9 +2,16 @@ $region_cursor_32: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAA
2
2
  $region_cursor_32_2x: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABGdBTUEAALGPC/xhBQAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAQKADAAQAAAABAAAAQAAAAABlmWCKAAABWUlEQVR4Ae2YOxKDIBCGNaM5R4pYWOT+B0lhYQrPEZ1JGGcoZIy6Lj4IH40DLAt8+8srSUgQgAAEIAABCEAAAnESSM867Y9JmrGlJi1pf1li9M822dknd7/dREN8NY3IHgWIcB1oPBdZqVLsVFCAJRHK113dtbsFCtg78iZib9Nn7vTbmshenbJdsihgF8wjnZRF0ZdWdT1S+7tI+8+7nlGAS+Ss+bX7/Nx8UMAcoaPr506A2vGhAC3Brdq7J76t+oleAQDYSlqh+FW/CHk6meVSP77WiOh/gUUvp1NytpF7lGXSdd2U6aCubds+n+fuxXBgNshkWZY8q6ovQwEDNOsz3hQgHcLa26DtBwVYEsqveheQRsKsGbwIKYPmtXn02yAAvOopQGcoIMCgeR0yCvCKM0Bn0StAfReQBp2ToJQY9hCAAAQgAAEIQAACEIAABCAAAQhAwDOBLxaXQ2IYNsXoAAAAAElFTkSuQmCC';
3
3
  $region_cursor_32_3x: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAABGdBTUEAALGPC/xhBQAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAYKADAAQAAAABAAAAYAAAAACK+310AAACaUlEQVR4Ae3bwUrDQBDG8VTE+Bg9CD0IBd//IYSCh4KHPobxopuDYQgGOtNvdhvy39O2zc5uf9Odpol2HQ0BBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAgXSBXfoMdzDBT2mZy9iVFo3/EB3IOI0ACdA4hqM8hkeudODLfi9Z+eflIonDDpAwxoOQgLidZOTmSpBV85YRVfmya2AHWI0GfRLQAN1OuekSZCGWfkxl/4hjB9gsNOiTgAbodsrVlKBSCr7Kwnu7+Fl/KGXkefbc3T9kBzROEQlonIDVlCDr9Ho4dMMwdH3fdx/ns30p3M8+21laGDtgSabS8ySgEvTSNKssQUtvxvt8xrUd7xrYAV4x8fEkQAzqDbe5EuS9BO0F9R7PDvCKiY8nAWJQb7hNlKClS81erIzj2QEZqo6YJMCBlXEoCchQdcQkAQ6sjENJQIaqI2b6WVDFy7y9aq6aZ03sAMenNeNQEpCh6ogZ/seCa+ewZeHteOy+y52sW9t4N+yvjXfFbm1PJcb76TSFqVmC0r8DpndVOiO+xbOvRfvqeNF1RMdVTcD4SVM0i67aAYp1RWJULUGRBf43JuOmvJ2nZgniS9jKN+iTgAbodsr07wDVdi5nU/xpos0cfY0AJUjjGI5CAsJ0moEkQOMYjkICwnSagSRA4xiOQgLCdJqBJEDjGI5CAsJ0moEkQOMYjkICwnSagSRA4xiOQgLCdJqBJEDjGI6SfkcsvLLZQC5Hz0B4qBGgBGkciYIAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIITAK/BlNVeN572e8AAAAASUVORK5CYII=';
4
4
 
5
+ $region_cursor_new: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAAEEfUpiAAAABGdBTUEAALGPC/xhBQAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAACPTkDJAAAFEUlEQVRYCcVXeUyjVRD/erCl5SjtrpblWHbxADlTD0TDESIak0UJUYyE/zAhGo7gP+oaMDEhIRFNiCRG4A8NmGACwYsAG0lIIBSMCBSCrRDCXbkRiq2bbvv5m9qv2+srn3UTJ5nO++bNmzdv5r2ZKcMEgp6entfAF9Gc2EMglsZOLg1OTk7Gj4+PP6KxP+Tn57O+3CfBkHsyL29sbHzqyWBmZ2eLwfDc1TnvZiwtLanBsRcWFpJ+tzVOKdePDDSqu7v7g8nJyS8wDvec5Ma0MjYrK+tx0Ac5piAqMhgMLwWTJGNZvV7PK+QUIKG5ubmbgTSJDw4OHJi4CtQA3cfkBkSlLS0tWzab7f3s7Oy60tJSLXhuz5FzbAqF4g48OpCamsrrrCgIqvf39xdAJcCAQNtxWwcUCMYU9/b2vpqXl0cueTkURWLXhaQzsAUFBez8/HxJMEXu2+Jr1vb2NtPR0aELDw9/D+PbbW1t5F4/8FMgk8n+hFRGQkKCIikpyYqbpRoaGkpUqVSjeAifYM5vjZ9WF+NKc3PzQUNDwzP4jgBeHx0dvQVK11YQKPr6+t5dX1//rbW19XWEbnl8fHzh8PBwGqsFK/HciR6lCij4CJ6LBY//0yWiXS45HA4LUcFb+ghGTE9Ps6enp1ahSgI6RKlUhp+dnf0hRElABWRVdHS0XIgSXgVClQRUkJKSwqysrMwik+n29vZ+Pjo6+hIKA8rSRp4QgYfD9vf3t4F5HUhVgfIUXSDeBII5NyhGRkbewZemvLxcqdPppjs7Ow2Ey8vLP4Hvd4V9Mw/tEga8u7u7+9XOzk764OBgm91uFyOd1+Non2VmZn6OecoXQUG6ubn5rdFopNwXA1ROTEw0g8YBvTblcwxrsViOV1dXHXDi26DFcrn8xbW1tVookAIvBFKchMVGPN9FPOVfhoeHF0CNjY2NKZjzsoJPG/mCStgDwKsIpUGtVqdj7FVr8S0IyCIKZcCiKUjD/yFEfpKYTKYncPwXMCbn8wUtZPuCKSQDZDMzMwkoyLeRYswodlSjKFcJCqIQq4IZQOtF2FiGwsBQiqqtrf0O36f305CLDPA6BB6T05C6urrvKdXdD0P+lQGcNSiPznxbX1/vNATtA3VdIYVG0KvmNiaKboEpKipys2CEHM3rD8hXFjSz5chZP2LyLvDCfOVWwjMg70RQs0R1LiMjg0WCdAwMDFAyfA6YB3zWhU+DZgOvASOBglI35C7Oa2KxmM3JyXE0NTV9HBsb+zXW/A40azQaBoaVREZGpotEIimqOUOyEonESn8mkpOTpyBnB1LvywvBnhPNUVypr6IM9heQWgYGSfktZPpKtMPX0EKapNJ7kTSbzezY2Nhj1dXVS0jibxYXF89hCRkSUkjICEIKB1FyrXpra+sbavxQchcrKyspST0CvAF8VKvVFiBUehSBhaqqKgoL5W7ey37PdEgFAM5qjjqNYFmWKAPXs/Hx8dS+mIB3gNKampoYq9XqQF8siYmJof6IQsCtx9AbnIq8WUG/yANR7e3tz5eUlHzY1dX1EGJuKysr+zUuLm4ROSoLueESPKQKCwuz486c5ebm2s7Pz2+lpaWNYW3IoeCsIoOpTFKVfxj4VGJiYj6oFphGFPX2jampKf0ioKKi4hUX/zIorfM7sB8DQkKA1nH3gsaciymkSpz8Ck7tAB7i+wxoc8lwcvj8B0I1gFvvS0kfZxzNcfH325hb+Dfx/eE8ndH+AwAAAABJRU5ErkJggg==';
6
+ $region_cursor_new_2x: 'data:image/png;base64,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';
7
+ $region_cursor_new_3x: 'data:image/png;base64,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';
8
+
5
9
  .ba-RegionCreator {
6
- cursor: url($region_cursor_32) 16 16, crosshair; /* Legacy */
7
- cursor: image-set(url($region_cursor_32) 1x, url($region_cursor_32_2x) 2x, url($region_cursor_32_3x) 3x) 16 16, crosshair; /* Webkit */ /* stylelint-disable-line */
10
+ $offset_x: 9;
11
+ $offset_y: 4;
12
+
13
+ cursor: url($region_cursor_new) $offset_x $offset_y, auto; /* Legacy */
14
+ cursor: image-set(url($region_cursor_new) 1x, url($region_cursor_new_2x) 2x, url($region_cursor_new_3x) 3x) $offset_x $offset_y, auto; /* Webkit */ /* stylelint-disable-line */
8
15
  }
9
16
 
10
17
  .ba-RegionCreator-rect {
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import classNames from 'classnames';
3
+ import PointerCapture, { Status as DrawingStatus } from '../components/PointerCapture';
3
4
  import PopupCursor from '../components/Popups/PopupCursor';
4
5
  import RegionRect, { RegionRectRef } from './RegionRect';
5
6
  import useAutoScroll from '../common/useAutoScroll';
@@ -9,6 +10,7 @@ import './RegionCreator.scss';
9
10
 
10
11
  type Props = {
11
12
  className?: string;
13
+ onAbort: () => void;
12
14
  onStart: () => void;
13
15
  onStop: (shape: Rect) => void;
14
16
  };
@@ -16,10 +18,12 @@ type Props = {
16
18
  const MIN_X = 0; // Minimum region x position must be positive
17
19
  const MIN_Y = 0; // Minimum region y position must be positive
18
20
  const MIN_SIZE = 10; // Minimum region size must be large enough to be clickable
19
- const MOUSE_PRIMARY = 1; // Primary mouse button
20
21
 
21
- export default function RegionCreator({ className, onStart, onStop }: Props): JSX.Element {
22
- const [isDrawing, setIsDrawing] = React.useState<boolean>(false);
22
+ const isValid = (x1: number, y1: number, x2: number, y2: number): boolean =>
23
+ Math.abs(x2 - x1) >= MIN_SIZE || Math.abs(y2 - y1) >= MIN_SIZE;
24
+
25
+ export default function RegionCreator({ className, onAbort, onStart, onStop }: Props): JSX.Element {
26
+ const [drawingStatus, setDrawingStatus] = React.useState<DrawingStatus>(DrawingStatus.init);
23
27
  const [isHovering, setIsHovering] = React.useState<boolean>(false);
24
28
  const creatorElRef = React.useRef<HTMLDivElement>(null);
25
29
  const positionX1Ref = React.useRef<number | null>(null);
@@ -86,20 +90,19 @@ export default function RegionCreator({ className, onStart, onStop }: Props): JS
86
90
  const startDraw = (x: number, y: number): void => {
87
91
  const [x1, y1] = getPosition(x, y);
88
92
 
89
- setIsDrawing(true);
93
+ setDrawingStatus(DrawingStatus.dragging);
90
94
 
91
95
  positionX1Ref.current = x1;
92
96
  positionY1Ref.current = y1;
93
97
  positionX2Ref.current = null;
94
98
  positionY2Ref.current = null;
95
99
  regionDirtyRef.current = true;
96
-
97
- onStart();
98
100
  };
99
- const stopDraw = (): void => {
101
+
102
+ const stopDraw = React.useCallback((): void => {
100
103
  const shape = getShape();
101
104
 
102
- setIsDrawing(false);
105
+ setDrawingStatus(DrawingStatus.init);
103
106
 
104
107
  positionX1Ref.current = null;
105
108
  positionY1Ref.current = null;
@@ -109,60 +112,45 @@ export default function RegionCreator({ className, onStart, onStop }: Props): JS
109
112
 
110
113
  if (shape) {
111
114
  onStop(shape);
115
+ } else {
116
+ onAbort();
112
117
  }
113
- };
114
- const updateDraw = (x: number, y: number): void => {
115
- const [x2, y2] = getPosition(x, y);
116
-
117
- positionX2Ref.current = x2;
118
- positionY2Ref.current = y2;
119
- regionDirtyRef.current = true;
120
- };
118
+ }, [onAbort, onStop, setDrawingStatus]);
119
+
120
+ const updateDraw = React.useCallback(
121
+ (x: number, y: number): void => {
122
+ const [x2, y2] = getPosition(x, y);
123
+ const { current: x1 } = positionX1Ref;
124
+ const { current: y1 } = positionY1Ref;
125
+ const { current: prevX2 } = positionX2Ref;
126
+
127
+ // Suppress the creation of a small region if the intention of the user is to click on the document
128
+ if (prevX2 === null && !isValid(x1 ?? 0, y1 ?? 0, x2, y2)) {
129
+ return;
130
+ }
121
131
 
122
- // Event Handlers
123
- const handleClick = (event: React.MouseEvent): void => {
124
- event.preventDefault();
125
- event.stopPropagation();
126
- event.nativeEvent.stopImmediatePropagation();
127
- };
128
- const handleMouseDown = ({ buttons, clientX, clientY }: React.MouseEvent): void => {
129
- if (buttons !== MOUSE_PRIMARY) {
130
- return;
131
- }
132
+ positionX2Ref.current = x2;
133
+ positionY2Ref.current = y2;
134
+ regionDirtyRef.current = true;
132
135
 
133
- startDraw(clientX, clientY);
134
- };
135
- const handleMouseMove = ({ buttons, clientX, clientY }: MouseEvent): void => {
136
- if (buttons !== MOUSE_PRIMARY || !isDrawing) {
137
- return;
138
- }
136
+ if (drawingStatus !== DrawingStatus.drawing) {
137
+ setDrawingStatus(DrawingStatus.drawing);
138
+ onStart();
139
+ }
140
+ },
141
+ [drawingStatus, onStart, setDrawingStatus],
142
+ );
139
143
 
140
- updateDraw(clientX, clientY);
141
- };
144
+ // Event Handlers
142
145
  const handleMouseOut = (): void => {
143
146
  setIsHovering(false);
144
147
  };
145
148
  const handleMouseOver = (): void => {
146
149
  setIsHovering(true);
147
150
  };
148
- const handleMouseUp = (): void => {
149
- stopDraw();
150
- };
151
151
  const handleScroll = (x: number, y: number): void => {
152
152
  updateDraw(x, y);
153
153
  };
154
- const handleTouchCancel = (): void => {
155
- stopDraw();
156
- };
157
- const handleTouchEnd = (): void => {
158
- stopDraw();
159
- };
160
- const handleTouchMove = ({ targetTouches }: React.TouchEvent): void => {
161
- updateDraw(targetTouches[0].clientX, targetTouches[0].clientY);
162
- };
163
- const handleTouchStart = ({ targetTouches }: React.TouchEvent): void => {
164
- startDraw(targetTouches[0].clientX, targetTouches[0].clientY);
165
- };
166
154
 
167
155
  const renderStep = (callback: () => void): void => {
168
156
  renderHandleRef.current = window.requestAnimationFrame(callback);
@@ -184,12 +172,7 @@ export default function RegionCreator({ className, onStart, onStop }: Props): JS
184
172
  };
185
173
 
186
174
  React.useEffect(() => {
187
- // Document-level mousemove and mouseup event listeners allow the creator component to respond even if
188
- // the cursor leaves the drawing area before the mouse button is released, which finishes the shape
189
- if (isDrawing) {
190
- document.addEventListener('mousemove', handleMouseMove);
191
- document.addEventListener('mouseup', handleMouseUp);
192
-
175
+ if (drawingStatus !== DrawingStatus.init) {
193
176
  renderStep(renderRect);
194
177
  }
195
178
 
@@ -200,36 +183,32 @@ export default function RegionCreator({ className, onStart, onStop }: Props): JS
200
183
  if (renderHandle) {
201
184
  window.cancelAnimationFrame(renderHandle);
202
185
  }
203
-
204
- document.removeEventListener('mousemove', handleMouseMove);
205
- document.removeEventListener('mouseup', handleMouseUp);
206
186
  };
207
- }, [isDrawing]); // eslint-disable-line react-hooks/exhaustive-deps
187
+ }, [drawingStatus]); // eslint-disable-line react-hooks/exhaustive-deps
208
188
 
209
189
  useAutoScroll({
210
- enabled: isDrawing,
190
+ enabled: drawingStatus !== DrawingStatus.init,
211
191
  onScroll: handleScroll,
212
192
  reference: creatorElRef.current,
213
193
  });
214
194
 
215
195
  return (
216
196
  // eslint-disable-next-line jsx-a11y/mouse-events-have-key-events
217
- <div
197
+ <PointerCapture
218
198
  ref={creatorElRef}
219
199
  className={classNames(className, 'ba-RegionCreator')}
220
200
  data-testid="ba-RegionCreator"
221
- onClick={handleClick}
222
- onMouseDown={handleMouseDown}
201
+ onDrawStart={startDraw}
202
+ onDrawStop={stopDraw}
203
+ onDrawUpdate={updateDraw}
223
204
  onMouseOut={handleMouseOut}
224
205
  onMouseOver={handleMouseOver}
225
- onTouchCancel={handleTouchCancel}
226
- onTouchEnd={handleTouchEnd}
227
- onTouchMove={handleTouchMove}
228
- onTouchStart={handleTouchStart}
229
- role="presentation"
206
+ status={drawingStatus}
230
207
  >
231
- {isDrawing && <RegionRect ref={regionRectRef} className="ba-RegionCreator-rect" isActive />}
232
- {!isDrawing && isHovering && <PopupCursor />}
233
- </div>
208
+ {drawingStatus === DrawingStatus.drawing && (
209
+ <RegionRect ref={regionRectRef} className="ba-RegionCreator-rect" isActive />
210
+ )}
211
+ {drawingStatus === DrawingStatus.init && isHovering && <PopupCursor />}
212
+ </PointerCapture>
234
213
  );
235
214
  }
@@ -2,8 +2,9 @@ import React from 'react';
2
2
  import classNames from 'classnames';
3
3
  import noop from 'lodash/noop';
4
4
  import RegionAnnotation from './RegionAnnotation';
5
- import useOutsideEvent from '../common/useOutsideEvent';
5
+ import useIsListInteractive from '../common/useIsListInteractive';
6
6
  import { AnnotationRegion } from '../@types';
7
+ import { checkValue } from '../utils/util';
7
8
 
8
9
  export type Props = {
9
10
  activeId?: string | null;
@@ -12,10 +13,6 @@ export type Props = {
12
13
  onSelect?: (annotationId: string | null) => void;
13
14
  };
14
15
 
15
- export function checkValue(value: number): boolean {
16
- return value >= 0 && value <= 100; // Values cannot be negative or larger than 100%
17
- }
18
-
19
16
  export function filterRegion({ target }: AnnotationRegion): boolean {
20
17
  const { shape } = target;
21
18
  const { height, width, x, y } = shape;
@@ -32,16 +29,15 @@ export function sortRegion({ target: targetA }: AnnotationRegion, { target: targ
32
29
  }
33
30
 
34
31
  export function RegionList({ activeId, annotations, className, onSelect = noop }: Props): JSX.Element {
35
- const [isListening, setIsListening] = React.useState(true);
32
+ const isListening = useIsListInteractive();
36
33
  const rootElRef = React.createRef<HTMLDivElement>();
37
34
 
38
- // Document-level event handlers for focus and pointer control
39
- useOutsideEvent('click', rootElRef, (): void => onSelect(null));
40
- useOutsideEvent('mousedown', rootElRef, (): void => setIsListening(false));
41
- useOutsideEvent('mouseup', rootElRef, (): void => setIsListening(true));
42
-
43
35
  return (
44
- <div ref={rootElRef} className={classNames(className, { 'is-listening': isListening })}>
36
+ <div
37
+ ref={rootElRef}
38
+ className={classNames(className, { 'is-listening': isListening })}
39
+ data-resin-component="regionList"
40
+ >
45
41
  {annotations
46
42
  .filter(filterRegion)
47
43
  .sort(sortRegion)
@@ -1,47 +1,15 @@
1
1
  import * as React from 'react';
2
2
  import * as ReactDOM from 'react-dom';
3
- import BaseManager, { Options, Props } from '../common/BaseManager';
4
- import RegionContainer from './RegionContainer';
3
+ import BaseManager, { Props } from '../common/BaseManager';
4
+ import RegionAnnotationsContainer from './RegionAnnotationsContainer';
5
5
 
6
- export default class RegionManager implements BaseManager {
7
- page: number;
8
-
9
- pageEl: HTMLElement;
10
-
11
- rootEl: HTMLElement;
12
-
13
- constructor({ page, pageEl, referenceEl }: Options) {
14
- this.page = page;
15
- this.pageEl = pageEl;
16
- this.rootEl = this.insert(referenceEl);
17
- }
18
-
19
- destroy(): void {
20
- ReactDOM.unmountComponentAtNode(this.rootEl);
21
-
22
- this.rootEl.remove();
23
- }
24
-
25
- exists(pageEl: HTMLElement): boolean {
26
- return pageEl.contains(this.rootEl);
27
- }
28
-
29
- insert(referenceEl: HTMLElement): HTMLElement {
30
- // Find the nearest applicable reference and document elements
31
- const documentEl = this.pageEl.ownerDocument || document;
32
- const parentEl = referenceEl.parentNode || documentEl;
33
-
34
- // Construct a layer element where we can inject a root React component
35
- const rootLayerEl = documentEl.createElement('div');
36
- rootLayerEl.classList.add('ba-Layer');
37
- rootLayerEl.classList.add('ba-Layer--region');
38
- rootLayerEl.dataset.testid = 'ba-Layer--region';
39
-
40
- // Insert the new layer element immediately after the reference element
41
- return parentEl.insertBefore(rootLayerEl, referenceEl.nextSibling);
6
+ export default class RegionListManager extends BaseManager {
7
+ decorate(): void {
8
+ this.reactEl.classList.add('ba-Layer--region');
9
+ this.reactEl.dataset.testid = 'ba-Layer--region';
42
10
  }
43
11
 
44
12
  render(props: Props): void {
45
- ReactDOM.render(<RegionContainer page={this.page} {...props} />, this.rootEl);
13
+ ReactDOM.render(<RegionAnnotationsContainer location={this.location} {...props} />, this.reactEl);
46
14
  }
47
15
  }
@@ -1,5 +1,12 @@
1
1
  @import '~box-ui-elements/es/styles/variables';
2
2
 
3
+ $ba-RegionRect-color: $bdl-yellorange;
4
+ $ba-RegionRect-bgColor--default: rgba($ba-RegionRect-color, 0); // Default color uses zero alpha value for transition
5
+ $ba-RegionRect-bgColor--hover: rgba($ba-RegionRect-color, .1);
6
+ $ba-RegionRect-bgColor--active: rgba($ba-RegionRect-color, 0);
7
+ $ba-RegionRect-transitionDelay: 25ms;
8
+ $ba-RegionRect-transitionDuration: 200ms;
9
+
3
10
  @mixin ba-RegionRect {
4
11
  $border-radius: 6px;
5
12
  $border-size-inner: 1px;
@@ -19,29 +26,54 @@
19
26
  bottom: $border-size-outer;
20
27
  left: $border-size-outer;
21
28
  display: block;
22
- background: rgba($bdl-yellorange, 0); // Zero alpha value for transition
29
+ background-color: $ba-RegionRect-bgColor--default;
23
30
  border-radius: $border-radius;
24
- box-shadow: 0 0 0 $border-size-inner rgba($white, 1), 0 0 0 $border-size-outer rgba($bdl-yellorange, .6), 0 0 0 0 rgba(0, 0, 0, 0);
25
- transition: background 200ms ease, box-shadow 200ms ease;
31
+ box-shadow: 0 0 0 $border-size-inner rgba($white, 1), 0 0 0 $border-size-outer rgba($ba-RegionRect-color, .6), 0 0 0 0 rgba(0, 0, 0, 0);
32
+ transition: background-color $ba-RegionRect-transitionDuration ease, box-shadow $ba-RegionRect-transitionDuration ease;
26
33
  content: '';
27
34
  }
28
35
 
29
36
  &:hover {
30
37
  &::after {
31
- background: rgba($bdl-yellorange, .1);
32
- box-shadow: 0 0 0 $border-size-inner rgba($white, 1), 0 0 0 $border-size-outer rgba($bdl-yellorange, 1), 0 8px 16px 0 rgba(0, 0, 0, .1);
38
+ background-color: $ba-RegionRect-bgColor--hover;
39
+ box-shadow: 0 0 0 $border-size-inner rgba($white, 1), 0 0 0 $border-size-outer rgba($ba-RegionRect-color, 1), 0 8px 16px 0 rgba(0, 0, 0, .1);
33
40
  }
34
41
  }
35
42
 
36
- &.is-active,
37
- &:focus {
43
+ &.is-active {
44
+ &::after {
45
+ background-color: $ba-RegionRect-bgColor--active;
46
+ box-shadow: 0 0 0 $border-size-inner rgba($ba-RegionRect-color, 1), 0 0 0 $border-size-outer rgba($ba-RegionRect-color, 1), 0 8px 52px 0 rgba(0, 0, 0, .2);
47
+ transition-delay: $ba-RegionRect-transitionDelay;
48
+ }
49
+ }
50
+ }
51
+
52
+ @mixin ba-RegionRect-callout {
53
+ &::after {
54
+ animation: ba-RegionRect-callout 1s ease-in-out $ba-RegionRect-transitionDelay 0 normal none; // Disabled by default
55
+ }
56
+
57
+ &:not(:focus-within) {
38
58
  &::after {
39
- background: rgba($bdl-yellorange, 0);
40
- box-shadow: 0 0 0 $border-size-inner rgba($bdl-yellorange, 1), 0 0 0 $border-size-outer rgba($bdl-yellorange, 1), 0 8px 52px 0 rgba(0, 0, 0, .2);
59
+ animation-iteration-count: 1; // Only play the animation if the user is not interacting directly with the element or its descendents
60
+ animation-fill-mode: forwards;
41
61
  }
42
62
  }
43
63
  }
44
64
 
65
+ @keyframes ba-RegionRect-callout {
66
+ 0%,
67
+ 100% {
68
+ background-color: transparent;
69
+ }
70
+
71
+ 30%,
72
+ 70% {
73
+ background-color: $ba-RegionRect-bgColor--hover;
74
+ }
75
+ }
76
+
45
77
  .ba-RegionRect {
46
78
  @include ba-RegionRect;
47
79
  }
@@ -1,23 +1,29 @@
1
- import * as React from 'react';
1
+ import React from 'react';
2
+ import noop from 'lodash/noop';
2
3
  import classNames from 'classnames';
3
- import { Shape, styleShape } from './regionUtil';
4
+ import useMountId from '../common/useMountId';
5
+ import { Shape } from '../@types';
6
+ import { styleShape } from './regionUtil';
4
7
  import './RegionRect.scss';
5
8
 
6
9
  type Props = {
7
10
  className?: string;
8
11
  isActive?: boolean;
12
+ onMount?: (uuid: string) => void;
9
13
  shape?: Shape;
10
14
  };
11
15
 
12
16
  export type RegionRectRef = HTMLDivElement;
13
17
 
14
18
  export function RegionRect(props: Props, ref: React.Ref<RegionRectRef>): JSX.Element {
15
- const { className, isActive, shape } = props;
19
+ const { className, isActive, onMount = noop, shape } = props;
20
+ const uuid = useMountId(onMount);
16
21
 
17
22
  return (
18
23
  <div
19
24
  ref={ref}
20
25
  className={classNames('ba-RegionRect', className, { 'is-active': isActive })}
26
+ data-ba-reference-id={uuid}
21
27
  style={styleShape(shape)}
22
28
  />
23
29
  );
@@ -1,3 +1,4 @@
1
1
  export * from './actions';
2
2
  export * from './regionUtil';
3
+ export { default as RegionCreationManager } from './RegionCreationManager';
3
4
  export { default as RegionManager } from './RegionManager';
@@ -1,12 +1,5 @@
1
1
  import * as React from 'react';
2
- import { Annotation, AnnotationRegion, Position } from '../@types';
3
-
4
- export type Shape = {
5
- height: number;
6
- width: number;
7
- x: number;
8
- y: number;
9
- };
2
+ import { Annotation, AnnotationRegion, Position, Shape } from '../@types';
10
3
 
11
4
  export const EMPTY_STYLE = { display: 'none' };
12
5
 
@@ -0,0 +1,28 @@
1
+ export type Point = {
2
+ x: number;
3
+ y: number;
4
+ };
5
+
6
+ export const invertYCoordinate = ({ x, y }: Point, height: number): Point => ({
7
+ x,
8
+ y: height > 0 ? height - y : y,
9
+ });
10
+
11
+ export const rotatePoint = ({ x, y }: Point, rotationInDegrees: number): Point => {
12
+ const radians = (rotationInDegrees * Math.PI) / 180;
13
+ const cosine = Math.cos(radians);
14
+ const sine = Math.sin(radians);
15
+
16
+ // Formula to apply a rotation to a point is:
17
+ // x' = x * cos(θ) - y * sin(θ)
18
+ // y' = x * sin(θ) + y * cos(θ)
19
+ return {
20
+ x: x * cosine - y * sine,
21
+ y: x * sine + y * cosine,
22
+ };
23
+ };
24
+
25
+ export const translatePoint = ({ x, y }: Point, { dx = 0, dy = 0 }: { dx?: number; dy?: number }): Point => ({
26
+ x: x + dx,
27
+ y: y + dy,
28
+ });
@@ -48,3 +48,4 @@ export const fetchAnnotationsAction = createAsyncThunk<APICollection<Annotation>
48
48
 
49
49
  export const removeAnnotationAction = createAction<string>('REMOVE_ANNOTATION');
50
50
  export const setActiveAnnotationIdAction = createAction<string | null>('SET_ACTIVE_ANNOTATION_ID');
51
+ export const setIsInitialized = createAction('SET_IS_INITIALIZED');
@@ -1,10 +1,12 @@
1
1
  import { createReducer, combineReducers } from '@reduxjs/toolkit';
2
+ import { formatDrawing, isDrawing } from '../../drawing/drawingUtil';
2
3
  import { AnnotationsState } from './types';
3
4
  import {
4
5
  createAnnotationAction,
5
6
  fetchAnnotationsAction,
6
7
  removeAnnotationAction,
7
8
  setActiveAnnotationIdAction,
9
+ setIsInitialized,
8
10
  } from './actions';
9
11
 
10
12
  const activeAnnotationId = createReducer<AnnotationsState['activeId']>(null, builder =>
@@ -29,20 +31,25 @@ const annotationsAllIds = createReducer<AnnotationsState['allIds']>([], builder
29
31
  const annotationsById = createReducer<AnnotationsState['byId']>({}, builder =>
30
32
  builder
31
33
  .addCase(createAnnotationAction.fulfilled, (state, { payload }) => {
32
- state[payload.id] = payload;
34
+ state[payload.id] = isDrawing(payload) ? formatDrawing(payload) : payload;
33
35
  })
34
36
  .addCase(removeAnnotationAction, (state, { payload: id }) => {
35
37
  delete state[id];
36
38
  })
37
39
  .addCase(fetchAnnotationsAction.fulfilled, (state, { payload }) => {
38
40
  payload.entries.forEach(annotation => {
39
- state[annotation.id] = annotation;
41
+ state[annotation.id] = isDrawing(annotation) ? formatDrawing(annotation) : annotation;
40
42
  });
41
43
  }),
42
44
  );
43
45
 
46
+ const setAnnotationsIsInitialized = createReducer(false, builder => {
47
+ builder.addCase(setIsInitialized, () => true);
48
+ });
49
+
44
50
  export default combineReducers({
45
51
  activeId: activeAnnotationId,
46
52
  allIds: annotationsAllIds,
47
53
  byId: annotationsById,
54
+ isInitialized: setAnnotationsIsInitialized,
48
55
  });
@@ -9,3 +9,4 @@ export const getAnnotation = ({ annotations }: State, id: string): Annotation |
9
9
  export const getAnnotations = ({ annotations }: State): Annotation[] => [...Object.values(annotations.byId)];
10
10
  export const getAnnotationsForLocation = (state: State, location: number): Annotation[] =>
11
11
  getAnnotations(state).filter(annotation => getProp(annotation, 'target.location.value') === location);
12
+ export const getIsInitialized = ({ annotations }: State): boolean => annotations.isInitialized;
@@ -4,4 +4,5 @@ export type AnnotationsState = {
4
4
  activeId: string | null;
5
5
  allIds: string[];
6
6
  byId: Record<string, Annotation>;
7
+ isInitialized: boolean;
7
8
  };
@@ -1,4 +1,5 @@
1
1
  import { createAction } from '@reduxjs/toolkit';
2
2
  import { Mode } from './types';
3
3
 
4
+ export const setColorAction = createAction<string>('SET_COLOR');
4
5
  export const toggleAnnotationModeAction = createAction<Mode>('TOGGLE_ANNOTATION_MODE');