box-annotations 5.2.1-beta.2 → 5.2.1-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/annotations.css +3 -4
  2. package/dist/annotations.js +1 -1
  3. package/dist/annotations.js.LICENSE.txt +3 -43
  4. package/i18n/en-US.js +4 -0
  5. package/i18n/en-US.properties +8 -0
  6. package/i18n/json/src/components/BoundingBoxHighlight/messages.json +1 -0
  7. package/i18n/json/src/components/Popups/messages.json +1 -1
  8. package/index.js +1 -1
  9. package/package.json +33 -15
  10. package/src/@types/api.ts +3 -2
  11. package/src/@types/events.ts +28 -7
  12. package/src/@types/global.ts +0 -9
  13. package/src/@types/model.ts +14 -0
  14. package/src/BoxAnnotations.ts +4 -1
  15. package/src/adapters/threadedAnnotationsAdapters.ts +164 -0
  16. package/src/api/APIFactory.ts +6 -1
  17. package/src/api/types.ts +50 -1
  18. package/src/boundingBoxHighlight/BoundingBoxHighlightContainer.tsx +33 -0
  19. package/src/boundingBoxHighlight/BoundingBoxHighlightManager.tsx +19 -0
  20. package/src/boundingBoxHighlight/index.ts +1 -0
  21. package/src/common/AnnotationCallbacksContext.ts +7 -0
  22. package/src/common/BaseAnnotator.scss +14 -1
  23. package/src/common/BaseAnnotator.ts +161 -14
  24. package/src/common/BaseManager.ts +2 -0
  25. package/src/common/DeselectListener.tsx +3 -1
  26. package/src/common/withProviders.tsx +6 -2
  27. package/src/components/BoundingBoxHighlight/BoundingBoxHighlightList.scss +8 -0
  28. package/src/components/BoundingBoxHighlight/BoundingBoxHighlightList.tsx +67 -0
  29. package/src/components/BoundingBoxHighlight/BoundingBoxHighlightNav.scss +73 -0
  30. package/src/components/BoundingBoxHighlight/BoundingBoxHighlightNav.tsx +68 -0
  31. package/src/components/BoundingBoxHighlight/BoundingBoxHighlightRect.scss +22 -0
  32. package/src/components/BoundingBoxHighlight/BoundingBoxHighlightRect.tsx +77 -0
  33. package/src/components/BoundingBoxHighlight/index.ts +4 -0
  34. package/src/components/BoundingBoxHighlight/messages.ts +14 -0
  35. package/src/components/Popups/PopupDrawingToolbar.tsx +0 -7
  36. package/src/components/Popups/PopupReply.tsx +8 -28
  37. package/src/components/Popups/PopupV2.scss +28 -0
  38. package/src/components/Popups/PopupV2.tsx +382 -0
  39. package/src/components/Popups/messages.ts +10 -0
  40. package/src/document/DocumentAnnotator.ts +79 -10
  41. package/src/document/docUtil.ts +2 -1
  42. package/src/drawing/DrawingAnnotations.tsx +23 -14
  43. package/src/drawing/DrawingAnnotationsContainer.tsx +3 -0
  44. package/src/drawing/DrawingCreator.tsx +9 -16
  45. package/src/drawing/DrawingManager.tsx +13 -2
  46. package/src/drawing/DrawingTarget.tsx +1 -0
  47. package/src/highlight/HighlightAnnotations.tsx +27 -14
  48. package/src/highlight/HighlightContainer.tsx +3 -0
  49. package/src/highlight/HighlightCreatorManager.ts +8 -5
  50. package/src/highlight/HighlightListener.ts +4 -2
  51. package/src/highlight/HighlightManager.tsx +13 -2
  52. package/src/highlight/HighlightTarget.tsx +1 -0
  53. package/src/icons/ChevronLeft.tsx +24 -0
  54. package/src/icons/ChevronRight.tsx +24 -0
  55. package/src/image/ImageAnnotator.ts +50 -3
  56. package/src/media/MediaAnnotator.ts +2 -1
  57. package/src/popup/PopupContainer.tsx +11 -1
  58. package/src/popup/PopupLayer.tsx +97 -22
  59. package/src/popup/PopupManager.tsx +24 -4
  60. package/src/region/RegionAnnotation.tsx +1 -0
  61. package/src/region/RegionCreation.tsx +4 -7
  62. package/src/region/RegionCreationContainer.tsx +2 -2
  63. package/src/region/RegionCreator.tsx +9 -17
  64. package/src/store/annotations/actions.ts +167 -3
  65. package/src/store/annotations/reducer.ts +71 -1
  66. package/src/store/boundingBoxHighlights/actions.ts +6 -0
  67. package/src/store/boundingBoxHighlights/index.ts +4 -0
  68. package/src/store/boundingBoxHighlights/reducer.ts +33 -0
  69. package/src/store/boundingBoxHighlights/selectors.ts +12 -0
  70. package/src/store/boundingBoxHighlights/types.ts +13 -0
  71. package/src/store/createRootReducer.ts +2 -0
  72. package/src/store/eventing/boundingBoxHighlightNav.ts +13 -0
  73. package/src/store/eventing/create.ts +3 -3
  74. package/src/store/eventing/delete.ts +27 -0
  75. package/src/store/eventing/middleware.ts +42 -3
  76. package/src/store/eventing/replyCreate.ts +34 -0
  77. package/src/store/eventing/replyDelete.ts +35 -0
  78. package/src/store/eventing/replyUpdate.ts +39 -0
  79. package/src/store/eventing/types.ts +4 -4
  80. package/src/store/eventing/update.ts +29 -0
  81. package/src/store/highlight/actions.ts +32 -3
  82. package/src/store/highlight/types.ts +1 -0
  83. package/src/store/index.ts +1 -0
  84. package/src/store/options/actions.ts +2 -0
  85. package/src/store/options/reducer.ts +9 -2
  86. package/src/store/options/selectors.ts +5 -1
  87. package/src/store/options/types.ts +7 -1
  88. package/src/store/types.ts +2 -0
  89. package/src/utils/rotate.ts +43 -0
  90. package/src/utils/scroll.ts +8 -3
  91. package/src/components/Popups/PopupReplyV2.tsx +0 -10
@@ -12,9 +12,9 @@ object-assign
12
12
 
13
13
  /*!
14
14
  * Box Annotations
15
- *
15
+ *
16
16
  * Copyright 2016-present Box, Inc. All Rights Reserved.
17
- *
17
+ *
18
18
  * This source code is licensed under the Box Software License Agreement found
19
19
  * in the LICENSE file in the root directory of this source tree. Additional
20
20
  * third party license disclosures can be found in the THIRD_PARTY_LICENSES
@@ -30,46 +30,6 @@ object-assign
30
30
  * Licensed under MIT License
31
31
  */
32
32
 
33
- /**
34
- * @license React
35
- * react-dom.production.min.js
36
- *
37
- * Copyright (c) Facebook, Inc. and its affiliates.
38
- *
39
- * This source code is licensed under the MIT license found in the
40
- * LICENSE file in the root directory of this source tree.
41
- */
42
-
43
- /**
44
- * @license React
45
- * react.production.min.js
46
- *
47
- * Copyright (c) Facebook, Inc. and its affiliates.
48
- *
49
- * This source code is licensed under the MIT license found in the
50
- * LICENSE file in the root directory of this source tree.
51
- */
52
-
53
- /**
54
- * @license React
55
- * scheduler.production.min.js
56
- *
57
- * Copyright (c) Facebook, Inc. and its affiliates.
58
- *
59
- * This source code is licensed under the MIT license found in the
60
- * LICENSE file in the root directory of this source tree.
61
- */
62
-
63
- /**
64
- * @license React
65
- * use-sync-external-store-with-selector.production.min.js
66
- *
67
- * Copyright (c) Facebook, Inc. and its affiliates.
68
- *
69
- * This source code is licensed under the MIT license found in the
70
- * LICENSE file in the root directory of this source tree.
71
- */
72
-
73
33
  /** @license React v0.18.0
74
34
  * scheduler.production.min.js
75
35
  *
@@ -79,7 +39,7 @@ object-assign
79
39
  * LICENSE file in the root directory of this source tree.
80
40
  */
81
41
 
82
- /** @license React v16.12.0
42
+ /** @license React v16.13.1
83
43
  * react-is.production.min.js
84
44
  *
85
45
  * Copyright (c) Facebook, Inc. and its affiliates.
package/i18n/en-US.js CHANGED
@@ -4,8 +4,12 @@ export default {
4
4
  "ba.annotationsLoadError": "We’re sorry, the annotations failed to load for this file.",
5
5
  "ba.annotationsPost": "Post",
6
6
  "ba.annotationsSave": "Save",
7
+ "ba.boundingBoxHighlight.viewNextReference": "View next reference",
8
+ "ba.boundingBoxHighlight.viewPrevReference": "View previous reference",
7
9
  "ba.popup.reply.comment": "Comment",
8
10
  "ba.popup.reply.field": "Type a comment",
11
+ "ba.popup.reply.mentionLoading": "Loading users...",
12
+ "ba.popup.reply.mentionSelector": "Mention a collaborator",
9
13
  "ba.popups.cancel": "Cancel",
10
14
  "ba.popups.drawing.addComment": "Add Comment",
11
15
  "ba.popups.drawing.delete": "Delete",
@@ -8,10 +8,18 @@ ba.annotationsLoadError = We’re sorry, the annotations failed to load for this
8
8
  ba.annotationsPost = Post
9
9
  # Label for the save button
10
10
  ba.annotationsSave = Save
11
+ # Aria label for the button to view the next reference
12
+ ba.boundingBoxHighlight.viewNextReference = View next reference
13
+ # Aria label for the button to view the previous reference
14
+ ba.boundingBoxHighlight.viewPrevReference = View previous reference
11
15
  # Aria label description for reply popup
12
16
  ba.popup.reply.comment = Comment
13
17
  # Aria label description for annotation comment field
14
18
  ba.popup.reply.field = Type a comment
19
+ # Aria label for mention selector loading state
20
+ ba.popup.reply.mentionLoading = Loading users...
21
+ # Aria role description for mention user selector
22
+ ba.popup.reply.mentionSelector = Mention a collaborator
15
23
  # Button label for cancelling the creation of a description, comment, or reply
16
24
  ba.popups.cancel = Cancel
17
25
  # Button label for adding a comment in the drawing toolbar
@@ -0,0 +1 @@
1
+ [{"id":"ba.boundingBoxHighlight.viewNextReference","description":"Aria label for the button to view the next reference","defaultMessage":"View next reference"},{"id":"ba.boundingBoxHighlight.viewPrevReference","description":"Aria label for the button to view the previous reference","defaultMessage":"View previous reference"}]
@@ -1 +1 @@
1
- [{"id":"ba.popup.reply.comment","description":"Aria label description for reply popup","defaultMessage":"Comment"},{"id":"ba.popup.reply.field","description":"Aria label description for annotation comment field","defaultMessage":"Type a comment"},{"id":"ba.popups.cancel","description":"Button label for cancelling the creation of a description, comment, or reply","defaultMessage":"Cancel"},{"id":"ba.popups.post","description":"Button label for creating a description, comment, or reply","defaultMessage":"Post"},{"id":"ba.popups.drawing.addComment","description":"Button label for adding a comment in the drawing toolbar","defaultMessage":"Add Comment"},{"id":"ba.popups.drawing.delete","description":"Button title for deleting a staged drawing","defaultMessage":"Delete"},{"id":"ba.popups.drawing.redo","description":"Button title for redoing a staged drawing","defaultMessage":"Redo"},{"id":"ba.popups.drawing.undo","description":"Button title for undoing a staged drawing","defaultMessage":"Undo"},{"id":"ba.popups.replyField.placeholder","description":"Placeholder for reply field editor","defaultMessage":"Type a comment..."},{"id":"ba.popups.popupHighlight.promoter","description":"Popup message for highlight promoter","defaultMessage":"Highlight and Comment"},{"id":"ba.popups.popupHighlight.restrictedPrompt","description":"Prompt message when selection crosses multiple pages","defaultMessage":"Comments restricted to single page"},{"id":"ba.popups.popupList.prompt","description":"Prompt message for empty popup list","defaultMessage":"Mention someone to notify them"},{"id":"ba.popups.popupCursor.regionPrompt","description":"Prompt message following cursor in region annotations mode","defaultMessage":"Draw a box to comment"}]
1
+ [{"id":"ba.popup.reply.comment","description":"Aria label description for reply popup","defaultMessage":"Comment"},{"id":"ba.popup.reply.mentionLoading","description":"Aria label for mention selector loading state","defaultMessage":"Loading users..."},{"id":"ba.popup.reply.mentionSelector","description":"Aria role description for mention user selector","defaultMessage":"Mention a collaborator"},{"id":"ba.popup.reply.field","description":"Aria label description for annotation comment field","defaultMessage":"Type a comment"},{"id":"ba.popups.cancel","description":"Button label for cancelling the creation of a description, comment, or reply","defaultMessage":"Cancel"},{"id":"ba.popups.post","description":"Button label for creating a description, comment, or reply","defaultMessage":"Post"},{"id":"ba.popups.drawing.addComment","description":"Button label for adding a comment in the drawing toolbar","defaultMessage":"Add Comment"},{"id":"ba.popups.drawing.delete","description":"Button title for deleting a staged drawing","defaultMessage":"Delete"},{"id":"ba.popups.drawing.redo","description":"Button title for redoing a staged drawing","defaultMessage":"Redo"},{"id":"ba.popups.drawing.undo","description":"Button title for undoing a staged drawing","defaultMessage":"Undo"},{"id":"ba.popups.replyField.placeholder","description":"Placeholder for reply field editor","defaultMessage":"Type a comment..."},{"id":"ba.popups.popupHighlight.promoter","description":"Popup message for highlight promoter","defaultMessage":"Highlight and Comment"},{"id":"ba.popups.popupHighlight.restrictedPrompt","description":"Prompt message when selection crosses multiple pages","defaultMessage":"Comments restricted to single page"},{"id":"ba.popups.popupList.prompt","description":"Prompt message for empty popup list","defaultMessage":"Mention someone to notify them"},{"id":"ba.popups.popupCursor.regionPrompt","description":"Prompt message following cursor in region annotations mode","defaultMessage":"Draw a box to comment"}]
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import './dist/annotations.css';
2
- import './dist/annotations.js';
2
+ import BoxAnnotations from './dist/annotations.js';
3
3
 
4
4
  export default BoxAnnotations;
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "box-annotations",
3
- "version": "5.2.1-beta.2",
3
+ "version": "5.2.1-beta.20",
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"
@@ -28,7 +33,13 @@
28
33
  "@babel/preset-react": "^7.25.9",
29
34
  "@babel/preset-typescript": "^7.25.9",
30
35
  "@box/frontend": "^10.0.0",
36
+ "@box/blueprint-web": "^16.8.2",
37
+ "@box/blueprint-web-assets": "^5.5.0",
38
+ "@box/collaboration-popover": "^2.1.25",
31
39
  "@box/languages": "^1.1.0",
40
+ "@box/readable-time": "^2.1.25",
41
+ "@box/threaded-annotations": "^4.0.6",
42
+ "@box/user-selector": "^2.1.27",
32
43
  "@cfaester/enzyme-adapter-react-18": "^0.8.0",
33
44
  "@commitlint/cli": "^8.3.5",
34
45
  "@commitlint/config-conventional": "^8.2.0",
@@ -50,11 +61,11 @@
50
61
  "@types/uuid": "^8.3.0",
51
62
  "@typescript-eslint/eslint-plugin": "^7.3.1",
52
63
  "@typescript-eslint/parser": "^7.3.1",
53
- "autoprefixer": "^9.7.6",
64
+ "autoprefixer": "^10.4.19",
54
65
  "axios": "^0.24.0",
55
66
  "babel-eslint": "^10.1.0",
56
67
  "babel-jest": "^24.9.0",
57
- "babel-loader": "^8.3.0",
68
+ "babel-loader": "^9.2.1",
58
69
  "babel-plugin-istanbul": "^6.0.0",
59
70
  "babel-plugin-react-intl": "^7.5.1",
60
71
  "babel-plugin-transform-require-ignore": "^0.1.1",
@@ -62,9 +73,11 @@
62
73
  "box-ui-elements": "^20.0.0",
63
74
  "circular-dependency-plugin": "^5.2.0",
64
75
  "classnames": "^2.2.5",
76
+ "clsx": "^2.1.1",
65
77
  "conventional-github-releaser": "^3.1.3",
66
78
  "core-js": "^3.38.1",
67
- "css-loader": "^3.5.2",
79
+ "css-loader": "^7.1.2",
80
+ "css-minimizer-webpack-plugin": "^7.0.4",
68
81
  "cypress": "^4.4.1",
69
82
  "draft-js": "0.10.5",
70
83
  "enzyme": "^3.11.0",
@@ -90,12 +103,10 @@
90
103
  "jest-environment-jsdom-sixteen": "^1.0.3",
91
104
  "lint-staged": "^9.5.0",
92
105
  "lodash": "^4.17.15",
93
- "mini-css-extract-plugin": "^0.9.0",
106
+ "mini-css-extract-plugin": "^2.10.1",
94
107
  "npm-run-all": "^4.1.5",
95
- "optimize-css-assets-webpack-plugin": "^5.0.3",
96
- "postcss-loader": "^3.0.0",
108
+ "postcss-loader": "^8.1.1",
97
109
  "prettier": "^1.19.1",
98
- "raw-loader": "^4.0.1",
99
110
  "react": "18.3.1",
100
111
  "react-dom": "18.3.1",
101
112
  "react-intl": "6.4.2",
@@ -105,27 +116,34 @@
105
116
  "redux": "^4.0.5",
106
117
  "regenerator-runtime": "^0.13.9",
107
118
  "sass": "^1.64.1",
108
- "sass-loader": "^8.0.2",
119
+ "sass-loader": "^16.0.4",
109
120
  "scroll-into-view-if-needed": "^2.2.24",
110
- "style-loader": "^1.1.4",
121
+ "style-loader": "^4.0.0",
111
122
  "stylelint": "^12.0.0",
112
123
  "stylelint-config-rational-order": "^0.1.2",
113
124
  "stylelint-config-standard": "^19.0.0",
114
125
  "stylelint-order": "^3.1.1",
115
126
  "tabbable": "^1.1.3",
116
- "terser-webpack-plugin": "^4.2.3",
117
127
  "typescript": "4.9.5",
118
128
  "uuid": "^8.3.1",
119
129
  "wait-on": "^4.0.2",
120
- "webpack": "^4.47.0",
121
- "webpack-cli": "^3.3.11",
122
- "webpack-dev-server": "^3.10.3",
130
+ "webpack": "^5.105.4",
131
+ "webpack-cli": "^5.1.4",
132
+ "webpack-dev-server": "^5.2.3",
123
133
  "worker-farm": "^1.7.0"
124
134
  },
135
+ "peerDependencies": {
136
+ "@box/blueprint-web": "^16.8.2",
137
+ "@box/blueprint-web-assets": "^5.5.0",
138
+ "@box/collaboration-popover": "^2.1.25",
139
+ "@box/readable-time": "^2.1.25",
140
+ "@box/threaded-annotations": "^4.0.6",
141
+ "@box/user-selector": "^2.1.27"
142
+ },
125
143
  "scripts": {
126
144
  "build": "yarn setup && yarn build:prod:dist",
127
145
  "build:i18n": "props2es",
128
- "build:dev:dist": "LANGUAGE=en-US BABEL_ENV=dev NODE_ENV=dev webpack --config scripts/webpack.config.js --mode development --progress --colors",
146
+ "build:dev:dist": "LANGUAGE=en-US BABEL_ENV=dev NODE_ENV=dev webpack --config scripts/webpack.config.js --mode development --progress",
129
147
  "build:prod:dist": "LANGUAGE=en-US BABEL_ENV=production NODE_ENV=production webpack --config scripts/webpack.config.js --mode production",
130
148
  "clean": "rm -rf dist reports/* i18n/json i18n/*.js",
131
149
  "cy:open": "yarn cy:wait; yarn cypress open",
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 TokenResolver = () => TokenLiteral | Promise<TokenLiteral>;
14
- export type Token = TokenLiteral | TokenResolver;
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;
@@ -1,15 +1,36 @@
1
1
  enum Event {
2
- ACTIVE_CHANGE = 'annotations_active_change',
2
+ // Inbound: emitted by the host; box-annotations listens.
3
3
  ACTIVE_SET = 'annotations_active_set',
4
- CREATOR_STAGED_CHANGE = 'creator_staged_change',
5
- CREATOR_STATUS_CHANGE = 'creator_status_change',
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
- ANNOTATION_REMOVE = 'annotations_remove',
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
- COLOR_SET = 'annotations_color_set',
12
- VISIBLE_SET = 'annotations_visible_set',
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).
26
+ BOUNDING_BOX_HIGHLIGHT_NAVIGATE = 'bounding_box_highlight_navigate',
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',
13
34
  }
14
35
 
15
36
  // Existing legacy events, don't rename
@@ -19,4 +40,4 @@ enum LegacyEvent {
19
40
  SCALE = 'scaleannotations',
20
41
  }
21
42
 
22
- export { Event, LegacyEvent };
43
+ export { Event, LegacyEvent, SidebarEvent };
@@ -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
@@ -12,6 +12,11 @@ export interface Annotation {
12
12
  modified_by: User;
13
13
  permissions: Permissions;
14
14
  replies?: Array<Reply>;
15
+ resolution?: {
16
+ resolved_at: string;
17
+ resolved_by: User;
18
+ } | null;
19
+ status?: 'open' | 'resolved';
15
20
  target: Target;
16
21
  type: 'annotation';
17
22
  }
@@ -81,15 +86,24 @@ export interface Rect extends Position {
81
86
  width: number;
82
87
  }
83
88
 
89
+ export interface ReplyPermissions {
90
+ can_delete?: boolean;
91
+ can_edit?: boolean;
92
+ can_reply?: boolean;
93
+ can_resolve?: boolean;
94
+ }
95
+
84
96
  export interface Reply {
85
97
  created_at: string;
86
98
  created_by: User;
87
99
  id: string;
88
100
  message: string;
101
+ modified_at?: string;
89
102
  parent: {
90
103
  id: string;
91
104
  type: string;
92
105
  };
106
+ permissions?: ReplyPermissions;
93
107
  type: 'reply';
94
108
  }
95
109
 
@@ -16,6 +16,7 @@ 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 = {
@@ -43,6 +44,8 @@ type ViewerOption = {
43
44
 
44
45
  type ViewerOptions = Record<string, ViewerOption>;
45
46
 
47
+ type GlobalWithBoxAnnotations = typeof globalThis & { BoxAnnotations: typeof BoxAnnotations };
48
+
46
49
  /**
47
50
  * NAME: The name of the annotator.
48
51
  * CONSTRUCTOR: Constructor for the annotator.
@@ -145,5 +148,5 @@ class BoxAnnotations {
145
148
  }
146
149
  }
147
150
 
148
- global.BoxAnnotations = BoxAnnotations;
151
+ (globalThis as GlobalWithBoxAnnotations).BoxAnnotations = BoxAnnotations;
149
152
  export default BoxAnnotations;
@@ -0,0 +1,164 @@
1
+ import type { DocumentNodeV2, MentionNodeV2, ParagraphNodeV2, TextMessageTypeV2, TextNodeV2 } from '@box/threaded-annotations';
2
+
3
+ import type { Annotation, Collaborator, Reply, UserMini } from '../@types';
4
+
5
+ const MENTION_REGEX = /@\[(\d+):([^\]]+)\]/g;
6
+
7
+ /**
8
+ * Parses a plain-text line into paragraph content nodes.
9
+ * Recognizes @[userId:userName] mention markup and converts to MentionNode / TextNode.
10
+ */
11
+ const parseLine = (line: string): (MentionNodeV2 | TextNodeV2)[] => {
12
+ const nodes: (MentionNodeV2 | TextNodeV2)[] = [];
13
+ let lastIndex = 0;
14
+
15
+ // Reset regex state since it's global
16
+ MENTION_REGEX.lastIndex = 0;
17
+
18
+ let match = MENTION_REGEX.exec(line);
19
+ while (match !== null) {
20
+ const [fullMatch, userId, userName] = match;
21
+ const { index } = match;
22
+
23
+ if (index > lastIndex) {
24
+ nodes.push({ type: 'text', text: line.slice(lastIndex, index) });
25
+ }
26
+
27
+ nodes.push({
28
+ type: 'mention',
29
+ attrs: {
30
+ authorId: '',
31
+ mentionId: userId,
32
+ mentionedUserId: userId,
33
+ mentionedUserName: userName,
34
+ },
35
+ });
36
+
37
+ lastIndex = index + fullMatch.length;
38
+ match = MENTION_REGEX.exec(line);
39
+ }
40
+
41
+ if (lastIndex < line.length) {
42
+ nodes.push({ type: 'text', text: line.slice(lastIndex) });
43
+ }
44
+
45
+ return nodes;
46
+ };
47
+
48
+ /**
49
+ * Deserializes plain text with @[userId:userName] mention markup into a TipTap DocumentNode.
50
+ * This is the reverse of serializeMentionMarkup from @box/threaded-annotations.
51
+ *
52
+ * Newlines are treated as paragraph breaks.
53
+ */
54
+ export const deserializeMentionMarkup = (text: string): DocumentNodeV2 => {
55
+ if (!text) {
56
+ return { type: 'doc', content: [] };
57
+ }
58
+
59
+ const lines = text.split('\n');
60
+ const content: ParagraphNodeV2[] = lines.map(line => {
61
+ const nodes = parseLine(line);
62
+
63
+ return {
64
+ type: 'paragraph',
65
+ ...(nodes.length > 0 ? { content: nodes } : {}),
66
+ };
67
+ });
68
+
69
+ return { type: 'doc', content };
70
+ };
71
+
72
+ /**
73
+ * Returns the edit timestamp consumers use to render an edited indicator.
74
+ * Compares parsed instants, not raw strings, so equivalent ISO formats
75
+ * (Z vs +00:00, fractional precision) are treated as unedited.
76
+ */
77
+ const toUpdatedAt = (createdAt: string, modifiedAt: string | undefined): number | undefined => {
78
+ if (!modifiedAt) return undefined;
79
+ const modifiedMs = new Date(modifiedAt).getTime();
80
+ if (Number.isNaN(modifiedMs)) return undefined;
81
+ const createdMs = new Date(createdAt).getTime();
82
+ if (modifiedMs === createdMs) return undefined;
83
+ return modifiedMs;
84
+ };
85
+
86
+ /**
87
+ * Converts a box-annotations Reply to a threaded-annotations TextMessageType.
88
+ */
89
+ export const replyToTextMessage = (reply: Reply): TextMessageTypeV2 => ({
90
+ author: {
91
+ email: reply.created_by?.login ?? '',
92
+ id: parseInt(reply.created_by?.id ?? '0', 10),
93
+ name: reply.created_by?.name ?? '',
94
+ },
95
+ createdAt: new Date(reply.created_at).getTime(),
96
+ id: reply.id,
97
+ message: deserializeMentionMarkup(reply.message),
98
+ permissions: {
99
+ canDelete: reply.permissions?.can_delete ?? false,
100
+ canEdit: reply.permissions?.can_edit ?? false,
101
+ canReply: reply.permissions?.can_reply ?? false,
102
+ canResolve: reply.permissions?.can_resolve ?? false,
103
+ },
104
+ updatedAt: toUpdatedAt(reply.created_at, reply.modified_at),
105
+ });
106
+
107
+ // The root message shares the annotation's author and permissions; description
108
+ // comes back sparse ({ message } only) from the list endpoint.
109
+ const descriptionToTextMessage = (annotation: Annotation): TextMessageTypeV2 => ({
110
+ author: {
111
+ email: annotation.created_by?.login ?? '',
112
+ id: parseInt(annotation.created_by?.id ?? '0', 10),
113
+ name: annotation.created_by?.name ?? '',
114
+ },
115
+ createdAt: new Date(annotation.created_at).getTime(),
116
+ id: annotation.id,
117
+ message: deserializeMentionMarkup(annotation.description?.message ?? ''),
118
+ permissions: {
119
+ canDelete: annotation.permissions?.can_delete ?? false,
120
+ canEdit: annotation.permissions?.can_edit ?? false,
121
+ canReply: annotation.permissions?.can_reply ?? false,
122
+ canResolve: annotation.permissions?.can_resolve ?? false,
123
+ },
124
+ updatedAt: toUpdatedAt(annotation.created_at, annotation.modified_at),
125
+ });
126
+
127
+ /**
128
+ * Maps a full Annotation (description + replies) to an array of TextMessageType
129
+ * suitable for ThreadedAnnotationsV2.
130
+ */
131
+ export const annotationToMessages = (annotation: Annotation): TextMessageTypeV2[] => {
132
+ const messages: TextMessageTypeV2[] = [];
133
+
134
+ if (annotation.description) {
135
+ messages.push(descriptionToTextMessage(annotation));
136
+ }
137
+
138
+ if (annotation.replies) {
139
+ annotation.replies.forEach(reply => {
140
+ messages.push(replyToTextMessage(reply));
141
+ });
142
+ }
143
+
144
+ return messages;
145
+ };
146
+
147
+ /**
148
+ * Converts a box-annotations Collaborator to a @box/user-selector UserContactType.
149
+ */
150
+ export const collaboratorToUserContact = (collaborator: Collaborator): {
151
+ email: string;
152
+ id: number;
153
+ name: string;
154
+ value: string;
155
+ } => {
156
+ const item = collaborator.item as UserMini | undefined;
157
+
158
+ return {
159
+ email: item?.email ?? item?.login ?? '',
160
+ id: parseInt(item?.id ?? collaborator.id, 10),
161
+ name: item?.name ?? collaborator.name,
162
+ value: collaborator.id,
163
+ };
164
+ };
@@ -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, 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[];
@@ -33,6 +33,23 @@ export interface AnnotationsAPI {
33
33
  errorCallback: (error: APIError) => void,
34
34
  ): Promise<void>;
35
35
 
36
+ createAnnotationReply(
37
+ fileId: string | null,
38
+ annotationId: string,
39
+ permissions: Permissions,
40
+ message: string,
41
+ successCallback: (result: Reply) => void,
42
+ errorCallback: (error: APIError) => void,
43
+ ): Promise<void>;
44
+
45
+ deleteAnnotation(
46
+ fileId: string | null,
47
+ annotationId: string,
48
+ permissions: Permissions,
49
+ successCallback: () => void,
50
+ errorCallback: (error: APIError) => void,
51
+ ): Promise<void>;
52
+
36
53
  getAnnotations(
37
54
  fileId: string | null,
38
55
  fileVersionId: string | null,
@@ -41,8 +58,40 @@ export interface AnnotationsAPI {
41
58
  errorCallback: (error: APIError) => void,
42
59
  limit?: number,
43
60
  shouldFetchAll?: boolean,
61
+ shouldFetchReplies?: boolean,
44
62
  ): Promise<void>;
45
63
 
64
+ updateAnnotation(
65
+ fileId: string | null,
66
+ annotationId: string,
67
+ permissions: Permissions,
68
+ payload: { message?: string; status?: string },
69
+ successCallback: (result: Annotation) => void,
70
+ errorCallback: (error: APIError) => void,
71
+ ): Promise<void>;
72
+
73
+ destroy(): void;
74
+ }
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
+
46
95
  destroy(): void;
47
96
  }
48
97
 
@@ -0,0 +1,33 @@
1
+ import { connect } from 'react-redux';
2
+ import { BoundingBoxHighlightList } from '../components/BoundingBoxHighlight';
3
+ import withProviders from '../common/withProviders';
4
+ import {
5
+ AppState,
6
+ BoundingBox,
7
+ getBoundingBoxHighlights,
8
+ getBoundingBoxHighlightsForPage,
9
+ getSelectedBoundingBoxHighlightId,
10
+ navigateBoundingBoxHighlightAction,
11
+ setSelectedBoundingBoxHighlightAction,
12
+ } from '../store';
13
+
14
+ export type Props = {
15
+ location: number;
16
+ };
17
+
18
+ const mapStateToProps = (state: AppState, { location }: Props): {
19
+ allBoundingBoxes: BoundingBox[];
20
+ boundingBoxes: BoundingBox[];
21
+ selectedId: string | null;
22
+ } => ({
23
+ allBoundingBoxes: getBoundingBoxHighlights(state),
24
+ boundingBoxes: getBoundingBoxHighlightsForPage(state, location),
25
+ selectedId: getSelectedBoundingBoxHighlightId(state),
26
+ });
27
+
28
+ const mapDispatchToProps = {
29
+ onNavigate: navigateBoundingBoxHighlightAction,
30
+ onSelect: setSelectedBoundingBoxHighlightAction,
31
+ };
32
+
33
+ export default connect(mapStateToProps, mapDispatchToProps)(withProviders(BoundingBoxHighlightList));