box-annotations 4.1.1-beta.5 → 5.1.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.
@@ -0,0 +1,89 @@
1
+ /*
2
+ object-assign
3
+ (c) Sindre Sorhus
4
+ @license MIT
5
+ */
6
+
7
+ /*!
8
+ Copyright (c) 2017 Jed Watson.
9
+ Licensed under the MIT License (MIT), see
10
+ http://jedwatson.github.io/classnames
11
+ */
12
+
13
+ /*!
14
+ * Box Annotations
15
+ *
16
+ * Copyright 2016-present Box, Inc. All Rights Reserved.
17
+ *
18
+ * This source code is licensed under the Box Software License Agreement found
19
+ * in the LICENSE file in the root directory of this source tree. Additional
20
+ * third party license disclosures can be found in the THIRD_PARTY_LICENSES
21
+ * file in the same directory.
22
+ */
23
+
24
+ /*!
25
+ * UAParser.js v0.7.21
26
+ * Lightweight JavaScript-based User-Agent string parser
27
+ * https://github.com/faisalman/ua-parser-js
28
+ *
29
+ * Copyright © 2012-2019 Faisal Salman <f@faisalman.com>
30
+ * Licensed under MIT License
31
+ */
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
+ /** @license React v0.18.0
74
+ * scheduler.production.min.js
75
+ *
76
+ * Copyright (c) Facebook, Inc. and its affiliates.
77
+ *
78
+ * This source code is licensed under the MIT license found in the
79
+ * LICENSE file in the root directory of this source tree.
80
+ */
81
+
82
+ /** @license React v16.12.0
83
+ * react-is.production.min.js
84
+ *
85
+ * Copyright (c) Facebook, Inc. and its affiliates.
86
+ *
87
+ * This source code is licensed under the MIT license found in the
88
+ * LICENSE file in the root directory of this source tree.
89
+ */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "box-annotations",
3
- "version": "4.1.1-beta.5",
3
+ "version": "5.1.0",
4
4
  "description": "Box Annotations",
5
5
  "author": "Box (https://www.box.com/)",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -16,18 +16,20 @@
16
16
  "yarn": ">=1.10.x"
17
17
  },
18
18
  "devDependencies": {
19
- "@babel/cli": "^7.8.4",
20
- "@babel/core": "^7.9.0",
21
- "@babel/plugin-proposal-class-properties": "^7.8.3",
22
- "@babel/plugin-transform-flow-strip-types": "^7.9.0",
23
- "@babel/plugin-transform-object-assign": "^7.8.3",
24
- "@babel/plugin-transform-runtime": "^7.9.0",
25
- "@babel/polyfill": "^7.8.7",
26
- "@babel/preset-env": "^7.9.5",
27
- "@babel/preset-react": "^7.9.4",
28
- "@babel/preset-typescript": "^7.9.0",
29
- "@box/frontend": "^6.4.0",
19
+ "@babel/cli": "^7.25.9",
20
+ "@babel/core": "^7.25.9",
21
+ "@babel/eslint-parser": "^7.24.7",
22
+ "@babel/plugin-proposal-class-properties": "^7.18.6",
23
+ "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
24
+ "@babel/plugin-transform-flow-strip-types": "^7.25.9",
25
+ "@babel/plugin-transform-object-assign": "^7.25.9",
26
+ "@babel/plugin-transform-runtime": "^7.25.9",
27
+ "@babel/preset-env": "^7.25.9",
28
+ "@babel/preset-react": "^7.25.9",
29
+ "@babel/preset-typescript": "^7.25.9",
30
+ "@box/frontend": "^10.0.0",
30
31
  "@box/languages": "^1.1.0",
32
+ "@cfaester/enzyme-adapter-react-18": "^0.8.0",
31
33
  "@commitlint/cli": "^8.3.5",
32
34
  "@commitlint/config-conventional": "^8.2.0",
33
35
  "@formatjs/intl-pluralrules": "^1.5.5",
@@ -39,46 +41,45 @@
39
41
  "@types/enzyme": "^3.10.5",
40
42
  "@types/jest": "^24.9.0",
41
43
  "@types/lodash": "^4.14.150",
42
- "@types/react": "^17.0.2",
43
- "@types/react-dom": "^17.0.1",
44
- "@types/react-redux": "^7.1.16",
44
+ "@types/react": "18.3.12",
45
+ "@types/react-dom": "18.3.1",
46
+ "@types/react-redux": "^7.1.33",
45
47
  "@types/uuid": "^8.3.0",
46
- "@typescript-eslint/eslint-plugin": "^2.29.0",
47
- "@typescript-eslint/parser": "^2.29.0",
48
- "@wojtekmaj/enzyme-adapter-react-17": "^0.4.1",
48
+ "@typescript-eslint/eslint-plugin": "^7.3.1",
49
+ "@typescript-eslint/parser": "^7.3.1",
49
50
  "autoprefixer": "^9.7.6",
50
51
  "axios": "^0.24.0",
51
52
  "babel-eslint": "^10.1.0",
52
53
  "babel-jest": "^24.9.0",
53
- "babel-loader": "^8.1.0",
54
+ "babel-loader": "^8.3.0",
54
55
  "babel-plugin-istanbul": "^6.0.0",
55
56
  "babel-plugin-react-intl": "^7.5.1",
56
57
  "babel-plugin-transform-require-ignore": "^0.1.1",
57
58
  "box-node-sdk": "^1.33.0",
58
- "box-ui-elements": "^18.1.0",
59
+ "box-ui-elements": "^20.0.0",
59
60
  "circular-dependency-plugin": "^5.2.0",
60
61
  "classnames": "^2.2.5",
61
62
  "conventional-github-releaser": "^3.1.3",
62
- "core-js": "^3.6.5",
63
+ "core-js": "^3.38.1",
63
64
  "css-loader": "^3.5.2",
64
65
  "cypress": "^4.4.1",
65
66
  "draft-js": "0.10.5",
66
67
  "enzyme": "^3.11.0",
67
68
  "enzyme-to-json": "^3.4.4",
68
- "eslint": "^6.7.2",
69
- "eslint-config-airbnb": "^18.1.0",
70
- "eslint-config-prettier": "^6.11.0",
69
+ "eslint": "^8.57.0",
70
+ "eslint-config-airbnb": "^19.0.4",
71
+ "eslint-config-prettier": "^9.1.0",
71
72
  "eslint-plugin-babel": "^5.3.0",
72
73
  "eslint-plugin-cypress": "^2.10.3",
73
- "eslint-plugin-flowtype": "^4.7.0",
74
- "eslint-plugin-formatjs": "^1.5.4",
75
- "eslint-plugin-import": "^2.20.2",
74
+ "eslint-plugin-flowtype": "^8.0.3",
75
+ "eslint-plugin-formatjs": "^4.13.3",
76
+ "eslint-plugin-import": "^2.26.0",
76
77
  "eslint-plugin-jest": "^23.8.2",
77
78
  "eslint-plugin-jsx-a11y": "^6.2.3",
78
- "eslint-plugin-lodash": "^6.0.0",
79
- "eslint-plugin-prettier": "^3.1.3",
80
- "eslint-plugin-react": "^7.19.0",
81
- "eslint-plugin-react-hooks": "^1.7.0",
79
+ "eslint-plugin-lodash": "^7.4.0",
80
+ "eslint-plugin-prettier": "^5.2.1",
81
+ "eslint-plugin-react": "^7.34.3",
82
+ "eslint-plugin-react-hooks": "^4.6.0",
82
83
  "formik": "^2.1.4",
83
84
  "husky": "^3.1.0",
84
85
  "jest": "^24.9.0",
@@ -92,13 +93,14 @@
92
93
  "postcss-loader": "^3.0.0",
93
94
  "prettier": "^1.19.1",
94
95
  "raw-loader": "^4.0.1",
95
- "react": "^17.0.1",
96
- "react-dom": "^17.0.1",
97
- "react-intl": "^3.12.0",
98
- "react-redux": "^7.2.2",
96
+ "react": "18.3.1",
97
+ "react-dom": "18.3.1",
98
+ "react-intl": "6.4.2",
99
+ "react-redux": "^9.1.2",
99
100
  "react-tether": "1.0.5",
100
101
  "react-textarea-autosize": "^7.1.2",
101
102
  "redux": "^4.0.5",
103
+ "regenerator-runtime": "^0.13.9",
102
104
  "sass": "^1.64.1",
103
105
  "sass-loader": "^8.0.2",
104
106
  "scroll-into-view-if-needed": "^2.2.24",
@@ -108,11 +110,11 @@
108
110
  "stylelint-config-standard": "^19.0.0",
109
111
  "stylelint-order": "^3.1.1",
110
112
  "tabbable": "^1.1.3",
111
- "typescript": "^3.8.3",
112
- "uglifyjs-webpack-plugin": "^2.2.0",
113
+ "terser-webpack-plugin": "^4.2.3",
114
+ "typescript": "4.9.5",
113
115
  "uuid": "^8.3.1",
114
116
  "wait-on": "^4.0.2",
115
- "webpack": "^4.43.0",
117
+ "webpack": "^4.47.0",
116
118
  "webpack-cli": "^3.3.11",
117
119
  "webpack-dev-server": "^3.10.3",
118
120
  "worker-farm": "^1.7.0"
@@ -120,8 +122,8 @@
120
122
  "scripts": {
121
123
  "build": "yarn setup && yarn build:prod:dist",
122
124
  "build:i18n": "props2es",
123
- "build:dev:dist": "LANGUAGE=en-US BABEL_ENV=dev NODE_ENV=dev NODE_OPTIONS=--openssl-legacy-provider webpack --config scripts/webpack.config.js --mode development --progress --colors",
124
- "build:prod:dist": "LANGUAGE=en-US BABEL_ENV=production NODE_ENV=production NODE_OPTIONS=--openssl-legacy-provider webpack --config scripts/webpack.config.js --mode production",
125
+ "build:dev:dist": "LANGUAGE=en-US BABEL_ENV=dev NODE_ENV=dev webpack --config scripts/webpack.config.js --mode development --progress --colors",
126
+ "build:prod:dist": "LANGUAGE=en-US BABEL_ENV=production NODE_ENV=production webpack --config scripts/webpack.config.js --mode production",
125
127
  "clean": "rm -rf dist reports/* i18n/json i18n/*.js",
126
128
  "cy:open": "yarn cy:wait; yarn cypress open",
127
129
  "cy:run": "yarn cy:wait; yarn cypress run --spec test/integration/**/*.test.js",
@@ -138,7 +140,7 @@
138
140
  "release:pre": "scripts/release.sh -x",
139
141
  "setup": "yarn install && yarn clean && yarn build:i18n",
140
142
  "start": "yarn setup && yarn build:dev:dist --watch",
141
- "start:dev": "yarn setup && LANGUAGE=en-US BABEL_ENV=dev NODE_ENV=dev NODE_OPTIONS=--openssl-legacy-provider yarn webpack-dev-server --config scripts/webpack.config.js --mode development",
143
+ "start:dev": "yarn setup && LANGUAGE=en-US BABEL_ENV=dev NODE_ENV=dev yarn webpack-dev-server --config scripts/webpack.config.js --mode development",
142
144
  "start:linked": "IS_LINKED=1 yarn start",
143
145
  "test": "BABEL_ENV=test NODE_ENV=test yarn jest",
144
146
  "test:e2e": "node scripts/cypress.js",
@@ -153,6 +155,9 @@
153
155
  }
154
156
  },
155
157
  "resolutions": {
156
- "uglify-es": "3.3.4"
158
+ "**/react-intl/**/@types/react": "^18.3.12",
159
+ "**/@types/react": "^18.3.12",
160
+ "**/@types/react-dom": "^18.2.0",
161
+ "acorn": "8.14.0"
157
162
  }
158
163
  }
@@ -1,8 +1,5 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-namespace */
2
- declare namespace Intl {
3
- const RelativeTimeFormat: {};
4
- }
5
-
2
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
6
3
  declare namespace NodeJS {
7
4
  interface Global {
8
5
  window: any;
@@ -1,17 +1,4 @@
1
- @font-face {
2
- font-weight: normal;
3
- font-family: 'Lato';
4
- font-style: normal;
5
- src: local('Lato Regular'), local('Lato-Regular'), url('https://cdn01.boxcdn.net/fonts/1.0.2/lato/Lato-Regular.woff2') format('woff2'), url('https://cdn01.boxcdn.net/fonts/1.0.2/lato/Lato-Regular.woff') format('woff');
6
- }
7
-
8
- @font-face {
9
- font-weight: bold;
10
- font-family: 'Lato';
11
- font-style: normal;
12
- src: local('Lato Bold'), local('Lato-Bold'), url('https://cdn01.boxcdn.net/fonts/1.0.2/lato/Lato-Bold.woff2') format('woff2'), url('https://cdn01.boxcdn.net/fonts/1.0.2/lato/Lato-Bold.woff') format('woff');
13
- }
14
-
1
+ @import '~box-ui-elements/es/elements/common/fonts';
15
2
  @import '~box-ui-elements/es/styles/variables';
16
3
 
17
4
  .ba {
@@ -1,4 +1,4 @@
1
- import * as ReactDOM from 'react-dom';
1
+ import * as ReactDOM from 'react-dom/client';
2
2
  import { IntlShape } from 'react-intl';
3
3
  import { Store } from 'redux';
4
4
  import { applyResinTags } from '../utils/resin';
@@ -28,6 +28,8 @@ export default class BaseManager implements Manager {
28
28
 
29
29
  reactEl: HTMLElement;
30
30
 
31
+ root: ReactDOM.Root | null = null;
32
+
31
33
  constructor({ location = 1, referenceEl, resinTags = {} }: Options) {
32
34
  this.location = location;
33
35
  this.reactEl = this.insert(referenceEl, {
@@ -35,6 +37,8 @@ export default class BaseManager implements Manager {
35
37
  feature: 'annotations',
36
38
  });
37
39
 
40
+ this.root = ReactDOM.createRoot(this.reactEl);
41
+
38
42
  this.decorate();
39
43
  }
40
44
 
@@ -43,11 +47,11 @@ export default class BaseManager implements Manager {
43
47
  }
44
48
 
45
49
  destroy(): void {
46
- ReactDOM.unmountComponentAtNode(this.reactEl);
47
-
48
- const parentEl = this.reactEl.parentNode;
49
- if (parentEl) {
50
- parentEl.removeChild(this.reactEl);
50
+ if (this.root) {
51
+ this.root.unmount();
52
+ }
53
+ if (this.reactEl.parentNode) {
54
+ this.reactEl.parentNode.removeChild(this.reactEl);
51
55
  }
52
56
  }
53
57
 
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import * as ReactDOM from 'react-dom';
2
+ import * as ReactDOM from 'react-dom/client';
3
3
  import { Provider } from 'react-redux';
4
4
  import { Store } from 'redux';
5
5
  import BaseManager from './BaseManager';
@@ -20,11 +20,14 @@ export default class DeselectManager extends BaseManager {
20
20
  }
21
21
 
22
22
  render({ store }: Props): void {
23
- ReactDOM.render(
23
+ if (!this.root) {
24
+ this.root = ReactDOM.createRoot(this.reactEl);
25
+ }
26
+
27
+ this.root.render(
24
28
  <Provider store={store}>
25
29
  <DeselectListener />
26
30
  </Provider>,
27
- this.reactEl,
28
31
  );
29
32
  }
30
33
  }
@@ -1,4 +1,4 @@
1
- import * as React from 'react';
1
+ import React from 'react';
2
2
  import noop from 'lodash/noop';
3
3
  import { getScrollParent } from './util';
4
4
 
@@ -31,3 +31,21 @@ export default function create(
31
31
  ): Instance {
32
32
  return popper.createPopper(reference, popup, mergeWith({}, defaults, options, merger) as Options);
33
33
  }
34
+
35
+ export function clientBoundingRect(height: number, width: number, x: number, y: number): DOMRect {
36
+ const rect = {
37
+ bottom: y + height,
38
+ height,
39
+ left: x,
40
+ right: x + width,
41
+ top: y,
42
+ width,
43
+ x,
44
+ y,
45
+ };
46
+
47
+ return {
48
+ ...rect,
49
+ toJSON: () => ({ ...rect }),
50
+ };
51
+ }
@@ -1,4 +1,4 @@
1
- import * as React from 'react';
1
+ import React from 'react';
2
2
  import classNames from 'classnames';
3
3
  import createPopper, { Instance, Options, PopupReference } from './Popper';
4
4
  import './PopupBase.scss';
@@ -1,8 +1,8 @@
1
- import * as React from 'react';
1
+ import React from 'react';
2
2
  import { FormattedMessage } from 'react-intl';
3
3
  import messages from './messages';
4
4
  import PopupBase from './PopupBase';
5
- import { Options, VirtualElement } from './Popper';
5
+ import { clientBoundingRect, Options, VirtualElement } from './Popper';
6
6
  import './PopupCursor.scss';
7
7
 
8
8
  const options: Partial<Options> = {
@@ -18,14 +18,7 @@ const options: Partial<Options> = {
18
18
  };
19
19
 
20
20
  const generateGetBoundingClientRect = (x = 0, y = 0) => {
21
- return () => ({
22
- width: 0,
23
- height: 0,
24
- top: y,
25
- right: x,
26
- bottom: y,
27
- left: x,
28
- });
21
+ return () => clientBoundingRect(0, 0, x, y);
29
22
  };
30
23
 
31
24
  export default function PopupCursor(): JSX.Element {
@@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl';
5
5
  import messages from './messages';
6
6
  import PopupBase from './PopupBase';
7
7
  import useOutsideEvent from '../../common/useOutsideEvent';
8
- import { Options } from './Popper';
8
+ import { clientBoundingRect, Options } from './Popper';
9
9
  import { Shape } from '../../@types/model';
10
10
  import './PopupHighlight.scss';
11
11
 
@@ -50,14 +50,7 @@ export default function PopupHighlight({ onCancel = noop, onSubmit = noop, shape
50
50
  const { height, width, x, y } = shape;
51
51
 
52
52
  const reference = {
53
- getBoundingClientRect: () => ({
54
- bottom: y + height,
55
- height,
56
- left: x,
57
- right: x + width,
58
- top: y,
59
- width,
60
- }),
53
+ getBoundingClientRect: () => clientBoundingRect(height, width, x, y),
61
54
  };
62
55
 
63
56
  const handleKeydownOutside = (event: Event): void => {
@@ -4,7 +4,7 @@ import { FormattedMessage } from 'react-intl';
4
4
  import messages from './messages';
5
5
  import PopupBase from './PopupBase';
6
6
  import useOutsideEvent from '../../common/useOutsideEvent';
7
- import { Options } from './Popper';
7
+ import { clientBoundingRect, Options } from './Popper';
8
8
  import { Shape } from '../../@types/model';
9
9
  import './PopupHighlightError.scss';
10
10
 
@@ -48,14 +48,7 @@ export default function PopupHighlightError({ onCancel = noop, shape }: Props):
48
48
  const { height, width, x, y } = shape;
49
49
 
50
50
  const reference = {
51
- getBoundingClientRect: () => ({
52
- bottom: y + height,
53
- height,
54
- left: x,
55
- right: x + width,
56
- top: y,
57
- width,
58
- }),
51
+ getBoundingClientRect: () => clientBoundingRect(height, width, x, y),
59
52
  };
60
53
 
61
54
  useOutsideEvent('mousedown', popupRef.current?.popupRef, onCancel);
@@ -1,12 +1,11 @@
1
- import * as React from 'react';
2
- import * as Popper from '@popperjs/core';
3
- import * as ReactRedux from 'react-redux';
1
+ import React from 'react';
2
+ import Popper from '@popperjs/core';
3
+ import { useSelector } from 'react-redux';
4
4
  import { useIntl } from 'react-intl';
5
5
  import FocusTrap from 'box-ui-elements/es/components/focus-trap/FocusTrap';
6
6
  import messages from './messages';
7
7
  import PopupBase from './PopupBase';
8
8
  import ReplyForm from '../ReplyForm';
9
- import usePrevious from '../../common/usePrevious';
10
9
  import { getScale, getRotation } from '../../store/options';
11
10
  import { PopupReference } from './Popper';
12
11
  import './PopupReply.scss';
@@ -89,18 +88,16 @@ export default function PopupReply({
89
88
  const intl = useIntl();
90
89
  const popupRef = React.useRef<PopupBase>(null);
91
90
  const popupOptions = React.useRef<Partial<Popper.Options>>(getOptions()); // Keep the options reference the same between renders
92
- const rotation = ReactRedux.useSelector(getRotation);
93
- const prevRotation = usePrevious(rotation);
94
- const scale = ReactRedux.useSelector(getScale);
95
- const prevScale = usePrevious(scale);
91
+ const rotation = useSelector(getRotation);
92
+ const scale = useSelector(getScale);
96
93
 
97
94
  React.useEffect(() => {
98
95
  const { current: popup } = popupRef;
99
96
 
100
- if (popup?.popper && (rotation !== prevRotation || scale !== prevScale)) {
97
+ if (popup?.popper) {
101
98
  popup.popper.update();
102
99
  }
103
- }, [popupRef, prevRotation, prevScale, rotation, scale]);
100
+ }, [popupRef, rotation, scale]);
104
101
 
105
102
  return (
106
103
  <FocusTrap>
@@ -7,7 +7,7 @@ export type Props = {
7
7
  isDisabled?: boolean;
8
8
  isPrimary?: boolean;
9
9
  onClick?: (event: React.MouseEvent) => void;
10
- type: 'button' | 'submit' | 'reset' | undefined;
10
+ type?: 'button' | 'submit' | 'reset' | undefined;
11
11
  } & HTMLAttributes<HTMLButtonElement>;
12
12
 
13
13
  export default function ReplyButton({ children, isDisabled, isPrimary, onClick = noop, ...rest }: Props): JSX.Element {
@@ -70,7 +70,14 @@ const ReplyForm = (props: Props): JSX.Element => {
70
70
  }, [formRef]); // eslint-disable-line react-hooks/exhaustive-deps
71
71
 
72
72
  return (
73
- <Form ref={formRef} className="ba-ReplyForm" data-testid="ba-ReplyForm">
73
+ <Form
74
+ ref={formRef}
75
+ className="ba-ReplyForm"
76
+ data-testid="ba-ReplyForm"
77
+ onPointerEnterCapture={undefined}
78
+ onPointerLeaveCapture={undefined}
79
+ placeholder={undefined}
80
+ >
74
81
  <div className="ba-Popup-main">
75
82
  <ReplyField
76
83
  className="ba-Popup-field"
@@ -1,4 +1,4 @@
1
- import * as React from 'react';
1
+ import React from 'react';
2
2
  import classNames from 'classnames';
3
3
  import DecoratedDrawingPath from './DecoratedDrawingPath';
4
4
  import DrawingList from './DrawingList';
@@ -35,7 +35,7 @@ export default function DrawingCreator({
35
35
  const drawingPathRef = React.useRef<DrawingPathRef>(null);
36
36
  const drawingSVGRef = React.useRef<DrawingSVGRef>(null);
37
37
  const renderHandleRef = React.useRef<number | null>(null);
38
- const stroke = { color, size };
38
+ const stroke = React.useMemo(() => ({ color, size }), [color, size]);
39
39
 
40
40
  const getPoints = React.useCallback((): Array<Position> => {
41
41
  const { current: creatorEl } = creatorElRef;
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import * as ReactDOM from 'react-dom';
2
+ import * as ReactDOM from 'react-dom/client';
3
3
  import BaseManager, { Props } from '../common/BaseManager';
4
4
  import DrawingAnnotationsContainer from './DrawingAnnotationsContainer';
5
5
 
@@ -10,6 +10,10 @@ export default class DrawingListManager extends BaseManager {
10
10
  }
11
11
 
12
12
  render(props: Props): void {
13
- ReactDOM.render(<DrawingAnnotationsContainer location={this.location} {...props} />, this.reactEl);
13
+ if (!this.root) {
14
+ this.root = ReactDOM.createRoot(this.reactEl);
15
+ }
16
+
17
+ this.root.render(<DrawingAnnotationsContainer location={this.location} {...props} />);
14
18
  }
15
19
  }
@@ -18,7 +18,6 @@ export const createDrawingAction = (arg: CreateArg) => (dispatch: AppThunkDispat
18
18
  message,
19
19
  type: 'reply' as const,
20
20
  },
21
- // eslint-disable-next-line @typescript-eslint/camelcase
22
21
  file_version: {
23
22
  id: getFileVersionId(state),
24
23
  },
@@ -27,7 +26,6 @@ export const createDrawingAction = (arg: CreateArg) => (dispatch: AppThunkDispat
27
26
  type: 'page' as const,
28
27
  value: location,
29
28
  },
30
- // eslint-disable-next-line @typescript-eslint/camelcase
31
29
  path_groups: pathGroups,
32
30
  type: 'drawing' as const,
33
31
  },
@@ -20,7 +20,6 @@ export function formatDrawing(annotation: AnnotationDrawing): AnnotationDrawing
20
20
 
21
21
  return {
22
22
  target: {
23
- // eslint-disable-next-line @typescript-eslint/camelcase
24
23
  path_groups: pathGroups.map(pathGroup => addClientIds(pathGroup)),
25
24
  ...targetRest,
26
25
  },
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import * as ReactDOM from 'react-dom';
2
+ import * as ReactDOM from 'react-dom/client';
3
3
  import BaseManager, { Props } from '../common/BaseManager';
4
4
  import HighlightContainer from './HighlightContainer';
5
5
 
@@ -10,6 +10,10 @@ export default class HighlightManager extends BaseManager {
10
10
  }
11
11
 
12
12
  render(props: Props): void {
13
- ReactDOM.render(<HighlightContainer location={this.location} {...props} />, this.reactEl);
13
+ if (!this.root) {
14
+ this.root = ReactDOM.createRoot(this.reactEl);
15
+ }
16
+
17
+ this.root.render(<HighlightContainer location={this.location} {...props} />);
14
18
  }
15
19
  }
@@ -18,7 +18,6 @@ export const createHighlightAction = (arg: CreateArg) => (dispatch: AppThunkDisp
18
18
  message,
19
19
  type: 'reply' as const,
20
20
  },
21
- // eslint-disable-next-line @typescript-eslint/camelcase
22
21
  file_version: {
23
22
  id: getFileVersionId(state),
24
23
  },
@@ -30,7 +30,7 @@ export type Props = {
30
30
  status: CreatorStatus;
31
31
  };
32
32
 
33
- const modeStagedMap: { [M in Mode]?: Function } = {
33
+ const modeStagedMap: { [M in Mode]?: (staged: CreatorItem | null) => boolean } = {
34
34
  [Mode.DRAWING]: isCreatorStagedDrawing,
35
35
  [Mode.HIGHLIGHT]: isCreatorStagedHighlight,
36
36
  [Mode.REGION]: isCreatorStagedRegion,
@@ -52,7 +52,7 @@ const PopupLayer = (props: Props): JSX.Element | null => {
52
52
  } = props;
53
53
 
54
54
  const [reference, setReference] = React.useState<PopupReference | null>(null);
55
- const canCreate = (modeStagedMap[mode] || noop)(staged) || isPromoting;
55
+ const canCreate = (modeStagedMap[mode]?.(staged ?? null) ?? false) || isPromoting;
56
56
  const canReply = status !== CreatorStatus.started && status !== CreatorStatus.init;
57
57
  const isPending = status === CreatorStatus.pending;
58
58