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
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { getFormattedCommentText } from 'box-ui-elements/es/components/form-elements/draft-js-mention-selector';
|
|
3
|
-
import Button from 'box-ui-elements/es/components/button';
|
|
4
|
-
import PrimaryButton from 'box-ui-elements/es/components/primary-button';
|
|
2
|
+
import { getFormattedCommentText } from 'box-ui-elements/es/components/form-elements/draft-js-mention-selector/utils';
|
|
5
3
|
import { KEYS } from 'box-ui-elements/es/constants';
|
|
6
4
|
import { EditorState } from 'draft-js';
|
|
7
5
|
import { Form, FormikProps } from 'formik';
|
|
8
6
|
import { FormattedMessage, useIntl } from 'react-intl';
|
|
9
7
|
import messages from '../Popups/messages';
|
|
8
|
+
import ReplyButton from './ReplyButton';
|
|
10
9
|
import ReplyField from '../ReplyField';
|
|
11
10
|
import { FormValues, PropsFromState as ContainerProps } from './ReplyFormContainer';
|
|
11
|
+
import './ReplyForm.scss';
|
|
12
12
|
|
|
13
13
|
export type ReplyFormProps = {
|
|
14
14
|
isPending: boolean;
|
|
@@ -22,30 +22,55 @@ export type Props = ReplyFormProps &
|
|
|
22
22
|
ContainerProps &
|
|
23
23
|
Pick<FormikProps<FormValues>, 'errors' | 'setFieldValue' | 'values'>;
|
|
24
24
|
|
|
25
|
-
const ReplyForm = (
|
|
25
|
+
const ReplyForm = (props: Props): JSX.Element => {
|
|
26
|
+
const { errors, isPending, onCancel, onChange, setFieldValue, values } = props;
|
|
27
|
+
|
|
28
|
+
const formRef = React.useRef<HTMLFormElement>(null);
|
|
26
29
|
const intl = useIntl();
|
|
27
30
|
const hasErrors = Object.keys(errors).length > 0;
|
|
28
31
|
const { editorState } = values;
|
|
29
32
|
|
|
33
|
+
const handleCancel = (): void => {
|
|
34
|
+
onCancel(getFormattedCommentText(editorState).text);
|
|
35
|
+
};
|
|
30
36
|
// Instead of deferring to the Formik handleChange helper, we need to use the setFieldValue helper
|
|
31
37
|
// method in order for DraftJS to work correctly by setting back the whole editorState
|
|
32
38
|
const handleChange = (nextEditorState: EditorState): void => {
|
|
33
39
|
onChange(getFormattedCommentText(nextEditorState).text);
|
|
34
40
|
setFieldValue('editorState', nextEditorState);
|
|
35
41
|
};
|
|
36
|
-
|
|
42
|
+
|
|
43
|
+
const handleKeyDown = (event: KeyboardEvent): void => {
|
|
37
44
|
if (event.key !== KEYS.escape) {
|
|
38
45
|
return;
|
|
39
46
|
}
|
|
40
47
|
|
|
48
|
+
event.preventDefault();
|
|
41
49
|
event.stopPropagation();
|
|
42
|
-
event.nativeEvent.stopImmediatePropagation();
|
|
43
50
|
|
|
44
|
-
|
|
51
|
+
handleCancel();
|
|
45
52
|
};
|
|
46
53
|
|
|
54
|
+
// The entire annotation mode keydown event listener is attached to real dom's document
|
|
55
|
+
// Have to attach this event listener to real dom too, in order to capture the keydown event
|
|
56
|
+
// before annotation mode event listner captures it
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
const { current: formEl } = formRef;
|
|
59
|
+
|
|
60
|
+
if (formEl) {
|
|
61
|
+
// Add event listener to real DOM
|
|
62
|
+
formEl.addEventListener('keydown', handleKeyDown);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return () => {
|
|
66
|
+
if (formEl) {
|
|
67
|
+
formEl.removeEventListener('keydown', handleKeyDown);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}, [formRef]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
71
|
+
|
|
47
72
|
return (
|
|
48
|
-
<Form className="ba-ReplyForm" data-testid="ba-ReplyForm"
|
|
73
|
+
<Form ref={formRef} className="ba-ReplyForm" data-testid="ba-ReplyForm">
|
|
49
74
|
<div className="ba-Popup-main">
|
|
50
75
|
<ReplyField
|
|
51
76
|
className="ba-Popup-field"
|
|
@@ -57,12 +82,24 @@ const ReplyForm = ({ errors, isPending, onCancel, onChange, setFieldValue, value
|
|
|
57
82
|
/>
|
|
58
83
|
</div>
|
|
59
84
|
<div className="ba-Popup-footer">
|
|
60
|
-
<
|
|
85
|
+
<ReplyButton
|
|
86
|
+
data-resin-target="cancel"
|
|
87
|
+
data-testid="ba-Popup-cancel"
|
|
88
|
+
isDisabled={isPending}
|
|
89
|
+
onClick={handleCancel}
|
|
90
|
+
type="button"
|
|
91
|
+
>
|
|
61
92
|
<FormattedMessage {...messages.buttonCancel} />
|
|
62
|
-
</
|
|
63
|
-
<
|
|
93
|
+
</ReplyButton>
|
|
94
|
+
<ReplyButton
|
|
95
|
+
data-resin-target="post"
|
|
96
|
+
data-testid="ba-Popup-submit"
|
|
97
|
+
isDisabled={hasErrors || isPending}
|
|
98
|
+
isPrimary
|
|
99
|
+
type="submit"
|
|
100
|
+
>
|
|
64
101
|
<FormattedMessage {...messages.buttonPost} />
|
|
65
|
-
</
|
|
102
|
+
</ReplyButton>
|
|
66
103
|
</div>
|
|
67
104
|
</Form>
|
|
68
105
|
);
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
getFormattedCommentText,
|
|
4
|
-
} from 'box-ui-elements/es/components/form-elements/draft-js-mention-selector';
|
|
1
|
+
import createMentionSelectorState from 'box-ui-elements/es/components/form-elements/draft-js-mention-selector/createMentionSelectorState';
|
|
2
|
+
import { getFormattedCommentText } from 'box-ui-elements/es/components/form-elements/draft-js-mention-selector/utils';
|
|
5
3
|
import { EditorState, SelectionState } from 'draft-js';
|
|
6
4
|
import { FormikBag, withFormik } from 'formik';
|
|
7
5
|
import { connect } from 'react-redux';
|
|
@@ -29,6 +27,8 @@ export const mapStateToProps = (state: AppState): PropsFromState => ({
|
|
|
29
27
|
cursorPosition: getCreatorCursor(state),
|
|
30
28
|
});
|
|
31
29
|
|
|
30
|
+
export const mapPropsToErrors = (): FormErrors => ({ editorState: 'initial' });
|
|
31
|
+
|
|
32
32
|
export const mapPropsToValues = ({ cursorPosition: prevCursorPosition, value = '' }: Props): FormValues => {
|
|
33
33
|
const mentionState = withMentionDecorator(createMentionSelectorState(value));
|
|
34
34
|
const cursorPosition = value ? prevCursorPosition : 0;
|
|
@@ -68,8 +68,10 @@ export const handleSubmit = (
|
|
|
68
68
|
const ReplyFormContainer = connect(mapStateToProps)(
|
|
69
69
|
withFormik<Props, FormValues>({
|
|
70
70
|
handleSubmit,
|
|
71
|
+
mapPropsToErrors,
|
|
71
72
|
mapPropsToValues,
|
|
72
73
|
validate,
|
|
74
|
+
validateOnMount: true,
|
|
73
75
|
})(ReplyForm),
|
|
74
76
|
);
|
|
75
77
|
|
package/src/constants.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// Annotation CSS constants
|
|
2
|
-
export const CLASS_ANNOTATIONS_LOADED = 'ba-annotations-loaded';
|
|
3
|
-
|
|
4
1
|
export const ANNOTATOR_EVENT = {
|
|
5
2
|
fetch: 'annotationsfetched',
|
|
6
3
|
error: 'annotationerror',
|
|
@@ -8,8 +5,4 @@ export const ANNOTATOR_EVENT = {
|
|
|
8
5
|
setVisibility: 'annotationsetvisibility',
|
|
9
6
|
};
|
|
10
7
|
|
|
11
|
-
export const
|
|
12
|
-
type: 'user',
|
|
13
|
-
id: '0',
|
|
14
|
-
email: '',
|
|
15
|
-
};
|
|
8
|
+
export const MOUSE_PRIMARY = 1;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
@import '~box-ui-elements/es/styles/variables';
|
|
2
|
+
@import '../highlight/HighlightCreator';
|
|
3
|
+
|
|
1
4
|
.bp-doc {
|
|
2
5
|
.ba-Layer {
|
|
3
6
|
position: absolute;
|
|
@@ -7,4 +10,16 @@
|
|
|
7
10
|
width: 100%;
|
|
8
11
|
pointer-events: none;
|
|
9
12
|
}
|
|
13
|
+
|
|
14
|
+
&.ba-is-create--highlight {
|
|
15
|
+
.textLayer {
|
|
16
|
+
> span {
|
|
17
|
+
@include ba-HighlightCreator-cursor;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
::selection {
|
|
21
|
+
background-color: rgba($bdl-yellow, .33);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
10
25
|
}
|
|
@@ -1,32 +1,105 @@
|
|
|
1
|
-
import BaseAnnotator from '../common/BaseAnnotator';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import BaseAnnotator, { ANNOTATION_CLASSES, Options } from '../common/BaseAnnotator';
|
|
2
|
+
import PopupManager from '../popup/PopupManager';
|
|
3
|
+
import { centerDrawing, DrawingManager, isDrawing } from '../drawing';
|
|
4
|
+
import {
|
|
5
|
+
centerHighlight,
|
|
6
|
+
HighlightCreatorManager,
|
|
7
|
+
HighlightListener,
|
|
8
|
+
HighlightManager,
|
|
9
|
+
isHighlight,
|
|
10
|
+
} from '../highlight';
|
|
11
|
+
import { centerRegion, isRegion, RegionCreationManager, RegionManager } from '../region';
|
|
12
|
+
import { Event } from '../@types';
|
|
4
13
|
import { getAnnotation } from '../store/annotations';
|
|
5
|
-
import {
|
|
14
|
+
import { getSelection } from './docUtil';
|
|
15
|
+
import { Manager } from '../common/BaseManager';
|
|
16
|
+
import { getFileId, getIsCurrentFileVersion, Mode } from '../store';
|
|
17
|
+
import { scrollToLocation } from '../utils/scroll';
|
|
6
18
|
import './DocumentAnnotator.scss';
|
|
7
19
|
|
|
8
|
-
|
|
20
|
+
// Pdf.js textLayer enhancement requires waiting for 300ms (they hardcode this magic number)
|
|
21
|
+
const TEXT_LAYER_ENHANCEMENT = 300;
|
|
9
22
|
|
|
10
23
|
export default class DocumentAnnotator extends BaseAnnotator {
|
|
11
24
|
annotatedEl?: HTMLElement;
|
|
12
25
|
|
|
13
|
-
|
|
26
|
+
highlightListener?: HighlightListener;
|
|
14
27
|
|
|
15
|
-
|
|
28
|
+
managers: Map<number, Set<Manager>> = new Map();
|
|
29
|
+
|
|
30
|
+
constructor(options: Options) {
|
|
31
|
+
super(options);
|
|
32
|
+
|
|
33
|
+
this.highlightListener = new HighlightListener({ getSelection, store: this.store });
|
|
34
|
+
|
|
35
|
+
this.addListener(Event.ANNOTATIONS_MODE_CHANGE, this.handleChangeMode);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
destroy(): void {
|
|
39
|
+
this.removeListener(Event.ANNOTATIONS_MODE_CHANGE, this.handleChangeMode);
|
|
40
|
+
|
|
41
|
+
if (this.highlightListener) {
|
|
42
|
+
this.highlightListener.destroy();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (this.managers) {
|
|
46
|
+
this.managers.forEach(managers => managers.forEach(manager => manager.destroy()));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
super.destroy();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getAnnotatedElement(): HTMLElement | null | undefined {
|
|
53
|
+
return this.containerEl?.querySelector('.bp-doc');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
getPageManagers(pageEl: HTMLElement): Set<Manager> {
|
|
57
|
+
const fileId = getFileId(this.store.getState());
|
|
58
|
+
const isCurrentFileVersion = getIsCurrentFileVersion(this.store.getState());
|
|
16
59
|
const pageNumber = this.getPageNumber(pageEl);
|
|
17
60
|
const pageReferenceEl = this.getPageReference(pageEl);
|
|
18
|
-
const managers = this.managers.get(pageNumber) ||
|
|
61
|
+
const managers = this.managers.get(pageNumber) || new Set();
|
|
62
|
+
const resinTags = { fileid: fileId, iscurrent: isCurrentFileVersion };
|
|
19
63
|
|
|
20
64
|
// Destroy any managers that were attached to page elements that no longer exist
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
65
|
+
managers.forEach(manager => {
|
|
66
|
+
if (!manager.exists(pageEl)) {
|
|
67
|
+
manager.destroy();
|
|
68
|
+
managers.delete(manager);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
25
71
|
|
|
26
72
|
// Lazily instantiate managers as pages are added or re-rendered
|
|
27
|
-
if (managers.
|
|
28
|
-
|
|
29
|
-
managers.
|
|
73
|
+
if (managers.size === 0) {
|
|
74
|
+
managers.add(new PopupManager({ location: pageNumber, referenceEl: pageReferenceEl, resinTags }));
|
|
75
|
+
managers.add(new DrawingManager({ location: pageNumber, referenceEl: pageReferenceEl, resinTags }));
|
|
76
|
+
|
|
77
|
+
const textLayer = pageEl.querySelector('.textLayer') as HTMLElement;
|
|
78
|
+
|
|
79
|
+
if (textLayer) {
|
|
80
|
+
managers.add(
|
|
81
|
+
new HighlightCreatorManager({
|
|
82
|
+
getSelection,
|
|
83
|
+
referenceEl: textLayer,
|
|
84
|
+
selectionChangeDelay: TEXT_LAYER_ENHANCEMENT,
|
|
85
|
+
store: this.store,
|
|
86
|
+
}),
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
managers.add(new HighlightManager({ location: pageNumber, referenceEl: pageReferenceEl, resinTags }));
|
|
91
|
+
|
|
92
|
+
managers.add(new RegionManager({ location: pageNumber, referenceEl: pageReferenceEl, resinTags }));
|
|
93
|
+
|
|
94
|
+
const canvasLayerEl = pageEl.querySelector<HTMLElement>('.canvasWrapper');
|
|
95
|
+
|
|
96
|
+
managers.add(
|
|
97
|
+
new RegionCreationManager({
|
|
98
|
+
location: pageNumber,
|
|
99
|
+
referenceEl: canvasLayerEl || pageReferenceEl,
|
|
100
|
+
resinTags,
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
30
103
|
}
|
|
31
104
|
|
|
32
105
|
return managers;
|
|
@@ -52,23 +125,26 @@ export default class DocumentAnnotator extends BaseAnnotator {
|
|
|
52
125
|
return this.annotatedEl ? Array.from(this.annotatedEl.querySelectorAll('.page')) : [];
|
|
53
126
|
}
|
|
54
127
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (!this.rootEl) {
|
|
128
|
+
handleChangeMode = ({ mode }: { mode: Mode }): void => {
|
|
129
|
+
if (!this.annotatedEl) {
|
|
59
130
|
return;
|
|
60
131
|
}
|
|
61
132
|
|
|
62
|
-
this.
|
|
63
|
-
this.annotatedEl.classList.add(CLASS_ANNOTATIONS_LOADED);
|
|
133
|
+
this.removeAnnotationClasses();
|
|
64
134
|
|
|
65
|
-
this.
|
|
66
|
-
|
|
135
|
+
const annotatedElement = this.annotatedEl;
|
|
136
|
+
const className = ANNOTATION_CLASSES[mode];
|
|
137
|
+
if (className) {
|
|
138
|
+
annotatedElement.classList.add(className);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
67
141
|
|
|
68
142
|
render(): void {
|
|
69
143
|
this.getPages()
|
|
70
144
|
.filter(({ dataset }) => dataset.loaded && dataset.pageNumber)
|
|
71
145
|
.forEach(pageEl => this.renderPage(pageEl));
|
|
146
|
+
|
|
147
|
+
this.postRender();
|
|
72
148
|
}
|
|
73
149
|
|
|
74
150
|
renderPage(pageEl: HTMLElement): void {
|
|
@@ -92,43 +168,24 @@ export default class DocumentAnnotator extends BaseAnnotator {
|
|
|
92
168
|
}
|
|
93
169
|
|
|
94
170
|
const annotation = getAnnotation(this.store.getState(), annotationId);
|
|
95
|
-
const annotationPage = annotation?.target.location.value;
|
|
171
|
+
const annotationPage = annotation?.target.location.value ?? 1;
|
|
172
|
+
const annotationPageEl = this.getPage(annotationPage);
|
|
96
173
|
|
|
97
|
-
if (!annotation || !annotationPage) {
|
|
174
|
+
if (!annotation || !annotationPage || !annotationPageEl || !this.annotatedEl) {
|
|
98
175
|
return;
|
|
99
176
|
}
|
|
100
177
|
|
|
178
|
+
let offsets = null;
|
|
101
179
|
if (isRegion(annotation)) {
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const pageEl = this.getPage(page);
|
|
108
|
-
|
|
109
|
-
if (!this.annotatedEl || !pageEl) {
|
|
110
|
-
return;
|
|
180
|
+
offsets = centerRegion(annotation.target.shape);
|
|
181
|
+
} else if (isHighlight(annotation)) {
|
|
182
|
+
offsets = centerHighlight(annotation.target.shapes);
|
|
183
|
+
} else if (isDrawing(annotation)) {
|
|
184
|
+
offsets = centerDrawing(annotation.target.path_groups);
|
|
111
185
|
}
|
|
112
186
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const parentCenterY = Math.round(this.annotatedEl.clientHeight / 2);
|
|
116
|
-
const offsetCenterX = Math.round(pageEl.clientWidth * (offsetX / 100));
|
|
117
|
-
const offsetCenterY = Math.round(pageEl.clientHeight * (offsetY / 100));
|
|
118
|
-
const offsetScrollLeft = pageEl.offsetLeft - parentCenterX + offsetCenterX;
|
|
119
|
-
const offsetScrollTop = pageEl.offsetTop - parentCenterY + offsetCenterY;
|
|
120
|
-
const scrollLeft = Math.max(0, Math.min(offsetScrollLeft, this.annotatedEl.scrollWidth));
|
|
121
|
-
const scrollTop = Math.max(0, Math.min(offsetScrollTop, this.annotatedEl.scrollHeight));
|
|
122
|
-
|
|
123
|
-
if (canSmoothScroll && Math.abs(this.annotatedEl.scrollTop - scrollTop) < SCROLL_THRESHOLD) {
|
|
124
|
-
this.annotatedEl.scrollTo({
|
|
125
|
-
behavior: 'smooth',
|
|
126
|
-
left: scrollLeft,
|
|
127
|
-
top: scrollTop,
|
|
128
|
-
});
|
|
129
|
-
} else {
|
|
130
|
-
this.annotatedEl.scrollLeft = scrollLeft;
|
|
131
|
-
this.annotatedEl.scrollTop = scrollTop;
|
|
187
|
+
if (offsets) {
|
|
188
|
+
scrollToLocation(this.annotatedEl, annotationPageEl, { offsets });
|
|
132
189
|
}
|
|
133
190
|
}
|
|
134
191
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
type Selection = {
|
|
2
|
+
containerRect: DOMRect;
|
|
3
|
+
hasError?: boolean;
|
|
4
|
+
location: number;
|
|
5
|
+
range: Range;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Finds the closest ancestor DOM element with the specified class.
|
|
10
|
+
*/
|
|
11
|
+
export function findClosestElWithClass(element: Element | null, className: string): Element | null {
|
|
12
|
+
for (let el = element; el && el !== document.body; el = el.parentNode as Element | null) {
|
|
13
|
+
if (el.classList && el.classList.contains(className)) {
|
|
14
|
+
return el;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Returns the page element and page number that the element is on.
|
|
23
|
+
*/
|
|
24
|
+
export function getPageNumber(element: Element | null): number | undefined {
|
|
25
|
+
const pageEl = findClosestElWithClass(element, 'page');
|
|
26
|
+
const pageNumber = pageEl && pageEl.getAttribute('data-page-number');
|
|
27
|
+
|
|
28
|
+
return pageNumber ? parseInt(pageNumber, 10) : undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getSelection(): Selection | null {
|
|
32
|
+
const selection = window.getSelection();
|
|
33
|
+
|
|
34
|
+
if (!selection || selection.isCollapsed || !selection.rangeCount || !selection.focusNode) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Firefox allows to select multiple ranges in the document by using Ctrl/Cmd+click
|
|
39
|
+
// We only care about the last range
|
|
40
|
+
let range = selection.getRangeAt(selection.rangeCount - 1);
|
|
41
|
+
|
|
42
|
+
const containerEl = findClosestElWithClass(range.endContainer as Element, 'textLayer');
|
|
43
|
+
const startPage = getPageNumber(range.startContainer as Element);
|
|
44
|
+
const endPage = getPageNumber(range.endContainer as Element);
|
|
45
|
+
|
|
46
|
+
if (!containerEl || !startPage || !endPage) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const hasError = startPage !== endPage;
|
|
51
|
+
let location = endPage;
|
|
52
|
+
|
|
53
|
+
if (hasError) {
|
|
54
|
+
range = document.createRange();
|
|
55
|
+
range.setEnd(selection.focusNode, selection.focusOffset);
|
|
56
|
+
range.collapse(false); // Parameter toStart is not optional in IE
|
|
57
|
+
location = getPageNumber(selection.focusNode as Element) ?? endPage;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { containerRect: containerEl.getBoundingClientRect(), hasError, location, range };
|
|
61
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { white } from 'box-ui-elements/es/styles/variables';
|
|
3
|
+
import DrawingPath from './DrawingPath';
|
|
4
|
+
import SVGFilterContext from './SVGFilterContext';
|
|
5
|
+
import { getPathCommands } from './drawingUtil';
|
|
6
|
+
import { Position } from '../@types';
|
|
7
|
+
import './DecoratedDrawingPath.scss';
|
|
8
|
+
|
|
9
|
+
export type Props = {
|
|
10
|
+
borderStrokeWidth?: number;
|
|
11
|
+
isDecorated?: boolean;
|
|
12
|
+
points?: Position[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default function DecoratedDrawingPath({
|
|
16
|
+
borderStrokeWidth = 0,
|
|
17
|
+
isDecorated = false,
|
|
18
|
+
points = [],
|
|
19
|
+
}: Props): JSX.Element {
|
|
20
|
+
const filterID = React.useContext(SVGFilterContext);
|
|
21
|
+
const pathCommands = getPathCommands(points);
|
|
22
|
+
return (
|
|
23
|
+
<g className="ba-DecoratedDrawingPath">
|
|
24
|
+
{isDecorated && (
|
|
25
|
+
<g className="ba-DecoratedDrawingPath-decoration">
|
|
26
|
+
<DrawingPath
|
|
27
|
+
className="ba-DecoratedDrawingPath-shadow"
|
|
28
|
+
filter={`url(#${filterID})`}
|
|
29
|
+
pathCommands={pathCommands}
|
|
30
|
+
/>
|
|
31
|
+
<DrawingPath
|
|
32
|
+
className="ba-DecoratedDrawingPath-border"
|
|
33
|
+
fill="none"
|
|
34
|
+
pathCommands={pathCommands}
|
|
35
|
+
stroke={white}
|
|
36
|
+
strokeWidth={borderStrokeWidth}
|
|
37
|
+
/>
|
|
38
|
+
</g>
|
|
39
|
+
)}
|
|
40
|
+
<DrawingPath pathCommands={pathCommands} />
|
|
41
|
+
</g>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
.ba-DrawingAnnotations-list {
|
|
2
|
+
position: absolute;
|
|
3
|
+
top: 0;
|
|
4
|
+
left: 0;
|
|
5
|
+
width: 100%;
|
|
6
|
+
height: 100%;
|
|
7
|
+
pointer-events: none;
|
|
8
|
+
|
|
9
|
+
&.is-listening {
|
|
10
|
+
.ba-DrawingTarget {
|
|
11
|
+
pointer-events: auto; // Delegate event control to avoid re-rendering every target on mousedown/up
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.ba-DrawingAnnotations-creator,
|
|
17
|
+
.ba-DrawingAnnotations-target {
|
|
18
|
+
position: absolute;
|
|
19
|
+
top: 0;
|
|
20
|
+
left: 0;
|
|
21
|
+
width: 100%;
|
|
22
|
+
height: 100%;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.ba-DrawingAnnotations-creator {
|
|
26
|
+
pointer-events: auto;
|
|
27
|
+
touch-action: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.ba-DrawingAnnotations-toolbar {
|
|
31
|
+
opacity: 1;
|
|
32
|
+
transition: opacity 200ms ease;
|
|
33
|
+
pointer-events: auto;
|
|
34
|
+
|
|
35
|
+
&.ba-is-drawing {
|
|
36
|
+
opacity: 0;
|
|
37
|
+
pointer-events: none;
|
|
38
|
+
}
|
|
39
|
+
}
|