@atlaskit/react-ufo 3.12.4 → 3.13.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 (109) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/create-experimental-interaction-metrics-payload/index.js +2 -1
  3. package/dist/cjs/create-payload/utils/get-vc-metrics.js +2 -1
  4. package/dist/cjs/vc/index.js +4 -2
  5. package/dist/cjs/vc/vc-observer/getVCRevisionDebugDetails.js +41 -0
  6. package/dist/cjs/vc/vc-observer/index.js +63 -33
  7. package/dist/cjs/vc/vc-observer/observers/index.js +3 -2
  8. package/dist/cjs/vc/vc-observer/observers/ssr-placeholders/index.js +38 -12
  9. package/dist/cjs/vc/vc-observer/observers/ssr-placeholders/ssr-scripts/collectSSRPlaceholderDimensions.js +9 -0
  10. package/dist/cjs/vc/vc-observer-new/index.js +13 -7
  11. package/dist/cjs/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +211 -36
  12. package/dist/cjs/vc/vc-observer-new/metric-calculator/fy25_03/index.js +4 -4
  13. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +94 -4
  14. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.js +108 -0
  15. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +16 -57
  16. package/dist/cjs/vc/vc-observer-new/viewport-observer/index.js +15 -5
  17. package/dist/cjs/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +3 -1
  18. package/dist/es2019/create-experimental-interaction-metrics-payload/index.js +2 -1
  19. package/dist/es2019/create-payload/utils/get-vc-metrics.js +1 -0
  20. package/dist/es2019/vc/index.js +4 -2
  21. package/dist/es2019/vc/vc-observer/getVCRevisionDebugDetails.js +32 -0
  22. package/dist/es2019/vc/vc-observer/index.js +36 -1
  23. package/dist/es2019/vc/vc-observer/observers/index.js +2 -1
  24. package/dist/es2019/vc/vc-observer/observers/ssr-placeholders/index.js +38 -13
  25. package/dist/es2019/vc/vc-observer/observers/ssr-placeholders/ssr-scripts/collectSSRPlaceholderDimensions.js +9 -1
  26. package/dist/es2019/vc/vc-observer-new/index.js +12 -6
  27. package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +115 -17
  28. package/dist/es2019/vc/vc-observer-new/metric-calculator/fy25_03/index.js +4 -4
  29. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +44 -1
  30. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.js +75 -0
  31. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +2 -20
  32. package/dist/es2019/vc/vc-observer-new/viewport-observer/index.js +15 -5
  33. package/dist/es2019/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +3 -1
  34. package/dist/esm/create-experimental-interaction-metrics-payload/index.js +2 -1
  35. package/dist/esm/create-payload/utils/get-vc-metrics.js +2 -1
  36. package/dist/esm/vc/index.js +4 -2
  37. package/dist/esm/vc/vc-observer/getVCRevisionDebugDetails.js +35 -0
  38. package/dist/esm/vc/vc-observer/index.js +63 -33
  39. package/dist/esm/vc/vc-observer/observers/index.js +3 -2
  40. package/dist/esm/vc/vc-observer/observers/ssr-placeholders/index.js +38 -12
  41. package/dist/esm/vc/vc-observer/observers/ssr-placeholders/ssr-scripts/collectSSRPlaceholderDimensions.js +9 -0
  42. package/dist/esm/vc/vc-observer-new/index.js +13 -7
  43. package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +211 -36
  44. package/dist/esm/vc/vc-observer-new/metric-calculator/fy25_03/index.js +4 -4
  45. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +94 -5
  46. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.js +106 -0
  47. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +2 -55
  48. package/dist/esm/vc/vc-observer-new/viewport-observer/index.js +15 -5
  49. package/dist/esm/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +3 -1
  50. package/dist/types/config/index.d.ts +1 -0
  51. package/dist/types/vc/types.d.ts +2 -0
  52. package/dist/types/vc/vc-observer/getVCRevisionDebugDetails.d.ts +30 -0
  53. package/dist/types/vc/vc-observer/index.d.ts +1 -1
  54. package/dist/types/vc/vc-observer/observers/index.d.ts +3 -0
  55. package/dist/types/vc/vc-observer/observers/ssr-placeholders/index.d.ts +4 -1
  56. package/dist/types/vc/vc-observer/observers/ssr-placeholders/ssr-scripts/collectSSRPlaceholderDimensions.d.ts +1 -1
  57. package/dist/types/vc/vc-observer-new/index.d.ts +2 -0
  58. package/dist/types/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +4 -1
  59. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +5 -1
  60. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.d.ts +1 -0
  61. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/index.d.ts +2 -4
  62. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +20 -2
  63. package/dist/types/vc/vc-observer-new/metric-calculator/types.d.ts +2 -0
  64. package/dist/types/vc/vc-observer-new/types.d.ts +5 -1
  65. package/dist/types/vc/vc-observer-new/viewport-observer/mutation-observer/index.d.ts +2 -0
  66. package/dist/types/vc/vc-observer-new/viewport-observer/types.d.ts +2 -0
  67. package/dist/types-ts4.5/config/index.d.ts +1 -0
  68. package/dist/types-ts4.5/vc/types.d.ts +2 -0
  69. package/dist/types-ts4.5/vc/vc-observer/getVCRevisionDebugDetails.d.ts +30 -0
  70. package/dist/types-ts4.5/vc/vc-observer/index.d.ts +1 -1
  71. package/dist/types-ts4.5/vc/vc-observer/observers/index.d.ts +3 -0
  72. package/dist/types-ts4.5/vc/vc-observer/observers/ssr-placeholders/index.d.ts +4 -1
  73. package/dist/types-ts4.5/vc/vc-observer/observers/ssr-placeholders/ssr-scripts/collectSSRPlaceholderDimensions.d.ts +1 -1
  74. package/dist/types-ts4.5/vc/vc-observer-new/index.d.ts +2 -0
  75. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +4 -1
  76. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +5 -1
  77. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.d.ts +1 -0
  78. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/index.d.ts +2 -4
  79. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +20 -2
  80. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/types.d.ts +2 -0
  81. package/dist/types-ts4.5/vc/vc-observer-new/types.d.ts +5 -1
  82. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/mutation-observer/index.d.ts +2 -0
  83. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/types.d.ts +2 -0
  84. package/package.json +4 -1
  85. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +0 -367
  86. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +0 -398
  87. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +0 -5
  88. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +0 -152
  89. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +0 -108
  90. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +0 -248
  91. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +0 -263
  92. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +0 -1
  93. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +0 -99
  94. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +0 -60
  95. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +0 -361
  96. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +0 -391
  97. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +0 -1
  98. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +0 -145
  99. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +0 -101
  100. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.d.ts +0 -39
  101. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.d.ts +0 -10
  102. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.d.ts +0 -43
  103. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.d.ts +0 -12
  104. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.d.ts +0 -25
  105. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.d.ts +0 -39
  106. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.d.ts +0 -10
  107. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.d.ts +0 -43
  108. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.d.ts +0 -12
  109. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.d.ts +0 -25
@@ -1,248 +0,0 @@
1
- import isViewportEntryData from '../../utils/is-viewport-entry-data';
2
- import taskYield from '../../utils/task-yield';
3
- const MAX_HEATMAP_SIZE = 1000;
4
- function createEmptyHeatmapEntry() {
5
- return {
6
- head: null,
7
- previousEntries: []
8
- };
9
- }
10
- function createEmptyMap(heatmapWidth, heatmapHeight) {
11
- return Array.from({
12
- length: heatmapHeight
13
- }).map(() => Array.from({
14
- length: heatmapWidth
15
- }).map(createEmptyHeatmapEntry));
16
- }
17
- function isRectInside(a, b) {
18
- if (!a || !b) {
19
- return false;
20
- }
21
-
22
- // Check if all corners of rectangle a are within the bounds of rectangle b
23
- return a.left >= b.left && a.right <= b.right && a.top >= b.top && a.bottom <= b.bottom;
24
- }
25
- export default class Heatmap {
26
- /**
27
- * Heatmap Width
28
- */
29
-
30
- /**
31
- * Heatmap Height
32
- */
33
-
34
- /**
35
- * Heatmap Area (width * height)
36
- */
37
-
38
- constructor({
39
- viewport,
40
- heatmapSize
41
- }) {
42
- // TODO timeOrigin? do we need? for SSR??
43
- this.viewport = viewport;
44
- const safeSize = Math.min(heatmapSize, MAX_HEATMAP_SIZE);
45
- if (viewport.width === 0 || viewport.height === 0) {
46
- this.width = safeSize;
47
- this.height = safeSize;
48
- this.scaleX = 1;
49
- this.scaleY = 1;
50
- this.heatmapAreaSize = 0;
51
- this.map = createEmptyMap(safeSize, safeSize);
52
- return;
53
- }
54
- const aspectRatio = viewport.width / viewport.height;
55
- if (aspectRatio > 1) {
56
- // Landscape orientation
57
- this.width = safeSize;
58
- this.height = Math.round(safeSize / aspectRatio);
59
- } else {
60
- // Portrait orientation
61
- this.width = safeSize;
62
- this.height = Math.round(safeSize * aspectRatio);
63
- }
64
- this.scaleX = this.width / viewport.width;
65
- this.scaleY = this.height / viewport.height;
66
- this.heatmapAreaSize = this.width * this.height;
67
- this.map = createEmptyMap(this.width, this.height);
68
- }
69
- getHeatmap() {
70
- return this.map;
71
- }
72
- getCell(row, col) {
73
- var _this$map$row;
74
- return (_this$map$row = this.map[row]) === null || _this$map$row === void 0 ? void 0 : _this$map$row[col];
75
- }
76
-
77
- /**
78
- * Map Dom Rect to Heatmap Rect, rounded up to occupy full cell.
79
- * @param rect DOM Rect
80
- * @returns
81
- */
82
- mapDOMRectToHeatmap(rect) {
83
- const scaledX = rect.x * this.scaleX;
84
- const scaledY = rect.y * this.scaleY;
85
- const scaledWidth = rect.width * this.scaleX;
86
- const scaledHeight = rect.height * this.scaleY;
87
- return {
88
- left: Math.floor(scaledX),
89
- right: Math.ceil(scaledX + scaledWidth),
90
- top: Math.floor(scaledY),
91
- bottom: Math.ceil(scaledY + scaledHeight)
92
- };
93
- }
94
-
95
- /**
96
- * Calculate the ratio of a HeatmapRect compared to the full heatmap
97
- *
98
- * This function determines what fraction of the heatmap is covered by the given heatmap rectangle.
99
- *
100
- * @param rect
101
- */
102
- getRatio(rect) {
103
- if (this.viewport.width === 0 || this.viewport.height === 0) {
104
- return 0;
105
- }
106
- const {
107
- right,
108
- left,
109
- bottom,
110
- top
111
- } = rect;
112
- const rectWidth = right - left;
113
- const rectHeight = bottom - top;
114
- const rectArea = rectWidth * rectHeight;
115
- const ratio = rectArea / this.heatmapAreaSize;
116
- if (ratio > 1) {
117
- return 1;
118
- }
119
- return ratio;
120
- }
121
- async applyEntriesToHeatmap(entries) {
122
- for (let i = 0; i < entries.length; i++) {
123
- const entry = entries[i];
124
- const {
125
- time,
126
- type,
127
- data
128
- } = entry;
129
- if (isViewportEntryData(data)) {
130
- const rect = this.mapDOMRectToHeatmap(data.rect);
131
- const ratio = this.getRatio(rect);
132
- const heatmapEntryData = {
133
- time,
134
- elementName: data.elementName,
135
- ratio: ratio !== null && ratio !== void 0 ? ratio : null,
136
- rect,
137
- source: type
138
- };
139
- const roundedTop = Math.floor(rect.top);
140
- const roundedBottom = Math.min(rect.bottom, this.height);
141
- const roundedLeft = Math.floor(rect.left);
142
- const roundedRight = Math.min(rect.right, this.width);
143
- for (let row = roundedTop; row < roundedBottom; row++) {
144
- for (let col = roundedLeft; col < roundedRight; col++) {
145
- const cell = this.getCell(row, col);
146
- if (!cell) {
147
- continue;
148
- }
149
- const previousEntry = cell.head;
150
-
151
- // When elements are added at the same time
152
- // we try to keep the inner element changes as the head
153
- if ((previousEntry === null || previousEntry === void 0 ? void 0 : previousEntry.time) === entry.time && isRectInside(previousEntry.rect, heatmapEntryData.rect)) {
154
- cell.previousEntries.push({
155
- ...heatmapEntryData,
156
- source: 'mutation:parent-mounted'
157
- });
158
- continue;
159
- }
160
- cell.head = {
161
- ...heatmapEntryData,
162
- source: heatmapEntryData.source || null
163
- };
164
- if (previousEntry !== null) {
165
- cell.previousEntries.push(previousEntry);
166
- }
167
- }
168
- }
169
- }
170
- // Every 100 events processed
171
- // we give the browser the power
172
- // to process any other high priority task
173
- if (i % 100 === 0) {
174
- await taskYield();
175
- }
176
- }
177
- }
178
- async getVCPercentMetrics(vcPercentCheckpoint) {
179
- const sortedCheckpoints = [...vcPercentCheckpoint].sort((a, b) => a - b);
180
- const flattenHeatmap = this.map.flat();
181
- const totalCells = flattenHeatmap.length;
182
- const timestampMap = new Map();
183
- for (let i = 0; i < flattenHeatmap.length; i++) {
184
- var _cellHead$time, _timestampMap$get;
185
- const cell = flattenHeatmap[i];
186
- const cellHead = cell.head;
187
- const timestamp = Math.trunc((_cellHead$time = cellHead === null || cellHead === void 0 ? void 0 : cellHead.time) !== null && _cellHead$time !== void 0 ? _cellHead$time : 0);
188
- const elementName = cellHead === null || cellHead === void 0 ? void 0 : cellHead.elementName;
189
- const curr = (_timestampMap$get = timestampMap.get(timestamp)) !== null && _timestampMap$get !== void 0 ? _timestampMap$get : {
190
- cellCount: 0,
191
- domElements: new Set()
192
- };
193
- curr.cellCount += 1;
194
- if (elementName) {
195
- curr.domElements.add(elementName);
196
- }
197
- timestampMap.set(timestamp, curr);
198
-
199
- // Every 10000 heatmap entries processed
200
- // we give the browser the power
201
- // to process any other high priority task
202
- if (i > 10000 && i % 10000 === 0) {
203
- await taskYield();
204
- }
205
- }
206
- const sortedTimings = [...timestampMap.keys()].sort((a, b) => a - b);
207
- let totalCellPainted = 0;
208
- const result = {};
209
- let domElementsBuffer = new Set();
210
- for (let i = 0; i < sortedTimings.length; i++) {
211
- const timestamp = sortedTimings[i];
212
- const timestampInfo = timestampMap.get(timestamp);
213
- if (!timestampInfo) {
214
- throw new Error('unexpected timestampInfo not found');
215
- }
216
- const {
217
- cellCount,
218
- domElements
219
- } = timestampInfo;
220
- totalCellPainted += cellCount;
221
- const currVCRatio = totalCellPainted / totalCells;
222
- const currVCPercent = Math.round(currVCRatio * 100);
223
- domElements.forEach(domElement => {
224
- domElementsBuffer.add(domElement);
225
- });
226
- let matchesAnyCheckpoints = false;
227
- while (sortedCheckpoints.length > 0 && currVCPercent >= sortedCheckpoints[0]) {
228
- const checkpoint = sortedCheckpoints.shift();
229
- const domElements = [...domElementsBuffer];
230
- if (!checkpoint) {
231
- break;
232
- }
233
- matchesAnyCheckpoints = true;
234
- result[checkpoint.toString()] = {
235
- t: timestamp,
236
- e: domElements
237
- };
238
- }
239
- if (matchesAnyCheckpoints) {
240
- domElementsBuffer.clear();
241
- }
242
- if (i % 500 === 0) {
243
- await taskYield();
244
- }
245
- }
246
- return result;
247
- }
248
- }
@@ -1,263 +0,0 @@
1
- import isViewportEntryData from '../../utils/is-viewport-entry-data';
2
- import taskYield from '../../utils/task-yield';
3
- const MAX_HEATMAP_SIZE = 1000;
4
- function createEmptyHeatmapEntry() {
5
- return {
6
- head: null,
7
- previousEntries: []
8
- };
9
- }
10
- function createEmptyMap(heatmapWidth, heatmapHeight) {
11
- return Array.from({
12
- length: heatmapHeight
13
- }).map(() => Array.from({
14
- length: heatmapWidth
15
- }).map(createEmptyHeatmapEntry));
16
- }
17
- function isRectInside(a, b) {
18
- if (!a || !b) {
19
- return false;
20
- }
21
-
22
- // Check if all corners of rectangle a are within the bounds of rectangle b
23
- return a.left >= b.left && a.right <= b.right && a.top >= b.top && a.bottom <= b.bottom;
24
- }
25
- class Heatmap {
26
- /**
27
- * Heatmap Width
28
- */
29
-
30
- /**
31
- * Heatmap Height
32
- */
33
-
34
- /**
35
- * Heatmap Area (width * height)
36
- */
37
-
38
- constructor({
39
- viewport,
40
- heatmapSize
41
- }) {
42
- // TODO timeOrigin? do we need? for SSR??
43
- this.viewport = viewport;
44
- const safeSize = Math.min(heatmapSize, MAX_HEATMAP_SIZE);
45
- if (viewport.width === 0 || viewport.height === 0) {
46
- this.width = safeSize;
47
- this.height = safeSize;
48
- this.scaleX = 1;
49
- this.scaleY = 1;
50
- this.heatmapAreaSize = 0;
51
- this.map = createEmptyMap(safeSize, safeSize);
52
- return;
53
- }
54
- const aspectRatio = viewport.width / viewport.height;
55
- if (aspectRatio > 1) {
56
- // Landscape orientation
57
- this.width = safeSize;
58
- this.height = Math.round(safeSize / aspectRatio);
59
- } else {
60
- // Portrait orientation
61
- this.width = safeSize;
62
- this.height = Math.round(safeSize * aspectRatio);
63
- }
64
- this.scaleX = this.width / viewport.width;
65
- this.scaleY = this.height / viewport.height;
66
- this.heatmapAreaSize = this.width * this.height;
67
- this.map = createEmptyMap(this.width, this.height);
68
- }
69
- getHeatmap() {
70
- return this.map;
71
- }
72
- getCell(row, col) {
73
- var _this$map$row;
74
- return (_this$map$row = this.map[row]) === null || _this$map$row === void 0 ? void 0 : _this$map$row[col];
75
- }
76
-
77
- /**
78
- * Map Dom Rect to Heatmap Rect, rounded up to occupy full cell.
79
- * @param rect DOM Rect
80
- * @returns
81
- */
82
- mapDOMRectToHeatmap(rect) {
83
- const scaledX = rect.x * this.scaleX;
84
- const scaledY = rect.y * this.scaleY;
85
- const scaledWidth = rect.width * this.scaleX;
86
- const scaledHeight = rect.height * this.scaleY;
87
- return {
88
- left: Math.floor(scaledX),
89
- right: Math.ceil(scaledX + scaledWidth),
90
- top: Math.floor(scaledY),
91
- bottom: Math.ceil(scaledY + scaledHeight)
92
- };
93
- }
94
-
95
- /**
96
- * Calculate the ratio of a HeatmapRect compared to the full heatmap
97
- *
98
- * This function determines what fraction of the heatmap is covered by the given heatmap rectangle.
99
- *
100
- * @param rect
101
- */
102
- getRatio(rect) {
103
- if (this.viewport.width === 0 || this.viewport.height === 0) {
104
- return 0;
105
- }
106
- const {
107
- right,
108
- left,
109
- bottom,
110
- top
111
- } = rect;
112
- const rectWidth = right - left;
113
- const rectHeight = bottom - top;
114
- const rectArea = rectWidth * rectHeight;
115
- const ratio = rectArea / this.heatmapAreaSize;
116
- if (ratio > 1) {
117
- return 1;
118
- }
119
- return ratio;
120
- }
121
- async applyEntriesToHeatmap(entries) {
122
- for (let i = 0; i < entries.length; i++) {
123
- const entry = entries[i];
124
- const {
125
- time,
126
- type,
127
- data
128
- } = entry;
129
- if (isViewportEntryData(data)) {
130
- const rect = this.mapDOMRectToHeatmap(data.rect);
131
- const ratio = this.getRatio(rect);
132
- const heatmapEntryData = {
133
- time,
134
- elementName: data.elementName,
135
- ratio: ratio !== null && ratio !== void 0 ? ratio : null,
136
- rect,
137
- source: type
138
- };
139
- const roundedTop = Math.floor(rect.top);
140
- const roundedBottom = Math.min(rect.bottom, this.height);
141
- const roundedLeft = Math.floor(rect.left);
142
- const roundedRight = Math.min(rect.right, this.width);
143
- for (let row = roundedTop; row < roundedBottom; row++) {
144
- for (let col = roundedLeft; col < roundedRight; col++) {
145
- const cell = this.getCell(row, col);
146
- if (!cell) {
147
- continue;
148
- }
149
- const previousEntry = cell.head;
150
-
151
- // When elements are added at the same time
152
- // we try to keep the inner element changes as the head
153
- if ((previousEntry === null || previousEntry === void 0 ? void 0 : previousEntry.time) === entry.time && isRectInside(previousEntry.rect, heatmapEntryData.rect)) {
154
- cell.previousEntries.push({
155
- ...heatmapEntryData,
156
- source: 'mutation:parent-mounted'
157
- });
158
- continue;
159
- }
160
- cell.head = {
161
- ...heatmapEntryData,
162
- source: heatmapEntryData.source || null
163
- };
164
- if (previousEntry !== null) {
165
- cell.previousEntries.push(previousEntry);
166
- }
167
- }
168
- }
169
- }
170
- // Every 100 events processed
171
- // we give the browser the power
172
- // to process any other high priority task
173
- if (i % 100 === 0) {
174
- await taskYield();
175
- }
176
- }
177
- }
178
- async getVCPercentMetrics(vcPercentCheckpoint, startTime) {
179
- const sortedCheckpoints = [...vcPercentCheckpoint].sort((a, b) => a - b);
180
- const flattenHeatmap = this.map.flat();
181
- const totalCells = flattenHeatmap.length;
182
- const timestampMap = new Map();
183
- for (let i = 0; i < flattenHeatmap.length; i++) {
184
- var _cellHead$time, _timestampMap$get;
185
- const cell = flattenHeatmap[i];
186
- const cellHead = cell.head;
187
- const timestamp = Math.trunc((_cellHead$time = cellHead === null || cellHead === void 0 ? void 0 : cellHead.time) !== null && _cellHead$time !== void 0 ? _cellHead$time : 0);
188
- const elementName = cellHead === null || cellHead === void 0 ? void 0 : cellHead.elementName;
189
- const curr = (_timestampMap$get = timestampMap.get(timestamp)) !== null && _timestampMap$get !== void 0 ? _timestampMap$get : {
190
- cellCount: 0,
191
- domElements: new Set()
192
- };
193
- curr.cellCount += 1;
194
- if (elementName) {
195
- curr.domElements.add(elementName);
196
- }
197
- timestampMap.set(timestamp, curr);
198
-
199
- // Every 10000 heatmap entries processed
200
- // we give the browser the power
201
- // to process any other high priority task
202
- if (i > 10000 && i % 10000 === 0) {
203
- await taskYield();
204
- }
205
- }
206
- const sortedTimings = [...timestampMap.keys()].sort((a, b) => a - b);
207
- let totalCellPainted = 0;
208
- const result = {};
209
- let domElementsBuffer = new Set();
210
- for (let i = 0; i < sortedTimings.length; i++) {
211
- const timestamp = sortedTimings[i];
212
- const timestampInfo = timestampMap.get(timestamp);
213
- if (!timestampInfo) {
214
- throw new Error('unexpected timestampInfo not found');
215
- }
216
- const {
217
- cellCount,
218
- domElements
219
- } = timestampInfo;
220
- totalCellPainted += cellCount;
221
- const currVCRatio = totalCellPainted / totalCells;
222
- const currVCPercent = Math.round(currVCRatio * 100);
223
- domElements.forEach(domElement => {
224
- domElementsBuffer.add(domElement);
225
- });
226
- let matchesAnyCheckpoints = false;
227
- while (sortedCheckpoints.length > 0 && currVCPercent >= sortedCheckpoints[0]) {
228
- const checkpoint = sortedCheckpoints.shift();
229
- const domElements = [...domElementsBuffer];
230
- if (!checkpoint) {
231
- break;
232
- }
233
- matchesAnyCheckpoints = true;
234
- result[checkpoint.toString()] = {
235
- t: Math.round(timestamp - startTime),
236
- e: domElements
237
- };
238
- }
239
- if (matchesAnyCheckpoints) {
240
- domElementsBuffer.clear();
241
- }
242
- if (i % 500 === 0) {
243
- await taskYield();
244
- }
245
- }
246
- return result;
247
- }
248
- }
249
- async function calculateTTVCPercentiles({
250
- orderedEntries,
251
- viewport,
252
- percentiles,
253
- startTime
254
- }) {
255
- const heatmap = new Heatmap({
256
- viewport,
257
- heatmapSize: 200
258
- });
259
- await heatmap.applyEntriesToHeatmap(orderedEntries);
260
- const vcDetails = await heatmap.getVCPercentMetrics(percentiles, startTime);
261
- return vcDetails;
262
- }
263
- export default calculateTTVCPercentiles;
@@ -1,99 +0,0 @@
1
- /**
2
- * Calculate the union areas of all rectangles using Sweep Line Algorithm
3
- *
4
- * Reference:
5
- * https://en.wikipedia.org/wiki/Sweep_line_algorithm
6
- * https://www.hackerearth.com/practice/math/geometry/line-sweep-technique/tutorial/#:~:text=A%20sweep%20line%20is%20an,order%20to%20discretize%20the%20sweep.
7
- *
8
- * @param rectangles
9
- * @returns
10
- */
11
- function calculateUnionArea(rectangles) {
12
- // Step 1: Create sweep line events
13
- const events = createSweepLineEvents(rectangles);
14
-
15
- // Step 2: Process events to calculate total area
16
- const activeIntervals = new Map();
17
- let totalArea = 0;
18
- let previousX = 0;
19
- for (const event of events) {
20
- // Calculate height at current x-position
21
- const currentHeight = calculateActiveHeight(activeIntervals);
22
- // Add area since last x-position
23
- totalArea += currentHeight * (event.x - previousX);
24
- // Update x-position
25
- previousX = event.x;
26
- // Update active intervals
27
- updateActiveIntervals(activeIntervals, event);
28
- }
29
- return totalArea;
30
- }
31
- export default calculateUnionArea;
32
- function createSweepLineEvents(rectangles) {
33
- const events = [];
34
- for (const rect of rectangles) {
35
- // Create start and end events for each rectangle
36
- events.push({
37
- x: rect.left,
38
- type: 'start',
39
- top: rect.top,
40
- bottom: rect.bottom
41
- });
42
- events.push({
43
- x: rect.right,
44
- type: 'end',
45
- top: rect.top,
46
- bottom: rect.bottom
47
- });
48
- }
49
- // Sort events by x-coordinate (and type as tiebreaker)
50
- return events.sort((a, b) => a.x === b.x ? a.type === 'end' ? 1 : -1 : a.x - b.x);
51
- }
52
- function calculateActiveHeight(intervals) {
53
- if (intervals.size === 0) {
54
- return 0;
55
- }
56
- // Get all unique y-coordinates
57
- const coordinates = [];
58
- for (const [key] of intervals) {
59
- const [start, end] = key.split(',').map(Number);
60
- coordinates.push(start, end);
61
- }
62
- // Sort coordinates
63
- const sortedCoords = [...new Set(coordinates)].sort((a, b) => a - b);
64
- let totalHeight = 0;
65
- // Check each segment between consecutive coordinates
66
- for (let i = 0; i < sortedCoords.length; i++) {
67
- const y1 = sortedCoords[i];
68
- const y2 = sortedCoords[i + 1];
69
- // Check if this segment is covered by any active interval
70
- let covered = false;
71
- for (const [key, count] of intervals) {
72
- if (count <= 0) {
73
- continue;
74
- }
75
- const [start, end] = key.split(',').map(Number);
76
- if (start <= y1 && end >= y2) {
77
- covered = true;
78
- break;
79
- }
80
- }
81
- if (covered) {
82
- totalHeight += y2 - y1;
83
- }
84
- }
85
- return totalHeight;
86
- }
87
- function updateActiveIntervals(intervals, event) {
88
- const key = `${event.top},${event.bottom}`;
89
- if (event.type === 'start') {
90
- intervals.set(key, (intervals.get(key) || 0) + 1);
91
- } else {
92
- const count = intervals.get(key) || 0;
93
- if (count > 1) {
94
- intervals.set(key, count - 1);
95
- } else {
96
- intervals.delete(key);
97
- }
98
- }
99
- }
@@ -1,60 +0,0 @@
1
- import isViewportEntryData from '../../utils/is-viewport-entry-data';
2
- import taskYield from '../../utils/task-yield';
3
- import calculateUnionArea from './calc-union-area';
4
- async function calculateTTVCPercentiles({
5
- orderedEntries,
6
- viewport,
7
- percentiles,
8
- startTime
9
- }) {
10
- const sortedPercentiles = [...percentiles].sort((a, b) => a - b);
11
- const viewportArea = viewport.width * viewport.height;
12
- const checkpoints = {};
13
- let activeRects = orderedEntries.filter(e => isViewportEntryData(e.data)).map(e => e.data.rect);
14
- const removeActiveRect = rectToRemove => {
15
- const index = activeRects.indexOf(rectToRemove);
16
- // Check if the element exists in the array
17
- if (index !== -1) {
18
- // Remove the element at the found index
19
- activeRects.splice(index, 1);
20
- }
21
- };
22
- let domElementsBuffer = new Set();
23
- for (let i = 0; i < orderedEntries.length; i++) {
24
- const iEntry = orderedEntries[i];
25
- const iEntryData = iEntry.data;
26
- if (!isViewportEntryData(iEntryData)) {
27
- continue;
28
- }
29
- const {
30
- rect,
31
- elementName
32
- } = iEntryData;
33
- domElementsBuffer.add(elementName);
34
- removeActiveRect(rect);
35
- const exclusionArea = calculateUnionArea(activeRects);
36
- const currentArea = viewportArea - exclusionArea;
37
- const currVCPercent = Math.round(currentArea / viewportArea * 100);
38
- let matchesAnyCheckpoints = false;
39
- while (sortedPercentiles.length > 0 && currVCPercent >= sortedPercentiles[0]) {
40
- const checkpoint = sortedPercentiles.shift();
41
- const domElements = [...domElementsBuffer];
42
- if (!checkpoint) {
43
- break;
44
- }
45
- matchesAnyCheckpoints = true;
46
- checkpoints[checkpoint.toString()] = {
47
- t: Math.round(iEntry.time - startTime),
48
- e: domElements
49
- };
50
- }
51
- if (matchesAnyCheckpoints) {
52
- domElementsBuffer.clear();
53
- }
54
- if (i % 500 === 0) {
55
- await taskYield();
56
- }
57
- }
58
- return checkpoints;
59
- }
60
- export default calculateTTVCPercentiles;