@atlaskit/react-ufo 3.14.0 → 3.14.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/ignore-holds/index.js +62 -0
  3. package/dist/cjs/interaction-ignore/ufo-interaction-ignore.js +7 -49
  4. package/dist/cjs/segment/index.js +7 -0
  5. package/dist/cjs/segment/segment.js +3 -1
  6. package/dist/cjs/segment/third-party-segment.js +25 -0
  7. package/dist/cjs/vc/vc-observer-new/metric-calculator/fy25_03/index.js +12 -6
  8. package/dist/cjs/vc/vc-observer-new/viewport-observer/index.js +30 -6
  9. package/dist/cjs/vc/vc-observer-new/viewport-observer/utils/get-component-name-and-child-props.js +85 -0
  10. package/dist/es2019/ignore-holds/index.js +51 -0
  11. package/dist/es2019/interaction-ignore/ufo-interaction-ignore.js +7 -45
  12. package/dist/es2019/segment/index.js +2 -1
  13. package/dist/es2019/segment/segment.js +2 -1
  14. package/dist/es2019/segment/third-party-segment.js +18 -0
  15. package/dist/es2019/vc/vc-observer-new/metric-calculator/fy25_03/index.js +12 -6
  16. package/dist/es2019/vc/vc-observer-new/viewport-observer/index.js +32 -6
  17. package/dist/es2019/vc/vc-observer-new/viewport-observer/utils/get-component-name-and-child-props.js +71 -0
  18. package/dist/esm/ignore-holds/index.js +53 -0
  19. package/dist/esm/interaction-ignore/ufo-interaction-ignore.js +7 -47
  20. package/dist/esm/segment/index.js +2 -1
  21. package/dist/esm/segment/segment.js +3 -1
  22. package/dist/esm/segment/third-party-segment.js +18 -0
  23. package/dist/esm/vc/vc-observer-new/metric-calculator/fy25_03/index.js +12 -6
  24. package/dist/esm/vc/vc-observer-new/viewport-observer/index.js +30 -6
  25. package/dist/esm/vc/vc-observer-new/viewport-observer/utils/get-component-name-and-child-props.js +76 -0
  26. package/dist/types/ignore-holds/index.d.ts +31 -0
  27. package/dist/types/interaction-ignore/ufo-interaction-ignore.d.ts +6 -21
  28. package/dist/types/segment/index.d.ts +1 -0
  29. package/dist/types/segment/segment.d.ts +4 -2
  30. package/dist/types/segment/third-party-segment.d.ts +6 -0
  31. package/dist/types/vc/vc-observer-new/types.d.ts +1 -1
  32. package/dist/types/vc/vc-observer-new/viewport-observer/utils/get-component-name-and-child-props.d.ts +15 -0
  33. package/dist/types-ts4.5/ignore-holds/index.d.ts +31 -0
  34. package/dist/types-ts4.5/interaction-ignore/ufo-interaction-ignore.d.ts +6 -21
  35. package/dist/types-ts4.5/segment/index.d.ts +1 -0
  36. package/dist/types-ts4.5/segment/segment.d.ts +4 -2
  37. package/dist/types-ts4.5/segment/third-party-segment.d.ts +6 -0
  38. package/dist/types-ts4.5/vc/vc-observer-new/types.d.ts +1 -1
  39. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/utils/get-component-name-and-child-props.d.ts +15 -0
  40. package/package.json +7 -4
@@ -5,6 +5,7 @@ import { RLLPlaceholderHandlers } from '../../vc-observer/observers/rll-placehol
5
5
  import { createIntersectionObserver } from './intersection-observer';
6
6
  import createMutationObserver from './mutation-observer';
7
7
  import createPerformanceObserver from './performance-observer';
8
+ import { checkThirdPartySegmentWithIgnoreReason, createMutationTypeWithIgnoredReason } from './utils/get-component-name-and-child-props';
8
9
  function isElementVisible(element) {
9
10
  if (!(element instanceof HTMLElement)) {
10
11
  return true;
@@ -86,7 +87,7 @@ export default class ViewportObserver {
86
87
  return this.mapVisibleNodeRects.get(n);
87
88
  });
88
89
  addedNodes.forEach(addedNodeRef => {
89
- var _this$intersectionObs3;
90
+ var _this$intersectionObs4;
90
91
  const addedNode = addedNodeRef.deref();
91
92
  if (!addedNode) {
92
93
  return;
@@ -108,7 +109,17 @@ export default class ViewportObserver {
108
109
  (_this$intersectionObs2 = this.intersectionObserver) === null || _this$intersectionObs2 === void 0 ? void 0 : _this$intersectionObs2.watchAndTag(addedNode, 'mutation:media');
109
110
  return;
110
111
  }
111
- (_this$intersectionObs3 = this.intersectionObserver) === null || _this$intersectionObs3 === void 0 ? void 0 : _this$intersectionObs3.watchAndTag(addedNode, createElementMutationsWatcher(removedNodeRects));
112
+ const {
113
+ isWithinThirdPartySegment,
114
+ ignoredReason
115
+ } = checkThirdPartySegmentWithIgnoreReason(addedNode);
116
+ if (isWithinThirdPartySegment) {
117
+ var _this$intersectionObs3;
118
+ const assignedReason = createMutationTypeWithIgnoredReason(ignoredReason || 'third-party-element');
119
+ (_this$intersectionObs3 = this.intersectionObserver) === null || _this$intersectionObs3 === void 0 ? void 0 : _this$intersectionObs3.watchAndTag(addedNode, assignedReason);
120
+ return;
121
+ }
122
+ (_this$intersectionObs4 = this.intersectionObserver) === null || _this$intersectionObs4 === void 0 ? void 0 : _this$intersectionObs4.watchAndTag(addedNode, createElementMutationsWatcher(removedNodeRects));
112
123
  });
113
124
  });
114
125
  _defineProperty(this, "handleAttributeMutation", ({
@@ -117,8 +128,8 @@ export default class ViewportObserver {
117
128
  oldValue,
118
129
  newValue
119
130
  }) => {
120
- var _this$intersectionObs4;
121
- (_this$intersectionObs4 = this.intersectionObserver) === null || _this$intersectionObs4 === void 0 ? void 0 : _this$intersectionObs4.watchAndTag(target, ({
131
+ var _this$intersectionObs5;
132
+ (_this$intersectionObs5 = this.intersectionObserver) === null || _this$intersectionObs5 === void 0 ? void 0 : _this$intersectionObs5.watchAndTag(target, ({
122
133
  target,
123
134
  rect
124
135
  }) => {
@@ -157,6 +168,21 @@ export default class ViewportObserver {
157
168
  }
158
169
  };
159
170
  }
171
+ const {
172
+ isWithinThirdPartySegment,
173
+ ignoredReason
174
+ } = checkThirdPartySegmentWithIgnoreReason(target);
175
+ if (isWithinThirdPartySegment) {
176
+ const assignedReason = createMutationTypeWithIgnoredReason(ignoredReason || 'third-party-element');
177
+ return {
178
+ type: assignedReason,
179
+ mutationData: {
180
+ attributeName,
181
+ oldValue,
182
+ newValue
183
+ }
184
+ };
185
+ }
160
186
  const lastElementRect = this.mapVisibleNodeRects.get(target);
161
187
  if (lastElementRect && sameRectSize(rect, lastElementRect)) {
162
188
  return {
@@ -239,12 +265,12 @@ export default class ViewportObserver {
239
265
  this.isStarted = true;
240
266
  }
241
267
  stop() {
242
- var _this$mutationObserve2, _this$intersectionObs5, _this$performanceObse2;
268
+ var _this$mutationObserve2, _this$intersectionObs6, _this$performanceObse2;
243
269
  if (!this.isStarted) {
244
270
  return;
245
271
  }
246
272
  (_this$mutationObserve2 = this.mutationObserver) === null || _this$mutationObserve2 === void 0 ? void 0 : _this$mutationObserve2.disconnect();
247
- (_this$intersectionObs5 = this.intersectionObserver) === null || _this$intersectionObs5 === void 0 ? void 0 : _this$intersectionObs5.disconnect();
273
+ (_this$intersectionObs6 = this.intersectionObserver) === null || _this$intersectionObs6 === void 0 ? void 0 : _this$intersectionObs6.disconnect();
248
274
  (_this$performanceObse2 = this.performanceObserver) === null || _this$performanceObse2 === void 0 ? void 0 : _this$performanceObse2.disconnect();
249
275
  this.isStarted = false;
250
276
  }
@@ -0,0 +1,71 @@
1
+ // Using the React Fiber tree to traverse up the DOM and check if a node is within a specific component
2
+ // and extract child component props if needed.
3
+ export function checkWithinComponentAndExtractChildProps(node, targetComponentName, childComponentConfig) {
4
+ // Get the React fiber from the DOM node
5
+ const key = Object.keys(node).find(key => key.startsWith('__reactFiber$') || key.startsWith('__reactInternalInstance$'));
6
+ if (!key) {
7
+ return {
8
+ isWithin: false
9
+ };
10
+ }
11
+ const fiber = node[key];
12
+ if (!fiber) {
13
+ return {
14
+ isWithin: false
15
+ };
16
+ }
17
+
18
+ // Traverse up the fiber tree
19
+ let currentFiber = fiber;
20
+ let childProp;
21
+ while (currentFiber) {
22
+ let componentName;
23
+ if (currentFiber.type) {
24
+ if (typeof currentFiber.type === 'function') {
25
+ componentName = currentFiber.type.displayName || currentFiber.type.name;
26
+ } else if (typeof currentFiber.type === 'object' && (currentFiber.type.displayName || currentFiber.type.name)) {
27
+ componentName = currentFiber.type.displayName || currentFiber.type.name;
28
+ }
29
+ }
30
+
31
+ // Check if this is a child component we're looking for
32
+ if (childComponentConfig && componentName === childComponentConfig.componentName) {
33
+ const props = currentFiber.memoizedProps || currentFiber.pendingProps;
34
+ if (props && props[childComponentConfig.propName] !== undefined) {
35
+ // Overwrite with the nearest child prop (closest to the target component)
36
+ childProp = childComponentConfig.extractValue ? childComponentConfig.extractValue(props) : props[childComponentConfig.propName];
37
+ }
38
+ }
39
+
40
+ // Check if we found the target component
41
+ if (componentName === targetComponentName) {
42
+ return {
43
+ isWithin: true,
44
+ ...(childComponentConfig && {
45
+ childProp: childProp
46
+ })
47
+ };
48
+ }
49
+ currentFiber = currentFiber.return;
50
+ }
51
+ return {
52
+ isWithin: false
53
+ };
54
+ }
55
+
56
+ // Check if the node is within a UFOThirdPartySegment and extract any UFOIgnoreHolds reason
57
+ export function checkThirdPartySegmentWithIgnoreReason(node) {
58
+ const result = checkWithinComponentAndExtractChildProps(node, 'UFOThirdPartySegment', {
59
+ componentName: 'UFOIgnoreHolds',
60
+ propName: 'reason'
61
+ });
62
+ return {
63
+ isWithinThirdPartySegment: result.isWithin,
64
+ ignoredReason: result.childProp
65
+ };
66
+ }
67
+
68
+ // Helper function to create mutation type from UFOIgnoreHoldsReason
69
+ export function createMutationTypeWithIgnoredReason(reason) {
70
+ return `mutation:${reason}`;
71
+ }
@@ -0,0 +1,53 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ 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; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ import React, { useContext, useMemo } from 'react';
5
+ import InteractionContext from '@atlaskit/interaction-context';
6
+ /**
7
+ * Prevent a subtree from holding up an interaction
8
+ * Use this when you have a component which loads in late, but
9
+ * isn't considered to be a breach of SLO
10
+ *
11
+ * ```js
12
+ * <App>
13
+ * <Main />
14
+ * <Sidebar>
15
+ * <UFOIgnoreHolds>
16
+ * <InsightsButton />
17
+ * </UFOIgnoreHolds>
18
+ * </Sidebar>
19
+ * </App>
20
+ * ```
21
+ *
22
+ * Has an `ignore` prop, to allow you to use it conditionally
23
+ * Has a `reason` prop, to specify why the hold is being ignored
24
+ */
25
+ export default function UFOIgnoreHolds(_ref) {
26
+ var children = _ref.children,
27
+ _ref$ignore = _ref.ignore,
28
+ ignore = _ref$ignore === void 0 ? true : _ref$ignore,
29
+ reason = _ref.reason;
30
+ var parentContext = useContext(InteractionContext);
31
+ var ignoredInteractionContext = useMemo(function () {
32
+ if (!parentContext) {
33
+ return null;
34
+ }
35
+ return _objectSpread(_objectSpread({}, parentContext), {}, {
36
+ hold: function hold() {
37
+ if (!ignore) {
38
+ return parentContext.hold.apply(parentContext, arguments);
39
+ }
40
+ }
41
+ });
42
+ }, [parentContext, ignore]);
43
+
44
+ // react-18: Use children directly
45
+ var kids = children != null ? children : null;
46
+ if (!ignoredInteractionContext) {
47
+ return /*#__PURE__*/React.createElement(React.Fragment, null, kids);
48
+ }
49
+ return /*#__PURE__*/React.createElement(InteractionContext.Provider, {
50
+ value: ignoredInteractionContext
51
+ }, kids);
52
+ }
53
+ UFOIgnoreHolds.displayName = 'UFOIgnoreHolds';
@@ -1,51 +1,11 @@
1
- import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
- 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; }
3
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
- import React, { useContext, useMemo } from 'react';
5
- import InteractionContext from '@atlaskit/interaction-context';
1
+ import React from 'react';
2
+ import UFOIgnoreHolds from '../ignore-holds';
6
3
 
7
4
  /**
8
- * Prevent a subtree from holding up an interaction
9
- * Use this when you have a component which loads in late, but
10
- * isn't considered to be a breach of SLO
11
- *
12
- * ```js
13
- * <App>
14
- * <Main />
15
- * <Sidebar>
16
- * <UFOInteractionIgnore>
17
- * <InsightsButton />
18
- * </UFOInteractionIgnore>
19
- * </Sidebar>
20
- * </App>
21
- * ```
22
- *
23
- * Has an `ignore` prop, to allow you to use it conditionally
5
+ * Legacy wrapper component that delegates to `UFOIgnoreHolds`.
6
+ * Use `UFOIgnoreHolds` instead for new implementations.
7
+ * This component is maintained for backward compatibility only.
24
8
  */
25
- export default function UFOInteractionIgnore(_ref) {
26
- var children = _ref.children,
27
- _ref$ignore = _ref.ignore,
28
- ignore = _ref$ignore === void 0 ? true : _ref$ignore;
29
- var parentContext = useContext(InteractionContext);
30
- var ignoredInteractionContext = useMemo(function () {
31
- if (!parentContext) {
32
- return null;
33
- }
34
- return _objectSpread(_objectSpread({}, parentContext), {}, {
35
- hold: function hold() {
36
- if (!ignore) {
37
- return parentContext.hold.apply(parentContext, arguments);
38
- }
39
- }
40
- });
41
- }, [parentContext, ignore]);
42
-
43
- // react-18: Use children directly
44
- var kids = children != null ? children : null;
45
- if (!ignoredInteractionContext) {
46
- return /*#__PURE__*/React.createElement(React.Fragment, null, kids);
47
- }
48
- return /*#__PURE__*/React.createElement(InteractionContext.Provider, {
49
- value: ignoredInteractionContext
50
- }, kids);
9
+ export default function UFOInteractionIgnore(props) {
10
+ return /*#__PURE__*/React.createElement(UFOIgnoreHolds, props);
51
11
  }
@@ -1,2 +1,3 @@
1
1
  import UFOSegment from './segment';
2
- export default UFOSegment;
2
+ export default UFOSegment;
3
+ export { UFOThirdPartySegment } from './third-party-segment';
@@ -30,7 +30,9 @@ export default function UFOSegment(_ref) {
30
30
  var segmentName = _ref.name,
31
31
  children = _ref.children,
32
32
  _ref$mode = _ref.mode,
33
- mode = _ref$mode === void 0 ? 'single' : _ref$mode;
33
+ mode = _ref$mode === void 0 ? 'single' : _ref$mode,
34
+ _ref$type = _ref.type,
35
+ type = _ref$type === void 0 ? 'first-party' : _ref$type;
34
36
  var parentContext = useContext(UFOInteractionContext);
35
37
  var segmentIdMap = useMemo(function () {
36
38
  if (!(parentContext !== null && parentContext !== void 0 && parentContext.segmentIdMap)) {
@@ -0,0 +1,18 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
3
+ var _excluded = ["children"];
4
+ import React from 'react';
5
+ import { fg } from '@atlaskit/platform-feature-flags';
6
+ import UFOIgnoreHolds from '../ignore-holds';
7
+ import UFOSegment from './segment';
8
+ export function UFOThirdPartySegment(props) {
9
+ var children = props.children,
10
+ otherProps = _objectWithoutProperties(props, _excluded);
11
+ return /*#__PURE__*/React.createElement(UFOSegment, _extends({
12
+ type: "third-party"
13
+ }, otherProps), /*#__PURE__*/React.createElement(UFOIgnoreHolds, {
14
+ ignore: fg('platform_ufo_exclude_3p_elements_from_ttai'),
15
+ reason: "third-party-element"
16
+ }, children));
17
+ }
18
+ UFOThirdPartySegment.displayName = 'UFOThirdPartySegment';
@@ -10,7 +10,16 @@ import AbstractVCCalculatorBase from '../abstract-base-vc-calculator';
10
10
  import isViewportEntryData from '../utils/is-viewport-entry-data';
11
11
  var ABORTING_WINDOW_EVENT = ['wheel', 'scroll', 'keydown', 'resize'];
12
12
  var REVISION_NO = 'fy25.03';
13
- var CONSIDERED_ENTRY_TYPE = ['mutation:child-element', 'mutation:element', 'mutation:attribute', 'layout-shift', 'window:event'];
13
+ var getConsideredEntryTypes = function getConsideredEntryTypes() {
14
+ var entryTypes = ['mutation:child-element', 'mutation:element', 'mutation:attribute', 'layout-shift', 'window:event'];
15
+
16
+ // If not exclude 3p elements from ttvc,
17
+ // including the tags into the ConsideredEntryTypes so that it won't be ignored for TTVC calculation
18
+ if (!fg('platform_ufo_exclude_3p_elements_from_ttvc')) {
19
+ entryTypes.push('mutation:third-party-element');
20
+ }
21
+ return entryTypes;
22
+ };
14
23
 
15
24
  // TODO: AFO-3523
16
25
  // Those are the attributes we have found when testing the 'fy25.03' manually.
@@ -28,7 +37,7 @@ var VCCalculator_FY25_03 = /*#__PURE__*/function (_AbstractVCCalculator) {
28
37
  return _createClass(VCCalculator_FY25_03, [{
29
38
  key: "isEntryIncluded",
30
39
  value: function isEntryIncluded(entry) {
31
- if (!CONSIDERED_ENTRY_TYPE.includes(entry.data.type)) {
40
+ if (!getConsideredEntryTypes().includes(entry.data.type)) {
32
41
  return false;
33
42
  }
34
43
  if (entry.data.type === 'mutation:attribute') {
@@ -37,10 +46,7 @@ var VCCalculator_FY25_03 = /*#__PURE__*/function (_AbstractVCCalculator) {
37
46
  if (!attributeName || KNOWN_ATTRIBUTES_THAT_DOES_NOT_CAUSE_LAYOUT_SHIFTS.includes(attributeName)) {
38
47
  return false;
39
48
  }
40
- if (attributeName === 'data-aui-version' && fg('platform_ufo_filter_out_aui_attribute_changes')) {
41
- return false;
42
- }
43
- if (attributeName === 'data-testid' || attributeName === 'data-vc' || attributeName === 'data-ssr-placeholder' || attributeName === 'data-ssr-placeholder-replace' || attributeName === 'data-vc-nvs' || attributeName === 'data-media-vc-wrapper' || (attributeName === 'data-renderer-start-pos' || attributeName === 'data-table-local-id' || attributeName === 'spellcheck') && fg('platform_ufo_ignore_extra_attributes') || attributeName === 'data-auto-scrollable' || attributeName === 'id' || attributeName === 'tabindex' || NON_VISUAL_ARIA_ATTRIBUTES.includes(attributeName)) {
49
+ if (attributeName === 'data-aui-version' || attributeName === 'data-testid' || attributeName === 'data-vc' || attributeName === 'data-ssr-placeholder' || attributeName === 'data-ssr-placeholder-replace' || attributeName === 'data-vc-nvs' || attributeName === 'data-media-vc-wrapper' || (attributeName === 'data-renderer-start-pos' || attributeName === 'data-table-local-id' || attributeName === 'spellcheck') && fg('platform_ufo_ignore_extra_attributes') || attributeName === 'data-auto-scrollable' || attributeName === 'id' || attributeName === 'tabindex' || NON_VISUAL_ARIA_ATTRIBUTES.includes(attributeName)) {
44
50
  return false;
45
51
  }
46
52
  return true;
@@ -10,6 +10,7 @@ import { RLLPlaceholderHandlers } from '../../vc-observer/observers/rll-placehol
10
10
  import { createIntersectionObserver } from './intersection-observer';
11
11
  import createMutationObserver from './mutation-observer';
12
12
  import createPerformanceObserver from './performance-observer';
13
+ import { checkThirdPartySegmentWithIgnoreReason, createMutationTypeWithIgnoredReason } from './utils/get-component-name-and-child-props';
13
14
  function isElementVisible(element) {
14
15
  if (!(element instanceof HTMLElement)) {
15
16
  return true;
@@ -93,7 +94,7 @@ var ViewportObserver = /*#__PURE__*/function () {
93
94
  return _this.mapVisibleNodeRects.get(n);
94
95
  });
95
96
  addedNodes.forEach(function (addedNodeRef) {
96
- var _this$intersectionObs3;
97
+ var _this$intersectionObs4;
97
98
  var addedNode = addedNodeRef.deref();
98
99
  if (!addedNode) {
99
100
  return;
@@ -115,16 +116,25 @@ var ViewportObserver = /*#__PURE__*/function () {
115
116
  (_this$intersectionObs2 = _this.intersectionObserver) === null || _this$intersectionObs2 === void 0 || _this$intersectionObs2.watchAndTag(addedNode, 'mutation:media');
116
117
  return;
117
118
  }
118
- (_this$intersectionObs3 = _this.intersectionObserver) === null || _this$intersectionObs3 === void 0 || _this$intersectionObs3.watchAndTag(addedNode, createElementMutationsWatcher(removedNodeRects));
119
+ var _checkThirdPartySegme = checkThirdPartySegmentWithIgnoreReason(addedNode),
120
+ isWithinThirdPartySegment = _checkThirdPartySegme.isWithinThirdPartySegment,
121
+ ignoredReason = _checkThirdPartySegme.ignoredReason;
122
+ if (isWithinThirdPartySegment) {
123
+ var _this$intersectionObs3;
124
+ var assignedReason = createMutationTypeWithIgnoredReason(ignoredReason || 'third-party-element');
125
+ (_this$intersectionObs3 = _this.intersectionObserver) === null || _this$intersectionObs3 === void 0 || _this$intersectionObs3.watchAndTag(addedNode, assignedReason);
126
+ return;
127
+ }
128
+ (_this$intersectionObs4 = _this.intersectionObserver) === null || _this$intersectionObs4 === void 0 || _this$intersectionObs4.watchAndTag(addedNode, createElementMutationsWatcher(removedNodeRects));
119
129
  });
120
130
  });
121
131
  _defineProperty(this, "handleAttributeMutation", function (_ref5) {
122
- var _this$intersectionObs4;
132
+ var _this$intersectionObs5;
123
133
  var target = _ref5.target,
124
134
  attributeName = _ref5.attributeName,
125
135
  oldValue = _ref5.oldValue,
126
136
  newValue = _ref5.newValue;
127
- (_this$intersectionObs4 = _this.intersectionObserver) === null || _this$intersectionObs4 === void 0 || _this$intersectionObs4.watchAndTag(target, function (_ref6) {
137
+ (_this$intersectionObs5 = _this.intersectionObserver) === null || _this$intersectionObs5 === void 0 || _this$intersectionObs5.watchAndTag(target, function (_ref6) {
128
138
  var target = _ref6.target,
129
139
  rect = _ref6.rect;
130
140
  if (isContainedWithinMediaWrapper(target)) {
@@ -162,6 +172,20 @@ var ViewportObserver = /*#__PURE__*/function () {
162
172
  }
163
173
  };
164
174
  }
175
+ var _checkThirdPartySegme2 = checkThirdPartySegmentWithIgnoreReason(target),
176
+ isWithinThirdPartySegment = _checkThirdPartySegme2.isWithinThirdPartySegment,
177
+ ignoredReason = _checkThirdPartySegme2.ignoredReason;
178
+ if (isWithinThirdPartySegment) {
179
+ var assignedReason = createMutationTypeWithIgnoredReason(ignoredReason || 'third-party-element');
180
+ return {
181
+ type: assignedReason,
182
+ mutationData: {
183
+ attributeName: attributeName,
184
+ oldValue: oldValue,
185
+ newValue: newValue
186
+ }
187
+ };
188
+ }
165
189
  var lastElementRect = _this.mapVisibleNodeRects.get(target);
166
190
  if (lastElementRect && sameRectSize(rect, lastElementRect)) {
167
191
  return {
@@ -258,12 +282,12 @@ var ViewportObserver = /*#__PURE__*/function () {
258
282
  }, {
259
283
  key: "stop",
260
284
  value: function stop() {
261
- var _this$mutationObserve2, _this$intersectionObs5, _this$performanceObse2;
285
+ var _this$mutationObserve2, _this$intersectionObs6, _this$performanceObse2;
262
286
  if (!this.isStarted) {
263
287
  return;
264
288
  }
265
289
  (_this$mutationObserve2 = this.mutationObserver) === null || _this$mutationObserve2 === void 0 || _this$mutationObserve2.disconnect();
266
- (_this$intersectionObs5 = this.intersectionObserver) === null || _this$intersectionObs5 === void 0 || _this$intersectionObs5.disconnect();
290
+ (_this$intersectionObs6 = this.intersectionObserver) === null || _this$intersectionObs6 === void 0 || _this$intersectionObs6.disconnect();
267
291
  (_this$performanceObse2 = this.performanceObserver) === null || _this$performanceObse2 === void 0 || _this$performanceObse2.disconnect();
268
292
  this.isStarted = false;
269
293
  }
@@ -0,0 +1,76 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import _typeof from "@babel/runtime/helpers/typeof";
3
+ 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; }
4
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
+ // Using the React Fiber tree to traverse up the DOM and check if a node is within a specific component
6
+ // and extract child component props if needed.
7
+ export function checkWithinComponentAndExtractChildProps(node, targetComponentName, childComponentConfig) {
8
+ // Get the React fiber from the DOM node
9
+ var key = Object.keys(node).find(function (key) {
10
+ return key.startsWith('__reactFiber$') || key.startsWith('__reactInternalInstance$');
11
+ });
12
+ if (!key) {
13
+ return {
14
+ isWithin: false
15
+ };
16
+ }
17
+ var fiber = node[key];
18
+ if (!fiber) {
19
+ return {
20
+ isWithin: false
21
+ };
22
+ }
23
+
24
+ // Traverse up the fiber tree
25
+ var currentFiber = fiber;
26
+ var childProp;
27
+ while (currentFiber) {
28
+ var componentName = void 0;
29
+ if (currentFiber.type) {
30
+ if (typeof currentFiber.type === 'function') {
31
+ componentName = currentFiber.type.displayName || currentFiber.type.name;
32
+ } else if (_typeof(currentFiber.type) === 'object' && (currentFiber.type.displayName || currentFiber.type.name)) {
33
+ componentName = currentFiber.type.displayName || currentFiber.type.name;
34
+ }
35
+ }
36
+
37
+ // Check if this is a child component we're looking for
38
+ if (childComponentConfig && componentName === childComponentConfig.componentName) {
39
+ var props = currentFiber.memoizedProps || currentFiber.pendingProps;
40
+ if (props && props[childComponentConfig.propName] !== undefined) {
41
+ // Overwrite with the nearest child prop (closest to the target component)
42
+ childProp = childComponentConfig.extractValue ? childComponentConfig.extractValue(props) : props[childComponentConfig.propName];
43
+ }
44
+ }
45
+
46
+ // Check if we found the target component
47
+ if (componentName === targetComponentName) {
48
+ return _objectSpread({
49
+ isWithin: true
50
+ }, childComponentConfig && {
51
+ childProp: childProp
52
+ });
53
+ }
54
+ currentFiber = currentFiber.return;
55
+ }
56
+ return {
57
+ isWithin: false
58
+ };
59
+ }
60
+
61
+ // Check if the node is within a UFOThirdPartySegment and extract any UFOIgnoreHolds reason
62
+ export function checkThirdPartySegmentWithIgnoreReason(node) {
63
+ var result = checkWithinComponentAndExtractChildProps(node, 'UFOThirdPartySegment', {
64
+ componentName: 'UFOIgnoreHolds',
65
+ propName: 'reason'
66
+ });
67
+ return {
68
+ isWithinThirdPartySegment: result.isWithin,
69
+ ignoredReason: result.childProp
70
+ };
71
+ }
72
+
73
+ // Helper function to create mutation type from UFOIgnoreHoldsReason
74
+ export function createMutationTypeWithIgnoredReason(reason) {
75
+ return "mutation:".concat(reason);
76
+ }
@@ -0,0 +1,31 @@
1
+ import React, { type ReactNode } from 'react';
2
+ export type UFOIgnoreHoldsReason = 'third-party-element';
3
+ export type UFOIgnoreHoldsProps = {
4
+ children?: ReactNode;
5
+ ignore?: boolean;
6
+ reason?: UFOIgnoreHoldsReason;
7
+ };
8
+ /**
9
+ * Prevent a subtree from holding up an interaction
10
+ * Use this when you have a component which loads in late, but
11
+ * isn't considered to be a breach of SLO
12
+ *
13
+ * ```js
14
+ * <App>
15
+ * <Main />
16
+ * <Sidebar>
17
+ * <UFOIgnoreHolds>
18
+ * <InsightsButton />
19
+ * </UFOIgnoreHolds>
20
+ * </Sidebar>
21
+ * </App>
22
+ * ```
23
+ *
24
+ * Has an `ignore` prop, to allow you to use it conditionally
25
+ * Has a `reason` prop, to specify why the hold is being ignored
26
+ */
27
+ declare function UFOIgnoreHolds({ children, ignore, reason }: UFOIgnoreHoldsProps): React.JSX.Element;
28
+ declare namespace UFOIgnoreHolds {
29
+ var displayName: string;
30
+ }
31
+ export default UFOIgnoreHolds;
@@ -1,23 +1,8 @@
1
- import React, { type ReactNode } from 'react';
1
+ import React from 'react';
2
+ import { UFOIgnoreHoldsProps } from '../ignore-holds';
2
3
  /**
3
- * Prevent a subtree from holding up an interaction
4
- * Use this when you have a component which loads in late, but
5
- * isn't considered to be a breach of SLO
6
- *
7
- * ```js
8
- * <App>
9
- * <Main />
10
- * <Sidebar>
11
- * <UFOInteractionIgnore>
12
- * <InsightsButton />
13
- * </UFOInteractionIgnore>
14
- * </Sidebar>
15
- * </App>
16
- * ```
17
- *
18
- * Has an `ignore` prop, to allow you to use it conditionally
4
+ * Legacy wrapper component that delegates to `UFOIgnoreHolds`.
5
+ * Use `UFOIgnoreHolds` instead for new implementations.
6
+ * This component is maintained for backward compatibility only.
19
7
  */
20
- export default function UFOInteractionIgnore({ children, ignore, }: {
21
- children?: ReactNode;
22
- ignore?: boolean;
23
- }): React.JSX.Element;
8
+ export default function UFOInteractionIgnore(props: UFOIgnoreHoldsProps): React.JSX.Element;
@@ -1,2 +1,3 @@
1
1
  import UFOSegment from './segment';
2
2
  export default UFOSegment;
3
+ export { UFOThirdPartySegment } from './third-party-segment';
@@ -1,11 +1,13 @@
1
1
  import React, { type ReactNode } from 'react';
2
- type Props = {
2
+ export type UFOSegmentType = 'third-party' | 'first-party';
3
+ export type Props = {
3
4
  name: string;
4
5
  children: ReactNode;
5
6
  mode?: 'list' | 'single';
7
+ type?: UFOSegmentType;
6
8
  };
7
9
  /** A portion of the page we apply measurement to */
8
- declare function UFOSegment({ name: segmentName, children, mode }: Props): React.JSX.Element;
10
+ declare function UFOSegment({ name: segmentName, children, mode, type, }: Props): React.JSX.Element;
9
11
  declare namespace UFOSegment {
10
12
  var displayName: string;
11
13
  }
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import { Props as SegmentProps } from './segment';
3
+ export declare function UFOThirdPartySegment(props: Omit<SegmentProps, 'type'>): React.JSX.Element;
4
+ export declare namespace UFOThirdPartySegment {
5
+ var displayName: string;
6
+ }
@@ -1,5 +1,5 @@
1
1
  import type { ObservedWindowEvent } from './window-event-observer';
2
- export type VCObserverEntryType = 'mutation:child-element' | 'mutation:remount' | 'mutation:element' | 'mutation:element-replacement' | 'mutation:attribute:no-layout-shift' | 'mutation:attribute:non-visual-style' | 'mutation:attribute' | 'mutation:media' | 'mutation:rll-placeholder' | 'layout-shift' | 'window:event' | 'unknown';
2
+ export type VCObserverEntryType = 'mutation:child-element' | 'mutation:remount' | 'mutation:element' | 'mutation:element-replacement' | 'mutation:attribute:no-layout-shift' | 'mutation:attribute:non-visual-style' | 'mutation:attribute' | 'mutation:media' | 'mutation:rll-placeholder' | 'mutation:third-party-element' | 'layout-shift' | 'window:event' | 'unknown';
3
3
  export type ViewportEntryData = {
4
4
  readonly type: VCObserverEntryType;
5
5
  readonly elementName: string;
@@ -0,0 +1,15 @@
1
+ import { UFOIgnoreHoldsReason } from '../../../../ignore-holds';
2
+ import { VCObserverEntryType } from '../../types';
3
+ export declare function checkWithinComponentAndExtractChildProps<T = string>(node: HTMLElement, targetComponentName: string, childComponentConfig?: {
4
+ componentName: string;
5
+ propName: string;
6
+ extractValue?: (props: any) => T;
7
+ }): {
8
+ isWithin: boolean;
9
+ childProp?: T;
10
+ };
11
+ export declare function checkThirdPartySegmentWithIgnoreReason(node: HTMLElement): {
12
+ isWithinThirdPartySegment: boolean;
13
+ ignoredReason?: UFOIgnoreHoldsReason;
14
+ };
15
+ export declare function createMutationTypeWithIgnoredReason(reason: UFOIgnoreHoldsReason): VCObserverEntryType;
@@ -0,0 +1,31 @@
1
+ import React, { type ReactNode } from 'react';
2
+ export type UFOIgnoreHoldsReason = 'third-party-element';
3
+ export type UFOIgnoreHoldsProps = {
4
+ children?: ReactNode;
5
+ ignore?: boolean;
6
+ reason?: UFOIgnoreHoldsReason;
7
+ };
8
+ /**
9
+ * Prevent a subtree from holding up an interaction
10
+ * Use this when you have a component which loads in late, but
11
+ * isn't considered to be a breach of SLO
12
+ *
13
+ * ```js
14
+ * <App>
15
+ * <Main />
16
+ * <Sidebar>
17
+ * <UFOIgnoreHolds>
18
+ * <InsightsButton />
19
+ * </UFOIgnoreHolds>
20
+ * </Sidebar>
21
+ * </App>
22
+ * ```
23
+ *
24
+ * Has an `ignore` prop, to allow you to use it conditionally
25
+ * Has a `reason` prop, to specify why the hold is being ignored
26
+ */
27
+ declare function UFOIgnoreHolds({ children, ignore, reason }: UFOIgnoreHoldsProps): React.JSX.Element;
28
+ declare namespace UFOIgnoreHolds {
29
+ var displayName: string;
30
+ }
31
+ export default UFOIgnoreHolds;