box-annotations 5.2.1-beta.7 → 5.3.0
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/dist/annotations.css +1 -1
- package/dist/annotations.js +1 -1
- package/dist/annotations.js.LICENSE.txt +1 -1
- package/index.js +1 -1
- package/package.json +33 -22
- package/src/@types/api.ts +3 -2
- package/src/@types/events.ts +27 -10
- package/src/@types/global.ts +0 -9
- package/src/@types/model.ts +9 -0
- package/src/BoxAnnotations.ts +5 -1
- package/src/adapters/threadedAnnotationsAdapters.ts +39 -26
- package/src/api/APIFactory.ts +6 -1
- package/src/api/types.ts +23 -1
- package/src/common/AnnotationCallbacksContext.ts +7 -0
- package/src/common/BaseAnnotator.scss +14 -1
- package/src/common/BaseAnnotator.ts +46 -2
- package/src/common/BaseManager.ts +2 -0
- package/src/common/DeselectListener.tsx +3 -1
- package/src/common/withProviders.tsx +6 -2
- package/src/components/Popups/PopupDrawingToolbar.tsx +0 -7
- package/src/components/Popups/PopupV2.scss +4 -1
- package/src/components/Popups/PopupV2.tsx +122 -47
- package/src/document/DocumentAnnotator.ts +8 -4
- package/src/document/docUtil.ts +2 -1
- package/src/drawing/DrawingAnnotations.tsx +23 -14
- package/src/drawing/DrawingAnnotationsContainer.tsx +3 -0
- package/src/drawing/DrawingCreator.tsx +9 -16
- package/src/drawing/DrawingManager.tsx +13 -2
- package/src/highlight/HighlightAnnotations.tsx +27 -14
- package/src/highlight/HighlightContainer.tsx +3 -0
- package/src/highlight/HighlightCreatorManager.ts +8 -5
- package/src/highlight/HighlightListener.ts +4 -2
- package/src/highlight/HighlightManager.tsx +13 -2
- package/src/image/ImageAnnotator.ts +3 -2
- package/src/media/MediaAnnotator.ts +2 -1
- package/src/popup/PopupContainer.tsx +8 -1
- package/src/popup/PopupLayer.tsx +41 -6
- package/src/popup/PopupManager.tsx +24 -4
- package/src/region/RegionCreation.tsx +11 -8
- package/src/region/RegionCreationContainer.tsx +2 -2
- package/src/region/RegionCreator.tsx +9 -17
- package/src/store/annotations/actions.ts +89 -0
- package/src/store/annotations/reducer.ts +48 -0
- package/src/store/eventing/create.ts +3 -3
- package/src/store/eventing/delete.ts +27 -0
- package/src/store/eventing/middleware.ts +40 -4
- package/src/store/eventing/replyCreate.ts +34 -0
- package/src/store/eventing/replyDelete.ts +35 -0
- package/src/store/eventing/replyUpdate.ts +39 -0
- package/src/store/eventing/types.ts +4 -4
- package/src/store/eventing/update.ts +29 -0
- package/src/store/highlight/actions.ts +32 -3
- package/src/store/highlight/types.ts +1 -0
- package/src/store/options/selectors.ts +2 -2
- package/src/store/options/types.ts +2 -2
- package/src/utils/rotate.ts +43 -0
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "box-annotations",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"description": "Box Annotations",
|
|
5
5
|
"author": "Box (https://www.box.com/)",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
7
|
+
"sideEffects": [
|
|
8
|
+
"./dist/annotations.js",
|
|
9
|
+
"**/*.css",
|
|
10
|
+
"**/*.scss"
|
|
11
|
+
],
|
|
7
12
|
"repository": {
|
|
8
13
|
"type": "git",
|
|
9
14
|
"url": "git@github.com:box/box-annotations.git"
|
|
@@ -12,7 +17,7 @@
|
|
|
12
17
|
"registry": "https://registry.npmjs.org/"
|
|
13
18
|
},
|
|
14
19
|
"engines": {
|
|
15
|
-
"node": ">=
|
|
20
|
+
"node": ">=20.x",
|
|
16
21
|
"yarn": ">=1.10.x"
|
|
17
22
|
},
|
|
18
23
|
"devDependencies": {
|
|
@@ -27,14 +32,17 @@
|
|
|
27
32
|
"@babel/preset-env": "^7.25.9",
|
|
28
33
|
"@babel/preset-react": "^7.25.9",
|
|
29
34
|
"@babel/preset-typescript": "^7.25.9",
|
|
35
|
+
"@box/blueprint-web": "^16.12.0",
|
|
36
|
+
"@box/blueprint-web-assets": "^5.5.12",
|
|
37
|
+
"@box/collaboration-popover": "^2.1.32",
|
|
38
|
+
"@box/combobox-with-api": "^1.42.22",
|
|
30
39
|
"@box/frontend": "^10.0.0",
|
|
31
|
-
"@box/
|
|
32
|
-
"@box/blueprint-web-assets": "^4.114.3",
|
|
33
|
-
"@box/collaboration-popover": "^1.60.2",
|
|
40
|
+
"@box/item-icon": "^3.2.0",
|
|
34
41
|
"@box/languages": "^1.1.0",
|
|
35
|
-
"@box/readable-time": "^1.
|
|
36
|
-
"@box/threaded-annotations": "^
|
|
37
|
-
"@box/
|
|
42
|
+
"@box/readable-time": "^2.1.32",
|
|
43
|
+
"@box/threaded-annotations": "^4.11.0",
|
|
44
|
+
"@box/types": "^2.1.8",
|
|
45
|
+
"@box/user-selector": "^2.1.35",
|
|
38
46
|
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
|
|
39
47
|
"@commitlint/cli": "^8.3.5",
|
|
40
48
|
"@commitlint/config-conventional": "^8.2.0",
|
|
@@ -48,7 +56,7 @@
|
|
|
48
56
|
"@types/classnames": "^2.2.10",
|
|
49
57
|
"@types/draft-js": "^0.10.40",
|
|
50
58
|
"@types/enzyme": "^3.10.5",
|
|
51
|
-
"@types/jest": "^
|
|
59
|
+
"@types/jest": "^29.5.14",
|
|
52
60
|
"@types/lodash": "^4.14.150",
|
|
53
61
|
"@types/react": "18.3.12",
|
|
54
62
|
"@types/react-dom": "18.3.1",
|
|
@@ -59,7 +67,7 @@
|
|
|
59
67
|
"autoprefixer": "^10.4.19",
|
|
60
68
|
"axios": "^0.24.0",
|
|
61
69
|
"babel-eslint": "^10.1.0",
|
|
62
|
-
"babel-jest": "^
|
|
70
|
+
"babel-jest": "^29.7.0",
|
|
63
71
|
"babel-loader": "^9.2.1",
|
|
64
72
|
"babel-plugin-istanbul": "^6.0.0",
|
|
65
73
|
"babel-plugin-react-intl": "^7.5.1",
|
|
@@ -73,7 +81,7 @@
|
|
|
73
81
|
"core-js": "^3.38.1",
|
|
74
82
|
"css-loader": "^7.1.2",
|
|
75
83
|
"css-minimizer-webpack-plugin": "^7.0.4",
|
|
76
|
-
"cypress": "^
|
|
84
|
+
"cypress": "^15.20.1",
|
|
77
85
|
"draft-js": "0.10.5",
|
|
78
86
|
"enzyme": "^3.11.0",
|
|
79
87
|
"enzyme-to-json": "^3.4.4",
|
|
@@ -93,9 +101,9 @@
|
|
|
93
101
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
94
102
|
"formik": "^2.1.4",
|
|
95
103
|
"husky": "^3.1.0",
|
|
96
|
-
"jest": "^
|
|
97
|
-
"jest-canvas-mock": "^2.
|
|
98
|
-
"jest-environment-jsdom
|
|
104
|
+
"jest": "^29.7.0",
|
|
105
|
+
"jest-canvas-mock": "^2.5.2",
|
|
106
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
99
107
|
"lint-staged": "^9.5.0",
|
|
100
108
|
"lodash": "^4.17.15",
|
|
101
109
|
"mini-css-extract-plugin": "^2.10.1",
|
|
@@ -121,19 +129,22 @@
|
|
|
121
129
|
"tabbable": "^1.1.3",
|
|
122
130
|
"typescript": "4.9.5",
|
|
123
131
|
"uuid": "^8.3.1",
|
|
124
|
-
"wait-on": "^
|
|
132
|
+
"wait-on": "^9.0.5",
|
|
125
133
|
"webpack": "^5.105.4",
|
|
126
134
|
"webpack-cli": "^5.1.4",
|
|
127
135
|
"webpack-dev-server": "^5.2.3",
|
|
128
136
|
"worker-farm": "^1.7.0"
|
|
129
137
|
},
|
|
130
138
|
"peerDependencies": {
|
|
131
|
-
"@box/blueprint-web": "^
|
|
132
|
-
"@box/blueprint-web-assets": "^
|
|
133
|
-
"@box/collaboration-popover": "^1.
|
|
134
|
-
"@box/
|
|
135
|
-
"@box/
|
|
136
|
-
"@box/
|
|
139
|
+
"@box/blueprint-web": "^16.12.0",
|
|
140
|
+
"@box/blueprint-web-assets": "^5.5.12",
|
|
141
|
+
"@box/collaboration-popover": "^2.1.32",
|
|
142
|
+
"@box/combobox-with-api": "^1.42.22",
|
|
143
|
+
"@box/item-icon": "^3.2.0",
|
|
144
|
+
"@box/readable-time": "^2.1.32",
|
|
145
|
+
"@box/threaded-annotations": "^4.11.0",
|
|
146
|
+
"@box/types": "^2.1.8",
|
|
147
|
+
"@box/user-selector": "^2.1.35"
|
|
137
148
|
},
|
|
138
149
|
"scripts": {
|
|
139
150
|
"build": "yarn setup && yarn build:prod:dist",
|
|
@@ -143,7 +154,7 @@
|
|
|
143
154
|
"clean": "rm -rf dist reports/* i18n/json i18n/*.js",
|
|
144
155
|
"cy:open": "yarn cy:wait; yarn cypress open",
|
|
145
156
|
"cy:run": "yarn cy:wait; yarn cypress run --spec test/integration/**/*.test.js",
|
|
146
|
-
"cy:wait": "wait-on http-get://
|
|
157
|
+
"cy:wait": "wait-on http-get://127.0.0.1:8001",
|
|
147
158
|
"lint": "npm-run-all lint:*",
|
|
148
159
|
"lint:js": "eslint .",
|
|
149
160
|
"lint:scss": "stylelint \"src/**/*.scss\" --syntax scss",
|
package/src/@types/api.ts
CHANGED
|
@@ -10,5 +10,6 @@ export interface Permissions {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export type TokenLiteral = null | undefined | string | { read: string; write?: string };
|
|
13
|
-
export type
|
|
14
|
-
export type
|
|
13
|
+
export type TokenMap = { [typedFileId: string]: TokenLiteral };
|
|
14
|
+
export type TokenResolver = (typedFileId?: string) => TokenLiteral | TokenMap | Promise<TokenLiteral | TokenMap>;
|
|
15
|
+
export type Token = TokenLiteral | TokenMap | TokenResolver;
|
package/src/@types/events.ts
CHANGED
|
@@ -1,19 +1,36 @@
|
|
|
1
1
|
enum Event {
|
|
2
|
-
|
|
2
|
+
// Inbound: emitted by the host; box-annotations listens.
|
|
3
3
|
ACTIVE_SET = 'annotations_active_set',
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
ANNOTATION_REMOVE = 'annotations_remove',
|
|
5
|
+
BOUNDING_BOX_HIGHLIGHTS_SET = 'bounding_box_highlights_set',
|
|
6
|
+
BOUNDING_BOX_HIGHLIGHT_SELECT = 'bounding_box_highlight_select',
|
|
7
|
+
COLOR_SET = 'annotations_color_set',
|
|
8
|
+
VIEW_MODE_SET = 'view_mode_set',
|
|
9
|
+
VISIBLE_SET = 'annotations_visible_set',
|
|
10
|
+
|
|
11
|
+
// Outbound: emitted by box-annotations; the host listens.
|
|
12
|
+
ACTIVE_CHANGE = 'annotations_active_change',
|
|
6
13
|
ANNOTATION_CREATE = 'annotations_create',
|
|
14
|
+
ANNOTATION_DELETE = 'annotations_delete',
|
|
7
15
|
ANNOTATION_FETCH_ERROR = 'annotations_fetch_error',
|
|
8
|
-
|
|
16
|
+
ANNOTATION_REPLY_CREATE = 'annotations_reply_create',
|
|
17
|
+
ANNOTATION_REPLY_DELETE = 'annotations_reply_delete',
|
|
18
|
+
ANNOTATION_REPLY_UPDATE = 'annotations_reply_update',
|
|
19
|
+
ANNOTATION_UPDATE = 'annotations_update',
|
|
9
20
|
ANNOTATIONS_INITIALIZED = 'annotations_initialized',
|
|
10
21
|
ANNOTATIONS_MODE_CHANGE = 'annotations_mode_change',
|
|
11
|
-
|
|
12
|
-
|
|
22
|
+
CREATOR_STAGED_CHANGE = 'creator_staged_change',
|
|
23
|
+
CREATOR_STATUS_CHANGE = 'creator_status_change',
|
|
24
|
+
|
|
25
|
+
// Internal: box-annotations emits and listens to itself (redux state change → DOM side effect).
|
|
13
26
|
BOUNDING_BOX_HIGHLIGHT_NAVIGATE = 'bounding_box_highlight_navigate',
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
enum SidebarEvent {
|
|
30
|
+
SIDEBAR_ANNOTATION_UPDATE = 'sidebar.annotations_update',
|
|
31
|
+
SIDEBAR_REPLY_CREATE = 'sidebar.annotations_reply_create',
|
|
32
|
+
SIDEBAR_REPLY_DELETE = 'sidebar.annotations_reply_delete',
|
|
33
|
+
SIDEBAR_REPLY_UPDATE = 'sidebar.annotations_reply_update',
|
|
17
34
|
}
|
|
18
35
|
|
|
19
36
|
// Existing legacy events, don't rename
|
|
@@ -23,4 +40,4 @@ enum LegacyEvent {
|
|
|
23
40
|
SCALE = 'scaleannotations',
|
|
24
41
|
}
|
|
25
42
|
|
|
26
|
-
export { Event, LegacyEvent };
|
|
43
|
+
export { Event, LegacyEvent, SidebarEvent };
|
package/src/@types/global.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-namespace */
|
|
2
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3
|
-
declare namespace NodeJS {
|
|
4
|
-
interface Global {
|
|
5
|
-
window: any;
|
|
6
|
-
BoxAnnotations: any;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
1
|
declare module 'box-annotations-locale-data';
|
|
11
2
|
declare module 'box-elements-messages';
|
|
12
3
|
declare module 'box-ui-elements/es/*'; // TODO: Figure out why types don't register properly
|
package/src/@types/model.ts
CHANGED
|
@@ -86,15 +86,24 @@ export interface Rect extends Position {
|
|
|
86
86
|
width: number;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
export interface ReplyPermissions {
|
|
90
|
+
can_delete?: boolean;
|
|
91
|
+
can_edit?: boolean;
|
|
92
|
+
can_reply?: boolean;
|
|
93
|
+
can_resolve?: boolean;
|
|
94
|
+
}
|
|
95
|
+
|
|
89
96
|
export interface Reply {
|
|
90
97
|
created_at: string;
|
|
91
98
|
created_by: User;
|
|
92
99
|
id: string;
|
|
93
100
|
message: string;
|
|
101
|
+
modified_at?: string;
|
|
94
102
|
parent: {
|
|
95
103
|
id: string;
|
|
96
104
|
type: string;
|
|
97
105
|
};
|
|
106
|
+
permissions?: ReplyPermissions;
|
|
98
107
|
type: 'reply';
|
|
99
108
|
}
|
|
100
109
|
|
package/src/BoxAnnotations.ts
CHANGED
|
@@ -16,9 +16,11 @@ type Annotator = {
|
|
|
16
16
|
type AnnotationsOptions = {
|
|
17
17
|
features: Features;
|
|
18
18
|
intl: IntlOptions;
|
|
19
|
+
onCopyLink?: (params: { annotationId: string; fileVersionId: string }) => void;
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
export type Features = {
|
|
23
|
+
isRichTextEnabled?: boolean;
|
|
22
24
|
isThreadedAnnotation?: boolean;
|
|
23
25
|
[key: string]: boolean | undefined;
|
|
24
26
|
};
|
|
@@ -43,6 +45,8 @@ type ViewerOption = {
|
|
|
43
45
|
|
|
44
46
|
type ViewerOptions = Record<string, ViewerOption>;
|
|
45
47
|
|
|
48
|
+
type GlobalWithBoxAnnotations = typeof globalThis & { BoxAnnotations: typeof BoxAnnotations };
|
|
49
|
+
|
|
46
50
|
/**
|
|
47
51
|
* NAME: The name of the annotator.
|
|
48
52
|
* CONSTRUCTOR: Constructor for the annotator.
|
|
@@ -145,5 +149,5 @@ class BoxAnnotations {
|
|
|
145
149
|
}
|
|
146
150
|
}
|
|
147
151
|
|
|
148
|
-
|
|
152
|
+
(globalThis as GlobalWithBoxAnnotations).BoxAnnotations = BoxAnnotations;
|
|
149
153
|
export default BoxAnnotations;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DocumentNodeV2, MentionNodeV2, ParagraphNodeV2, TextMessageTypeV2, TextNodeV2 } from '@box/threaded-annotations';
|
|
2
|
+
import { parseMessageMarkdown } from '@box/threaded-annotations';
|
|
2
3
|
|
|
3
4
|
import type { Annotation, Collaborator, Reply, UserMini } from '../@types';
|
|
4
5
|
|
|
@@ -69,10 +70,27 @@ export const deserializeMentionMarkup = (text: string): DocumentNodeV2 => {
|
|
|
69
70
|
return { type: 'doc', content };
|
|
70
71
|
};
|
|
71
72
|
|
|
73
|
+
const toDocumentNode = (text: string, isRichTextEnabled = false): DocumentNodeV2 =>
|
|
74
|
+
isRichTextEnabled ? (parseMessageMarkdown(text) as DocumentNodeV2) : deserializeMentionMarkup(text);
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Returns the edit timestamp consumers use to render an edited indicator.
|
|
78
|
+
* Compares parsed instants, not raw strings, so equivalent ISO formats
|
|
79
|
+
* (Z vs +00:00, fractional precision) are treated as unedited.
|
|
80
|
+
*/
|
|
81
|
+
const toUpdatedAt = (createdAt: string, modifiedAt: string | undefined): number | undefined => {
|
|
82
|
+
if (!modifiedAt) return undefined;
|
|
83
|
+
const modifiedMs = new Date(modifiedAt).getTime();
|
|
84
|
+
if (Number.isNaN(modifiedMs)) return undefined;
|
|
85
|
+
const createdMs = new Date(createdAt).getTime();
|
|
86
|
+
if (modifiedMs === createdMs) return undefined;
|
|
87
|
+
return modifiedMs;
|
|
88
|
+
};
|
|
89
|
+
|
|
72
90
|
/**
|
|
73
91
|
* Converts a box-annotations Reply to a threaded-annotations TextMessageType.
|
|
74
92
|
*/
|
|
75
|
-
export const replyToTextMessage = (reply: Reply): TextMessageTypeV2 => ({
|
|
93
|
+
export const replyToTextMessage = (reply: Reply, isRichTextEnabled = false): TextMessageTypeV2 => ({
|
|
76
94
|
author: {
|
|
77
95
|
email: reply.created_by?.login ?? '',
|
|
78
96
|
id: parseInt(reply.created_by?.id ?? '0', 10),
|
|
@@ -80,55 +98,50 @@ export const replyToTextMessage = (reply: Reply): TextMessageTypeV2 => ({
|
|
|
80
98
|
},
|
|
81
99
|
createdAt: new Date(reply.created_at).getTime(),
|
|
82
100
|
id: reply.id,
|
|
83
|
-
message:
|
|
101
|
+
message: toDocumentNode(reply.message, isRichTextEnabled),
|
|
84
102
|
permissions: {
|
|
85
|
-
canDelete: false,
|
|
86
|
-
canEdit: false,
|
|
87
|
-
canReply:
|
|
88
|
-
canResolve: false,
|
|
103
|
+
canDelete: reply.permissions?.can_delete ?? false,
|
|
104
|
+
canEdit: reply.permissions?.can_edit ?? false,
|
|
105
|
+
canReply: reply.permissions?.can_reply ?? false,
|
|
106
|
+
canResolve: reply.permissions?.can_resolve ?? false,
|
|
89
107
|
},
|
|
108
|
+
updatedAt: toUpdatedAt(reply.created_at, reply.modified_at),
|
|
90
109
|
});
|
|
91
110
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
* Falls back to annotation-level fields when the description object is sparse
|
|
96
|
-
* (the list endpoint returns description with only { message }).
|
|
97
|
-
*/
|
|
98
|
-
const descriptionToTextMessage = (
|
|
99
|
-
annotation: Annotation,
|
|
100
|
-
reply: Reply,
|
|
101
|
-
): TextMessageTypeV2 => ({
|
|
111
|
+
// The root message shares the annotation's author and permissions; description
|
|
112
|
+
// comes back sparse ({ message } only) from the list endpoint.
|
|
113
|
+
const descriptionToTextMessage = (annotation: Annotation, isRichTextEnabled = false): TextMessageTypeV2 => ({
|
|
102
114
|
author: {
|
|
103
|
-
email:
|
|
104
|
-
id: parseInt(
|
|
105
|
-
name:
|
|
115
|
+
email: annotation.created_by?.login ?? '',
|
|
116
|
+
id: parseInt(annotation.created_by?.id ?? '0', 10),
|
|
117
|
+
name: annotation.created_by?.name ?? '',
|
|
106
118
|
},
|
|
107
|
-
createdAt: new Date(
|
|
108
|
-
id:
|
|
109
|
-
message:
|
|
119
|
+
createdAt: new Date(annotation.created_at).getTime(),
|
|
120
|
+
id: annotation.id,
|
|
121
|
+
message: toDocumentNode(annotation.description?.message ?? '', isRichTextEnabled),
|
|
110
122
|
permissions: {
|
|
111
123
|
canDelete: annotation.permissions?.can_delete ?? false,
|
|
112
124
|
canEdit: annotation.permissions?.can_edit ?? false,
|
|
113
|
-
canReply:
|
|
125
|
+
canReply: annotation.permissions?.can_reply ?? false,
|
|
114
126
|
canResolve: annotation.permissions?.can_resolve ?? false,
|
|
115
127
|
},
|
|
128
|
+
updatedAt: toUpdatedAt(annotation.created_at, annotation.modified_at),
|
|
116
129
|
});
|
|
117
130
|
|
|
118
131
|
/**
|
|
119
132
|
* Maps a full Annotation (description + replies) to an array of TextMessageType
|
|
120
133
|
* suitable for ThreadedAnnotationsV2.
|
|
121
134
|
*/
|
|
122
|
-
export const annotationToMessages = (annotation: Annotation): TextMessageTypeV2[] => {
|
|
135
|
+
export const annotationToMessages = (annotation: Annotation, isRichTextEnabled = false): TextMessageTypeV2[] => {
|
|
123
136
|
const messages: TextMessageTypeV2[] = [];
|
|
124
137
|
|
|
125
138
|
if (annotation.description) {
|
|
126
|
-
messages.push(descriptionToTextMessage(annotation,
|
|
139
|
+
messages.push(descriptionToTextMessage(annotation, isRichTextEnabled));
|
|
127
140
|
}
|
|
128
141
|
|
|
129
142
|
if (annotation.replies) {
|
|
130
143
|
annotation.replies.forEach(reply => {
|
|
131
|
-
messages.push(replyToTextMessage(reply));
|
|
144
|
+
messages.push(replyToTextMessage(reply, isRichTextEnabled));
|
|
132
145
|
});
|
|
133
146
|
}
|
|
134
147
|
|
package/src/api/APIFactory.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import Annotations from 'box-ui-elements/es/api/Annotations';
|
|
2
2
|
import FileCollaborators from 'box-ui-elements/es/api/FileCollaborators';
|
|
3
|
+
import ThreadedComments from 'box-ui-elements/es/api/ThreadedComments';
|
|
3
4
|
import { DEFAULT_HOSTNAME_API } from 'box-ui-elements/es/constants';
|
|
4
|
-
import { AnnotationsAPI, CollaboratorsAPI, APIOptions } from './types';
|
|
5
|
+
import { AnnotationsAPI, CollaboratorsAPI, APIOptions, ThreadedCommentsAPI } from './types';
|
|
5
6
|
|
|
6
7
|
export default class APIFactory {
|
|
7
8
|
options: APIOptions;
|
|
@@ -21,4 +22,8 @@ export default class APIFactory {
|
|
|
21
22
|
getCollaboratorsAPI(): CollaboratorsAPI {
|
|
22
23
|
return new FileCollaborators(this.options);
|
|
23
24
|
}
|
|
25
|
+
|
|
26
|
+
getThreadedCommentsAPI(): ThreadedCommentsAPI {
|
|
27
|
+
return new ThreadedComments(this.options);
|
|
28
|
+
}
|
|
24
29
|
}
|
package/src/api/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Annotation, Collaborator, NewAnnotation, Permissions, Reply, Token } from '../@types';
|
|
1
|
+
import { Annotation, Collaborator, NewAnnotation, Permissions, Reply, ReplyPermissions, Token } from '../@types';
|
|
2
2
|
|
|
3
3
|
export type APICollection<R> = {
|
|
4
4
|
entries: R[];
|
|
@@ -73,6 +73,28 @@ export interface AnnotationsAPI {
|
|
|
73
73
|
destroy(): void;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
export interface ThreadedCommentsAPI {
|
|
77
|
+
deleteComment(args: {
|
|
78
|
+
commentId: string;
|
|
79
|
+
errorCallback: (error: APIError) => void;
|
|
80
|
+
fileId: string | null;
|
|
81
|
+
permissions: ReplyPermissions;
|
|
82
|
+
successCallback: () => void;
|
|
83
|
+
}): void;
|
|
84
|
+
|
|
85
|
+
updateComment(args: {
|
|
86
|
+
commentId: string;
|
|
87
|
+
errorCallback: (error: APIError) => void;
|
|
88
|
+
fileId: string | null;
|
|
89
|
+
message?: string;
|
|
90
|
+
permissions: ReplyPermissions;
|
|
91
|
+
status?: string;
|
|
92
|
+
successCallback: (reply: Reply) => void;
|
|
93
|
+
}): void;
|
|
94
|
+
|
|
95
|
+
destroy(): void;
|
|
96
|
+
}
|
|
97
|
+
|
|
76
98
|
export interface CollaboratorsAPI {
|
|
77
99
|
getFileCollaborators(
|
|
78
100
|
fileId: string | null,
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
@import '~box-ui-elements/es/styles/common/buttons';
|
|
8
8
|
|
|
9
9
|
&.is-hidden {
|
|
10
|
-
.ba-Layer
|
|
10
|
+
.ba-Layer,
|
|
11
|
+
.ba-PopupPortal {
|
|
11
12
|
display: none;
|
|
12
13
|
}
|
|
13
14
|
}
|
|
@@ -27,3 +28,15 @@
|
|
|
27
28
|
.ba-Layer--popup {
|
|
28
29
|
z-index: 4;
|
|
29
30
|
}
|
|
31
|
+
|
|
32
|
+
.ba-PopupPortal {
|
|
33
|
+
position: relative;
|
|
34
|
+
z-index: 10;
|
|
35
|
+
pointer-events: none;
|
|
36
|
+
|
|
37
|
+
.ba-Popup,
|
|
38
|
+
.ba-PopupV2 {
|
|
39
|
+
z-index: 150;
|
|
40
|
+
pointer-events: auto;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -6,7 +6,7 @@ import DeselectManager from './DeselectManager';
|
|
|
6
6
|
import EventEmitter from './EventEmitter';
|
|
7
7
|
import i18n from '../utils/i18n';
|
|
8
8
|
import messages from '../messages';
|
|
9
|
-
import { Event, IntlOptions, LegacyEvent, Permissions } from '../@types';
|
|
9
|
+
import { Event, IntlOptions, LegacyEvent, Permissions, SidebarEvent, Token } from '../@types';
|
|
10
10
|
import { BoundingBox, getBoundingBoxHighlights } from '../store/boundingBoxHighlights';
|
|
11
11
|
import { ViewMode } from '../store/options/types';
|
|
12
12
|
import { Features } from '../BoxAnnotations';
|
|
@@ -44,11 +44,13 @@ export type Options = {
|
|
|
44
44
|
initialViewMode?: ViewMode;
|
|
45
45
|
intl: IntlOptions;
|
|
46
46
|
locale?: string;
|
|
47
|
-
|
|
47
|
+
onCopyLink?: (params: { annotationId: string; fileVersionId: string }) => void;
|
|
48
|
+
token: Token;
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
export const CSS_CONTAINER_CLASS = 'ba';
|
|
51
52
|
export const CSS_LOADED_CLASS = 'ba-annotations-loaded';
|
|
53
|
+
export const CSS_POPUP_PORTAL_CLASS = 'ba-PopupPortal';
|
|
52
54
|
export const ANNOTATION_CLASSES: { [M in store.Mode]?: string } = {
|
|
53
55
|
[store.Mode.HIGHLIGHT]: 'ba-is-create--highlight',
|
|
54
56
|
[store.Mode.REGION]: 'ba-is-create--region',
|
|
@@ -59,6 +61,8 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
59
61
|
|
|
60
62
|
containerEl?: HTMLElement | null;
|
|
61
63
|
|
|
64
|
+
popupPortalEl?: HTMLElement | null;
|
|
65
|
+
|
|
62
66
|
container: Container;
|
|
63
67
|
|
|
64
68
|
deselectManager: DeselectManager | null = null;
|
|
@@ -67,6 +71,8 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
67
71
|
|
|
68
72
|
intl: IntlShape;
|
|
69
73
|
|
|
74
|
+
onCopyLink?: (params: { annotationId: string; fileVersionId: string }) => void;
|
|
75
|
+
|
|
70
76
|
store: store.AppStore;
|
|
71
77
|
|
|
72
78
|
constructor({
|
|
@@ -79,6 +85,7 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
79
85
|
initialMode,
|
|
80
86
|
initialViewMode = 'annotations',
|
|
81
87
|
intl,
|
|
88
|
+
onCopyLink,
|
|
82
89
|
token,
|
|
83
90
|
}: Options) {
|
|
84
91
|
super();
|
|
@@ -108,6 +115,7 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
108
115
|
this.container = container;
|
|
109
116
|
this.features = features;
|
|
110
117
|
this.intl = i18n.createIntlProvider(intl);
|
|
118
|
+
this.onCopyLink = onCopyLink;
|
|
111
119
|
this.store = store.createStore(initialState, {
|
|
112
120
|
api: new API({ apiHost, token }),
|
|
113
121
|
});
|
|
@@ -123,6 +131,11 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
123
131
|
this.addListener(Event.BOUNDING_BOX_HIGHLIGHT_SELECT, this.handleSelectBoundingBoxHighlight);
|
|
124
132
|
this.addListener(Event.VIEW_MODE_SET, this.handleSetViewMode);
|
|
125
133
|
|
|
134
|
+
this.addListener(SidebarEvent.SIDEBAR_ANNOTATION_UPDATE, this.handleSidebarAnnotationUpdate);
|
|
135
|
+
this.addListener(SidebarEvent.SIDEBAR_REPLY_CREATE, this.handleSidebarReplyCreate);
|
|
136
|
+
this.addListener(SidebarEvent.SIDEBAR_REPLY_UPDATE, this.handleSidebarReplyUpdate);
|
|
137
|
+
this.addListener(SidebarEvent.SIDEBAR_REPLY_DELETE, this.handleSidebarReplyDelete);
|
|
138
|
+
|
|
126
139
|
// Load any required data at startup
|
|
127
140
|
this.hydrate();
|
|
128
141
|
}
|
|
@@ -132,6 +145,11 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
132
145
|
this.containerEl.classList.remove(CSS_CONTAINER_CLASS);
|
|
133
146
|
}
|
|
134
147
|
|
|
148
|
+
if (this.popupPortalEl) {
|
|
149
|
+
this.popupPortalEl.remove();
|
|
150
|
+
this.popupPortalEl = null;
|
|
151
|
+
}
|
|
152
|
+
|
|
135
153
|
if (this.annotatedEl) {
|
|
136
154
|
this.annotatedEl.classList.remove(CSS_LOADED_CLASS);
|
|
137
155
|
}
|
|
@@ -151,6 +169,10 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
151
169
|
this.removeListener(Event.BOUNDING_BOX_HIGHLIGHT_NAVIGATE, this.handleNavigateBoundingBoxHighlight);
|
|
152
170
|
this.removeListener(Event.BOUNDING_BOX_HIGHLIGHT_SELECT, this.handleSelectBoundingBoxHighlight);
|
|
153
171
|
this.removeListener(Event.VIEW_MODE_SET, this.handleSetViewMode);
|
|
172
|
+
this.removeListener(SidebarEvent.SIDEBAR_ANNOTATION_UPDATE, this.handleSidebarAnnotationUpdate);
|
|
173
|
+
this.removeListener(SidebarEvent.SIDEBAR_REPLY_CREATE, this.handleSidebarReplyCreate);
|
|
174
|
+
this.removeListener(SidebarEvent.SIDEBAR_REPLY_UPDATE, this.handleSidebarReplyUpdate);
|
|
175
|
+
this.removeListener(SidebarEvent.SIDEBAR_REPLY_DELETE, this.handleSidebarReplyDelete);
|
|
154
176
|
}
|
|
155
177
|
|
|
156
178
|
public init(scale = 1, rotation = 0): void {
|
|
@@ -166,6 +188,12 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
166
188
|
this.annotatedEl.classList.add(CSS_LOADED_CLASS);
|
|
167
189
|
this.containerEl.classList.add(CSS_CONTAINER_CLASS);
|
|
168
190
|
|
|
191
|
+
if (!this.popupPortalEl) {
|
|
192
|
+
this.popupPortalEl = this.containerEl.ownerDocument.createElement('div');
|
|
193
|
+
this.popupPortalEl.classList.add(CSS_POPUP_PORTAL_CLASS);
|
|
194
|
+
this.containerEl.appendChild(this.popupPortalEl);
|
|
195
|
+
}
|
|
196
|
+
|
|
169
197
|
// Defer to the child class to render annotations
|
|
170
198
|
this.render();
|
|
171
199
|
|
|
@@ -338,6 +366,22 @@ export default class BaseAnnotator extends EventEmitter {
|
|
|
338
366
|
this.setViewMode(viewMode);
|
|
339
367
|
};
|
|
340
368
|
|
|
369
|
+
protected handleSidebarAnnotationUpdate = (annotation: store.SidebarAnnotationUpdatePayload): void => {
|
|
370
|
+
this.store.dispatch(store.applySidebarAnnotationUpdateAction(annotation));
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
protected handleSidebarReplyCreate = (payload: store.SidebarReplyMutationPayload): void => {
|
|
374
|
+
this.store.dispatch(store.applySidebarReplyCreateAction(payload));
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
protected handleSidebarReplyUpdate = (payload: store.SidebarReplyMutationPayload): void => {
|
|
378
|
+
this.store.dispatch(store.applySidebarReplyUpdateAction(payload));
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
protected handleSidebarReplyDelete = ({ annotationId, id }: { annotationId: string; id: string }): void => {
|
|
382
|
+
this.store.dispatch(store.applySidebarReplyDeleteAction({ annotationId, replyId: id }));
|
|
383
|
+
};
|
|
384
|
+
|
|
341
385
|
protected hydrate(): void {
|
|
342
386
|
// Redux dispatch method signature doesn't seem to like async actions
|
|
343
387
|
this.store.dispatch<any>(store.fetchAnnotationsAction()); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as ReactDOM from 'react-dom/client';
|
|
2
2
|
import { IntlShape } from 'react-intl';
|
|
3
3
|
import { Store } from 'redux';
|
|
4
|
+
import { AnnotationCallbacks } from './AnnotationCallbacksContext';
|
|
4
5
|
import { applyResinTags } from '../utils/resin';
|
|
5
6
|
import { TARGET_TYPE } from '../constants';
|
|
6
7
|
|
|
@@ -14,6 +15,7 @@ export type Options = {
|
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
export type Props = {
|
|
18
|
+
callbacks?: AnnotationCallbacks;
|
|
17
19
|
intl: IntlShape;
|
|
18
20
|
store: Store;
|
|
19
21
|
};
|
|
@@ -6,7 +6,9 @@ export default function DeselectListener(): null {
|
|
|
6
6
|
const dispatch = useDispatch();
|
|
7
7
|
|
|
8
8
|
React.useEffect(() => {
|
|
9
|
-
const handleMouseDown = (): void => {
|
|
9
|
+
const handleMouseDown = (event: MouseEvent): void => {
|
|
10
|
+
// Popup is portaled outside .ba-Layer; native mousedown still bubbles here.
|
|
11
|
+
if ((event.target as Element | null)?.closest?.('.ba-PopupV2, .ba-Popup')) return;
|
|
10
12
|
dispatch(setActiveAnnotationIdAction(null));
|
|
11
13
|
};
|
|
12
14
|
|
|
@@ -2,8 +2,10 @@ import * as React from 'react';
|
|
|
2
2
|
import { IntlShape, RawIntlProvider } from 'react-intl';
|
|
3
3
|
import { Provider as StoreProvider } from 'react-redux';
|
|
4
4
|
import { Store } from 'redux';
|
|
5
|
+
import AnnotationCallbacksContext, { AnnotationCallbacks } from './AnnotationCallbacksContext';
|
|
5
6
|
|
|
6
7
|
type WrapperProps = {
|
|
8
|
+
callbacks?: AnnotationCallbacks;
|
|
7
9
|
intl: IntlShape;
|
|
8
10
|
store: Store;
|
|
9
11
|
};
|
|
@@ -11,11 +13,13 @@ type WrapperProps = {
|
|
|
11
13
|
type WrapperReturn<P> = React.FC<P & WrapperProps>;
|
|
12
14
|
|
|
13
15
|
export default function withProviders<P extends object>(WrappedComponent: React.ComponentType<P>): WrapperReturn<P> {
|
|
14
|
-
return function RootProvider({ intl, store, ...rest }: P & WrapperProps): JSX.Element {
|
|
16
|
+
return function RootProvider({ callbacks, intl, store, ...rest }: P & WrapperProps): JSX.Element {
|
|
15
17
|
return (
|
|
16
18
|
<RawIntlProvider value={intl}>
|
|
17
19
|
<StoreProvider store={store}>
|
|
18
|
-
<
|
|
20
|
+
<AnnotationCallbacksContext.Provider value={callbacks ?? {}}>
|
|
21
|
+
<WrappedComponent {...(rest as P)} />
|
|
22
|
+
</AnnotationCallbacksContext.Provider>
|
|
19
23
|
</StoreProvider>
|
|
20
24
|
</RawIntlProvider>
|
|
21
25
|
);
|