box-annotations 4.0.0-beta.8 → 4.0.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -57
- package/dist/annotations.css +1 -1
- package/dist/annotations.js +1 -1
- package/i18n/bn-IN.js +6 -0
- package/i18n/bn-IN.properties +12 -0
- package/i18n/da-DK.js +6 -0
- package/i18n/da-DK.properties +12 -0
- package/i18n/de-DE.js +6 -0
- package/i18n/de-DE.properties +12 -0
- package/i18n/en-AU.js +6 -0
- package/i18n/en-AU.properties +12 -0
- package/i18n/en-CA.js +6 -0
- package/i18n/en-CA.properties +12 -0
- package/i18n/en-GB.js +6 -0
- package/i18n/en-GB.properties +12 -0
- package/i18n/en-US.js +6 -0
- package/i18n/en-US.properties +12 -0
- package/i18n/en-x-pseudo.js +16 -9
- package/i18n/en-x-pseudo.properties +23 -9
- package/i18n/es-419.js +6 -0
- package/i18n/es-419.properties +12 -0
- package/i18n/es-ES.js +6 -0
- package/i18n/es-ES.properties +12 -0
- package/i18n/fi-FI.js +6 -0
- package/i18n/fi-FI.properties +12 -0
- package/i18n/fr-CA.js +6 -0
- package/i18n/fr-CA.properties +12 -0
- package/i18n/fr-FR.js +6 -0
- package/i18n/fr-FR.properties +12 -0
- package/i18n/hi-IN.js +6 -0
- package/i18n/hi-IN.properties +12 -0
- package/i18n/it-IT.js +6 -0
- package/i18n/it-IT.properties +12 -0
- package/i18n/ja-JP.js +6 -0
- package/i18n/ja-JP.properties +12 -0
- package/i18n/json/src/components/Popups/messages.json +1 -1
- package/i18n/ko-KR.js +6 -0
- package/i18n/ko-KR.properties +12 -0
- package/i18n/nb-NO.js +6 -0
- package/i18n/nb-NO.properties +12 -0
- package/i18n/nl-NL.js +6 -0
- package/i18n/nl-NL.properties +12 -0
- package/i18n/pl-PL.js +6 -0
- package/i18n/pl-PL.properties +12 -0
- package/i18n/pt-BR.js +6 -0
- package/i18n/pt-BR.properties +12 -0
- package/i18n/ru-RU.js +6 -0
- package/i18n/ru-RU.properties +12 -0
- package/i18n/sv-SE.js +6 -0
- package/i18n/sv-SE.properties +12 -0
- package/i18n/tr-TR.js +6 -0
- package/i18n/tr-TR.properties +12 -0
- package/i18n/zh-CN.js +6 -0
- package/i18n/zh-CN.properties +12 -0
- package/i18n/zh-TW.js +6 -0
- package/i18n/zh-TW.properties +12 -0
- package/package.json +29 -29
- package/src/@types/events.ts +8 -2
- package/src/@types/global.ts +1 -0
- package/src/@types/model.ts +20 -7
- package/src/BoxAnnotations.ts +16 -2
- package/src/common/BaseAnnotator.scss +0 -1
- package/src/common/BaseAnnotator.ts +125 -19
- package/src/common/BaseManager.ts +67 -4
- package/src/common/DeselectListener.tsx +21 -0
- package/src/common/DeselectManager.tsx +30 -0
- package/src/common/useIsListInteractive.ts +10 -0
- package/src/common/useMountId.ts +14 -0
- package/src/common/useOutsideEvent.ts +9 -4
- package/src/common/usePrevious.ts +13 -0
- package/src/components/ItemList/ItemList.scss +7 -2
- package/src/components/ItemList/ItemList.tsx +45 -26
- package/src/components/ItemList/ItemRow.scss +19 -7
- package/src/components/ItemList/ItemRow.tsx +33 -12
- package/src/components/PointerCapture/PointerCapture.tsx +116 -0
- package/src/components/PointerCapture/index.ts +2 -0
- package/src/components/Popups/Popper.ts +1 -0
- package/src/components/Popups/PopupArrow.scss +91 -0
- package/src/components/Popups/PopupBase.scss +12 -81
- package/src/components/Popups/PopupBase.tsx +0 -1
- package/src/components/Popups/PopupCursor.tsx +1 -1
- package/src/components/Popups/PopupDrawingToolbar.scss +76 -0
- package/src/components/Popups/PopupDrawingToolbar.tsx +109 -0
- package/src/components/Popups/PopupHighlight.scss +39 -0
- package/src/components/Popups/PopupHighlight.tsx +97 -0
- package/src/components/Popups/PopupHighlightError.scss +23 -0
- package/src/components/Popups/PopupHighlightError.tsx +74 -0
- package/src/components/Popups/PopupList.scss +1 -4
- package/src/components/Popups/PopupList.tsx +2 -2
- package/src/components/Popups/PopupReply.tsx +51 -6
- package/src/components/Popups/messages.ts +30 -0
- package/src/components/ReplyField/ReplyField.scss +6 -1
- package/src/components/ReplyField/ReplyField.tsx +43 -42
- package/src/components/ReplyField/ReplyFieldContainer.tsx +2 -1
- package/src/components/ReplyForm/ReplyButton.tsx +38 -0
- package/src/components/ReplyForm/ReplyForm.scss +3 -0
- package/src/components/ReplyForm/ReplyForm.tsx +49 -12
- package/src/components/ReplyForm/ReplyFormContainer.tsx +6 -4
- package/src/constants.js +1 -8
- package/src/document/DocumentAnnotator.scss +15 -0
- package/src/document/DocumentAnnotator.ts +110 -53
- package/src/document/docUtil.ts +61 -0
- package/src/drawing/DecoratedDrawingPath.scss +9 -0
- package/src/drawing/DecoratedDrawingPath.tsx +43 -0
- package/src/drawing/DrawingAnnotations.scss +39 -0
- package/src/drawing/DrawingAnnotations.tsx +157 -0
- package/src/drawing/DrawingAnnotationsContainer.tsx +66 -0
- package/src/drawing/DrawingCreator.scss +9 -0
- package/src/drawing/DrawingCreator.tsx +183 -0
- package/src/drawing/DrawingCursor.ts +23 -0
- package/src/drawing/DrawingList.tsx +60 -0
- package/src/drawing/DrawingManager.tsx +15 -0
- package/src/drawing/DrawingPath.tsx +17 -0
- package/src/drawing/DrawingPathGroup.tsx +52 -0
- package/src/drawing/DrawingSVG.tsx +34 -0
- package/src/drawing/DrawingSVGGroup.tsx +23 -0
- package/src/drawing/DrawingTarget.scss +11 -0
- package/src/drawing/DrawingTarget.tsx +100 -0
- package/src/drawing/SVGFilterContext.js +5 -0
- package/src/drawing/actions.ts +48 -0
- package/src/drawing/drawingUtil.ts +106 -0
- package/src/drawing/index.ts +2 -0
- package/src/highlight/HighlightAnnotations.scss +12 -0
- package/src/highlight/HighlightAnnotations.tsx +144 -0
- package/src/highlight/HighlightCanvas.scss +9 -0
- package/src/highlight/HighlightCanvas.tsx +109 -0
- package/src/highlight/HighlightContainer.tsx +64 -0
- package/src/highlight/HighlightCreator.scss +12 -0
- package/src/highlight/HighlightCreatorManager.ts +79 -0
- package/src/highlight/HighlightList.scss +13 -0
- package/src/highlight/HighlightList.tsx +92 -0
- package/src/highlight/HighlightListener.ts +37 -0
- package/src/highlight/HighlightManager.tsx +15 -0
- package/src/highlight/HighlightSvg.scss +7 -0
- package/src/highlight/HighlightSvg.tsx +18 -0
- package/src/highlight/HighlightTarget.scss +5 -0
- package/src/highlight/HighlightTarget.tsx +106 -0
- package/src/highlight/actions.ts +36 -0
- package/src/highlight/highlightUtil.ts +143 -0
- package/src/highlight/index.ts +4 -0
- package/src/image/ImageAnnotator.scss +20 -0
- package/src/image/ImageAnnotator.ts +137 -0
- package/src/polyfill.ts +19 -0
- package/src/popup/PopupContainer.tsx +50 -0
- package/src/popup/PopupLayer.scss +3 -0
- package/src/popup/PopupLayer.tsx +103 -0
- package/src/popup/PopupManager.tsx +15 -0
- package/src/region/RegionAnnotation.scss +4 -0
- package/src/region/RegionAnnotation.tsx +15 -16
- package/src/region/RegionAnnotations.scss +1 -15
- package/src/region/RegionAnnotations.tsx +14 -115
- package/src/region/RegionAnnotationsContainer.tsx +24 -0
- package/src/region/RegionCreation.scss +17 -0
- package/src/region/RegionCreation.tsx +79 -0
- package/src/region/RegionCreationContainer.tsx +43 -0
- package/src/region/RegionCreationManager.tsx +15 -0
- package/src/region/RegionCreator.scss +9 -2
- package/src/region/RegionCreator.tsx +50 -71
- package/src/region/RegionList.tsx +8 -12
- package/src/region/RegionManager.tsx +7 -39
- package/src/region/RegionRect.scss +41 -9
- package/src/region/RegionRect.tsx +9 -3
- package/src/region/index.ts +1 -0
- package/src/region/regionUtil.ts +1 -8
- package/src/region/transformUtil.ts +28 -0
- package/src/store/annotations/actions.ts +1 -0
- package/src/store/annotations/reducer.ts +9 -2
- package/src/store/annotations/selectors.ts +1 -0
- package/src/store/annotations/types.ts +1 -0
- package/src/store/common/actions.ts +1 -0
- package/src/store/common/reducer.ts +16 -10
- package/src/store/common/selectors.ts +1 -0
- package/src/store/common/types.ts +3 -0
- package/src/store/createRootReducer.ts +4 -0
- package/src/store/creator/actions.ts +2 -0
- package/src/store/creator/reducer.ts +22 -3
- package/src/store/creator/selectors.ts +9 -1
- package/src/store/creator/types.ts +18 -3
- package/src/store/drawing/actions.ts +10 -0
- package/src/store/drawing/index.ts +4 -0
- package/src/store/drawing/reducer.ts +50 -0
- package/src/store/drawing/selectors.ts +9 -0
- package/src/store/drawing/types.ts +7 -0
- package/src/store/eventing/active.ts +4 -2
- package/src/store/eventing/fetch.ts +8 -0
- package/src/store/eventing/init.ts +14 -0
- package/src/store/eventing/middleware.ts +21 -3
- package/src/store/eventing/mode.ts +7 -0
- package/src/store/eventing/staged.ts +56 -0
- package/src/store/eventing/status.ts +19 -0
- package/src/store/highlight/actions.ts +106 -0
- package/src/store/highlight/index.ts +4 -0
- package/src/store/highlight/reducer.ts +37 -0
- package/src/store/highlight/selectors.ts +12 -0
- package/src/store/highlight/types.ts +14 -0
- package/src/store/index.ts +2 -0
- package/src/store/options/actions.ts +2 -0
- package/src/store/options/reducer.ts +17 -1
- package/src/store/options/selectors.ts +8 -0
- package/src/store/options/types.ts +5 -0
- package/src/store/types.ts +5 -1
- package/src/store/users/actions.ts +3 -2
- package/src/utils/resin.ts +22 -0
- package/src/utils/rotate.ts +131 -0
- package/src/utils/scroll.ts +45 -0
- package/src/utils/util.ts +3 -0
- package/src/region/RegionContainer.tsx +0 -49
- package/src/utils/polyfill.js +0 -161
|
@@ -0,0 +1,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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
101
|
+
|
|
102
|
+
const stopDraw = React.useCallback((): void => {
|
|
100
103
|
const shape = getShape();
|
|
101
104
|
|
|
102
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
if (drawingStatus !== DrawingStatus.drawing) {
|
|
137
|
+
setDrawingStatus(DrawingStatus.drawing);
|
|
138
|
+
onStart();
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
[drawingStatus, onStart, setDrawingStatus],
|
|
142
|
+
);
|
|
139
143
|
|
|
140
|
-
|
|
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
|
-
|
|
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
|
-
}, [
|
|
187
|
+
}, [drawingStatus]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
208
188
|
|
|
209
189
|
useAutoScroll({
|
|
210
|
-
enabled:
|
|
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
|
-
<
|
|
197
|
+
<PointerCapture
|
|
218
198
|
ref={creatorElRef}
|
|
219
199
|
className={classNames(className, 'ba-RegionCreator')}
|
|
220
200
|
data-testid="ba-RegionCreator"
|
|
221
|
-
|
|
222
|
-
|
|
201
|
+
onDrawStart={startDraw}
|
|
202
|
+
onDrawStop={stopDraw}
|
|
203
|
+
onDrawUpdate={updateDraw}
|
|
223
204
|
onMouseOut={handleMouseOut}
|
|
224
205
|
onMouseOver={handleMouseOver}
|
|
225
|
-
|
|
226
|
-
onTouchEnd={handleTouchEnd}
|
|
227
|
-
onTouchMove={handleTouchMove}
|
|
228
|
-
onTouchStart={handleTouchStart}
|
|
229
|
-
role="presentation"
|
|
206
|
+
status={drawingStatus}
|
|
230
207
|
>
|
|
231
|
-
{
|
|
232
|
-
|
|
233
|
-
|
|
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
|
|
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
|
|
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
|
|
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, {
|
|
4
|
-
import
|
|
3
|
+
import BaseManager, { Props } from '../common/BaseManager';
|
|
4
|
+
import RegionAnnotationsContainer from './RegionAnnotationsContainer';
|
|
5
5
|
|
|
6
|
-
export default class
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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(<
|
|
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:
|
|
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($
|
|
25
|
-
transition: background
|
|
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:
|
|
32
|
-
box-shadow: 0 0 0 $border-size-inner rgba($white, 1), 0 0 0 $border-size-outer rgba($
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import noop from 'lodash/noop';
|
|
2
3
|
import classNames from 'classnames';
|
|
3
|
-
import
|
|
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
|
);
|
package/src/region/index.ts
CHANGED
package/src/region/regionUtil.ts
CHANGED
|
@@ -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;
|