@atlaskit/editor-common 102.13.2 → 102.13.4

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 (30) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cjs/analytics/types/enums.js +1 -0
  3. package/dist/cjs/extensibility/Extension/Extension/index.js +0 -25
  4. package/dist/cjs/messages/link-toolbar.js +5 -0
  5. package/dist/cjs/monitoring/error.js +1 -1
  6. package/dist/cjs/react-node-view/getInlineNodeViewProducer.js +185 -106
  7. package/dist/cjs/ui/DropList/index.js +1 -1
  8. package/dist/cjs/utils/referentiality.js +2 -2
  9. package/dist/es2019/analytics/types/enums.js +1 -0
  10. package/dist/es2019/extensibility/Extension/Extension/index.js +1 -26
  11. package/dist/es2019/messages/link-toolbar.js +5 -0
  12. package/dist/es2019/monitoring/error.js +1 -1
  13. package/dist/es2019/react-node-view/getInlineNodeViewProducer.js +181 -97
  14. package/dist/es2019/ui/DropList/index.js +1 -1
  15. package/dist/esm/analytics/types/enums.js +1 -0
  16. package/dist/esm/extensibility/Extension/Extension/index.js +1 -26
  17. package/dist/esm/messages/link-toolbar.js +5 -0
  18. package/dist/esm/monitoring/error.js +1 -1
  19. package/dist/esm/react-node-view/getInlineNodeViewProducer.js +185 -106
  20. package/dist/esm/ui/DropList/index.js +1 -1
  21. package/dist/esm/utils/referentiality.js +2 -2
  22. package/dist/types/analytics/types/enums.d.ts +2 -1
  23. package/dist/types/analytics/types/find-replace-events.d.ts +1 -1
  24. package/dist/types/messages/link-toolbar.d.ts +5 -0
  25. package/dist/types/react-node-view/getInlineNodeViewProducer.d.ts +13 -0
  26. package/dist/types-ts4.5/analytics/types/enums.d.ts +2 -1
  27. package/dist/types-ts4.5/analytics/types/find-replace-events.d.ts +1 -1
  28. package/dist/types-ts4.5/messages/link-toolbar.d.ts +5 -0
  29. package/dist/types-ts4.5/react-node-view/getInlineNodeViewProducer.d.ts +13 -0
  30. package/package.json +2 -2
@@ -18,65 +18,155 @@ import { generateUniqueNodeKey } from './generateUniqueNodeKey';
18
18
  import { getOrCreateOnVisibleObserver } from './onVisibleObserverFactory';
19
19
  const isSSR = Boolean(process.env.REACT_SSR);
20
20
  export const inlineNodeViewClassname = 'inlineNodeView';
21
-
22
- // number of initial nodes to allow React to render before switching to fallback
23
- let initialReactRenderedNodeCount = 0;
24
21
  const canRenderFallback = node => {
25
22
  return node.type.isInline && node.type.isAtom && node.type.isLeaf;
26
23
  };
27
24
 
28
25
  // list of inline nodes with toDOM fallback implementations that can be virtualized. As
29
26
  // additional nodes are converted they should be added here
30
- const virtualizedNodeWhitelist = ['status', 'mention', 'emoji', 'date', 'inlineCard'];
31
- const virtualisationConfiguration = () => {
32
- let enableVirtualization = false;
33
- let reactRenderedDocumentPositionThreshold = 0;
34
- if (isSSR) {
35
- return {
36
- enableVirtualization,
37
- reactRenderedDocumentPositionThreshold,
38
- virtualizeCurrentNode: () => false
39
- };
40
- }
27
+ const virtualizedNodeAllowlist = ['status', 'mention', 'emoji', 'date', 'inlineCard'];
28
+ function checkExperimentExposure() {
41
29
  if (editorExperiment('platform_editor_inline_node_virtualization', 'off', {
42
30
  exposure: true
43
31
  })) {
44
- enableVirtualization = false;
45
- } else if (editorExperiment('platform_editor_inline_node_virtualization', 'fallback-small', {
46
- exposure: true
47
- })) {
48
- enableVirtualization = true;
49
- reactRenderedDocumentPositionThreshold = 100;
50
- } else if (editorExperiment('platform_editor_inline_node_virtualization', 'fallback-large', {
32
+ return false;
33
+ }
34
+ if (editorExperiment('platform_editor_inline_node_virtualization', 'fallback-small', {
51
35
  exposure: true
52
36
  })) {
53
- enableVirtualization = true;
54
- reactRenderedDocumentPositionThreshold = 400;
37
+ return true;
55
38
  }
39
+ return false;
40
+ }
41
+ function createNodeView({
42
+ nodeViewParams,
43
+ pmPluginFactoryParams,
44
+ Component,
45
+ extraComponentProps,
46
+ extraNodeViewProps
47
+ }) {
48
+ // We set a variable for the current node which is
49
+ // used for comparisions when doing updates, before being
50
+ // overwritten to the updated node.
51
+ let currentNode = nodeViewParams.node;
52
+ const key = generateUniqueNodeKey();
56
53
 
57
- // we need to be able to override the threshold to 0
58
- // for specific situation, primarily testing
59
- if (reactRenderedDocumentPositionThreshold !== 0 && fg('platform_editor_inline_node_virt_threshold_override')) {
60
- reactRenderedDocumentPositionThreshold = 0;
54
+ // First we setup the dom element which will be rendered and "tracked" by prosemirror
55
+ // and also used as a "editor portal" (not react portal) target by the editor
56
+ // portal provider api, for rendering the Component passed.
57
+
58
+ let domRef = document.createElement('span');
59
+ domRef.contentEditable = 'false';
60
+ setDomAttrs(nodeViewParams.node, domRef);
61
+ const fallbackRef = {
62
+ current: null
63
+ };
64
+
65
+ // @see ED-3790
66
+ // something gets messed up during mutation processing inside of a
67
+ // nodeView if DOM structure has nested plain "div"s, it doesn't see the
68
+ // difference between them and it kills the nodeView
69
+ domRef.classList.add(`${nodeViewParams.node.type.name}View-content-wrap`, `${inlineNodeViewClassname}`);
70
+
71
+ // This util is shared for tracking rendering, and the ErrorBoundary that
72
+ // is setup to wrap the Component when rendering
73
+ // NOTE: This is not a prosemirror dispatch
74
+ function dispatchAnalyticsEvent(payload) {
75
+ pmPluginFactoryParams.eventDispatcher.emit(analyticsEventKey, {
76
+ payload
77
+ });
61
78
  }
62
- return {
63
- enableVirtualization,
64
- reactRenderedDocumentPositionThreshold,
65
- virtualizeCurrentNode: nodeType => enableVirtualization && virtualizedNodeWhitelist.includes(nodeType) && !(initialReactRenderedNodeCount < reactRenderedDocumentPositionThreshold)
79
+
80
+ // This is called to render the Component into domRef which is inside the
81
+ // prosemirror View.
82
+ // Internally it uses the unstable_renderSubtreeIntoContainer api to render,
83
+ // to the passed dom element (domRef) which means it is automatically
84
+ // "cleaned up" when you do a "re render".
85
+ function renderComponent() {
86
+ pmPluginFactoryParams.portalProviderAPI.render(getPortalChildren({
87
+ dispatchAnalyticsEvent,
88
+ currentNode,
89
+ nodeViewParams,
90
+ Component,
91
+ extraComponentProps
92
+ }), domRef, key);
93
+ }
94
+ const {
95
+ samplingRate,
96
+ slowThreshold,
97
+ trackingEnabled
98
+ } = getPerformanceOptions(nodeViewParams.view);
99
+ trackingEnabled && startMeasureReactNodeViewRendered({
100
+ nodeTypeName: currentNode.type.name
101
+ });
102
+
103
+ // We render the component while creating the node view
104
+ renderComponent();
105
+ trackingEnabled && stopMeasureReactNodeViewRendered({
106
+ nodeTypeName: currentNode.type.name,
107
+ dispatchAnalyticsEvent,
108
+ samplingRate,
109
+ slowThreshold
110
+ });
111
+ const extraNodeViewPropsWithStopEvent = {
112
+ ...extraNodeViewProps
66
113
  };
67
- };
68
- function createNodeView({
114
+
115
+ // https://prosemirror.net/docs/ref/#view.NodeView
116
+ const nodeView = {
117
+ get dom() {
118
+ return domRef;
119
+ },
120
+ update(nextNode, _decorations) {
121
+ // Let ProseMirror handle the update if node types are different.
122
+ // This prevents an issue where it was not possible to select the
123
+ // inline node view then replace it by entering text - the node
124
+ // view contents would be deleted but the node view itself would
125
+ // stay in the view and become uneditable.
126
+ if (currentNode.type !== nextNode.type) {
127
+ return false;
128
+ }
129
+ // On updates, we only set the new attributes if the type, attributes, and marks
130
+ // have changed on the node.
131
+
132
+ // NOTE: this could mean attrs changes aren't reflected in the dom,
133
+ // when an attribute key which was previously present is no longer
134
+ // present.
135
+ // ie.
136
+ // -> Original attributes { text: "hello world", color: "red" }
137
+ // -> Updated attributes { color: "blue" }
138
+ // in this case, the dom text attribute will not be cleared.
139
+ //
140
+ // This may not be an issue with any of our current node schemas.
141
+ if (!currentNode.sameMarkup(nextNode)) {
142
+ setDomAttrs(nextNode, domRef);
143
+ }
144
+ currentNode = nextNode;
145
+ renderComponent();
146
+ return true;
147
+ },
148
+ destroy() {
149
+ // When prosemirror destroys the node view, we need to clean up
150
+ // what we have previously rendered using the editor portal
151
+ // provider api.
152
+ pmPluginFactoryParams.portalProviderAPI.remove(key);
153
+ // @ts-expect-error Expect an error as domRef is expected to be
154
+ // of HTMLSpanElement type however once the node view has
155
+ // been destroyed no other consumers should still be using it.
156
+ domRef = undefined;
157
+ fallbackRef.current = null;
158
+ },
159
+ ...extraNodeViewPropsWithStopEvent
160
+ };
161
+ return nodeView;
162
+ }
163
+ function createNodeViewVirtualized({
69
164
  nodeViewParams,
70
165
  pmPluginFactoryParams,
71
166
  Component,
72
167
  extraComponentProps,
73
168
  extraNodeViewProps
74
169
  }) {
75
- const {
76
- enableVirtualization,
77
- virtualizeCurrentNode
78
- } = virtualisationConfiguration();
79
- const virtualizeNode = virtualizeCurrentNode(nodeViewParams.node.type.name);
80
170
  // We set a variable for the current node which is
81
171
  // used for comparisions when doing updates, before being
82
172
  // overwritten to the updated node.
@@ -128,18 +218,20 @@ function createNodeView({
128
218
  nodeViewParams,
129
219
  Component,
130
220
  extraComponentProps
131
- }), domRef, key, enableVirtualization ? onBeforeReactDomRender : undefined);
221
+ }), domRef, key, onBeforeReactDomRender);
132
222
  }
133
223
  let didRenderComponentWithIntersectionObserver = false;
134
224
  let destroyed = false;
135
225
  let removeIntersectionObserver = () => {};
136
226
  function renderFallback() {
137
227
  var _currentNode$type, _currentNode$type$spe;
138
- if (canRenderFallback(currentNode) && typeof ((_currentNode$type = currentNode.type) === null || _currentNode$type === void 0 ? void 0 : (_currentNode$type$spe = _currentNode$type.spec) === null || _currentNode$type$spe === void 0 ? void 0 : _currentNode$type$spe.toDOM) === 'function') {
139
- const fallback = DOMSerializer.renderSpec(document, currentNode.type.spec.toDOM(currentNode));
140
- fallbackRef.current = fallback.dom;
141
- domRef.replaceChildren(fallback.dom);
228
+ if (!canRenderFallback(currentNode) || typeof ((_currentNode$type = currentNode.type) === null || _currentNode$type === void 0 ? void 0 : (_currentNode$type$spe = _currentNode$type.spec) === null || _currentNode$type$spe === void 0 ? void 0 : _currentNode$type$spe.toDOM) !== 'function') {
229
+ return;
142
230
  }
231
+ const fallback = DOMSerializer.renderSpec(document, currentNode.type.spec.toDOM(currentNode));
232
+ const dom = fallback.dom;
233
+ fallbackRef.current = dom;
234
+ domRef.replaceChildren(dom);
143
235
  }
144
236
  function attachNodeViewObserver() {
145
237
  const observer = getOrCreateOnVisibleObserver(nodeViewParams.view);
@@ -152,47 +244,22 @@ function createNodeView({
152
244
  });
153
245
  }
154
246
  }
155
- if (virtualizeNode) {
156
- renderFallback();
157
- // allow the fallback to render first before attaching the observer.
158
- // Will tweak this in a follow up PR to optimise rendering of visible
159
- // nodes without fallback rendering.
160
- setTimeout(() => {
161
- attachNodeViewObserver();
162
- }, 0);
163
- } else {
164
- initialReactRenderedNodeCount = initialReactRenderedNodeCount + 1;
165
- const {
166
- samplingRate,
167
- slowThreshold,
168
- trackingEnabled
169
- } = getPerformanceOptions(nodeViewParams.view);
170
- trackingEnabled && startMeasureReactNodeViewRendered({
171
- nodeTypeName: currentNode.type.name
172
- });
173
-
174
- // We render the component while creating the node view
175
- renderComponent();
176
- trackingEnabled && stopMeasureReactNodeViewRendered({
177
- nodeTypeName: currentNode.type.name,
178
- dispatchAnalyticsEvent,
179
- samplingRate,
180
- slowThreshold
181
- });
182
- }
247
+ renderFallback();
248
+ // allow the fallback to render first before attaching the observer.
249
+ // Will tweak this in a follow up PR to optimise rendering of visible
250
+ // nodes without fallback rendering.
251
+ setTimeout(() => {
252
+ attachNodeViewObserver();
253
+ }, 0);
183
254
  const extraNodeViewPropsWithStopEvent = {
184
255
  ...extraNodeViewProps,
185
- ...(enableVirtualization ? {
186
- // This is not related to virtualization, but it's something we should fix/handle
187
- // Remove this comment when virtualization experiment is cleaned up
188
- stopEvent(event) {
189
- const maybeStopEvent = extraNodeViewProps === null || extraNodeViewProps === void 0 ? void 0 : extraNodeViewProps.stopEvent;
190
- if (typeof maybeStopEvent === 'function') {
191
- return maybeStopEvent(event);
192
- }
193
- return false;
256
+ stopEvent: event => {
257
+ const maybeStopEvent = extraNodeViewProps === null || extraNodeViewProps === void 0 ? void 0 : extraNodeViewProps.stopEvent;
258
+ if (typeof maybeStopEvent === 'function') {
259
+ return maybeStopEvent(event);
194
260
  }
195
- } : {})
261
+ return false;
262
+ }
196
263
  };
197
264
 
198
265
  // https://prosemirror.net/docs/ref/#view.NodeView
@@ -225,19 +292,14 @@ function createNodeView({
225
292
  setDomAttrs(nextNode, domRef);
226
293
  }
227
294
  currentNode = nextNode;
228
- if (virtualizeNode) {
229
- if (didRenderComponentWithIntersectionObserver) {
230
- renderComponent();
231
- }
232
- } else {
295
+ if (didRenderComponentWithIntersectionObserver) {
233
296
  renderComponent();
234
297
  }
235
298
  return true;
236
299
  },
237
300
  destroy() {
238
- if (virtualizeNode) {
239
- removeIntersectionObserver();
240
- }
301
+ removeIntersectionObserver();
302
+ destroyed = true;
241
303
 
242
304
  // When prosemirror destroys the node view, we need to clean up
243
305
  // what we have previously rendered using the editor portal
@@ -248,9 +310,6 @@ function createNodeView({
248
310
  // been destroyed no other consumers should still be using it.
249
311
  domRef = undefined;
250
312
  fallbackRef.current = null;
251
- if (virtualizeNode) {
252
- destroyed = true;
253
- }
254
313
  },
255
314
  ...extraNodeViewPropsWithStopEvent
256
315
  };
@@ -343,6 +402,7 @@ function getPortalChildren({
343
402
  // [nodeViewName: string]: NodeViewProducer
344
403
  // }
345
404
 
405
+ const counterPerEditorViewMap = new WeakMap();
346
406
  // This return of this function is intended to be the value of a key
347
407
  // in a ProseMirror nodeViews object.
348
408
  export function getInlineNodeViewProducer({
@@ -352,10 +412,13 @@ export function getInlineNodeViewProducer({
352
412
  extraNodeViewProps
353
413
  }) {
354
414
  function nodeViewProducer(...nodeViewProducerParameters) {
355
- const nodeView = createNodeView({
415
+ var _node$type;
416
+ const view = nodeViewProducerParameters[1];
417
+ const node = nodeViewProducerParameters[0];
418
+ const parameters = {
356
419
  nodeViewParams: {
357
- node: nodeViewProducerParameters[0],
358
- view: nodeViewProducerParameters[1],
420
+ node,
421
+ view,
359
422
  getPos: nodeViewProducerParameters[2],
360
423
  decorations: nodeViewProducerParameters[3]
361
424
  },
@@ -363,8 +426,29 @@ export function getInlineNodeViewProducer({
363
426
  Component,
364
427
  extraComponentProps,
365
428
  extraNodeViewProps
366
- });
367
- return nodeView;
429
+ };
430
+ const isNodeTypeAllowedToBeVirtualized = virtualizedNodeAllowlist.includes((node === null || node === void 0 ? void 0 : (_node$type = node.type) === null || _node$type === void 0 ? void 0 : _node$type.name) || '');
431
+ if (!isNodeTypeAllowedToBeVirtualized || isSSR) {
432
+ return createNodeView(parameters);
433
+ }
434
+ if (fg('platform_editor_inline_node_virt_threshold_override')) {
435
+ return createNodeViewVirtualized(parameters);
436
+ }
437
+ let inlineNodeViewsVirtualizationCounter = counterPerEditorViewMap.get(view) || 0;
438
+ inlineNodeViewsVirtualizationCounter += 1;
439
+ counterPerEditorViewMap.set(view, inlineNodeViewsVirtualizationCounter);
440
+
441
+ // We never virtualize the 100th first elements
442
+ if (inlineNodeViewsVirtualizationCounter <= 100) {
443
+ return createNodeView(parameters);
444
+ }
445
+ if (
446
+ // Due to the experiment, we need to check experiment exposure
447
+ // when a document has more than 100 (virtulizables) nodes.
448
+ checkExperimentExposure()) {
449
+ return createNodeViewVirtualized(parameters);
450
+ }
451
+ return createNodeView(parameters);
368
452
  }
369
453
  return nodeViewProducer;
370
454
  }
@@ -13,7 +13,7 @@ import withAnalyticsContext from '@atlaskit/analytics-next/withAnalyticsContext'
13
13
  import withAnalyticsEvents from '@atlaskit/analytics-next/withAnalyticsEvents';
14
14
  import Layer from '../Layer';
15
15
  const packageName = "@atlaskit/editor-common";
16
- const packageVersion = "102.13.2";
16
+ const packageVersion = "102.13.4";
17
17
  const halfFocusRing = 1;
18
18
  const dropOffset = '0, 8';
19
19
  // Ignored via go/ees005
@@ -216,6 +216,7 @@ export var TRIGGER_METHOD = /*#__PURE__*/function (TRIGGER_METHOD) {
216
216
  TRIGGER_METHOD["KEYBOARD"] = "keyboard";
217
217
  TRIGGER_METHOD["SHORTCUT"] = "shortcut";
218
218
  TRIGGER_METHOD["TOOLBAR"] = "toolbar";
219
+ TRIGGER_METHOD["EXTERNAL"] = "external";
219
220
  return TRIGGER_METHOD;
220
221
  }({});
221
222
  export var ACTION_SUBJECT = /*#__PURE__*/function (ACTION_SUBJECT) {
@@ -9,13 +9,12 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
9
9
  * @jsx jsx
10
10
  */
11
11
 
12
- import React, { Fragment, useEffect } from 'react';
12
+ import React, { Fragment } from 'react';
13
13
 
14
14
  // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
15
15
  import { jsx } from '@emotion/react';
16
16
  import classnames from 'classnames';
17
17
  import { fg } from '@atlaskit/platform-feature-flags';
18
- import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
19
18
  import { useSharedPluginState } from '../../../hooks';
20
19
  import { overflowShadow } from '../../../ui';
21
20
  import { calculateBreakoutStyles } from '../../../utils';
@@ -118,30 +117,6 @@ function ExtensionWithPluginState(props) {
118
117
  setIsNodeHovered(didHover);
119
118
  }
120
119
  };
121
- var shouldFireNbmExperimentExposure = React.useMemo(function () {
122
- if (node.type.name === 'extension' && typeof getPos === 'function') {
123
- var pos = getPos();
124
- if (!isNaN(pos)) {
125
- var _view$state$doc$resol;
126
- var parentNameType = (_view$state$doc$resol = view.state.doc.resolve(pos).parent) === null || _view$state$doc$resol === void 0 || (_view$state$doc$resol = _view$state$doc$resol.type) === null || _view$state$doc$resol === void 0 ? void 0 : _view$state$doc$resol.name;
127
- if (['listItem', 'nestedExpand', 'blockquote', 'panel'].includes(parentNameType)) {
128
- return true;
129
- }
130
- }
131
- }
132
- return false;
133
- }, [node, getPos, view]);
134
- useEffect(function () {
135
- if (shouldFireNbmExperimentExposure) {
136
- // No-op editorExperiment evaluation to track usage of nested non-bodied macros
137
- // these can't be tracked at the point of diversion of the experience because that is a toggle of the
138
- // ProseMirror schema nodes for listItems, nestedExpand, blockquote, and panel. At that point the customer
139
- // has not yet been exposed
140
- editorExperiment('platform_editor_nested_non_bodied_macros', 'test', {
141
- exposure: true
142
- });
143
- }
144
- }, [shouldFireNbmExperimentExposure]);
145
120
  return jsx(Fragment, null, showMacroInteractionDesignUpdates && !isLivePageViewMode && jsx(ExtensionLozenge, {
146
121
  isNodeSelected: isNodeSelected,
147
122
  isNodeHovered: isNodeHovered,
@@ -60,6 +60,11 @@ export var linkToolbarMessages = defineMessages({
60
60
  defaultMessage: 'Go to Link Preferences',
61
61
  description: 'Go to Link Preferences'
62
62
  },
63
+ preferencesLink: {
64
+ id: 'fabric.editor.preferencesLink',
65
+ defaultMessage: 'Link preferences',
66
+ description: 'Go to link preferences'
67
+ },
63
68
  editDatasource: {
64
69
  id: 'fabric.editor.edit.datasource',
65
70
  defaultMessage: 'Edit search query',
@@ -7,7 +7,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
7
7
  import { isFedRamp } from './environment';
8
8
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
9
9
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
10
- var packageVersion = "102.13.2";
10
+ var packageVersion = "102.13.4";
11
11
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
12
12
  // Remove URL as it has UGC
13
13
  // Ignored via go/ees007