@atlaskit/react-ufo 3.1.4 → 3.3.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 (179) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +11 -0
  3. package/dist/cjs/create-experimental-interaction-metrics-payload/index.js +52 -25
  4. package/dist/cjs/create-payload/index.js +290 -231
  5. package/dist/cjs/interaction-metrics/index.js +61 -23
  6. package/dist/cjs/interaction-metrics/post-interaction-log.js +63 -34
  7. package/dist/cjs/interaction-metrics-init/index.js +26 -7
  8. package/dist/cjs/segment/schedule-on-paint.js +35 -0
  9. package/dist/cjs/segment/segment.js +10 -1
  10. package/dist/cjs/vc/index.js +105 -1
  11. package/dist/cjs/vc/types.js +5 -0
  12. package/dist/cjs/vc/vc-observer/index.js +198 -208
  13. package/dist/cjs/vc/vc-observer-new/entries-timeline/index.js +56 -0
  14. package/dist/cjs/vc/vc-observer-new/get-element-name.js +68 -0
  15. package/dist/cjs/vc/vc-observer-new/index.js +132 -0
  16. package/dist/cjs/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +75 -0
  17. package/dist/cjs/vc/vc-observer-new/metric-calculator/fy25_03/index.js +60 -0
  18. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.js +274 -0
  19. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +151 -0
  20. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +367 -0
  21. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +397 -0
  22. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +5 -0
  23. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +61 -0
  24. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +151 -0
  25. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +107 -0
  26. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/types.js +5 -0
  27. package/dist/cjs/vc/vc-observer-new/metric-calculator/types.js +5 -0
  28. package/dist/cjs/vc/vc-observer-new/metric-calculator/utils/get-viewport-height.js +16 -0
  29. package/dist/cjs/vc/vc-observer-new/metric-calculator/utils/get-viewport-width.js +16 -0
  30. package/dist/cjs/vc/vc-observer-new/metric-calculator/utils/is-viewport-entry-data.js +16 -0
  31. package/dist/cjs/vc/vc-observer-new/metric-calculator/utils/task-yield.js +45 -0
  32. package/dist/cjs/vc/vc-observer-new/types.js +5 -0
  33. package/dist/cjs/vc/vc-observer-new/viewport-observer/index.js +195 -0
  34. package/dist/cjs/vc/vc-observer-new/viewport-observer/intersection-observer/index.js +71 -0
  35. package/dist/cjs/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +65 -0
  36. package/dist/cjs/vc/vc-observer-new/viewport-observer/performance-observer/index.js +58 -0
  37. package/dist/cjs/vc/vc-observer-new/viewport-observer/types.js +1 -0
  38. package/dist/cjs/vc/vc-observer-new/window-event-observer/index.js +54 -0
  39. package/dist/es2019/create-experimental-interaction-metrics-payload/index.js +2 -2
  40. package/dist/es2019/create-payload/index.js +8 -7
  41. package/dist/es2019/interaction-metrics/index.js +3 -3
  42. package/dist/es2019/interaction-metrics/post-interaction-log.js +5 -5
  43. package/dist/es2019/interaction-metrics-init/index.js +26 -7
  44. package/dist/es2019/segment/schedule-on-paint.js +29 -0
  45. package/dist/es2019/segment/segment.js +9 -1
  46. package/dist/es2019/vc/index.js +56 -1
  47. package/dist/es2019/vc/types.js +1 -0
  48. package/dist/es2019/vc/vc-observer/index.js +1 -4
  49. package/dist/es2019/vc/vc-observer-new/entries-timeline/index.js +34 -0
  50. package/dist/es2019/vc/vc-observer-new/get-element-name.js +62 -0
  51. package/dist/es2019/vc/vc-observer-new/index.js +98 -0
  52. package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +38 -0
  53. package/dist/es2019/vc/vc-observer-new/metric-calculator/fy25_03/index.js +39 -0
  54. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.js +194 -0
  55. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +69 -0
  56. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +248 -0
  57. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +261 -0
  58. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +1 -0
  59. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +19 -0
  60. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +98 -0
  61. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +58 -0
  62. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/types.js +1 -0
  63. package/dist/es2019/vc/vc-observer-new/metric-calculator/types.js +1 -0
  64. package/dist/es2019/vc/vc-observer-new/metric-calculator/utils/get-viewport-height.js +9 -0
  65. package/dist/es2019/vc/vc-observer-new/metric-calculator/utils/get-viewport-width.js +9 -0
  66. package/dist/es2019/vc/vc-observer-new/metric-calculator/utils/is-viewport-entry-data.js +10 -0
  67. package/dist/es2019/vc/vc-observer-new/metric-calculator/utils/task-yield.js +17 -0
  68. package/dist/es2019/vc/vc-observer-new/types.js +1 -0
  69. package/dist/es2019/vc/vc-observer-new/viewport-observer/index.js +168 -0
  70. package/dist/es2019/vc/vc-observer-new/viewport-observer/intersection-observer/index.js +65 -0
  71. package/dist/es2019/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +48 -0
  72. package/dist/es2019/vc/vc-observer-new/viewport-observer/performance-observer/index.js +41 -0
  73. package/dist/es2019/vc/vc-observer-new/viewport-observer/types.js +0 -0
  74. package/dist/es2019/vc/vc-observer-new/window-event-observer/index.js +36 -0
  75. package/dist/esm/create-experimental-interaction-metrics-payload/index.js +52 -25
  76. package/dist/esm/create-payload/index.js +290 -231
  77. package/dist/esm/interaction-metrics/index.js +61 -23
  78. package/dist/esm/interaction-metrics/post-interaction-log.js +63 -34
  79. package/dist/esm/interaction-metrics-init/index.js +26 -7
  80. package/dist/esm/segment/schedule-on-paint.js +29 -0
  81. package/dist/esm/segment/segment.js +10 -1
  82. package/dist/esm/vc/index.js +104 -1
  83. package/dist/esm/vc/types.js +1 -0
  84. package/dist/esm/vc/vc-observer/index.js +198 -208
  85. package/dist/esm/vc/vc-observer-new/entries-timeline/index.js +50 -0
  86. package/dist/esm/vc/vc-observer-new/get-element-name.js +62 -0
  87. package/dist/esm/vc/vc-observer-new/index.js +126 -0
  88. package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +69 -0
  89. package/dist/esm/vc/vc-observer-new/metric-calculator/fy25_03/index.js +54 -0
  90. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.js +268 -0
  91. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +143 -0
  92. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +361 -0
  93. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +390 -0
  94. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +1 -0
  95. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +54 -0
  96. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +144 -0
  97. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +100 -0
  98. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/types.js +1 -0
  99. package/dist/esm/vc/vc-observer-new/metric-calculator/types.js +1 -0
  100. package/dist/esm/vc/vc-observer-new/metric-calculator/utils/get-viewport-height.js +10 -0
  101. package/dist/esm/vc/vc-observer-new/metric-calculator/utils/get-viewport-width.js +10 -0
  102. package/dist/esm/vc/vc-observer-new/metric-calculator/utils/is-viewport-entry-data.js +10 -0
  103. package/dist/esm/vc/vc-observer-new/metric-calculator/utils/task-yield.js +38 -0
  104. package/dist/esm/vc/vc-observer-new/types.js +1 -0
  105. package/dist/esm/vc/vc-observer-new/viewport-observer/index.js +189 -0
  106. package/dist/esm/vc/vc-observer-new/viewport-observer/intersection-observer/index.js +65 -0
  107. package/dist/esm/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +59 -0
  108. package/dist/esm/vc/vc-observer-new/viewport-observer/performance-observer/index.js +51 -0
  109. package/dist/esm/vc/vc-observer-new/viewport-observer/types.js +0 -0
  110. package/dist/esm/vc/vc-observer-new/window-event-observer/index.js +48 -0
  111. package/dist/types/common/index.d.ts +1 -0
  112. package/dist/types/create-experimental-interaction-metrics-payload/index.d.ts +3 -2
  113. package/dist/types/create-payload/index.d.ts +4 -4
  114. package/dist/types/interaction-metrics/post-interaction-log.d.ts +3 -2
  115. package/dist/types/segment/schedule-on-paint.d.ts +2 -0
  116. package/dist/types/vc/index.d.ts +3 -3
  117. package/dist/types/vc/types.d.ts +34 -0
  118. package/dist/types/vc/vc-observer/index.d.ts +5 -21
  119. package/dist/types/vc/vc-observer-new/entries-timeline/index.d.ts +13 -0
  120. package/dist/types/vc/vc-observer-new/get-element-name.d.ts +8 -0
  121. package/dist/types/vc/vc-observer-new/index.d.ts +18 -0
  122. package/dist/types/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +9 -0
  123. package/dist/types/vc/vc-observer-new/metric-calculator/fy25_03/index.d.ts +7 -0
  124. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.d.ts +91 -0
  125. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +4 -0
  126. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.d.ts +39 -0
  127. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.d.ts +8 -0
  128. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.d.ts +43 -0
  129. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/index.d.ts +3 -0
  130. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.d.ts +11 -0
  131. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.d.ts +23 -0
  132. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +9 -0
  133. package/dist/types/vc/vc-observer-new/metric-calculator/types.d.ts +20 -0
  134. package/dist/types/vc/vc-observer-new/metric-calculator/utils/get-viewport-height.d.ts +1 -0
  135. package/dist/types/vc/vc-observer-new/metric-calculator/utils/get-viewport-width.d.ts +1 -0
  136. package/dist/types/vc/vc-observer-new/metric-calculator/utils/is-viewport-entry-data.d.ts +2 -0
  137. package/dist/types/vc/vc-observer-new/metric-calculator/utils/task-yield.d.ts +1 -0
  138. package/dist/types/vc/vc-observer-new/types.d.ts +21 -0
  139. package/dist/types/vc/vc-observer-new/viewport-observer/index.d.ts +22 -0
  140. package/dist/types/vc/vc-observer-new/viewport-observer/intersection-observer/index.d.ts +30 -0
  141. package/dist/types/vc/vc-observer-new/viewport-observer/mutation-observer/index.d.ts +14 -0
  142. package/dist/types/vc/vc-observer-new/viewport-observer/performance-observer/index.d.ts +14 -0
  143. package/dist/types/vc/vc-observer-new/viewport-observer/types.d.ts +10 -0
  144. package/dist/types/vc/vc-observer-new/window-event-observer/index.d.ts +17 -0
  145. package/dist/types-ts4.5/common/index.d.ts +1 -0
  146. package/dist/types-ts4.5/create-experimental-interaction-metrics-payload/index.d.ts +3 -2
  147. package/dist/types-ts4.5/create-payload/index.d.ts +4 -4
  148. package/dist/types-ts4.5/interaction-metrics/post-interaction-log.d.ts +3 -2
  149. package/dist/types-ts4.5/segment/schedule-on-paint.d.ts +2 -0
  150. package/dist/types-ts4.5/vc/index.d.ts +3 -3
  151. package/dist/types-ts4.5/vc/types.d.ts +34 -0
  152. package/dist/types-ts4.5/vc/vc-observer/index.d.ts +5 -21
  153. package/dist/types-ts4.5/vc/vc-observer-new/entries-timeline/index.d.ts +13 -0
  154. package/dist/types-ts4.5/vc/vc-observer-new/get-element-name.d.ts +8 -0
  155. package/dist/types-ts4.5/vc/vc-observer-new/index.d.ts +18 -0
  156. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +9 -0
  157. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/fy25_03/index.d.ts +7 -0
  158. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.d.ts +91 -0
  159. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +4 -0
  160. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.d.ts +39 -0
  161. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.d.ts +8 -0
  162. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.d.ts +43 -0
  163. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/index.d.ts +3 -0
  164. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.d.ts +11 -0
  165. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.d.ts +23 -0
  166. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +9 -0
  167. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/types.d.ts +20 -0
  168. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/utils/get-viewport-height.d.ts +1 -0
  169. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/utils/get-viewport-width.d.ts +1 -0
  170. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/utils/is-viewport-entry-data.d.ts +2 -0
  171. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/utils/task-yield.d.ts +1 -0
  172. package/dist/types-ts4.5/vc/vc-observer-new/types.d.ts +21 -0
  173. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/index.d.ts +22 -0
  174. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/intersection-observer/index.d.ts +30 -0
  175. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/mutation-observer/index.d.ts +14 -0
  176. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/performance-observer/index.d.ts +14 -0
  177. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/types.d.ts +10 -0
  178. package/dist/types-ts4.5/vc/vc-observer-new/window-event-observer/index.d.ts +17 -0
  179. package/package.json +13 -1
@@ -0,0 +1,261 @@
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) {
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
+ }
249
+ export default async function calculateTTVCPercentiles({
250
+ orderedEntries,
251
+ viewport,
252
+ percentiles
253
+ }) {
254
+ const heatmap = new Heatmap({
255
+ viewport,
256
+ heatmapSize: 200
257
+ });
258
+ await heatmap.applyEntriesToHeatmap(orderedEntries);
259
+ const vcDetails = await heatmap.getVCPercentMetrics(percentiles);
260
+ return vcDetails;
261
+ }
@@ -0,0 +1,19 @@
1
+ import calcUsingCanvas from './canvas-heatmap';
2
+ import caclUsingOldHeatmap from './heatmap';
3
+ import calcUsingRectSweepingLine from './rect-sweeping-line';
4
+ export default async function calculateTTVCPercentiles(arg) {
5
+ const algo = 'canvas_heatmap';
6
+ if (algo === 'canvas_heatmap') {
7
+ const vcDetails = await calcUsingCanvas(arg);
8
+ return vcDetails;
9
+ }
10
+ if (algo === 'rect_sweeping') {
11
+ const vcDetails = await calcUsingRectSweepingLine(arg);
12
+ return vcDetails;
13
+ }
14
+ if (algo === 'old_heatmap') {
15
+ const vcDetails = await caclUsingOldHeatmap(arg);
16
+ return vcDetails;
17
+ }
18
+ throw new Error('unexpected Error algo not chosen correctly');
19
+ }
@@ -0,0 +1,98 @@
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
+ export default 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
+ function createSweepLineEvents(rectangles) {
32
+ const events = [];
33
+ for (const rect of rectangles) {
34
+ // Create start and end events for each rectangle
35
+ events.push({
36
+ x: rect.left,
37
+ type: 'start',
38
+ top: rect.top,
39
+ bottom: rect.bottom
40
+ });
41
+ events.push({
42
+ x: rect.right,
43
+ type: 'end',
44
+ top: rect.top,
45
+ bottom: rect.bottom
46
+ });
47
+ }
48
+ // Sort events by x-coordinate (and type as tiebreaker)
49
+ return events.sort((a, b) => a.x === b.x ? a.type === 'end' ? 1 : -1 : a.x - b.x);
50
+ }
51
+ function calculateActiveHeight(intervals) {
52
+ if (intervals.size === 0) {
53
+ return 0;
54
+ }
55
+ // Get all unique y-coordinates
56
+ const coordinates = [];
57
+ for (const [key] of intervals) {
58
+ const [start, end] = key.split(',').map(Number);
59
+ coordinates.push(start, end);
60
+ }
61
+ // Sort coordinates
62
+ const sortedCoords = [...new Set(coordinates)].sort((a, b) => a - b);
63
+ let totalHeight = 0;
64
+ // Check each segment between consecutive coordinates
65
+ for (let i = 0; i < sortedCoords.length; i++) {
66
+ const y1 = sortedCoords[i];
67
+ const y2 = sortedCoords[i + 1];
68
+ // Check if this segment is covered by any active interval
69
+ let covered = false;
70
+ for (const [key, count] of intervals) {
71
+ if (count <= 0) {
72
+ continue;
73
+ }
74
+ const [start, end] = key.split(',').map(Number);
75
+ if (start <= y1 && end >= y2) {
76
+ covered = true;
77
+ break;
78
+ }
79
+ }
80
+ if (covered) {
81
+ totalHeight += y2 - y1;
82
+ }
83
+ }
84
+ return totalHeight;
85
+ }
86
+ function updateActiveIntervals(intervals, event) {
87
+ const key = `${event.top},${event.bottom}`;
88
+ if (event.type === 'start') {
89
+ intervals.set(key, (intervals.get(key) || 0) + 1);
90
+ } else {
91
+ const count = intervals.get(key) || 0;
92
+ if (count > 1) {
93
+ intervals.set(key, count - 1);
94
+ } else {
95
+ intervals.delete(key);
96
+ }
97
+ }
98
+ }
@@ -0,0 +1,58 @@
1
+ import isViewportEntryData from '../../utils/is-viewport-entry-data';
2
+ import taskYield from '../../utils/task-yield';
3
+ import calculateUnionArea from './calc-union-area';
4
+ export default async function calculateTTVCPercentiles({
5
+ orderedEntries,
6
+ viewport,
7
+ percentiles
8
+ }) {
9
+ const sortedPercentiles = [...percentiles].sort((a, b) => a - b);
10
+ const viewportArea = viewport.width * viewport.height;
11
+ const checkpoints = {};
12
+ let activeRects = orderedEntries.filter(e => isViewportEntryData(e.data)).map(e => e.data.rect);
13
+ const removeActiveRect = rectToRemove => {
14
+ const index = activeRects.indexOf(rectToRemove);
15
+ // Check if the element exists in the array
16
+ if (index !== -1) {
17
+ // Remove the element at the found index
18
+ activeRects.splice(index, 1);
19
+ }
20
+ };
21
+ let domElementsBuffer = new Set();
22
+ for (let i = 0; i < orderedEntries.length; i++) {
23
+ const iEntry = orderedEntries[i];
24
+ const iEntryData = iEntry.data;
25
+ if (!isViewportEntryData(iEntryData)) {
26
+ continue;
27
+ }
28
+ const {
29
+ rect,
30
+ elementName
31
+ } = iEntryData;
32
+ domElementsBuffer.add(elementName);
33
+ removeActiveRect(rect);
34
+ const exclusionArea = calculateUnionArea(activeRects);
35
+ const currentArea = viewportArea - exclusionArea;
36
+ const currVCPercent = Math.round(currentArea / viewportArea * 100);
37
+ let matchesAnyCheckpoints = false;
38
+ while (sortedPercentiles.length > 0 && currVCPercent >= sortedPercentiles[0]) {
39
+ const checkpoint = sortedPercentiles.shift();
40
+ const domElements = [...domElementsBuffer];
41
+ if (!checkpoint) {
42
+ break;
43
+ }
44
+ matchesAnyCheckpoints = true;
45
+ checkpoints[checkpoint.toString()] = {
46
+ t: iEntry.time,
47
+ e: domElements
48
+ };
49
+ }
50
+ if (matchesAnyCheckpoints) {
51
+ domElementsBuffer.clear();
52
+ }
53
+ if (i % 500 === 0) {
54
+ await taskYield();
55
+ }
56
+ }
57
+ return checkpoints;
58
+ }
@@ -0,0 +1,9 @@
1
+ export default function getViewportHeight(document = window.document) {
2
+ let documentHeight;
3
+ try {
4
+ documentHeight = document.documentElement.clientHeight || 0;
5
+ } catch (e) {
6
+ documentHeight = 0;
7
+ }
8
+ return Math.max(documentHeight, window.innerHeight || 0);
9
+ }
@@ -0,0 +1,9 @@
1
+ export default function getViewportWidth(document = window.document) {
2
+ let documentWidth;
3
+ try {
4
+ documentWidth = document.documentElement.clientWidth || 0;
5
+ } catch (e) {
6
+ documentWidth = 0;
7
+ }
8
+ return Math.max(documentWidth, window.innerWidth || 0);
9
+ }
@@ -0,0 +1,10 @@
1
+ export default function isViewportEntryData(data) {
2
+ if (data) {
3
+ const hasElementName = typeof data.elementName === 'string';
4
+ const hasRect = typeof data.rect !== 'undefined';
5
+ if (hasRect && hasElementName) {
6
+ return true;
7
+ }
8
+ }
9
+ return false;
10
+ }
@@ -0,0 +1,17 @@
1
+ // See https://developer.mozilla.org/en-US/docs/Web/API/Prioritized_Task_Scheduling_API
2
+ export default async function taskYield() {
3
+ // This is using globalThis to allow the yield task to be used outside of a browser env
4
+ if ('scheduler' in globalThis &&
5
+ // @ts-ignore
6
+ 'yield' in globalThis.scheduler) {
7
+ // @ts-ignore
8
+ await scheduler.yield();
9
+ return;
10
+ }
11
+ let resolve = () => {};
12
+ const p = new Promise(a => {
13
+ resolve = a;
14
+ });
15
+ setTimeout(resolve, 0);
16
+ await p;
17
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,168 @@
1
+ import { isContainedWithinMediaWrapper } from '../../vc-observer/media-wrapper/vc-utils';
2
+ import { createIntersectionObserver } from './intersection-observer';
3
+ import createMutationObserver from './mutation-observer';
4
+ import createPerformanceObserver from './performance-observer';
5
+ function isElementVisible(element) {
6
+ if (!(element instanceof HTMLElement)) {
7
+ return true;
8
+ }
9
+ try {
10
+ const visible = element.checkVisibility({
11
+ // @ts-expect-error
12
+ visibilityProperty: true
13
+ });
14
+ return visible;
15
+ } catch (e) {
16
+ // there is no support for checkVisibility
17
+ return true;
18
+ }
19
+ }
20
+ function sameRectSize(a, b) {
21
+ if (!a || !b) {
22
+ return false;
23
+ }
24
+ return a.width === b.width && a.height === b.height;
25
+ }
26
+ function sameRectDimensions(a, b) {
27
+ if (!a || !b) {
28
+ return false;
29
+ }
30
+ return a.width === b.width && a.height === b.height && a.x === b.x && a.y === b.y;
31
+ }
32
+ export default class ViewportObserver {
33
+ constructor({
34
+ onChange
35
+ }) {
36
+ this.mapVisibleNodeRects = new WeakMap();
37
+ this.intersectionObserver = createIntersectionObserver({
38
+ onEntry: ({
39
+ target,
40
+ rect,
41
+ time,
42
+ type,
43
+ mutationData
44
+ }) => {
45
+ const visible = isElementVisible(target);
46
+ const lastElementRect = this.mapVisibleNodeRects.get(target);
47
+ this.mapVisibleNodeRects.set(target, rect);
48
+ onChange({
49
+ time,
50
+ type,
51
+ elementRef: new WeakRef(target),
52
+ visible,
53
+ rect,
54
+ previousRect: lastElementRect,
55
+ mutationData
56
+ });
57
+ }
58
+ });
59
+ this.mutationObserver = createMutationObserver({
60
+ onChildListMutation: ({
61
+ addedNodes,
62
+ removedNodes
63
+ }) => {
64
+ var _removedNodes$map;
65
+ const removedNodeRects = (_removedNodes$map = removedNodes === null || removedNodes === void 0 ? void 0 : removedNodes.map(n => this.mapVisibleNodeRects.get(n))) !== null && _removedNodes$map !== void 0 ? _removedNodes$map : [];
66
+ addedNodes.forEach(addedNode => {
67
+ var _this$intersectionObs3;
68
+ // for (const elem of addedNode.querySelectorAll('*')) {
69
+ // this.intersectionObserver?.watchAndTag(elem, 'mutation:child-element');
70
+ // }
71
+
72
+ const sameDeletedNode = removedNodes.find(n => n.isEqualNode(addedNode));
73
+ if (sameDeletedNode) {
74
+ var _this$intersectionObs;
75
+ (_this$intersectionObs = this.intersectionObserver) === null || _this$intersectionObs === void 0 ? void 0 : _this$intersectionObs.watchAndTag(addedNode, 'mutation:remount');
76
+ return;
77
+ }
78
+ if (isContainedWithinMediaWrapper(addedNode)) {
79
+ var _this$intersectionObs2;
80
+ (_this$intersectionObs2 = this.intersectionObserver) === null || _this$intersectionObs2 === void 0 ? void 0 : _this$intersectionObs2.watchAndTag(addedNode, 'mutation:media');
81
+ return;
82
+ }
83
+ (_this$intersectionObs3 = this.intersectionObserver) === null || _this$intersectionObs3 === void 0 ? void 0 : _this$intersectionObs3.watchAndTag(addedNode, ({
84
+ rect
85
+ }) => {
86
+ const wasDeleted = removedNodeRects.some(nr => sameRectDimensions(nr, rect));
87
+ if (wasDeleted) {
88
+ return 'mutation:element-replacement';
89
+ }
90
+ return 'mutation:element';
91
+ });
92
+ });
93
+ },
94
+ onAttributeMutation: ({
95
+ target,
96
+ attributeName
97
+ }) => {
98
+ var _this$intersectionObs4;
99
+ (_this$intersectionObs4 = this.intersectionObserver) === null || _this$intersectionObs4 === void 0 ? void 0 : _this$intersectionObs4.watchAndTag(target, ({
100
+ target,
101
+ rect
102
+ }) => {
103
+ if (isContainedWithinMediaWrapper(target)) {
104
+ return {
105
+ type: 'mutation:media',
106
+ mutationData: {
107
+ attributeName
108
+ }
109
+ };
110
+ }
111
+ const lastElementRect = this.mapVisibleNodeRects.get(target);
112
+ if (lastElementRect && sameRectSize(rect, lastElementRect)) {
113
+ return {
114
+ type: 'mutation:attribute:no-layout-shift',
115
+ mutationData: {
116
+ attributeName
117
+ }
118
+ };
119
+ }
120
+ return {
121
+ type: 'mutation:attribute',
122
+ mutationData: {
123
+ attributeName
124
+ }
125
+ };
126
+ });
127
+ }
128
+ });
129
+ this.performanceObserver = createPerformanceObserver({
130
+ onLayoutShift: ({
131
+ time,
132
+ changedRects
133
+ }) => {
134
+ for (const changedRect of changedRects) {
135
+ onChange({
136
+ time,
137
+ elementRef: new WeakRef(changedRect.node),
138
+ visible: true,
139
+ rect: changedRect.rect,
140
+ previousRect: changedRect.previousRect,
141
+ type: 'layout-shift'
142
+ });
143
+ }
144
+ }
145
+ });
146
+ }
147
+ start() {
148
+ var _this$mutationObserve, _this$performanceObse;
149
+ (_this$mutationObserve = this.mutationObserver) === null || _this$mutationObserve === void 0 ? void 0 : _this$mutationObserve.observe(document.body, {
150
+ attributeOldValue: true,
151
+ attributes: true,
152
+ childList: true,
153
+ subtree: true
154
+ });
155
+ (_this$performanceObse = this.performanceObserver) === null || _this$performanceObse === void 0 ? void 0 : _this$performanceObse.observe({
156
+ type: 'layout-shift',
157
+ buffered: true,
158
+ // @ts-ignore-error
159
+ durationThreshold: 30
160
+ });
161
+ }
162
+ stop() {
163
+ var _this$mutationObserve2, _this$intersectionObs5, _this$performanceObse2;
164
+ (_this$mutationObserve2 = this.mutationObserver) === null || _this$mutationObserve2 === void 0 ? void 0 : _this$mutationObserve2.disconnect();
165
+ (_this$intersectionObs5 = this.intersectionObserver) === null || _this$intersectionObs5 === void 0 ? void 0 : _this$intersectionObs5.disconnect();
166
+ (_this$performanceObse2 = this.performanceObserver) === null || _this$performanceObse2 === void 0 ? void 0 : _this$performanceObse2.disconnect();
167
+ }
168
+ }