@atlaskit/react-ufo 3.13.8 → 3.13.11

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 (23) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/cjs/interaction-id-context/index.js +46 -4
  3. package/dist/cjs/interaction-metrics-init/index.js +2 -1
  4. package/dist/cjs/load-hold/UFOLoadHold.js +32 -1
  5. package/dist/cjs/vc/vc-observer-new/index.js +40 -3
  6. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.js +3 -6
  7. package/dist/es2019/interaction-id-context/index.js +37 -2
  8. package/dist/es2019/interaction-metrics-init/index.js +2 -1
  9. package/dist/es2019/load-hold/UFOLoadHold.js +29 -2
  10. package/dist/es2019/vc/vc-observer-new/index.js +35 -0
  11. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.js +3 -6
  12. package/dist/esm/interaction-id-context/index.js +44 -3
  13. package/dist/esm/interaction-metrics-init/index.js +2 -1
  14. package/dist/esm/load-hold/UFOLoadHold.js +33 -2
  15. package/dist/esm/vc/vc-observer-new/index.js +40 -3
  16. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.js +3 -6
  17. package/dist/types/interaction-id-context/index.d.ts +2 -0
  18. package/dist/types/vc/vc-observer-new/index.d.ts +1 -0
  19. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.d.ts +0 -1
  20. package/dist/types-ts4.5/interaction-id-context/index.d.ts +2 -0
  21. package/dist/types-ts4.5/vc/vc-observer-new/index.d.ts +1 -0
  22. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.d.ts +0 -1
  23. package/package.json +7 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @atlaskit/ufo-interaction-ignore
2
2
 
3
+ ## 3.13.11
4
+
5
+ ### Patch Changes
6
+
7
+ - [#161290](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/161290)
8
+ [`26388d9d5e089`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/26388d9d5e089) -
9
+ AFO-3919 fix pre-existing UFOLoadHold not registering to new interaction
10
+
11
+ ## 3.13.10
12
+
13
+ ### Patch Changes
14
+
15
+ - [#160800](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/160800)
16
+ [`3763aa7aadc8c`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3763aa7aadc8c) -
17
+ AFO-3913 fix transitio VC v3 not reaching 100 in VC Chart
18
+
19
+ ## 3.13.9
20
+
21
+ ### Patch Changes
22
+
23
+ - [#161064](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/161064)
24
+ [`26461e98f0825`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/26461e98f0825) -
25
+ Fix enabling marking page-layour as a SSR placeholder
26
+ - [#161052](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/161052)
27
+ [`a16bd256e0ae6`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/a16bd256e0ae6) -
28
+ Clean up fg platform_ufo_use_offscreen_canvas
29
+
3
30
  ## 3.13.8
4
31
 
5
32
  ### Patch Changes
@@ -1,20 +1,62 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
6
- exports.useInteractionId = exports.getInteractionId = exports.default = exports.DefaultInteractionID = void 0;
7
+ exports.useInteractionId = exports.subscribeToInteractionIdChanges = exports.getInteractionId = exports.default = exports.DefaultInteractionID = void 0;
8
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
9
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
10
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
7
11
  var _react = require("react");
12
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
8
13
  // Same structure as react's useRef.
9
14
  // This allows modals to use a ref to scope their value
10
15
  // const id = useRef(null);
11
16
  // <InteractionIDContext.Provider value={id}>...<
12
17
 
13
- // The default InteractionID object is a global singleton
18
+ // Subscription system for interaction ID changes
19
+
20
+ var listeners = new Set();
21
+
22
+ // Observable interaction ID implementation
23
+ var ObservableInteractionID = /*#__PURE__*/function () {
24
+ function ObservableInteractionID() {
25
+ (0, _classCallCheck2.default)(this, ObservableInteractionID);
26
+ (0, _defineProperty2.default)(this, "_current", null);
27
+ }
28
+ return (0, _createClass2.default)(ObservableInteractionID, [{
29
+ key: "current",
30
+ get: function get() {
31
+ return this._current;
32
+ },
33
+ set: function set(newId) {
34
+ var oldId = this._current;
35
+ this._current = newId;
36
+
37
+ // Notify all listeners if the ID actually changed and feature flag is enabled
38
+ if (oldId !== newId && (0, _platformFeatureFlags.fg)('platform_ufo_hold_cross_interaction')) {
39
+ listeners.forEach(function (listener) {
40
+ return listener(newId);
41
+ });
42
+ }
43
+ }
44
+ }]);
45
+ }(); // The default InteractionID object is a global singleton
14
46
  // It's the one that holds the root value used in routing,
15
47
  // and is overwritten when we have new interactions start.
16
- var DefaultInteractionID = exports.DefaultInteractionID = {
17
- current: null
48
+ var DefaultInteractionID = exports.DefaultInteractionID = new ObservableInteractionID();
49
+
50
+ // Subscription functions
51
+ var subscribeToInteractionIdChanges = exports.subscribeToInteractionIdChanges = function subscribeToInteractionIdChanges(listener) {
52
+ if (!(0, _platformFeatureFlags.fg)('platform_ufo_hold_cross_interaction')) {
53
+ // Return a no-op unsubscribe function when feature flag is disabled
54
+ return function () {};
55
+ }
56
+ listeners.add(listener);
57
+ return function () {
58
+ listeners.delete(listener);
59
+ };
18
60
  };
19
61
 
20
62
  // We use a context to allow modals to have their own lifecycle
@@ -95,7 +95,8 @@ function init(analyticsWebClientAsync, config) {
95
95
  heatmapSize: config.vc.heatmapSize,
96
96
  oldDomUpdates: config.vc.oldDomUpdates,
97
97
  devToolsEnabled: config.vc.devToolsEnabled,
98
- selectorConfig: config.vc.selectorConfig
98
+ selectorConfig: config.vc.selectorConfig,
99
+ ssrEnablePageLayoutPlaceholder: config.vc.ssrEnablePageLayoutPlaceholder
99
100
  };
100
101
  (0, _vc.getVCObserver)(vcOptions).start({
101
102
  startTime: 0
@@ -6,12 +6,41 @@ Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
8
  exports.default = UFOLoadHold;
9
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
10
  var _react = _interopRequireWildcard(require("react"));
11
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
10
12
  var _interactionContext = _interopRequireDefault(require("../interaction-context"));
13
+ var _interactionIdContext = _interopRequireWildcard(require("../interaction-id-context"));
11
14
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
12
15
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
13
16
  var useLayoutEffectSAFE = typeof window === 'undefined' ? _react.useEffect : _react.useLayoutEffect;
14
17
 
18
+ /**
19
+ * Custom hook to track changes to the interaction ID.
20
+ * Uses a subscription system when feature flag is enabled, otherwise returns the current value.
21
+ */
22
+ function useInteractionIdValue() {
23
+ var interactionId = (0, _react.useContext)(_interactionIdContext.default);
24
+ var _useState = (0, _react.useState)((interactionId === null || interactionId === void 0 ? void 0 : interactionId.current) || null),
25
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
26
+ currentId = _useState2[0],
27
+ setCurrentId = _useState2[1];
28
+ useLayoutEffectSAFE(function () {
29
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_hold_cross_interaction')) {
30
+ // New subscription-based approach
31
+ setCurrentId((interactionId === null || interactionId === void 0 ? void 0 : interactionId.current) || null);
32
+ var unsubscribe = (0, _interactionIdContext.subscribeToInteractionIdChanges)(function (newId) {
33
+ setCurrentId(newId);
34
+ });
35
+ return unsubscribe;
36
+ } else {
37
+ // Legacy behavior - just return the current value without subscription
38
+ setCurrentId((interactionId === null || interactionId === void 0 ? void 0 : interactionId.current) || null);
39
+ }
40
+ }, [interactionId]);
41
+ return currentId;
42
+ }
43
+
15
44
  /**
16
45
  * Render this whenever you're loading.
17
46
  *
@@ -64,6 +93,8 @@ function UFOLoadHold(_ref) {
64
93
  hold = _ref$hold === void 0 ? true : _ref$hold,
65
94
  _ref$experimental = _ref.experimental,
66
95
  experimental = _ref$experimental === void 0 ? false : _ref$experimental;
96
+ var currentInteractionId = useInteractionIdValue();
97
+
67
98
  // react-18: useId instead
68
99
  var context = (0, _react.useContext)(_interactionContext.default);
69
100
  useLayoutEffectSAFE(function () {
@@ -73,7 +104,7 @@ function UFOLoadHold(_ref) {
73
104
  }
74
105
  return context.hold(name);
75
106
  }
76
- }, [hold, context, name]);
107
+ }, [hold, context, name, currentInteractionId]);
77
108
 
78
109
  // react-18: can return children directly
79
110
  return children != null ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, children) : null;
@@ -128,12 +128,15 @@ var VCObserverNew = exports.default = /*#__PURE__*/function () {
128
128
  case 0:
129
129
  start = param.start, stop = param.stop, interactionId = param.interactionId;
130
130
  results = [];
131
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_v3_add_start_entry')) {
132
+ this.addStartEntry(start);
133
+ }
131
134
  calculator_fy25_03 = new _fy25_.default();
132
135
  orderedEntries = this.entriesTimeline.getOrderedEntries({
133
136
  start: start,
134
137
  stop: stop
135
138
  });
136
- _context.next = 6;
139
+ _context.next = 7;
137
140
  return calculator_fy25_03.calculate({
138
141
  orderedEntries: orderedEntries,
139
142
  startTime: start,
@@ -141,13 +144,13 @@ var VCObserverNew = exports.default = /*#__PURE__*/function () {
141
144
  interactionId: interactionId,
142
145
  isPostInteraction: this.isPostInteraction
143
146
  });
144
- case 6:
147
+ case 7:
145
148
  fy25_03 = _context.sent;
146
149
  if (fy25_03) {
147
150
  results.push(fy25_03);
148
151
  }
149
152
  return _context.abrupt("return", results);
150
- case 9:
153
+ case 10:
151
154
  case "end":
152
155
  return _context.stop();
153
156
  }
@@ -158,6 +161,40 @@ var VCObserverNew = exports.default = /*#__PURE__*/function () {
158
161
  }
159
162
  return getVCResult;
160
163
  }()
164
+ }, {
165
+ key: "addStartEntry",
166
+ value: function addStartEntry(startTime) {
167
+ this.entriesTimeline.push({
168
+ time: startTime,
169
+ data: {
170
+ type: 'mutation:element',
171
+ elementName: 'START',
172
+ visible: true,
173
+ rect: {
174
+ x: 0,
175
+ y: 0,
176
+ width: window.innerWidth,
177
+ height: window.innerHeight,
178
+ top: 0,
179
+ left: 0,
180
+ bottom: window.innerHeight,
181
+ right: window.innerWidth,
182
+ toJSON: function toJSON() {
183
+ return {
184
+ x: this.x,
185
+ y: this.y,
186
+ width: this.width,
187
+ height: this.height,
188
+ top: this.top,
189
+ left: this.left,
190
+ bottom: this.bottom,
191
+ right: this.right
192
+ };
193
+ }
194
+ }
195
+ }
196
+ });
197
+ }
161
198
  }, {
162
199
  key: "getElementName",
163
200
  value: function getElementName(element) {
@@ -11,7 +11,6 @@ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"))
11
11
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
12
12
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
13
13
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
14
- var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
15
14
  var _taskYield = _interopRequireDefault(require("../../utils/task-yield"));
16
15
  // 24-bit color value
17
16
  /**
@@ -65,20 +64,18 @@ var ViewportCanvas = exports.ViewportCanvas = /*#__PURE__*/function () {
65
64
  }
66
65
  this.ctx = ctx;
67
66
  this.ctx.globalCompositeOperation = 'source-over';
68
- if ((0, _platformFeatureFlags.fg)('platform_ufo_use_offscreen_canvas')) {
69
- this.ctx.imageSmoothingEnabled = false; // Disable image smoothing for better performance
70
- }
67
+ this.ctx.imageSmoothingEnabled = false; // Disable image smoothing for better performance
68
+
71
69
  this.clear();
72
70
  }
73
71
 
74
72
  /**
75
73
  * Creates a canvas instance, falling back to HTMLCanvasElement if OffscreenCanvas is not available
76
- * or if the feature flag is disabled
77
74
  */
78
75
  return (0, _createClass2.default)(ViewportCanvas, [{
79
76
  key: "createCanvas",
80
77
  value: function createCanvas(width, height) {
81
- if (typeof OffscreenCanvas !== 'undefined' && (0, _platformFeatureFlags.fg)('platform_ufo_use_offscreen_canvas')) {
78
+ if (typeof OffscreenCanvas !== 'undefined') {
82
79
  return new OffscreenCanvas(width, height);
83
80
  }
84
81
  var canvas = document.createElement('canvas');
@@ -1,15 +1,50 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
1
2
  import { createContext } from 'react';
3
+ import { fg } from '@atlaskit/platform-feature-flags';
2
4
 
3
5
  // Same structure as react's useRef.
4
6
  // This allows modals to use a ref to scope their value
5
7
  // const id = useRef(null);
6
8
  // <InteractionIDContext.Provider value={id}>...<
7
9
 
10
+ // Subscription system for interaction ID changes
11
+
12
+ const listeners = new Set();
13
+
14
+ // Observable interaction ID implementation
15
+ class ObservableInteractionID {
16
+ constructor() {
17
+ _defineProperty(this, "_current", null);
18
+ }
19
+ get current() {
20
+ return this._current;
21
+ }
22
+ set current(newId) {
23
+ const oldId = this._current;
24
+ this._current = newId;
25
+
26
+ // Notify all listeners if the ID actually changed and feature flag is enabled
27
+ if (oldId !== newId && fg('platform_ufo_hold_cross_interaction')) {
28
+ listeners.forEach(listener => listener(newId));
29
+ }
30
+ }
31
+ }
32
+
8
33
  // The default InteractionID object is a global singleton
9
34
  // It's the one that holds the root value used in routing,
10
35
  // and is overwritten when we have new interactions start.
11
- export const DefaultInteractionID = {
12
- current: null
36
+ export const DefaultInteractionID = new ObservableInteractionID();
37
+
38
+ // Subscription functions
39
+ export const subscribeToInteractionIdChanges = listener => {
40
+ if (!fg('platform_ufo_hold_cross_interaction')) {
41
+ // Return a no-op unsubscribe function when feature flag is disabled
42
+ return () => {};
43
+ }
44
+ listeners.add(listener);
45
+ return () => {
46
+ listeners.delete(listener);
47
+ };
13
48
  };
14
49
 
15
50
  // We use a context to allow modals to have their own lifecycle
@@ -84,7 +84,8 @@ export function init(analyticsWebClientAsync, config) {
84
84
  heatmapSize: config.vc.heatmapSize,
85
85
  oldDomUpdates: config.vc.oldDomUpdates,
86
86
  devToolsEnabled: config.vc.devToolsEnabled,
87
- selectorConfig: config.vc.selectorConfig
87
+ selectorConfig: config.vc.selectorConfig,
88
+ ssrEnablePageLayoutPlaceholder: config.vc.ssrEnablePageLayoutPlaceholder
88
89
  };
89
90
  getVCObserver(vcOptions).start({
90
91
  startTime: 0
@@ -1,7 +1,32 @@
1
- import React, { useContext, useEffect, useLayoutEffect } from 'react';
1
+ import React, { useContext, useEffect, useLayoutEffect, useState } from 'react';
2
+ import { fg } from '@atlaskit/platform-feature-flags';
2
3
  import UFOInteractionContext from '../interaction-context';
4
+ import UFOInteractionIDContext, { subscribeToInteractionIdChanges } from '../interaction-id-context';
3
5
  const useLayoutEffectSAFE = typeof window === 'undefined' ? useEffect : useLayoutEffect;
4
6
 
7
+ /**
8
+ * Custom hook to track changes to the interaction ID.
9
+ * Uses a subscription system when feature flag is enabled, otherwise returns the current value.
10
+ */
11
+ function useInteractionIdValue() {
12
+ const interactionId = useContext(UFOInteractionIDContext);
13
+ const [currentId, setCurrentId] = useState((interactionId === null || interactionId === void 0 ? void 0 : interactionId.current) || null);
14
+ useLayoutEffectSAFE(() => {
15
+ if (fg('platform_ufo_hold_cross_interaction')) {
16
+ // New subscription-based approach
17
+ setCurrentId((interactionId === null || interactionId === void 0 ? void 0 : interactionId.current) || null);
18
+ const unsubscribe = subscribeToInteractionIdChanges(newId => {
19
+ setCurrentId(newId);
20
+ });
21
+ return unsubscribe;
22
+ } else {
23
+ // Legacy behavior - just return the current value without subscription
24
+ setCurrentId((interactionId === null || interactionId === void 0 ? void 0 : interactionId.current) || null);
25
+ }
26
+ }, [interactionId]);
27
+ return currentId;
28
+ }
29
+
5
30
  /**
6
31
  * Render this whenever you're loading.
7
32
  *
@@ -53,6 +78,8 @@ export default function UFOLoadHold({
53
78
  hold = true,
54
79
  experimental = false
55
80
  }) {
81
+ const currentInteractionId = useInteractionIdValue();
82
+
56
83
  // react-18: useId instead
57
84
  const context = useContext(UFOInteractionContext);
58
85
  useLayoutEffectSAFE(() => {
@@ -62,7 +89,7 @@ export default function UFOLoadHold({
62
89
  }
63
90
  return context.hold(name);
64
91
  }
65
- }, [hold, context, name]);
92
+ }, [hold, context, name, currentInteractionId]);
66
93
 
67
94
  // react-18: can return children directly
68
95
  return children != null ? /*#__PURE__*/React.createElement(React.Fragment, null, children) : null;
@@ -103,6 +103,9 @@ export default class VCObserverNew {
103
103
  interactionId
104
104
  } = param;
105
105
  const results = [];
106
+ if (fg('platform_ufo_v3_add_start_entry')) {
107
+ this.addStartEntry(start);
108
+ }
106
109
  const calculator_fy25_03 = new VCCalculator_FY25_03();
107
110
  const orderedEntries = this.entriesTimeline.getOrderedEntries({
108
111
  start,
@@ -120,6 +123,38 @@ export default class VCObserverNew {
120
123
  }
121
124
  return results;
122
125
  }
126
+ addStartEntry(startTime) {
127
+ this.entriesTimeline.push({
128
+ time: startTime,
129
+ data: {
130
+ type: 'mutation:element',
131
+ elementName: 'START',
132
+ visible: true,
133
+ rect: {
134
+ x: 0,
135
+ y: 0,
136
+ width: window.innerWidth,
137
+ height: window.innerHeight,
138
+ top: 0,
139
+ left: 0,
140
+ bottom: window.innerHeight,
141
+ right: window.innerWidth,
142
+ toJSON: function () {
143
+ return {
144
+ x: this.x,
145
+ y: this.y,
146
+ width: this.width,
147
+ height: this.height,
148
+ top: this.top,
149
+ left: this.left,
150
+ bottom: this.bottom,
151
+ right: this.right
152
+ };
153
+ }
154
+ }
155
+ }
156
+ });
157
+ }
123
158
  getElementName(element) {
124
159
  return getElementName(this.selectorConfig, element);
125
160
  }
@@ -1,4 +1,3 @@
1
- import { fg } from '@atlaskit/platform-feature-flags';
2
1
  import taskYield from '../../utils/task-yield';
3
2
 
4
3
  // 24-bit color value
@@ -52,18 +51,16 @@ export class ViewportCanvas {
52
51
  }
53
52
  this.ctx = ctx;
54
53
  this.ctx.globalCompositeOperation = 'source-over';
55
- if (fg('platform_ufo_use_offscreen_canvas')) {
56
- this.ctx.imageSmoothingEnabled = false; // Disable image smoothing for better performance
57
- }
54
+ this.ctx.imageSmoothingEnabled = false; // Disable image smoothing for better performance
55
+
58
56
  this.clear();
59
57
  }
60
58
 
61
59
  /**
62
60
  * Creates a canvas instance, falling back to HTMLCanvasElement if OffscreenCanvas is not available
63
- * or if the feature flag is disabled
64
61
  */
65
62
  createCanvas(width, height) {
66
- if (typeof OffscreenCanvas !== 'undefined' && fg('platform_ufo_use_offscreen_canvas')) {
63
+ if (typeof OffscreenCanvas !== 'undefined') {
67
64
  return new OffscreenCanvas(width, height);
68
65
  }
69
66
  const canvas = document.createElement('canvas');
@@ -1,15 +1,56 @@
1
+ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
2
+ import _createClass from "@babel/runtime/helpers/createClass";
3
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
1
4
  import { createContext } from 'react';
5
+ import { fg } from '@atlaskit/platform-feature-flags';
2
6
 
3
7
  // Same structure as react's useRef.
4
8
  // This allows modals to use a ref to scope their value
5
9
  // const id = useRef(null);
6
10
  // <InteractionIDContext.Provider value={id}>...<
7
11
 
8
- // The default InteractionID object is a global singleton
12
+ // Subscription system for interaction ID changes
13
+
14
+ var listeners = new Set();
15
+
16
+ // Observable interaction ID implementation
17
+ var ObservableInteractionID = /*#__PURE__*/function () {
18
+ function ObservableInteractionID() {
19
+ _classCallCheck(this, ObservableInteractionID);
20
+ _defineProperty(this, "_current", null);
21
+ }
22
+ return _createClass(ObservableInteractionID, [{
23
+ key: "current",
24
+ get: function get() {
25
+ return this._current;
26
+ },
27
+ set: function set(newId) {
28
+ var oldId = this._current;
29
+ this._current = newId;
30
+
31
+ // Notify all listeners if the ID actually changed and feature flag is enabled
32
+ if (oldId !== newId && fg('platform_ufo_hold_cross_interaction')) {
33
+ listeners.forEach(function (listener) {
34
+ return listener(newId);
35
+ });
36
+ }
37
+ }
38
+ }]);
39
+ }(); // The default InteractionID object is a global singleton
9
40
  // It's the one that holds the root value used in routing,
10
41
  // and is overwritten when we have new interactions start.
11
- export var DefaultInteractionID = {
12
- current: null
42
+ export var DefaultInteractionID = new ObservableInteractionID();
43
+
44
+ // Subscription functions
45
+ export var subscribeToInteractionIdChanges = function subscribeToInteractionIdChanges(listener) {
46
+ if (!fg('platform_ufo_hold_cross_interaction')) {
47
+ // Return a no-op unsubscribe function when feature flag is disabled
48
+ return function () {};
49
+ }
50
+ listeners.add(listener);
51
+ return function () {
52
+ listeners.delete(listener);
53
+ };
13
54
  };
14
55
 
15
56
  // We use a context to allow modals to have their own lifecycle
@@ -85,7 +85,8 @@ export function init(analyticsWebClientAsync, config) {
85
85
  heatmapSize: config.vc.heatmapSize,
86
86
  oldDomUpdates: config.vc.oldDomUpdates,
87
87
  devToolsEnabled: config.vc.devToolsEnabled,
88
- selectorConfig: config.vc.selectorConfig
88
+ selectorConfig: config.vc.selectorConfig,
89
+ ssrEnablePageLayoutPlaceholder: config.vc.ssrEnablePageLayoutPlaceholder
89
90
  };
90
91
  getVCObserver(vcOptions).start({
91
92
  startTime: 0
@@ -1,7 +1,36 @@
1
- import React, { useContext, useEffect, useLayoutEffect } from 'react';
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import React, { useContext, useEffect, useLayoutEffect, useState } from 'react';
3
+ import { fg } from '@atlaskit/platform-feature-flags';
2
4
  import UFOInteractionContext from '../interaction-context';
5
+ import UFOInteractionIDContext, { subscribeToInteractionIdChanges } from '../interaction-id-context';
3
6
  var useLayoutEffectSAFE = typeof window === 'undefined' ? useEffect : useLayoutEffect;
4
7
 
8
+ /**
9
+ * Custom hook to track changes to the interaction ID.
10
+ * Uses a subscription system when feature flag is enabled, otherwise returns the current value.
11
+ */
12
+ function useInteractionIdValue() {
13
+ var interactionId = useContext(UFOInteractionIDContext);
14
+ var _useState = useState((interactionId === null || interactionId === void 0 ? void 0 : interactionId.current) || null),
15
+ _useState2 = _slicedToArray(_useState, 2),
16
+ currentId = _useState2[0],
17
+ setCurrentId = _useState2[1];
18
+ useLayoutEffectSAFE(function () {
19
+ if (fg('platform_ufo_hold_cross_interaction')) {
20
+ // New subscription-based approach
21
+ setCurrentId((interactionId === null || interactionId === void 0 ? void 0 : interactionId.current) || null);
22
+ var unsubscribe = subscribeToInteractionIdChanges(function (newId) {
23
+ setCurrentId(newId);
24
+ });
25
+ return unsubscribe;
26
+ } else {
27
+ // Legacy behavior - just return the current value without subscription
28
+ setCurrentId((interactionId === null || interactionId === void 0 ? void 0 : interactionId.current) || null);
29
+ }
30
+ }, [interactionId]);
31
+ return currentId;
32
+ }
33
+
5
34
  /**
6
35
  * Render this whenever you're loading.
7
36
  *
@@ -54,6 +83,8 @@ export default function UFOLoadHold(_ref) {
54
83
  hold = _ref$hold === void 0 ? true : _ref$hold,
55
84
  _ref$experimental = _ref.experimental,
56
85
  experimental = _ref$experimental === void 0 ? false : _ref$experimental;
86
+ var currentInteractionId = useInteractionIdValue();
87
+
57
88
  // react-18: useId instead
58
89
  var context = useContext(UFOInteractionContext);
59
90
  useLayoutEffectSAFE(function () {
@@ -63,7 +94,7 @@ export default function UFOLoadHold(_ref) {
63
94
  }
64
95
  return context.hold(name);
65
96
  }
66
- }, [hold, context, name]);
97
+ }, [hold, context, name, currentInteractionId]);
67
98
 
68
99
  // react-18: can return children directly
69
100
  return children != null ? /*#__PURE__*/React.createElement(React.Fragment, null, children) : null;
@@ -121,12 +121,15 @@ var VCObserverNew = /*#__PURE__*/function () {
121
121
  case 0:
122
122
  start = param.start, stop = param.stop, interactionId = param.interactionId;
123
123
  results = [];
124
+ if (fg('platform_ufo_v3_add_start_entry')) {
125
+ this.addStartEntry(start);
126
+ }
124
127
  calculator_fy25_03 = new VCCalculator_FY25_03();
125
128
  orderedEntries = this.entriesTimeline.getOrderedEntries({
126
129
  start: start,
127
130
  stop: stop
128
131
  });
129
- _context.next = 6;
132
+ _context.next = 7;
130
133
  return calculator_fy25_03.calculate({
131
134
  orderedEntries: orderedEntries,
132
135
  startTime: start,
@@ -134,13 +137,13 @@ var VCObserverNew = /*#__PURE__*/function () {
134
137
  interactionId: interactionId,
135
138
  isPostInteraction: this.isPostInteraction
136
139
  });
137
- case 6:
140
+ case 7:
138
141
  fy25_03 = _context.sent;
139
142
  if (fy25_03) {
140
143
  results.push(fy25_03);
141
144
  }
142
145
  return _context.abrupt("return", results);
143
- case 9:
146
+ case 10:
144
147
  case "end":
145
148
  return _context.stop();
146
149
  }
@@ -151,6 +154,40 @@ var VCObserverNew = /*#__PURE__*/function () {
151
154
  }
152
155
  return getVCResult;
153
156
  }()
157
+ }, {
158
+ key: "addStartEntry",
159
+ value: function addStartEntry(startTime) {
160
+ this.entriesTimeline.push({
161
+ time: startTime,
162
+ data: {
163
+ type: 'mutation:element',
164
+ elementName: 'START',
165
+ visible: true,
166
+ rect: {
167
+ x: 0,
168
+ y: 0,
169
+ width: window.innerWidth,
170
+ height: window.innerHeight,
171
+ top: 0,
172
+ left: 0,
173
+ bottom: window.innerHeight,
174
+ right: window.innerWidth,
175
+ toJSON: function toJSON() {
176
+ return {
177
+ x: this.x,
178
+ y: this.y,
179
+ width: this.width,
180
+ height: this.height,
181
+ top: this.top,
182
+ left: this.left,
183
+ bottom: this.bottom,
184
+ right: this.right
185
+ };
186
+ }
187
+ }
188
+ }
189
+ });
190
+ }
154
191
  }, {
155
192
  key: "getElementName",
156
193
  value: function getElementName(element) {
@@ -2,7 +2,6 @@ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
2
  import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
3
3
  import _createClass from "@babel/runtime/helpers/createClass";
4
4
  import _regeneratorRuntime from "@babel/runtime/regenerator";
5
- import { fg } from '@atlaskit/platform-feature-flags';
6
5
  import taskYield from '../../utils/task-yield';
7
6
 
8
7
  // 24-bit color value
@@ -58,20 +57,18 @@ export var ViewportCanvas = /*#__PURE__*/function () {
58
57
  }
59
58
  this.ctx = ctx;
60
59
  this.ctx.globalCompositeOperation = 'source-over';
61
- if (fg('platform_ufo_use_offscreen_canvas')) {
62
- this.ctx.imageSmoothingEnabled = false; // Disable image smoothing for better performance
63
- }
60
+ this.ctx.imageSmoothingEnabled = false; // Disable image smoothing for better performance
61
+
64
62
  this.clear();
65
63
  }
66
64
 
67
65
  /**
68
66
  * Creates a canvas instance, falling back to HTMLCanvasElement if OffscreenCanvas is not available
69
- * or if the feature flag is disabled
70
67
  */
71
68
  return _createClass(ViewportCanvas, [{
72
69
  key: "createCanvas",
73
70
  value: function createCanvas(width, height) {
74
- if (typeof OffscreenCanvas !== 'undefined' && fg('platform_ufo_use_offscreen_canvas')) {
71
+ if (typeof OffscreenCanvas !== 'undefined') {
75
72
  return new OffscreenCanvas(width, height);
76
73
  }
77
74
  var canvas = document.createElement('canvas');
@@ -2,7 +2,9 @@
2
2
  export type InteractionIDContextType = {
3
3
  current: string | null;
4
4
  };
5
+ type InteractionIDListener = (newId: string | null) => void;
5
6
  export declare const DefaultInteractionID: InteractionIDContextType;
7
+ export declare const subscribeToInteractionIdChanges: (listener: InteractionIDListener) => (() => void);
6
8
  declare const _default: import("react").Context<InteractionIDContextType>;
7
9
  export default _default;
8
10
  export declare const getInteractionId: () => InteractionIDContextType;
@@ -17,5 +17,6 @@ export default class VCObserverNew {
17
17
  }): void;
18
18
  stop(): void;
19
19
  getVCResult(param: VCObserverGetVCResultParam): Promise<RevisionPayloadEntry[]>;
20
+ private addStartEntry;
20
21
  private getElementName;
21
22
  }
@@ -32,7 +32,6 @@ export declare class ViewportCanvas {
32
32
  constructor(viewport: Viewport, scaleFactor?: number);
33
33
  /**
34
34
  * Creates a canvas instance, falling back to HTMLCanvasElement if OffscreenCanvas is not available
35
- * or if the feature flag is disabled
36
35
  */
37
36
  private createCanvas;
38
37
  getScaledDimensions(): {
@@ -2,7 +2,9 @@
2
2
  export type InteractionIDContextType = {
3
3
  current: string | null;
4
4
  };
5
+ type InteractionIDListener = (newId: string | null) => void;
5
6
  export declare const DefaultInteractionID: InteractionIDContextType;
7
+ export declare const subscribeToInteractionIdChanges: (listener: InteractionIDListener) => (() => void);
6
8
  declare const _default: import("react").Context<InteractionIDContextType>;
7
9
  export default _default;
8
10
  export declare const getInteractionId: () => InteractionIDContextType;
@@ -17,5 +17,6 @@ export default class VCObserverNew {
17
17
  }): void;
18
18
  stop(): void;
19
19
  getVCResult(param: VCObserverGetVCResultParam): Promise<RevisionPayloadEntry[]>;
20
+ private addStartEntry;
20
21
  private getElementName;
21
22
  }
@@ -32,7 +32,6 @@ export declare class ViewportCanvas {
32
32
  constructor(viewport: Viewport, scaleFactor?: number);
33
33
  /**
34
34
  * Creates a canvas instance, falling back to HTMLCanvasElement if OffscreenCanvas is not available
35
- * or if the feature flag is disabled
36
35
  */
37
36
  private createCanvas;
38
37
  getScaledDimensions(): {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/react-ufo",
3
- "version": "3.13.8",
3
+ "version": "3.13.11",
4
4
  "description": "Parts of React UFO that are publicly available",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -127,9 +127,6 @@
127
127
  }
128
128
  },
129
129
  "platform-feature-flags": {
130
- "platform_ufo_use_offscreen_canvas": {
131
- "type": "boolean"
132
- },
133
130
  "platform_ufo_canvas_heatmap_full_precision": {
134
131
  "type": "boolean"
135
132
  },
@@ -168,6 +165,12 @@
168
165
  },
169
166
  "platform_ufo_ignore_non_vis_attributes": {
170
167
  "type": "boolean"
168
+ },
169
+ "platform_ufo_v3_add_start_entry": {
170
+ "type": "boolean"
171
+ },
172
+ "platform_ufo_hold_cross_interaction": {
173
+ "type": "boolean"
171
174
  }
172
175
  }
173
176
  }