@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.
- package/CHANGELOG.md +16 -0
- package/dist/cjs/create-experimental-interaction-metrics-payload/index.js +2 -1
- package/dist/cjs/create-payload/utils/get-vc-metrics.js +2 -1
- package/dist/cjs/vc/index.js +4 -2
- package/dist/cjs/vc/vc-observer/getVCRevisionDebugDetails.js +41 -0
- package/dist/cjs/vc/vc-observer/index.js +63 -33
- package/dist/cjs/vc/vc-observer/observers/index.js +3 -2
- package/dist/cjs/vc/vc-observer/observers/ssr-placeholders/index.js +38 -12
- package/dist/cjs/vc/vc-observer/observers/ssr-placeholders/ssr-scripts/collectSSRPlaceholderDimensions.js +9 -0
- package/dist/cjs/vc/vc-observer-new/index.js +13 -7
- package/dist/cjs/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +211 -36
- package/dist/cjs/vc/vc-observer-new/metric-calculator/fy25_03/index.js +4 -4
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +94 -4
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.js +108 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +16 -57
- package/dist/cjs/vc/vc-observer-new/viewport-observer/index.js +15 -5
- package/dist/cjs/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +3 -1
- package/dist/es2019/create-experimental-interaction-metrics-payload/index.js +2 -1
- package/dist/es2019/create-payload/utils/get-vc-metrics.js +1 -0
- package/dist/es2019/vc/index.js +4 -2
- package/dist/es2019/vc/vc-observer/getVCRevisionDebugDetails.js +32 -0
- package/dist/es2019/vc/vc-observer/index.js +36 -1
- package/dist/es2019/vc/vc-observer/observers/index.js +2 -1
- package/dist/es2019/vc/vc-observer/observers/ssr-placeholders/index.js +38 -13
- package/dist/es2019/vc/vc-observer/observers/ssr-placeholders/ssr-scripts/collectSSRPlaceholderDimensions.js +9 -1
- package/dist/es2019/vc/vc-observer-new/index.js +12 -6
- package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +115 -17
- package/dist/es2019/vc/vc-observer-new/metric-calculator/fy25_03/index.js +4 -4
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +44 -1
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.js +75 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +2 -20
- package/dist/es2019/vc/vc-observer-new/viewport-observer/index.js +15 -5
- package/dist/es2019/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +3 -1
- package/dist/esm/create-experimental-interaction-metrics-payload/index.js +2 -1
- package/dist/esm/create-payload/utils/get-vc-metrics.js +2 -1
- package/dist/esm/vc/index.js +4 -2
- package/dist/esm/vc/vc-observer/getVCRevisionDebugDetails.js +35 -0
- package/dist/esm/vc/vc-observer/index.js +63 -33
- package/dist/esm/vc/vc-observer/observers/index.js +3 -2
- package/dist/esm/vc/vc-observer/observers/ssr-placeholders/index.js +38 -12
- package/dist/esm/vc/vc-observer/observers/ssr-placeholders/ssr-scripts/collectSSRPlaceholderDimensions.js +9 -0
- package/dist/esm/vc/vc-observer-new/index.js +13 -7
- package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +211 -36
- package/dist/esm/vc/vc-observer-new/metric-calculator/fy25_03/index.js +4 -4
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +94 -5
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.js +106 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +2 -55
- package/dist/esm/vc/vc-observer-new/viewport-observer/index.js +15 -5
- package/dist/esm/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +3 -1
- package/dist/types/config/index.d.ts +1 -0
- package/dist/types/vc/types.d.ts +2 -0
- package/dist/types/vc/vc-observer/getVCRevisionDebugDetails.d.ts +30 -0
- package/dist/types/vc/vc-observer/index.d.ts +1 -1
- package/dist/types/vc/vc-observer/observers/index.d.ts +3 -0
- package/dist/types/vc/vc-observer/observers/ssr-placeholders/index.d.ts +4 -1
- package/dist/types/vc/vc-observer/observers/ssr-placeholders/ssr-scripts/collectSSRPlaceholderDimensions.d.ts +1 -1
- package/dist/types/vc/vc-observer-new/index.d.ts +2 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +4 -1
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +5 -1
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.d.ts +1 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/index.d.ts +2 -4
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +20 -2
- package/dist/types/vc/vc-observer-new/metric-calculator/types.d.ts +2 -0
- package/dist/types/vc/vc-observer-new/types.d.ts +5 -1
- package/dist/types/vc/vc-observer-new/viewport-observer/mutation-observer/index.d.ts +2 -0
- package/dist/types/vc/vc-observer-new/viewport-observer/types.d.ts +2 -0
- package/dist/types-ts4.5/config/index.d.ts +1 -0
- package/dist/types-ts4.5/vc/types.d.ts +2 -0
- package/dist/types-ts4.5/vc/vc-observer/getVCRevisionDebugDetails.d.ts +30 -0
- package/dist/types-ts4.5/vc/vc-observer/index.d.ts +1 -1
- package/dist/types-ts4.5/vc/vc-observer/observers/index.d.ts +3 -0
- package/dist/types-ts4.5/vc/vc-observer/observers/ssr-placeholders/index.d.ts +4 -1
- package/dist/types-ts4.5/vc/vc-observer/observers/ssr-placeholders/ssr-scripts/collectSSRPlaceholderDimensions.d.ts +1 -1
- package/dist/types-ts4.5/vc/vc-observer-new/index.d.ts +2 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +4 -1
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +5 -1
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.d.ts +1 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/index.d.ts +2 -4
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +20 -2
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/types.d.ts +2 -0
- package/dist/types-ts4.5/vc/vc-observer-new/types.d.ts +5 -1
- package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/mutation-observer/index.d.ts +2 -0
- package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/types.d.ts +2 -0
- package/package.json +4 -1
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +0 -367
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +0 -398
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +0 -5
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +0 -152
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +0 -108
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +0 -248
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +0 -263
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +0 -1
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +0 -99
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +0 -60
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +0 -361
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +0 -391
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +0 -1
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +0 -145
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +0 -101
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.d.ts +0 -39
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.d.ts +0 -10
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.d.ts +0 -43
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.d.ts +0 -12
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.d.ts +0 -25
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.d.ts +0 -39
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.d.ts +0 -10
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.d.ts +0 -43
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.d.ts +0 -12
- 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 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -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
|
-
}
|
package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js
DELETED
|
@@ -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;
|