@atlaskit/editor-common 119.16.0 → 119.16.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @atlaskit/editor-common
2
2
 
3
+ ## 119.16.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [`288e340380dbb`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/288e340380dbb) -
8
+ Behind experiment platform_editor_reduce_event_listener_count: cut the number of native DOM event
9
+ listeners the editor registers per node. React 18 registers a bubble and a capture listener for
10
+ every supported event type on each portal container (~130 listeners) and never removes them, so
11
+ (1) node views whose render() returns null no longer mount a portal at all, and (2) the breakout
12
+ resize handle tooltip portal is deferred until the pointer enters the node instead of being
13
+ mounted for every resizable node up front.
14
+
15
+ Also reduces the work the layout column divider decorations do on every `decorations(state)` read:
16
+ the mousedown listener is unbound when the widget decoration is destroyed, the widget DOM is built
17
+ lazily rather than once per call, the document walk no longer descends into node types that cannot
18
+ contain a layout, and the decoration key is derived from the layout's ordinal rather than its
19
+ position so an edit above a layout no longer invalidates and rebuilds every divider below it. The
20
+ widget now resolves its own layout section and column index from its live position at mousedown.
21
+
22
+ ## 119.16.1
23
+
24
+ ### Patch Changes
25
+
26
+ - Updated dependencies
27
+
3
28
  ## 119.16.0
4
29
 
5
30
  ### Minor Changes
@@ -28,7 +28,7 @@ var NETWORK_FAILURE_REGEX = /^network failure/i;
28
28
  var RESIZE_OBSERVER_LOOP_REGEX = /ResizeObserver loop completed with undelivered notifications/;
29
29
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
30
30
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
31
- var packageVersion = "119.15.1";
31
+ var packageVersion = "119.16.1";
32
32
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
33
33
  // Remove URL as it has UGC
34
34
  // Ignored via go/ees007
@@ -28,6 +28,7 @@ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/cl
28
28
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
29
29
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
30
30
  var _react = _interopRequireDefault(require("react"));
31
+ var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
31
32
  var _analytics = require("../analytics");
32
33
  var _coreUtils = require("../core-utils");
33
34
  var _eventDispatcher = require("../event-dispatcher");
@@ -50,6 +51,12 @@ var ReactNodeView = exports.default = /*#__PURE__*/function () {
50
51
  // Spreading props to pass through dynamic component props
51
52
  // Ignored via go/ees005
52
53
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ /**
55
+ * Tracks whether a React portal is currently mounted into `domRef`. Only used under
56
+ * `platform_editor_reduce_event_listener_count`, where a node view whose `render()`
57
+ * returns `null` does not mount one at all.
58
+ */
59
+ (0, _defineProperty2.default)(this, "hasMountedPortal", false);
53
60
  (0, _defineProperty2.default)(this, "decorations", []);
54
61
  (0, _defineProperty2.default)(this, "handleRef", function (node) {
55
62
  return _this._handleRef(node);
@@ -153,13 +160,41 @@ var ReactNodeView = exports.default = /*#__PURE__*/function () {
153
160
  if (!this.domRef || !component) {
154
161
  return;
155
162
  }
163
+
164
+ // React 18 does event delegation per portal container: `createPortal` calls
165
+ // `listenToAllSupportedEvents(container)`, which registers a bubble *and* a capture
166
+ // listener for every supported event type — roughly 130 real `addEventListener` calls
167
+ // on the container element. Those listeners are never removed (React has no
168
+ // un-listen path), so mounting a portal for a node view that renders nothing is pure
169
+ // overhead that scales with the number of such nodes in the document.
170
+ //
171
+ // `PortalProviderAPI.render` already invokes the thunk eagerly (see
172
+ // `getPortalProviderAPI`), so evaluating the element here does not change *when*
173
+ // `render()` runs — the pre-evaluated element is handed to the same render call below.
174
+ // If a node view starts out empty and later renders something, `update()` calls back into
175
+ // this method and the portal is mounted at that point.
176
+ var renderComponent = component;
177
+ if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_reduce_event_listener_count')) {
178
+ var element = component();
179
+ if (element === null) {
180
+ if (this.hasMountedPortal) {
181
+ this.portalProviderAPI.remove(this.key);
182
+ this.hasMountedPortal = false;
183
+ }
184
+ return;
185
+ }
186
+ this.hasMountedPortal = true;
187
+ renderComponent = function renderComponent() {
188
+ return element;
189
+ };
190
+ }
156
191
  var componentWithErrorBoundary = function componentWithErrorBoundary() {
157
192
  var _this3$node$type$name, _this3$node;
158
193
  return /*#__PURE__*/_react.default.createElement(_ErrorBoundary.ErrorBoundary, {
159
194
  component: _analytics.ACTION_SUBJECT.REACT_NODE_VIEW,
160
195
  componentId: (_this3$node$type$name = _this3 === null || _this3 === void 0 || (_this3$node = _this3.node) === null || _this3$node === void 0 || (_this3$node = _this3$node.type) === null || _this3$node === void 0 ? void 0 : _this3$node.name) !== null && _this3$node$type$name !== void 0 ? _this3$node$type$name : _analytics.ACTION_SUBJECT_ID.UNKNOWN_NODE,
161
196
  dispatchAnalyticsEvent: _this3.dispatchAnalyticsEvent
162
- }, component());
197
+ }, renderComponent());
163
198
  };
164
199
  this.portalProviderAPI.render(componentWithErrorBoundary,
165
200
  // Spreading props to pass through dynamic component props
@@ -24,7 +24,7 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
24
24
  * @jsx jsx
25
25
  */ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
26
26
  var packageName = "@atlaskit/editor-common";
27
- var packageVersion = "119.15.1";
27
+ var packageVersion = "119.16.1";
28
28
  var halfFocusRing = 1;
29
29
  var dropOffset = '0, 8';
30
30
  var fadeIn = (0, _react2.keyframes)({
@@ -14,7 +14,7 @@ const NETWORK_FAILURE_REGEX = /^network failure/i;
14
14
  const RESIZE_OBSERVER_LOOP_REGEX = /ResizeObserver loop completed with undelivered notifications/;
15
15
  const SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
16
16
  const packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
17
- const packageVersion = "119.15.1";
17
+ const packageVersion = "119.16.1";
18
18
  const sanitiseSentryEvents = (data, _hint) => {
19
19
  // Remove URL as it has UGC
20
20
  // Ignored via go/ees007
@@ -4,6 +4,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
4
  /* eslint-disable @atlaskit/editor/no-re-export */
5
5
 
6
6
  import React from 'react';
7
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
7
8
  import { ACTION_SUBJECT, ACTION_SUBJECT_ID } from '../analytics';
8
9
  import { isSSR } from '../core-utils';
9
10
  import { createDispatch } from '../event-dispatcher';
@@ -22,6 +23,12 @@ export default class ReactNodeView {
22
23
  // Spreading props to pass through dynamic component props
23
24
  // Ignored via go/ees005
24
25
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ /**
27
+ * Tracks whether a React portal is currently mounted into `domRef`. Only used under
28
+ * `platform_editor_reduce_event_listener_count`, where a node view whose `render()`
29
+ * returns `null` does not mount one at all.
30
+ */
31
+ _defineProperty(this, "hasMountedPortal", false);
25
32
  _defineProperty(this, "decorations", []);
26
33
  _defineProperty(this, "handleRef", node => this._handleRef(node));
27
34
  _defineProperty(this, "dispatchAnalyticsEvent", payload => {
@@ -116,13 +123,39 @@ export default class ReactNodeView {
116
123
  if (!this.domRef || !component) {
117
124
  return;
118
125
  }
126
+
127
+ // React 18 does event delegation per portal container: `createPortal` calls
128
+ // `listenToAllSupportedEvents(container)`, which registers a bubble *and* a capture
129
+ // listener for every supported event type — roughly 130 real `addEventListener` calls
130
+ // on the container element. Those listeners are never removed (React has no
131
+ // un-listen path), so mounting a portal for a node view that renders nothing is pure
132
+ // overhead that scales with the number of such nodes in the document.
133
+ //
134
+ // `PortalProviderAPI.render` already invokes the thunk eagerly (see
135
+ // `getPortalProviderAPI`), so evaluating the element here does not change *when*
136
+ // `render()` runs — the pre-evaluated element is handed to the same render call below.
137
+ // If a node view starts out empty and later renders something, `update()` calls back into
138
+ // this method and the portal is mounted at that point.
139
+ let renderComponent = component;
140
+ if (isExperimentEnabled('platform_editor_reduce_event_listener_count')) {
141
+ const element = component();
142
+ if (element === null) {
143
+ if (this.hasMountedPortal) {
144
+ this.portalProviderAPI.remove(this.key);
145
+ this.hasMountedPortal = false;
146
+ }
147
+ return;
148
+ }
149
+ this.hasMountedPortal = true;
150
+ renderComponent = () => element;
151
+ }
119
152
  const componentWithErrorBoundary = () => {
120
153
  var _this$node$type$name, _this$node, _this$node$type;
121
154
  return /*#__PURE__*/React.createElement(ErrorBoundary, {
122
155
  component: ACTION_SUBJECT.REACT_NODE_VIEW,
123
156
  componentId: (_this$node$type$name = this === null || this === void 0 ? void 0 : (_this$node = this.node) === null || _this$node === void 0 ? void 0 : (_this$node$type = _this$node.type) === null || _this$node$type === void 0 ? void 0 : _this$node$type.name) !== null && _this$node$type$name !== void 0 ? _this$node$type$name : ACTION_SUBJECT_ID.UNKNOWN_NODE,
124
157
  dispatchAnalyticsEvent: this.dispatchAnalyticsEvent
125
- }, component());
158
+ }, renderComponent());
126
159
  };
127
160
  this.portalProviderAPI.render(componentWithErrorBoundary,
128
161
  // Spreading props to pass through dynamic component props
@@ -14,7 +14,7 @@ import withAnalyticsEvents from '@atlaskit/analytics-next/withAnalyticsEvents';
14
14
  import { fg } from '@atlaskit/platform-feature-flags';
15
15
  import Layer from '../Layer';
16
16
  const packageName = "@atlaskit/editor-common";
17
- const packageVersion = "119.15.1";
17
+ const packageVersion = "119.16.1";
18
18
  const halfFocusRing = 1;
19
19
  const dropOffset = '0, 8';
20
20
  const fadeIn = keyframes({
@@ -20,7 +20,7 @@ var NETWORK_FAILURE_REGEX = /^network failure/i;
20
20
  var RESIZE_OBSERVER_LOOP_REGEX = /ResizeObserver loop completed with undelivered notifications/;
21
21
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
22
22
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
23
- var packageVersion = "119.15.1";
23
+ var packageVersion = "119.16.1";
24
24
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
25
25
  // Remove URL as it has UGC
26
26
  // Ignored via go/ees007
@@ -6,6 +6,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
6
6
  /* eslint-disable @atlaskit/editor/no-re-export */
7
7
 
8
8
  import React from 'react';
9
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
9
10
  import { ACTION_SUBJECT, ACTION_SUBJECT_ID } from '../analytics';
10
11
  import { isSSR } from '../core-utils';
11
12
  import { createDispatch } from '../event-dispatcher';
@@ -26,6 +27,12 @@ var ReactNodeView = /*#__PURE__*/function () {
26
27
  // Spreading props to pass through dynamic component props
27
28
  // Ignored via go/ees005
28
29
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ /**
31
+ * Tracks whether a React portal is currently mounted into `domRef`. Only used under
32
+ * `platform_editor_reduce_event_listener_count`, where a node view whose `render()`
33
+ * returns `null` does not mount one at all.
34
+ */
35
+ _defineProperty(this, "hasMountedPortal", false);
29
36
  _defineProperty(this, "decorations", []);
30
37
  _defineProperty(this, "handleRef", function (node) {
31
38
  return _this._handleRef(node);
@@ -129,13 +136,41 @@ var ReactNodeView = /*#__PURE__*/function () {
129
136
  if (!this.domRef || !component) {
130
137
  return;
131
138
  }
139
+
140
+ // React 18 does event delegation per portal container: `createPortal` calls
141
+ // `listenToAllSupportedEvents(container)`, which registers a bubble *and* a capture
142
+ // listener for every supported event type — roughly 130 real `addEventListener` calls
143
+ // on the container element. Those listeners are never removed (React has no
144
+ // un-listen path), so mounting a portal for a node view that renders nothing is pure
145
+ // overhead that scales with the number of such nodes in the document.
146
+ //
147
+ // `PortalProviderAPI.render` already invokes the thunk eagerly (see
148
+ // `getPortalProviderAPI`), so evaluating the element here does not change *when*
149
+ // `render()` runs — the pre-evaluated element is handed to the same render call below.
150
+ // If a node view starts out empty and later renders something, `update()` calls back into
151
+ // this method and the portal is mounted at that point.
152
+ var renderComponent = component;
153
+ if (isExperimentEnabled('platform_editor_reduce_event_listener_count')) {
154
+ var element = component();
155
+ if (element === null) {
156
+ if (this.hasMountedPortal) {
157
+ this.portalProviderAPI.remove(this.key);
158
+ this.hasMountedPortal = false;
159
+ }
160
+ return;
161
+ }
162
+ this.hasMountedPortal = true;
163
+ renderComponent = function renderComponent() {
164
+ return element;
165
+ };
166
+ }
132
167
  var componentWithErrorBoundary = function componentWithErrorBoundary() {
133
168
  var _this3$node$type$name, _this3$node;
134
169
  return /*#__PURE__*/React.createElement(ErrorBoundary, {
135
170
  component: ACTION_SUBJECT.REACT_NODE_VIEW,
136
171
  componentId: (_this3$node$type$name = _this3 === null || _this3 === void 0 || (_this3$node = _this3.node) === null || _this3$node === void 0 || (_this3$node = _this3$node.type) === null || _this3$node === void 0 ? void 0 : _this3$node.name) !== null && _this3$node$type$name !== void 0 ? _this3$node$type$name : ACTION_SUBJECT_ID.UNKNOWN_NODE,
137
172
  dispatchAnalyticsEvent: _this3.dispatchAnalyticsEvent
138
- }, component());
173
+ }, renderComponent());
139
174
  };
140
175
  this.portalProviderAPI.render(componentWithErrorBoundary,
141
176
  // Spreading props to pass through dynamic component props
@@ -21,7 +21,7 @@ import withAnalyticsEvents from '@atlaskit/analytics-next/withAnalyticsEvents';
21
21
  import { fg } from '@atlaskit/platform-feature-flags';
22
22
  import Layer from '../Layer';
23
23
  var packageName = "@atlaskit/editor-common";
24
- var packageVersion = "119.15.1";
24
+ var packageVersion = "119.16.1";
25
25
  var halfFocusRing = 1;
26
26
  var dropOffset = '0, 8';
27
27
  var fadeIn = keyframes({
@@ -14,6 +14,12 @@ export default class ReactNodeView<P = ReactComponentProps> implements NodeView
14
14
  private reactComponent?;
15
15
  private portalProviderAPI;
16
16
  private _viewShouldUpdate?;
17
+ /**
18
+ * Tracks whether a React portal is currently mounted into `domRef`. Only used under
19
+ * `platform_editor_reduce_event_listener_count`, where a node view whose `render()`
20
+ * returns `null` does not mount one at all.
21
+ */
22
+ private hasMountedPortal;
17
23
  protected eventDispatcher?: EventDispatcher;
18
24
  protected decorations: ReadonlyArray<Decoration>;
19
25
  reactComponentProps: P;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-common",
3
- "version": "119.16.0",
3
+ "version": "119.16.2",
4
4
  "description": "A package that contains common classes and components for editor and renderer",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -84,7 +84,7 @@
84
84
  "@atlaskit/task-decision": "^21.8.0",
85
85
  "@atlaskit/teams-app-config": "^2.2.0",
86
86
  "@atlaskit/textfield": "^10.1.0",
87
- "@atlaskit/tmp-editor-statsig": "^158.0.0",
87
+ "@atlaskit/tmp-editor-statsig": "^159.0.0",
88
88
  "@atlaskit/tokens": "^16.8.0",
89
89
  "@atlaskit/tooltip": "^24.2.0",
90
90
  "@atlaskit/width-detector": "^7.1.0",