@atlaskit/editor-common 114.6.2 → 114.8.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.
Files changed (29) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/cjs/monitoring/error.js +1 -1
  3. package/dist/cjs/node-anchor/limited-mode-document-thresholds.js +12 -0
  4. package/dist/cjs/node-anchor/node-anchor-provider.js +31 -82
  5. package/dist/cjs/ui/DropList/index.js +1 -1
  6. package/dist/cjs/ui/Popup/index.js +12 -5
  7. package/dist/cjs/ui/Popup/utils.js +12 -7
  8. package/dist/es2019/monitoring/error.js +1 -1
  9. package/dist/es2019/node-anchor/limited-mode-document-thresholds.js +6 -0
  10. package/dist/es2019/node-anchor/node-anchor-provider.js +31 -83
  11. package/dist/es2019/ui/DropList/index.js +1 -1
  12. package/dist/es2019/ui/Popup/index.js +12 -5
  13. package/dist/es2019/ui/Popup/utils.js +12 -7
  14. package/dist/esm/monitoring/error.js +1 -1
  15. package/dist/esm/node-anchor/limited-mode-document-thresholds.js +6 -0
  16. package/dist/esm/node-anchor/node-anchor-provider.js +31 -83
  17. package/dist/esm/ui/DropList/index.js +1 -1
  18. package/dist/esm/ui/Popup/index.js +12 -5
  19. package/dist/esm/ui/Popup/utils.js +12 -7
  20. package/dist/types/card/types.d.ts +15 -1
  21. package/dist/types/node-anchor/limited-mode-document-thresholds.d.ts +5 -0
  22. package/dist/types/ui/Popup/index.d.ts +0 -1
  23. package/dist/types/ui/Popup/utils.d.ts +2 -1
  24. package/dist/types-ts4.5/card/types.d.ts +15 -1
  25. package/dist/types-ts4.5/node-anchor/limited-mode-document-thresholds.d.ts +5 -0
  26. package/dist/types-ts4.5/ui/Popup/index.d.ts +0 -1
  27. package/dist/types-ts4.5/ui/Popup/utils.d.ts +2 -1
  28. package/limited-mode-document-thresholds/package.json +17 -0
  29. package/package.json +5 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # @atlaskit/editor-common
2
2
 
3
+ ## 114.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`019540f8c0a67`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/019540f8c0a67) -
8
+ Fix editor popup scroll parent detection for chromeless editor in modals.
9
+
10
+ When the `platform_editor_fix_scrolling_popup_position` experiment is enabled, the Popup component
11
+ now prefers an explicitly provided `scrollableElement` prop over the auto-detected DOM ancestor.
12
+ This fixes popup positioning (code block language selector, table options, selection toolbar) in
13
+ chromeless editors embedded within modals, where the scroll container uses `overflow: auto` and
14
+ cannot be found by the existing `findOverflowScrollParent` utility.
15
+
16
+ The pre-computed scroll parent is also threaded through to `calculateVerticalStickTop` and
17
+ `calculateVerticalStickBottom` to avoid redundant DOM traversal and ensure position calculations
18
+ use the same element as the scroll event listener.
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies
23
+
24
+ ## 114.7.0
25
+
26
+ ### Minor Changes
27
+
28
+ - [`f25ff7f70d948`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/f25ff7f70d948) -
29
+ FFCLEANUP-91667 Remove shipped limited-mode experiment from Statsig config; use fixed document
30
+ thresholds in editor-common for limited-mode detection.
31
+ - [`85699a44bd1f8`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/85699a44bd1f8) -
32
+ Add Confluence short-link URL expansion for native embeds. When a Confluence short link
33
+ (`/wiki/x/<token>`) is pasted, it is resolved to its canonical page URL via the Object Resolver
34
+ Service before matching against registered experience manifests, gated behind
35
+ `platform-native-embeds-short-link-expansion`.
36
+ - `NativeEmbedFacade`: new `expandShortLink`, `getExperienceIdFromUrlAsync`, and `setCardClient`
37
+ methods; `createExperienceForUrl` now expands short links before manifest matching.
38
+ - `editor-plugin-card`: exposes `resolveShortLinkUrl` action; `replaceQueuedUrlWithCard` supports
39
+ async `EmbedCardNodeTransformer` return values.
40
+ - `editor-plugin-native-embeds`: wires `cardClient` from config and passes `api` to
41
+ `cardToNativeEmbedNode` for async short-link resolution.
42
+ - `editor-common`: `EmbedCardNodeTransformer` type updated to allow `Promise` return;
43
+ `CardPluginActions` extended with `resolveShortLinkUrl`.
44
+
45
+ ### Patch Changes
46
+
47
+ - Updated dependencies
48
+
3
49
  ## 114.6.2
4
50
 
5
51
  ### Patch Changes
@@ -19,7 +19,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
19
19
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
20
20
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
21
21
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
22
- var packageVersion = "114.6.1";
22
+ var packageVersion = "114.7.0";
23
23
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
24
24
  // Remove URL as it has UGC
25
25
  // Ignored via go/ees007
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.LIMITED_MODE_INCLUDE_LEGACY_CONTENT_IN_THRESHOLD = exports.LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD = exports.LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD = void 0;
7
+ /** Fixed document thresholds for limited (performance) mode (previously Statsig-driven). */
8
+ var LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD = exports.LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD = 5000;
9
+ var LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD = exports.LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD = 30000;
10
+
11
+ /** When true, any legacy-content macro triggers limited mode regardless of size/count. */
12
+ var LIMITED_MODE_INCLUDE_LEGACY_CONTENT_IN_THRESHOLD = exports.LIMITED_MODE_INCLUDE_LEGACY_CONTENT_IN_THRESHOLD = true;
@@ -8,28 +8,9 @@ exports.getNodeIdProvider = exports.NodeAnchorProvider = void 0;
8
8
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
9
9
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
10
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
- var _expVal = require("@atlaskit/tmp-editor-statsig/expVal");
12
11
  var _utils = require("../utils");
13
12
  var _dynamicBitArray = require("./dynamic-bit-array");
14
- /**
15
- * Gets a numeric experiment param, returning undefined if the value is not a valid number.
16
- * This guards against test overrides returning booleans or strings for numeric params.
17
- */
18
- var getNumericExperimentParam = function getNumericExperimentParam(paramName, fallbackValue) {
19
- var rawValue = (0, _expVal.expVal)('cc_editor_limited_mode_expanded', paramName, fallbackValue);
20
- if (typeof rawValue === 'number') {
21
- return rawValue;
22
- }
23
-
24
- // Handle string values from test overrides
25
- if (typeof rawValue === 'string') {
26
- var parsed = parseInt(rawValue, 10);
27
- if (!isNaN(parsed)) {
28
- return parsed;
29
- }
30
- }
31
- return undefined;
32
- };
13
+ var _limitedModeDocumentThresholds = require("./limited-mode-document-thresholds");
33
14
  var NodeAnchorProvider = exports.NodeAnchorProvider = /*#__PURE__*/function () {
34
15
  function NodeAnchorProvider() {
35
16
  var limitedMode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
@@ -111,87 +92,55 @@ var NodeAnchorProvider = exports.NodeAnchorProvider = /*#__PURE__*/function () {
111
92
  }]);
112
93
  }();
113
94
  var nodeIdProviderMap = new WeakMap();
114
- var LIMITED_MODE_NODE_SIZE_THRESHOLD = 40000;
115
-
116
- /**
117
- * Calculates custom document size including LCM ADF lengths (for non-expanded path).
118
- * This function can be removed when cc_editor_limited_mode_expanded is cleaned up.
119
- */
120
- var getCustomDocSize = function getCustomDocSize(doc) {
121
- var lcmAdfLength = 0;
122
- doc.descendants(function (node) {
123
- var _node$attrs;
124
- if (((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.extensionKey) === 'legacy-content') {
125
- var _node$attrs$parameter, _node$attrs2;
126
- lcmAdfLength += (_node$attrs$parameter = (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 || (_node$attrs2 = _node$attrs2.parameters) === null || _node$attrs2 === void 0 || (_node$attrs2 = _node$attrs2.adf) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.length) !== null && _node$attrs$parameter !== void 0 ? _node$attrs$parameter : 0;
127
- }
128
- });
129
- return doc.nodeSize + lcmAdfLength;
130
- };
131
95
 
132
96
  /**
133
97
  * Determines whether limited mode should be enabled.
134
98
  * This logic mirrors the limited mode plugin implementation, but lives here to avoid a circular dependency.
135
99
  * If it changes, update the matching logic in `editor-plugin-limited-mode/src/pm-plugins/main.ts`.
136
100
  *
137
- * Under the expanded gate, limited mode is activated when ANY of the following conditions are met:
138
- * 1. Document size exceeds `docSizeThreshold` (if defined) - checked first as O(1)
139
- * 2. Node count exceeds `nodeCountThreshold` (if defined)
140
- * 3. Document contains a legacy-content macro (LCM) (if `includeLcmInThreshold` is true)
101
+ * Limited mode is activated when ANY of the following conditions are met:
102
+ * 1. Document size exceeds `LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD` checked first as O(1)
103
+ * 2. Node count exceeds `LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD`
104
+ * 3. Document contains a legacy-content macro (LCM)
141
105
  *
142
106
  * Performance optimisations:
143
107
  * - Doc size is checked first (O(1)) - if it exceeds threshold, we skip traversal entirely.
144
- * - If `includeLcmInThreshold` is enabled and we find an LCM, we exit traversal early.
145
- * - If neither node count nor LCM conditions are configured, we skip traversal entirely.
108
+ * - If we find an LCM during traversal, we exit early since limited mode will be enabled.
146
109
  */
147
110
  var isLimitedModeEnabled = function isLimitedModeEnabled(editorView) {
148
111
  var doc = editorView.state.doc;
149
- if ((0, _expVal.expVal)('cc_editor_limited_mode_expanded', 'isEnabled', false)) {
150
- var nodeCountThreshold = getNumericExperimentParam('nodeCountThreshold', 5000);
151
- var docSizeThreshold = getNumericExperimentParam('docSizeThreshold', 30000);
152
- var includeLcmInThreshold = Boolean((0, _expVal.expVal)('cc_editor_limited_mode_expanded', 'includeLcmInThreshold', false));
112
+ var nodeCountThreshold = _limitedModeDocumentThresholds.LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD;
113
+ var docSizeThreshold = _limitedModeDocumentThresholds.LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD;
153
114
 
154
- // Early exit: doc size exceeds threshold - O(1), no traversal needed
155
- if (docSizeThreshold !== undefined && doc.nodeSize > docSizeThreshold) {
156
- return true;
157
- }
115
+ // Early exit: doc size exceeds threshold - O(1), no traversal needed
116
+ if (doc.nodeSize > docSizeThreshold) {
117
+ return true;
118
+ }
119
+
120
+ // Single traversal for node count and LCM detection
121
+ var nodeCount = 0;
122
+ var hasLcm = false;
123
+ doc.descendants(function (node) {
124
+ var _node$attrs;
125
+ nodeCount += 1;
126
+ if (((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.extensionKey) === 'legacy-content') {
127
+ hasLcm = true;
158
128
 
159
- // Early exit: no traversal needed if neither condition is configured
160
- var needNodeCount = nodeCountThreshold !== undefined;
161
- if (!needNodeCount && !includeLcmInThreshold) {
129
+ // Early exit: LCM found limited mode will be enabled
162
130
  return false;
163
131
  }
132
+ });
164
133
 
165
- // Single traversal for node count and/or LCM detection
166
- var nodeCount = 0;
167
- var hasLcm = false;
168
- doc.descendants(function (node) {
169
- var _node$attrs3;
170
- nodeCount += 1;
171
- if (((_node$attrs3 = node.attrs) === null || _node$attrs3 === void 0 ? void 0 : _node$attrs3.extensionKey) === 'legacy-content') {
172
- hasLcm = true;
173
-
174
- // Early exit: LCM found and condition is enabled - no need to continue counting
175
- if (includeLcmInThreshold) {
176
- return false;
177
- }
178
- }
179
- });
180
-
181
- // LCM condition takes precedence (if we early exited traversal, this is why)
182
- if (includeLcmInThreshold && hasLcm) {
183
- return true;
184
- }
134
+ // LCM condition takes precedence (if we early exited traversal, this is why)
135
+ if (hasLcm) {
136
+ return true;
137
+ }
185
138
 
186
- // Check node count threshold
187
- if (needNodeCount && nodeCount > nodeCountThreshold) {
188
- return true;
189
- }
190
- return false;
191
- } else {
192
- var customDocSize = getCustomDocSize(doc);
193
- return customDocSize > LIMITED_MODE_NODE_SIZE_THRESHOLD;
139
+ // Check node count threshold
140
+ if (nodeCount > nodeCountThreshold) {
141
+ return true;
194
142
  }
143
+ return false;
195
144
  };
196
145
 
197
146
  // Get the NodeIdProvider for a specific EditorView instance.
@@ -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 = "114.6.1";
27
+ var packageVersion = "114.7.0";
28
28
  var halfFocusRing = 1;
29
29
  var dropOffset = '0, 8';
30
30
  var fadeIn = (0, _react2.keyframes)({
@@ -17,6 +17,7 @@ var _rafSchd = _interopRequireDefault(require("raf-schd"));
17
17
  var _reactDom = require("react-dom");
18
18
  var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
19
19
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
20
+ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
20
21
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
21
22
  var _utils = require("./utils");
22
23
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
@@ -119,7 +120,8 @@ var Popup = exports.default = /*#__PURE__*/function (_React$Component) {
119
120
  allowOutOfBounds: allowOutOfBounds,
120
121
  rect: rect,
121
122
  boundariesElement: boundariesElement || document.body,
122
- minPopupMargin: minPopupMargin
123
+ minPopupMargin: minPopupMargin,
124
+ scrollableElement: stick && (0, _expValEquals.expValEquals)('platform_editor_fix_scrolling_popup_position', 'isEnabled', true) ? this.scrollElement : undefined
123
125
  });
124
126
  position = onPositionCalculated ? onPositionCalculated(position) : position;
125
127
  if (typeof position.top !== 'undefined' && absoluteOffset !== null && absoluteOffset !== void 0 && absoluteOffset.top) {
@@ -268,12 +270,17 @@ var Popup = exports.default = /*#__PURE__*/function (_React$Component) {
268
270
 
269
271
  // Ignored via go/ees005
270
272
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
271
- this.scrollParentElement = (0, _utils.findOverflowScrollParent)(this.props.target);
272
- if (this.scrollParentElement && this.resizeObserver) {
273
- this.resizeObserver.observe(this.scrollParentElement);
273
+ var target = this.props.target;
274
+
275
+ // Resolve the effective scroll parent: prefer the explicitly provided scrollableElement
276
+ // prop over the auto-detected ancestor. Allows product consumers to provide the correct
277
+ // scroll container (e.g. a modal body) when auto-detection can't find it.
278
+ var scrollParentElement = (0, _expValEquals.expValEquals)('platform_editor_fix_scrolling_popup_position', 'isEnabled', true) ? this.props.scrollableElement || (0, _utils.findOverflowScrollParent)(target) : (0, _utils.findOverflowScrollParent)(target);
279
+ if (scrollParentElement && this.resizeObserver) {
280
+ this.resizeObserver.observe(scrollParentElement);
274
281
  }
275
282
  if (stick) {
276
- this.scrollElement = this.scrollParentElement;
283
+ this.scrollElement = scrollParentElement;
277
284
  } else {
278
285
  this.scrollElement = this.props.scrollableElement;
279
286
  }
@@ -145,8 +145,9 @@ var calculateVerticalStickBottom = function calculateVerticalStickBottom(_ref2)
145
145
  popup = _ref2.popup,
146
146
  offset = _ref2.offset,
147
147
  position = _ref2.position,
148
- boundariesElement = _ref2.boundariesElement;
149
- var scrollParent = findOverflowScrollParent(target) || boundariesElement;
148
+ boundariesElement = _ref2.boundariesElement,
149
+ scrollableElement = _ref2.scrollableElement;
150
+ var scrollParent = scrollableElement || findOverflowScrollParent(target) || boundariesElement;
150
151
  var newPos = _objectSpread({}, position);
151
152
  if (scrollParent) {
152
153
  var topOffsetTop = targetTop - scrollParent.getBoundingClientRect().top;
@@ -169,8 +170,9 @@ var calculateVerticalStickTop = function calculateVerticalStickTop(_ref3) {
169
170
  offset = _ref3.offset,
170
171
  position = _ref3.position,
171
172
  placement = _ref3.placement,
172
- boundariesElement = _ref3.boundariesElement;
173
- var scrollParent = findOverflowScrollParent(target) || boundariesElement;
173
+ boundariesElement = _ref3.boundariesElement,
174
+ scrollableElement = _ref3.scrollableElement;
175
+ var scrollParent = scrollableElement || findOverflowScrollParent(target) || boundariesElement;
174
176
  var newPos = _objectSpread({}, position);
175
177
  if (scrollParent) {
176
178
  var _scrollParent$getBoun = scrollParent.getBoundingClientRect(),
@@ -233,7 +235,8 @@ function calculatePosition(_ref5) {
233
235
  allowOutOfBounds = _ref5$allowOutOfBound === void 0 ? false : _ref5$allowOutOfBound,
234
236
  rect = _ref5.rect,
235
237
  boundariesElement = _ref5.boundariesElement,
236
- minPopupMargin = _ref5.minPopupMargin;
238
+ minPopupMargin = _ref5.minPopupMargin,
239
+ scrollableElement = _ref5.scrollableElement;
237
240
  var position = {};
238
241
  if (!target || !popup || !popup.offsetParent || !isHTMLElementNode(popup.offsetParent)) {
239
242
  return position;
@@ -292,7 +295,8 @@ function calculatePosition(_ref5) {
292
295
  offset: offset,
293
296
  position: position,
294
297
  placement: verticalPlacement,
295
- boundariesElement: boundariesElement
298
+ boundariesElement: boundariesElement,
299
+ scrollableElement: scrollableElement
296
300
  });
297
301
  }
298
302
  if (verticalPlacement !== 'top' && verticalPlacement !== 'start' && stick) {
@@ -303,7 +307,8 @@ function calculatePosition(_ref5) {
303
307
  popup: popup,
304
308
  offset: offset,
305
309
  position: position,
306
- boundariesElement: boundariesElement
310
+ boundariesElement: boundariesElement,
311
+ scrollableElement: scrollableElement
307
312
  });
308
313
  }
309
314
  var horizontalPosition = calculateHorizontalPlacement({
@@ -4,7 +4,7 @@ import { isFedRamp } from './environment';
4
4
  import { normaliseSentryBreadcrumbs, SERIALIZABLE_ATTRIBUTES } from './normalise-sentry-breadcrumbs';
5
5
  const SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
6
6
  const packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
7
- const packageVersion = "114.6.1";
7
+ const packageVersion = "114.7.0";
8
8
  const sanitiseSentryEvents = (data, _hint) => {
9
9
  // Remove URL as it has UGC
10
10
  // Ignored via go/ees007
@@ -0,0 +1,6 @@
1
+ /** Fixed document thresholds for limited (performance) mode (previously Statsig-driven). */
2
+ export const LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD = 5000;
3
+ export const LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD = 30000;
4
+
5
+ /** When true, any legacy-content macro triggers limited mode regardless of size/count. */
6
+ export const LIMITED_MODE_INCLUDE_LEGACY_CONTENT_IN_THRESHOLD = true;
@@ -1,27 +1,7 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
- import { expVal } from '@atlaskit/tmp-editor-statsig/expVal';
3
2
  import { isEmptyDocument } from '../utils';
4
3
  import { DynamicBitArray } from './dynamic-bit-array';
5
-
6
- /**
7
- * Gets a numeric experiment param, returning undefined if the value is not a valid number.
8
- * This guards against test overrides returning booleans or strings for numeric params.
9
- */
10
- const getNumericExperimentParam = (paramName, fallbackValue) => {
11
- const rawValue = expVal('cc_editor_limited_mode_expanded', paramName, fallbackValue);
12
- if (typeof rawValue === 'number') {
13
- return rawValue;
14
- }
15
-
16
- // Handle string values from test overrides
17
- if (typeof rawValue === 'string') {
18
- const parsed = parseInt(rawValue, 10);
19
- if (!isNaN(parsed)) {
20
- return parsed;
21
- }
22
- }
23
- return undefined;
24
- };
4
+ import { LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD, LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD } from './limited-mode-document-thresholds';
25
5
  export class NodeAnchorProvider {
26
6
  constructor(limitedMode = false, emptyDoc = false) {
27
7
  _defineProperty(this, "cache", new WeakMap());
@@ -85,87 +65,55 @@ export class NodeAnchorProvider {
85
65
  }
86
66
  }
87
67
  const nodeIdProviderMap = new WeakMap();
88
- const LIMITED_MODE_NODE_SIZE_THRESHOLD = 40000;
89
-
90
- /**
91
- * Calculates custom document size including LCM ADF lengths (for non-expanded path).
92
- * This function can be removed when cc_editor_limited_mode_expanded is cleaned up.
93
- */
94
- const getCustomDocSize = doc => {
95
- let lcmAdfLength = 0;
96
- doc.descendants(node => {
97
- var _node$attrs;
98
- if (((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.extensionKey) === 'legacy-content') {
99
- var _node$attrs$parameter, _node$attrs2, _node$attrs2$paramete, _node$attrs2$paramete2;
100
- lcmAdfLength += (_node$attrs$parameter = (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : (_node$attrs2$paramete = _node$attrs2.parameters) === null || _node$attrs2$paramete === void 0 ? void 0 : (_node$attrs2$paramete2 = _node$attrs2$paramete.adf) === null || _node$attrs2$paramete2 === void 0 ? void 0 : _node$attrs2$paramete2.length) !== null && _node$attrs$parameter !== void 0 ? _node$attrs$parameter : 0;
101
- }
102
- });
103
- return doc.nodeSize + lcmAdfLength;
104
- };
105
68
 
106
69
  /**
107
70
  * Determines whether limited mode should be enabled.
108
71
  * This logic mirrors the limited mode plugin implementation, but lives here to avoid a circular dependency.
109
72
  * If it changes, update the matching logic in `editor-plugin-limited-mode/src/pm-plugins/main.ts`.
110
73
  *
111
- * Under the expanded gate, limited mode is activated when ANY of the following conditions are met:
112
- * 1. Document size exceeds `docSizeThreshold` (if defined) - checked first as O(1)
113
- * 2. Node count exceeds `nodeCountThreshold` (if defined)
114
- * 3. Document contains a legacy-content macro (LCM) (if `includeLcmInThreshold` is true)
74
+ * Limited mode is activated when ANY of the following conditions are met:
75
+ * 1. Document size exceeds `LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD` checked first as O(1)
76
+ * 2. Node count exceeds `LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD`
77
+ * 3. Document contains a legacy-content macro (LCM)
115
78
  *
116
79
  * Performance optimisations:
117
80
  * - Doc size is checked first (O(1)) - if it exceeds threshold, we skip traversal entirely.
118
- * - If `includeLcmInThreshold` is enabled and we find an LCM, we exit traversal early.
119
- * - If neither node count nor LCM conditions are configured, we skip traversal entirely.
81
+ * - If we find an LCM during traversal, we exit early since limited mode will be enabled.
120
82
  */
121
83
  const isLimitedModeEnabled = editorView => {
122
84
  const doc = editorView.state.doc;
123
- if (expVal('cc_editor_limited_mode_expanded', 'isEnabled', false)) {
124
- const nodeCountThreshold = getNumericExperimentParam('nodeCountThreshold', 5000);
125
- const docSizeThreshold = getNumericExperimentParam('docSizeThreshold', 30000);
126
- const includeLcmInThreshold = Boolean(expVal('cc_editor_limited_mode_expanded', 'includeLcmInThreshold', false));
85
+ const nodeCountThreshold = LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD;
86
+ const docSizeThreshold = LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD;
127
87
 
128
- // Early exit: doc size exceeds threshold - O(1), no traversal needed
129
- if (docSizeThreshold !== undefined && doc.nodeSize > docSizeThreshold) {
130
- return true;
131
- }
88
+ // Early exit: doc size exceeds threshold - O(1), no traversal needed
89
+ if (doc.nodeSize > docSizeThreshold) {
90
+ return true;
91
+ }
92
+
93
+ // Single traversal for node count and LCM detection
94
+ let nodeCount = 0;
95
+ let hasLcm = false;
96
+ doc.descendants(node => {
97
+ var _node$attrs;
98
+ nodeCount += 1;
99
+ if (((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.extensionKey) === 'legacy-content') {
100
+ hasLcm = true;
132
101
 
133
- // Early exit: no traversal needed if neither condition is configured
134
- const needNodeCount = nodeCountThreshold !== undefined;
135
- if (!needNodeCount && !includeLcmInThreshold) {
102
+ // Early exit: LCM found limited mode will be enabled
136
103
  return false;
137
104
  }
105
+ });
138
106
 
139
- // Single traversal for node count and/or LCM detection
140
- let nodeCount = 0;
141
- let hasLcm = false;
142
- doc.descendants(node => {
143
- var _node$attrs3;
144
- nodeCount += 1;
145
- if (((_node$attrs3 = node.attrs) === null || _node$attrs3 === void 0 ? void 0 : _node$attrs3.extensionKey) === 'legacy-content') {
146
- hasLcm = true;
147
-
148
- // Early exit: LCM found and condition is enabled - no need to continue counting
149
- if (includeLcmInThreshold) {
150
- return false;
151
- }
152
- }
153
- });
154
-
155
- // LCM condition takes precedence (if we early exited traversal, this is why)
156
- if (includeLcmInThreshold && hasLcm) {
157
- return true;
158
- }
107
+ // LCM condition takes precedence (if we early exited traversal, this is why)
108
+ if (hasLcm) {
109
+ return true;
110
+ }
159
111
 
160
- // Check node count threshold
161
- if (needNodeCount && nodeCount > nodeCountThreshold) {
162
- return true;
163
- }
164
- return false;
165
- } else {
166
- const customDocSize = getCustomDocSize(doc);
167
- return customDocSize > LIMITED_MODE_NODE_SIZE_THRESHOLD;
112
+ // Check node count threshold
113
+ if (nodeCount > nodeCountThreshold) {
114
+ return true;
168
115
  }
116
+ return false;
169
117
  };
170
118
 
171
119
  // Get the NodeIdProvider for a specific EditorView instance.
@@ -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 = "114.6.1";
17
+ const packageVersion = "114.7.0";
18
18
  const halfFocusRing = 1;
19
19
  const dropOffset = '0, 8';
20
20
  const fadeIn = keyframes({
@@ -5,6 +5,7 @@ import rafSchedule from 'raf-schd';
5
5
  import { createPortal, flushSync } from 'react-dom';
6
6
  import { akEditorFloatingPanelZIndex } from '@atlaskit/editor-shared-styles';
7
7
  import { fg } from '@atlaskit/platform-feature-flags';
8
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
8
9
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
9
10
  import { calculatePlacement, calculatePosition, findOverflowScrollParent, validatePosition } from './utils';
10
11
  // Ignored via go/ees005
@@ -96,7 +97,8 @@ export default class Popup extends React.Component {
96
97
  allowOutOfBounds,
97
98
  rect,
98
99
  boundariesElement: boundariesElement || document.body,
99
- minPopupMargin
100
+ minPopupMargin,
101
+ scrollableElement: stick && expValEquals('platform_editor_fix_scrolling_popup_position', 'isEnabled', true) ? this.scrollElement : undefined
100
102
  });
101
103
  position = onPositionCalculated ? onPositionCalculated(position) : position;
102
104
  if (typeof position.top !== 'undefined' && absoluteOffset !== null && absoluteOffset !== void 0 && absoluteOffset.top) {
@@ -234,12 +236,17 @@ export default class Popup extends React.Component {
234
236
 
235
237
  // Ignored via go/ees005
236
238
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
237
- this.scrollParentElement = findOverflowScrollParent(this.props.target);
238
- if (this.scrollParentElement && this.resizeObserver) {
239
- this.resizeObserver.observe(this.scrollParentElement);
239
+ const target = this.props.target;
240
+
241
+ // Resolve the effective scroll parent: prefer the explicitly provided scrollableElement
242
+ // prop over the auto-detected ancestor. Allows product consumers to provide the correct
243
+ // scroll container (e.g. a modal body) when auto-detection can't find it.
244
+ const scrollParentElement = expValEquals('platform_editor_fix_scrolling_popup_position', 'isEnabled', true) ? this.props.scrollableElement || findOverflowScrollParent(target) : findOverflowScrollParent(target);
245
+ if (scrollParentElement && this.resizeObserver) {
246
+ this.resizeObserver.observe(scrollParentElement);
240
247
  }
241
248
  if (stick) {
242
- this.scrollElement = this.scrollParentElement;
249
+ this.scrollElement = scrollParentElement;
243
250
  } else {
244
251
  this.scrollElement = this.props.scrollableElement;
245
252
  }
@@ -131,9 +131,10 @@ const calculateVerticalStickBottom = ({
131
131
  popup,
132
132
  offset,
133
133
  position,
134
- boundariesElement
134
+ boundariesElement,
135
+ scrollableElement
135
136
  }) => {
136
- const scrollParent = findOverflowScrollParent(target) || boundariesElement;
137
+ const scrollParent = scrollableElement || findOverflowScrollParent(target) || boundariesElement;
137
138
  const newPos = {
138
139
  ...position
139
140
  };
@@ -158,9 +159,10 @@ const calculateVerticalStickTop = ({
158
159
  offset,
159
160
  position,
160
161
  placement,
161
- boundariesElement
162
+ boundariesElement,
163
+ scrollableElement
162
164
  }) => {
163
- const scrollParent = findOverflowScrollParent(target) || boundariesElement;
165
+ const scrollParent = scrollableElement || findOverflowScrollParent(target) || boundariesElement;
164
166
  const newPos = {
165
167
  ...position
166
168
  };
@@ -226,7 +228,8 @@ export function calculatePosition({
226
228
  allowOutOfBounds = false,
227
229
  rect,
228
230
  boundariesElement,
229
- minPopupMargin
231
+ minPopupMargin,
232
+ scrollableElement
230
233
  }) {
231
234
  let position = {};
232
235
  if (!target || !popup || !popup.offsetParent || !isHTMLElementNode(popup.offsetParent)) {
@@ -289,7 +292,8 @@ export function calculatePosition({
289
292
  offset,
290
293
  position,
291
294
  placement: verticalPlacement,
292
- boundariesElement
295
+ boundariesElement,
296
+ scrollableElement
293
297
  });
294
298
  }
295
299
  if (verticalPlacement !== 'top' && verticalPlacement !== 'start' && stick) {
@@ -300,7 +304,8 @@ export function calculatePosition({
300
304
  popup,
301
305
  offset,
302
306
  position,
303
- boundariesElement
307
+ boundariesElement,
308
+ scrollableElement
304
309
  });
305
310
  }
306
311
  const horizontalPosition = calculateHorizontalPlacement({
@@ -10,7 +10,7 @@ import { isFedRamp } from './environment';
10
10
  import { normaliseSentryBreadcrumbs, SERIALIZABLE_ATTRIBUTES } from './normalise-sentry-breadcrumbs';
11
11
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
12
12
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
13
- var packageVersion = "114.6.1";
13
+ var packageVersion = "114.7.0";
14
14
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
15
15
  // Remove URL as it has UGC
16
16
  // Ignored via go/ees007
@@ -0,0 +1,6 @@
1
+ /** Fixed document thresholds for limited (performance) mode (previously Statsig-driven). */
2
+ export var LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD = 5000;
3
+ export var LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD = 30000;
4
+
5
+ /** When true, any legacy-content macro triggers limited mode regardless of size/count. */
6
+ export var LIMITED_MODE_INCLUDE_LEGACY_CONTENT_IN_THRESHOLD = true;
@@ -1,29 +1,9 @@
1
1
  import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
2
2
  import _createClass from "@babel/runtime/helpers/createClass";
3
3
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
- import { expVal } from '@atlaskit/tmp-editor-statsig/expVal';
5
4
  import { isEmptyDocument } from '../utils';
6
5
  import { DynamicBitArray } from './dynamic-bit-array';
7
-
8
- /**
9
- * Gets a numeric experiment param, returning undefined if the value is not a valid number.
10
- * This guards against test overrides returning booleans or strings for numeric params.
11
- */
12
- var getNumericExperimentParam = function getNumericExperimentParam(paramName, fallbackValue) {
13
- var rawValue = expVal('cc_editor_limited_mode_expanded', paramName, fallbackValue);
14
- if (typeof rawValue === 'number') {
15
- return rawValue;
16
- }
17
-
18
- // Handle string values from test overrides
19
- if (typeof rawValue === 'string') {
20
- var parsed = parseInt(rawValue, 10);
21
- if (!isNaN(parsed)) {
22
- return parsed;
23
- }
24
- }
25
- return undefined;
26
- };
6
+ import { LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD, LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD } from './limited-mode-document-thresholds';
27
7
  export var NodeAnchorProvider = /*#__PURE__*/function () {
28
8
  function NodeAnchorProvider() {
29
9
  var limitedMode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
@@ -105,87 +85,55 @@ export var NodeAnchorProvider = /*#__PURE__*/function () {
105
85
  }]);
106
86
  }();
107
87
  var nodeIdProviderMap = new WeakMap();
108
- var LIMITED_MODE_NODE_SIZE_THRESHOLD = 40000;
109
-
110
- /**
111
- * Calculates custom document size including LCM ADF lengths (for non-expanded path).
112
- * This function can be removed when cc_editor_limited_mode_expanded is cleaned up.
113
- */
114
- var getCustomDocSize = function getCustomDocSize(doc) {
115
- var lcmAdfLength = 0;
116
- doc.descendants(function (node) {
117
- var _node$attrs;
118
- if (((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.extensionKey) === 'legacy-content') {
119
- var _node$attrs$parameter, _node$attrs2;
120
- lcmAdfLength += (_node$attrs$parameter = (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 || (_node$attrs2 = _node$attrs2.parameters) === null || _node$attrs2 === void 0 || (_node$attrs2 = _node$attrs2.adf) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.length) !== null && _node$attrs$parameter !== void 0 ? _node$attrs$parameter : 0;
121
- }
122
- });
123
- return doc.nodeSize + lcmAdfLength;
124
- };
125
88
 
126
89
  /**
127
90
  * Determines whether limited mode should be enabled.
128
91
  * This logic mirrors the limited mode plugin implementation, but lives here to avoid a circular dependency.
129
92
  * If it changes, update the matching logic in `editor-plugin-limited-mode/src/pm-plugins/main.ts`.
130
93
  *
131
- * Under the expanded gate, limited mode is activated when ANY of the following conditions are met:
132
- * 1. Document size exceeds `docSizeThreshold` (if defined) - checked first as O(1)
133
- * 2. Node count exceeds `nodeCountThreshold` (if defined)
134
- * 3. Document contains a legacy-content macro (LCM) (if `includeLcmInThreshold` is true)
94
+ * Limited mode is activated when ANY of the following conditions are met:
95
+ * 1. Document size exceeds `LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD` checked first as O(1)
96
+ * 2. Node count exceeds `LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD`
97
+ * 3. Document contains a legacy-content macro (LCM)
135
98
  *
136
99
  * Performance optimisations:
137
100
  * - Doc size is checked first (O(1)) - if it exceeds threshold, we skip traversal entirely.
138
- * - If `includeLcmInThreshold` is enabled and we find an LCM, we exit traversal early.
139
- * - If neither node count nor LCM conditions are configured, we skip traversal entirely.
101
+ * - If we find an LCM during traversal, we exit early since limited mode will be enabled.
140
102
  */
141
103
  var isLimitedModeEnabled = function isLimitedModeEnabled(editorView) {
142
104
  var doc = editorView.state.doc;
143
- if (expVal('cc_editor_limited_mode_expanded', 'isEnabled', false)) {
144
- var nodeCountThreshold = getNumericExperimentParam('nodeCountThreshold', 5000);
145
- var docSizeThreshold = getNumericExperimentParam('docSizeThreshold', 30000);
146
- var includeLcmInThreshold = Boolean(expVal('cc_editor_limited_mode_expanded', 'includeLcmInThreshold', false));
105
+ var nodeCountThreshold = LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD;
106
+ var docSizeThreshold = LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD;
147
107
 
148
- // Early exit: doc size exceeds threshold - O(1), no traversal needed
149
- if (docSizeThreshold !== undefined && doc.nodeSize > docSizeThreshold) {
150
- return true;
151
- }
108
+ // Early exit: doc size exceeds threshold - O(1), no traversal needed
109
+ if (doc.nodeSize > docSizeThreshold) {
110
+ return true;
111
+ }
112
+
113
+ // Single traversal for node count and LCM detection
114
+ var nodeCount = 0;
115
+ var hasLcm = false;
116
+ doc.descendants(function (node) {
117
+ var _node$attrs;
118
+ nodeCount += 1;
119
+ if (((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.extensionKey) === 'legacy-content') {
120
+ hasLcm = true;
152
121
 
153
- // Early exit: no traversal needed if neither condition is configured
154
- var needNodeCount = nodeCountThreshold !== undefined;
155
- if (!needNodeCount && !includeLcmInThreshold) {
122
+ // Early exit: LCM found limited mode will be enabled
156
123
  return false;
157
124
  }
125
+ });
158
126
 
159
- // Single traversal for node count and/or LCM detection
160
- var nodeCount = 0;
161
- var hasLcm = false;
162
- doc.descendants(function (node) {
163
- var _node$attrs3;
164
- nodeCount += 1;
165
- if (((_node$attrs3 = node.attrs) === null || _node$attrs3 === void 0 ? void 0 : _node$attrs3.extensionKey) === 'legacy-content') {
166
- hasLcm = true;
167
-
168
- // Early exit: LCM found and condition is enabled - no need to continue counting
169
- if (includeLcmInThreshold) {
170
- return false;
171
- }
172
- }
173
- });
174
-
175
- // LCM condition takes precedence (if we early exited traversal, this is why)
176
- if (includeLcmInThreshold && hasLcm) {
177
- return true;
178
- }
127
+ // LCM condition takes precedence (if we early exited traversal, this is why)
128
+ if (hasLcm) {
129
+ return true;
130
+ }
179
131
 
180
- // Check node count threshold
181
- if (needNodeCount && nodeCount > nodeCountThreshold) {
182
- return true;
183
- }
184
- return false;
185
- } else {
186
- var customDocSize = getCustomDocSize(doc);
187
- return customDocSize > LIMITED_MODE_NODE_SIZE_THRESHOLD;
132
+ // Check node count threshold
133
+ if (nodeCount > nodeCountThreshold) {
134
+ return true;
188
135
  }
136
+ return false;
189
137
  };
190
138
 
191
139
  // Get the NodeIdProvider for a specific EditorView instance.
@@ -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 = "114.6.1";
24
+ var packageVersion = "114.7.0";
25
25
  var halfFocusRing = 1;
26
26
  var dropOffset = '0, 8';
27
27
  var fadeIn = keyframes({
@@ -14,6 +14,7 @@ import rafSchedule from 'raf-schd';
14
14
  import { createPortal, flushSync } from 'react-dom';
15
15
  import { akEditorFloatingPanelZIndex } from '@atlaskit/editor-shared-styles';
16
16
  import { fg } from '@atlaskit/platform-feature-flags';
17
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
17
18
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
18
19
  import { calculatePlacement, calculatePosition as _calculatePosition, findOverflowScrollParent, validatePosition } from './utils';
19
20
  // Ignored via go/ees005
@@ -112,7 +113,8 @@ var Popup = /*#__PURE__*/function (_React$Component) {
112
113
  allowOutOfBounds: allowOutOfBounds,
113
114
  rect: rect,
114
115
  boundariesElement: boundariesElement || document.body,
115
- minPopupMargin: minPopupMargin
116
+ minPopupMargin: minPopupMargin,
117
+ scrollableElement: stick && expValEquals('platform_editor_fix_scrolling_popup_position', 'isEnabled', true) ? this.scrollElement : undefined
116
118
  });
117
119
  position = onPositionCalculated ? onPositionCalculated(position) : position;
118
120
  if (typeof position.top !== 'undefined' && absoluteOffset !== null && absoluteOffset !== void 0 && absoluteOffset.top) {
@@ -261,12 +263,17 @@ var Popup = /*#__PURE__*/function (_React$Component) {
261
263
 
262
264
  // Ignored via go/ees005
263
265
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
264
- this.scrollParentElement = findOverflowScrollParent(this.props.target);
265
- if (this.scrollParentElement && this.resizeObserver) {
266
- this.resizeObserver.observe(this.scrollParentElement);
266
+ var target = this.props.target;
267
+
268
+ // Resolve the effective scroll parent: prefer the explicitly provided scrollableElement
269
+ // prop over the auto-detected ancestor. Allows product consumers to provide the correct
270
+ // scroll container (e.g. a modal body) when auto-detection can't find it.
271
+ var scrollParentElement = expValEquals('platform_editor_fix_scrolling_popup_position', 'isEnabled', true) ? this.props.scrollableElement || findOverflowScrollParent(target) : findOverflowScrollParent(target);
272
+ if (scrollParentElement && this.resizeObserver) {
273
+ this.resizeObserver.observe(scrollParentElement);
267
274
  }
268
275
  if (stick) {
269
- this.scrollElement = this.scrollParentElement;
276
+ this.scrollElement = scrollParentElement;
270
277
  } else {
271
278
  this.scrollElement = this.props.scrollableElement;
272
279
  }
@@ -131,8 +131,9 @@ var calculateVerticalStickBottom = function calculateVerticalStickBottom(_ref2)
131
131
  popup = _ref2.popup,
132
132
  offset = _ref2.offset,
133
133
  position = _ref2.position,
134
- boundariesElement = _ref2.boundariesElement;
135
- var scrollParent = findOverflowScrollParent(target) || boundariesElement;
134
+ boundariesElement = _ref2.boundariesElement,
135
+ scrollableElement = _ref2.scrollableElement;
136
+ var scrollParent = scrollableElement || findOverflowScrollParent(target) || boundariesElement;
136
137
  var newPos = _objectSpread({}, position);
137
138
  if (scrollParent) {
138
139
  var topOffsetTop = targetTop - scrollParent.getBoundingClientRect().top;
@@ -155,8 +156,9 @@ var calculateVerticalStickTop = function calculateVerticalStickTop(_ref3) {
155
156
  offset = _ref3.offset,
156
157
  position = _ref3.position,
157
158
  placement = _ref3.placement,
158
- boundariesElement = _ref3.boundariesElement;
159
- var scrollParent = findOverflowScrollParent(target) || boundariesElement;
159
+ boundariesElement = _ref3.boundariesElement,
160
+ scrollableElement = _ref3.scrollableElement;
161
+ var scrollParent = scrollableElement || findOverflowScrollParent(target) || boundariesElement;
160
162
  var newPos = _objectSpread({}, position);
161
163
  if (scrollParent) {
162
164
  var _scrollParent$getBoun = scrollParent.getBoundingClientRect(),
@@ -219,7 +221,8 @@ export function calculatePosition(_ref5) {
219
221
  allowOutOfBounds = _ref5$allowOutOfBound === void 0 ? false : _ref5$allowOutOfBound,
220
222
  rect = _ref5.rect,
221
223
  boundariesElement = _ref5.boundariesElement,
222
- minPopupMargin = _ref5.minPopupMargin;
224
+ minPopupMargin = _ref5.minPopupMargin,
225
+ scrollableElement = _ref5.scrollableElement;
223
226
  var position = {};
224
227
  if (!target || !popup || !popup.offsetParent || !isHTMLElementNode(popup.offsetParent)) {
225
228
  return position;
@@ -278,7 +281,8 @@ export function calculatePosition(_ref5) {
278
281
  offset: offset,
279
282
  position: position,
280
283
  placement: verticalPlacement,
281
- boundariesElement: boundariesElement
284
+ boundariesElement: boundariesElement,
285
+ scrollableElement: scrollableElement
282
286
  });
283
287
  }
284
288
  if (verticalPlacement !== 'top' && verticalPlacement !== 'start' && stick) {
@@ -289,7 +293,8 @@ export function calculatePosition(_ref5) {
289
293
  popup: popup,
290
294
  offset: offset,
291
295
  position: position,
292
- boundariesElement: boundariesElement
296
+ boundariesElement: boundariesElement,
297
+ scrollableElement: scrollableElement
293
298
  });
294
299
  }
295
300
  var horizontalPosition = calculateHorizontalPlacement({
@@ -43,8 +43,13 @@ export type EmbedCardTransformAttrs = {
43
43
  * an alternative ProseMirror Node (e.g. a native embed extension node).
44
44
  *
45
45
  * Returns undefined if the URL is not supported or transformation is not possible.
46
+ *
47
+ * May return a Promise when the transformation requires async work, such as
48
+ * resolving a Confluence short-link URL via the Object Resolver Service before
49
+ * matching against a registered experience manifest. Callers that support async
50
+ * transformation should check for a Promise return value and await it.
46
51
  */
47
- export type EmbedCardNodeTransformer = (schema: Schema, attrs: EmbedCardTransformAttrs) => Node | undefined;
52
+ export type EmbedCardNodeTransformer = (schema: Schema, attrs: EmbedCardTransformAttrs) => Node | undefined | Promise<Node | undefined>;
48
53
  /**
49
54
  * Options for creating a transform command that replaces a selected card node
50
55
  * with an alternative node representation.
@@ -69,6 +74,15 @@ export interface EmbedCardTransformers {
69
74
  embedCardNodeTransformer: EmbedCardNodeTransformer;
70
75
  }
71
76
  export type CardPluginActions = {
77
+ /**
78
+ * Resolves a URL via the Object Resolver Service and returns the canonical
79
+ * expanded URL. Useful for expanding Confluence short-link URLs
80
+ * (`/wiki/x/<token>`) before matching against experience manifests.
81
+ *
82
+ * Returns `undefined` if the URL cannot be resolved or ORS returns no
83
+ * usable URL in its response data.
84
+ */
85
+ resolveShortLinkUrl: (url: string) => Promise<string | undefined>;
72
86
  getEndingToolbarItems: GetEndingToolbarItems;
73
87
  getStartingToolbarItems: GetStartingToolbarItems;
74
88
  hideLinkToolbar: HideLinkToolbarAction;
@@ -0,0 +1,5 @@
1
+ /** Fixed document thresholds for limited (performance) mode (previously Statsig-driven). */
2
+ export declare const LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD = 5000;
3
+ export declare const LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD = 30000;
4
+ /** When true, any legacy-content macro triggers limited mode regardless of size/count. */
5
+ export declare const LIMITED_MODE_INCLUDE_LEGACY_CONTENT_IN_THRESHOLD = true;
@@ -38,7 +38,6 @@ export interface State {
38
38
  }
39
39
  export default class Popup extends React.Component<Props, State> {
40
40
  scrollElement: undefined | false | HTMLElement;
41
- scrollParentElement: undefined | false | HTMLElement;
42
41
  rafIds: Set<number>;
43
42
  static defaultProps: {
44
43
  allowOutOfBound: boolean;
@@ -14,6 +14,7 @@ export interface CalculatePositionParams {
14
14
  rect?: DOMRect;
15
15
  stick?: boolean;
16
16
  target?: HTMLElement;
17
+ scrollableElement?: HTMLElement | false;
17
18
  }
18
19
  export declare function isBody(elem: HTMLElement | Element): boolean;
19
20
  export declare function isTextNode(elem: HTMLElement | Element): boolean;
@@ -30,7 +31,7 @@ export declare function calculatePlacement(target: HTMLElement, boundariesElemen
30
31
  * Calculates relative coordinates for placing popup along with the target.
31
32
  * Uses placement from calculatePlacement.
32
33
  */
33
- export declare function calculatePosition({ placement, target, popup, offset, stick, allowOutOfBounds, rect, boundariesElement, minPopupMargin, }: CalculatePositionParams): Position;
34
+ export declare function calculatePosition({ placement, target, popup, offset, stick, allowOutOfBounds, rect, boundariesElement, minPopupMargin, scrollableElement, }: CalculatePositionParams): Position;
34
35
  export declare function validatePosition(popup: HTMLElement): boolean;
35
36
  /**
36
37
  * Traverse DOM Tree upwards looking for popup parents with "overflow: scroll".
@@ -43,8 +43,13 @@ export type EmbedCardTransformAttrs = {
43
43
  * an alternative ProseMirror Node (e.g. a native embed extension node).
44
44
  *
45
45
  * Returns undefined if the URL is not supported or transformation is not possible.
46
+ *
47
+ * May return a Promise when the transformation requires async work, such as
48
+ * resolving a Confluence short-link URL via the Object Resolver Service before
49
+ * matching against a registered experience manifest. Callers that support async
50
+ * transformation should check for a Promise return value and await it.
46
51
  */
47
- export type EmbedCardNodeTransformer = (schema: Schema, attrs: EmbedCardTransformAttrs) => Node | undefined;
52
+ export type EmbedCardNodeTransformer = (schema: Schema, attrs: EmbedCardTransformAttrs) => Node | undefined | Promise<Node | undefined>;
48
53
  /**
49
54
  * Options for creating a transform command that replaces a selected card node
50
55
  * with an alternative node representation.
@@ -69,6 +74,15 @@ export interface EmbedCardTransformers {
69
74
  embedCardNodeTransformer: EmbedCardNodeTransformer;
70
75
  }
71
76
  export type CardPluginActions = {
77
+ /**
78
+ * Resolves a URL via the Object Resolver Service and returns the canonical
79
+ * expanded URL. Useful for expanding Confluence short-link URLs
80
+ * (`/wiki/x/<token>`) before matching against experience manifests.
81
+ *
82
+ * Returns `undefined` if the URL cannot be resolved or ORS returns no
83
+ * usable URL in its response data.
84
+ */
85
+ resolveShortLinkUrl: (url: string) => Promise<string | undefined>;
72
86
  getEndingToolbarItems: GetEndingToolbarItems;
73
87
  getStartingToolbarItems: GetStartingToolbarItems;
74
88
  hideLinkToolbar: HideLinkToolbarAction;
@@ -0,0 +1,5 @@
1
+ /** Fixed document thresholds for limited (performance) mode (previously Statsig-driven). */
2
+ export declare const LIMITED_MODE_DEFAULT_NODE_COUNT_THRESHOLD = 5000;
3
+ export declare const LIMITED_MODE_DEFAULT_DOC_SIZE_THRESHOLD = 30000;
4
+ /** When true, any legacy-content macro triggers limited mode regardless of size/count. */
5
+ export declare const LIMITED_MODE_INCLUDE_LEGACY_CONTENT_IN_THRESHOLD = true;
@@ -41,7 +41,6 @@ export interface State {
41
41
  }
42
42
  export default class Popup extends React.Component<Props, State> {
43
43
  scrollElement: undefined | false | HTMLElement;
44
- scrollParentElement: undefined | false | HTMLElement;
45
44
  rafIds: Set<number>;
46
45
  static defaultProps: {
47
46
  allowOutOfBound: boolean;
@@ -17,6 +17,7 @@ export interface CalculatePositionParams {
17
17
  rect?: DOMRect;
18
18
  stick?: boolean;
19
19
  target?: HTMLElement;
20
+ scrollableElement?: HTMLElement | false;
20
21
  }
21
22
  export declare function isBody(elem: HTMLElement | Element): boolean;
22
23
  export declare function isTextNode(elem: HTMLElement | Element): boolean;
@@ -36,7 +37,7 @@ export declare function calculatePlacement(target: HTMLElement, boundariesElemen
36
37
  * Calculates relative coordinates for placing popup along with the target.
37
38
  * Uses placement from calculatePlacement.
38
39
  */
39
- export declare function calculatePosition({ placement, target, popup, offset, stick, allowOutOfBounds, rect, boundariesElement, minPopupMargin, }: CalculatePositionParams): Position;
40
+ export declare function calculatePosition({ placement, target, popup, offset, stick, allowOutOfBounds, rect, boundariesElement, minPopupMargin, scrollableElement, }: CalculatePositionParams): Position;
40
41
  export declare function validatePosition(popup: HTMLElement): boolean;
41
42
  /**
42
43
  * Traverse DOM Tree upwards looking for popup parents with "overflow: scroll".
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@atlaskit/editor-common/limited-mode-document-thresholds",
3
+ "main": "../dist/cjs/node-anchor/limited-mode-document-thresholds.js",
4
+ "module": "../dist/esm/node-anchor/limited-mode-document-thresholds.js",
5
+ "module:es2019": "../dist/es2019/node-anchor/limited-mode-document-thresholds.js",
6
+ "sideEffects": [
7
+ "**/*.compiled.css"
8
+ ],
9
+ "types": "../dist/types/node-anchor/limited-mode-document-thresholds.d.ts",
10
+ "typesVersions": {
11
+ ">=4.5 <5.9": {
12
+ "*": [
13
+ "../dist/types-ts4.5/node-anchor/limited-mode-document-thresholds.d.ts"
14
+ ]
15
+ }
16
+ }
17
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-common",
3
- "version": "114.6.2",
3
+ "version": "114.8.0",
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/"
@@ -57,8 +57,8 @@
57
57
  "@atlaskit/editor-tables": "^2.9.0",
58
58
  "@atlaskit/editor-toolbar": "^1.0.0",
59
59
  "@atlaskit/editor-toolbar-model": "^0.4.0",
60
- "@atlaskit/emoji": "^70.1.0",
61
- "@atlaskit/icon": "^34.2.0",
60
+ "@atlaskit/emoji": "^70.2.0",
61
+ "@atlaskit/icon": "^34.3.0",
62
62
  "@atlaskit/icon-object": "^7.5.0",
63
63
  "@atlaskit/link": "^3.4.0",
64
64
  "@atlaskit/link-datasource": "^5.2.0",
@@ -86,9 +86,9 @@
86
86
  "@atlaskit/spinner": "^19.1.0",
87
87
  "@atlaskit/task-decision": "^20.0.0",
88
88
  "@atlaskit/textfield": "^8.3.0",
89
- "@atlaskit/tmp-editor-statsig": "^65.0.0",
89
+ "@atlaskit/tmp-editor-statsig": "^66.1.0",
90
90
  "@atlaskit/tokens": "^13.0.0",
91
- "@atlaskit/tooltip": "^21.1.0",
91
+ "@atlaskit/tooltip": "^21.2.0",
92
92
  "@atlaskit/width-detector": "^5.1.0",
93
93
  "@babel/runtime": "^7.0.0",
94
94
  "@compiled/react": "^0.20.0",