@gemx-dev/clarity-visualize 0.8.62 → 0.8.63
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/build/clarity.visualize.js +152 -102
- package/build/clarity.visualize.min.js +1 -1
- package/build/clarity.visualize.module.js +152 -102
- package/package.json +2 -2
- package/src/heatmap.ts +32 -3
- package/src/visualizer.ts +14 -2
- package/types/visualize.d.ts +2 -0
|
@@ -67,105 +67,6 @@ function __generator(thisArg, body) {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
var _a$1;
|
|
71
|
-
var DataHelper = /** @class */ (function () {
|
|
72
|
-
function DataHelper(state) {
|
|
73
|
-
var _this = this;
|
|
74
|
-
this.regionMap = {};
|
|
75
|
-
this.regions = {};
|
|
76
|
-
this.metrics = {};
|
|
77
|
-
this.lean = false;
|
|
78
|
-
this.reset = function () {
|
|
79
|
-
_this.metrics = {};
|
|
80
|
-
_this.lean = false;
|
|
81
|
-
_this.regions = {};
|
|
82
|
-
_this.regionMap = {};
|
|
83
|
-
};
|
|
84
|
-
this.metric = function (event) {
|
|
85
|
-
var _a;
|
|
86
|
-
if (_this.state.options.metadata) {
|
|
87
|
-
var metricMarkup = [];
|
|
88
|
-
var regionMarkup = [];
|
|
89
|
-
// Copy over metrics for future reference
|
|
90
|
-
for (var m in event.data) {
|
|
91
|
-
var eventType = typeof event.data[m];
|
|
92
|
-
if (eventType === "number" || (event.event === 1 /* Data.Event.Dimension */ && m === 37 /* Data.Dimension.InteractionNextPaint */.toString())) {
|
|
93
|
-
if (!(m in _this.metrics)) {
|
|
94
|
-
_this.metrics[m] = 0;
|
|
95
|
-
}
|
|
96
|
-
var key = parseInt(m, 10);
|
|
97
|
-
var value = eventType === "object" ? Number((_a = event.data[m]) === null || _a === void 0 ? void 0 : _a[0]) : event.data[m];
|
|
98
|
-
if (m in DataHelper.METRIC_MAP && (DataHelper.METRIC_MAP[m].unit === "html-price" || DataHelper.METRIC_MAP[m].unit === "ld-price")) {
|
|
99
|
-
_this.metrics[m] = value;
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
_this.metrics[m] += value;
|
|
103
|
-
}
|
|
104
|
-
_this.lean = key === 1 /* Data.Metric.Playback */ && value === 0 ? true : _this.lean;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
for (var entry in _this.metrics) {
|
|
108
|
-
if (entry in DataHelper.METRIC_MAP) {
|
|
109
|
-
var m = _this.metrics[entry];
|
|
110
|
-
var map = DataHelper.METRIC_MAP[entry];
|
|
111
|
-
var unit = "unit" in map ? map.unit : "" /* Data.Constant.Empty */;
|
|
112
|
-
metricMarkup.push("<li><h2>".concat(_this.value(m, unit), "<span>").concat(_this.key(unit), "</span></h2>").concat(map.name, "</li>"));
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
// Append region information to metadata
|
|
116
|
-
for (var name_1 in _this.regions) {
|
|
117
|
-
var r = _this.regions[name_1];
|
|
118
|
-
var className = (r.visibility === 10 /* Layout.RegionVisibility.Visible */ ? "visible" : (r.interaction === 20 /* Layout.InteractionState.Clicked */ ? "clicked" : "" /* Data.Constant.Empty */));
|
|
119
|
-
regionMarkup.push("<span class=\"".concat(className, "\">").concat(name_1, "</span>"));
|
|
120
|
-
}
|
|
121
|
-
_this.state.options.metadata.innerHTML = "<ul>".concat(metricMarkup.join("" /* Data.Constant.Empty */), "</ul><div>").concat(regionMarkup.join("" /* Data.Constant.Empty */), "</div>");
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
this.key = function (unit) {
|
|
125
|
-
switch (unit) {
|
|
126
|
-
case "html-price":
|
|
127
|
-
case "ld-price":
|
|
128
|
-
case "cls":
|
|
129
|
-
return "" /* Data.Constant.Empty */;
|
|
130
|
-
default: return unit;
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
this.value = function (num, unit) {
|
|
134
|
-
switch (unit) {
|
|
135
|
-
case "KB": return Math.round(num / 1024);
|
|
136
|
-
case "s": return Math.round(num / 10) / 100;
|
|
137
|
-
case "cls": return num / 1000;
|
|
138
|
-
case "html-price": return num / 100;
|
|
139
|
-
default: return num;
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
this.state = state;
|
|
143
|
-
}
|
|
144
|
-
DataHelper.prototype.region = function (event) {
|
|
145
|
-
var data = event.data;
|
|
146
|
-
for (var _i = 0, data_1 = data; _i < data_1.length; _i++) {
|
|
147
|
-
var r = data_1[_i];
|
|
148
|
-
if (!(r.name in this.regions)) {
|
|
149
|
-
this.regions[r.name] = { interaction: r.interaction, visibility: r.visibility };
|
|
150
|
-
}
|
|
151
|
-
this.regionMap[r.id] = r.name;
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
DataHelper.METRIC_MAP = (_a$1 = {},
|
|
155
|
-
_a$1[2 /* Data.Metric.TotalBytes */] = { name: "Total Bytes", unit: "KB" },
|
|
156
|
-
_a$1[4 /* Data.Metric.TotalCost */] = { name: "Total Cost", unit: "ms" },
|
|
157
|
-
_a$1[3 /* Data.Metric.LayoutCost */] = { name: "Layout Cost", unit: "ms" },
|
|
158
|
-
_a$1[8 /* Data.Metric.LargestPaint */] = { name: "LCP", unit: "s" },
|
|
159
|
-
_a$1[9 /* Data.Metric.CumulativeLayoutShift */] = { name: "CLS", unit: "cls" },
|
|
160
|
-
_a$1[7 /* Data.Metric.LongTaskCount */] = { name: "Long Tasks" },
|
|
161
|
-
_a$1[24 /* Data.Metric.CartTotal */] = { name: "Cart Total", unit: "html-price" },
|
|
162
|
-
_a$1[13 /* Data.Metric.ProductPrice */] = { name: "Product Price", unit: "ld-price" },
|
|
163
|
-
_a$1[6 /* Data.Metric.ThreadBlockedTime */] = { name: "Thread Blocked", unit: "ms" },
|
|
164
|
-
_a$1[37 /* Data.Dimension.InteractionNextPaint */] = { name: "INP", unit: "ms" },
|
|
165
|
-
_a$1);
|
|
166
|
-
return DataHelper;
|
|
167
|
-
}());
|
|
168
|
-
|
|
169
70
|
// tslint:disable: no-bitwise
|
|
170
71
|
function hash (input, precision) {
|
|
171
72
|
if (precision === void 0) { precision = null; }
|
|
@@ -186,12 +87,26 @@ function hash (input, precision) {
|
|
|
186
87
|
hash = Math.abs(hashOne + (hashTwo * 11579));
|
|
187
88
|
return (precision ? hash % Math.pow(2, precision) : hash).toString(36);
|
|
188
89
|
}
|
|
90
|
+
|
|
91
|
+
function __spreadArray(to, from, pack) {
|
|
92
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
93
|
+
if (ar || !(i in from)) {
|
|
94
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
95
|
+
ar[i] = from[i];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
99
|
+
}
|
|
189
100
|
|
|
190
101
|
var excludeClassNames = "load,active,fixed,visible,focus,show,collaps,animat" /* Constant.ExcludeClassNames */.split("," /* Constant.Comma */);
|
|
102
|
+
var extraExcludeClassNames = [];
|
|
191
103
|
var selectorMap = {};
|
|
192
104
|
function reset$8() {
|
|
193
105
|
selectorMap = {};
|
|
194
106
|
}
|
|
107
|
+
function configure(classNames) {
|
|
108
|
+
extraExcludeClassNames = classNames || [];
|
|
109
|
+
}
|
|
195
110
|
function get$1(input, type) {
|
|
196
111
|
var a = input.attributes;
|
|
197
112
|
var prefix = input.prefix ? input.prefix[type] : null;
|
|
@@ -263,7 +178,7 @@ function filter(value) {
|
|
|
263
178
|
if (!value) {
|
|
264
179
|
return false;
|
|
265
180
|
} // Do not process empty strings
|
|
266
|
-
if (
|
|
181
|
+
if (getExcludeClassNames().some(function (x) { return value.toLowerCase().indexOf(x) >= 0; })) {
|
|
267
182
|
return false;
|
|
268
183
|
}
|
|
269
184
|
for (var i = 0; i < value.length; i++) {
|
|
@@ -274,9 +189,13 @@ function filter(value) {
|
|
|
274
189
|
}
|
|
275
190
|
return true;
|
|
276
191
|
}
|
|
192
|
+
function getExcludeClassNames() {
|
|
193
|
+
return __spreadArray(__spreadArray([], excludeClassNames, true), extraExcludeClassNames, true);
|
|
194
|
+
}
|
|
277
195
|
|
|
278
196
|
var selector = /*#__PURE__*/Object.freeze({
|
|
279
197
|
__proto__: null,
|
|
198
|
+
configure: configure,
|
|
280
199
|
get: get$1,
|
|
281
200
|
reset: reset$8
|
|
282
201
|
});
|
|
@@ -312,6 +231,105 @@ function lookup(hash) {
|
|
|
312
231
|
|
|
313
232
|
var helper = { hash: hash, selector: selector, get: get$2, getNode: getNode, lookup: lookup };
|
|
314
233
|
|
|
234
|
+
var _a$1;
|
|
235
|
+
var DataHelper = /** @class */ (function () {
|
|
236
|
+
function DataHelper(state) {
|
|
237
|
+
var _this = this;
|
|
238
|
+
this.regionMap = {};
|
|
239
|
+
this.regions = {};
|
|
240
|
+
this.metrics = {};
|
|
241
|
+
this.lean = false;
|
|
242
|
+
this.reset = function () {
|
|
243
|
+
_this.metrics = {};
|
|
244
|
+
_this.lean = false;
|
|
245
|
+
_this.regions = {};
|
|
246
|
+
_this.regionMap = {};
|
|
247
|
+
};
|
|
248
|
+
this.metric = function (event) {
|
|
249
|
+
var _a;
|
|
250
|
+
if (_this.state.options.metadata) {
|
|
251
|
+
var metricMarkup = [];
|
|
252
|
+
var regionMarkup = [];
|
|
253
|
+
// Copy over metrics for future reference
|
|
254
|
+
for (var m in event.data) {
|
|
255
|
+
var eventType = typeof event.data[m];
|
|
256
|
+
if (eventType === "number" || (event.event === 1 /* Data.Event.Dimension */ && m === 37 /* Data.Dimension.InteractionNextPaint */.toString())) {
|
|
257
|
+
if (!(m in _this.metrics)) {
|
|
258
|
+
_this.metrics[m] = 0;
|
|
259
|
+
}
|
|
260
|
+
var key = parseInt(m, 10);
|
|
261
|
+
var value = eventType === "object" ? Number((_a = event.data[m]) === null || _a === void 0 ? void 0 : _a[0]) : event.data[m];
|
|
262
|
+
if (m in DataHelper.METRIC_MAP && (DataHelper.METRIC_MAP[m].unit === "html-price" || DataHelper.METRIC_MAP[m].unit === "ld-price")) {
|
|
263
|
+
_this.metrics[m] = value;
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
_this.metrics[m] += value;
|
|
267
|
+
}
|
|
268
|
+
_this.lean = key === 1 /* Data.Metric.Playback */ && value === 0 ? true : _this.lean;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
for (var entry in _this.metrics) {
|
|
272
|
+
if (entry in DataHelper.METRIC_MAP) {
|
|
273
|
+
var m = _this.metrics[entry];
|
|
274
|
+
var map = DataHelper.METRIC_MAP[entry];
|
|
275
|
+
var unit = "unit" in map ? map.unit : "" /* Data.Constant.Empty */;
|
|
276
|
+
metricMarkup.push("<li><h2>".concat(_this.value(m, unit), "<span>").concat(_this.key(unit), "</span></h2>").concat(map.name, "</li>"));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Append region information to metadata
|
|
280
|
+
for (var name_1 in _this.regions) {
|
|
281
|
+
var r = _this.regions[name_1];
|
|
282
|
+
var className = (r.visibility === 10 /* Layout.RegionVisibility.Visible */ ? "visible" : (r.interaction === 20 /* Layout.InteractionState.Clicked */ ? "clicked" : "" /* Data.Constant.Empty */));
|
|
283
|
+
regionMarkup.push("<span class=\"".concat(className, "\">").concat(name_1, "</span>"));
|
|
284
|
+
}
|
|
285
|
+
_this.state.options.metadata.innerHTML = "<ul>".concat(metricMarkup.join("" /* Data.Constant.Empty */), "</ul><div>").concat(regionMarkup.join("" /* Data.Constant.Empty */), "</div>");
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
this.key = function (unit) {
|
|
289
|
+
switch (unit) {
|
|
290
|
+
case "html-price":
|
|
291
|
+
case "ld-price":
|
|
292
|
+
case "cls":
|
|
293
|
+
return "" /* Data.Constant.Empty */;
|
|
294
|
+
default: return unit;
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
this.value = function (num, unit) {
|
|
298
|
+
switch (unit) {
|
|
299
|
+
case "KB": return Math.round(num / 1024);
|
|
300
|
+
case "s": return Math.round(num / 10) / 100;
|
|
301
|
+
case "cls": return num / 1000;
|
|
302
|
+
case "html-price": return num / 100;
|
|
303
|
+
default: return num;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
this.state = state;
|
|
307
|
+
}
|
|
308
|
+
DataHelper.prototype.region = function (event) {
|
|
309
|
+
var data = event.data;
|
|
310
|
+
for (var _i = 0, data_1 = data; _i < data_1.length; _i++) {
|
|
311
|
+
var r = data_1[_i];
|
|
312
|
+
if (!(r.name in this.regions)) {
|
|
313
|
+
this.regions[r.name] = { interaction: r.interaction, visibility: r.visibility };
|
|
314
|
+
}
|
|
315
|
+
this.regionMap[r.id] = r.name;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
DataHelper.METRIC_MAP = (_a$1 = {},
|
|
319
|
+
_a$1[2 /* Data.Metric.TotalBytes */] = { name: "Total Bytes", unit: "KB" },
|
|
320
|
+
_a$1[4 /* Data.Metric.TotalCost */] = { name: "Total Cost", unit: "ms" },
|
|
321
|
+
_a$1[3 /* Data.Metric.LayoutCost */] = { name: "Layout Cost", unit: "ms" },
|
|
322
|
+
_a$1[8 /* Data.Metric.LargestPaint */] = { name: "LCP", unit: "s" },
|
|
323
|
+
_a$1[9 /* Data.Metric.CumulativeLayoutShift */] = { name: "CLS", unit: "cls" },
|
|
324
|
+
_a$1[7 /* Data.Metric.LongTaskCount */] = { name: "Long Tasks" },
|
|
325
|
+
_a$1[24 /* Data.Metric.CartTotal */] = { name: "Cart Total", unit: "html-price" },
|
|
326
|
+
_a$1[13 /* Data.Metric.ProductPrice */] = { name: "Product Price", unit: "ld-price" },
|
|
327
|
+
_a$1[6 /* Data.Metric.ThreadBlockedTime */] = { name: "Thread Blocked", unit: "ms" },
|
|
328
|
+
_a$1[37 /* Data.Dimension.InteractionNextPaint */] = { name: "INP", unit: "ms" },
|
|
329
|
+
_a$1);
|
|
330
|
+
return DataHelper;
|
|
331
|
+
}());
|
|
332
|
+
|
|
315
333
|
var EnrichHelper = /** @class */ (function () {
|
|
316
334
|
function EnrichHelper() {
|
|
317
335
|
var _this = this;
|
|
@@ -882,12 +900,27 @@ var HeatmapHelper = /** @class */ (function () {
|
|
|
882
900
|
var points = {};
|
|
883
901
|
var localMax = 0;
|
|
884
902
|
var height = _this.state.window && _this.state.window.document ? _this.state.window.document.documentElement.clientHeight : 0;
|
|
903
|
+
// DEBUG LOG
|
|
904
|
+
console.group("[Heatmap] transform \u2014 total elements: ".concat(_this.data.length, ", height: ").concat(height, ", max: ").concat(_this.max));
|
|
885
905
|
for (var _i = 0, _a = _this.data; _i < _a.length; _i++) {
|
|
886
906
|
var element = _a[_i];
|
|
887
907
|
var el = _this.layout.get(element.hash);
|
|
908
|
+
if (!el) {
|
|
909
|
+
console.warn("[Heatmap] SKIP hash=\"".concat(element.hash, "\" \u2014 element not found in DOM"));
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
912
|
+
if (typeof el.getBoundingClientRect !== "function") {
|
|
913
|
+
console.warn("[Heatmap] SKIP hash=\"".concat(element.hash, "\" \u2014 getBoundingClientRect not available"), el);
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
916
|
+
var r = el.getBoundingClientRect();
|
|
917
|
+
var v = _this.visible(el, r, height);
|
|
918
|
+
if (!v && _this.max !== null) {
|
|
919
|
+
console.warn("[Heatmap] SKIP hash=\"".concat(element.hash, "\" \u2014 not visible on re-render"), { rect: { top: r.top, left: r.left, width: r.width, height: r.height }, maxIsSet: _this.max !== null });
|
|
920
|
+
continue;
|
|
921
|
+
}
|
|
888
922
|
if (el && typeof el.getBoundingClientRect === "function") {
|
|
889
|
-
|
|
890
|
-
var v = _this.visible(el, r, height);
|
|
923
|
+
console.log("[Heatmap] PROCESS hash=\"".concat(element.hash, "\" visible=").concat(v, " points=").concat(element.points), { rect: { top: r.top, left: r.left, width: r.width, height: r.height } });
|
|
891
924
|
// Process clicks for only visible elements
|
|
892
925
|
if (_this.max === null || v) {
|
|
893
926
|
for (var i = 0; i < element.points; i++) {
|
|
@@ -896,6 +929,7 @@ var HeatmapHelper = /** @class */ (function () {
|
|
|
896
929
|
var k = "".concat(x).concat("X" /* Constant.Separator */).concat(y).concat("X" /* Constant.Separator */).concat(v ? 1 : 0);
|
|
897
930
|
points[k] = k in points ? points[k] + element.clicks[i] : element.clicks[i];
|
|
898
931
|
localMax = Math.max(points[k], localMax);
|
|
932
|
+
console.log(" point[".concat(i, "] x=").concat(x, " y=").concat(y, " visible=").concat(v, " clicks=").concat(element.clicks[i]));
|
|
899
933
|
}
|
|
900
934
|
}
|
|
901
935
|
}
|
|
@@ -903,6 +937,7 @@ var HeatmapHelper = /** @class */ (function () {
|
|
|
903
937
|
// Set the max value from the firs t
|
|
904
938
|
_this.max = _this.max ? _this.max : localMax;
|
|
905
939
|
// Once all points are accounted for, convert everything into absolute (x, y)
|
|
940
|
+
var skippedInvisible = 0;
|
|
906
941
|
for (var _b = 0, _c = Object.keys(points); _b < _c.length; _b++) {
|
|
907
942
|
var coordinates = _c[_b];
|
|
908
943
|
var parts = coordinates.split("X" /* Constant.Separator */);
|
|
@@ -910,7 +945,12 @@ var HeatmapHelper = /** @class */ (function () {
|
|
|
910
945
|
if (parts[2] === "1") {
|
|
911
946
|
output.push({ x: parseInt(parts[0], 10), y: parseInt(parts[1], 10), a: alpha });
|
|
912
947
|
}
|
|
948
|
+
else {
|
|
949
|
+
skippedInvisible++;
|
|
950
|
+
}
|
|
913
951
|
}
|
|
952
|
+
console.log("[Heatmap] Result \u2014 drawn: ".concat(output.length, ", skipped invisible: ").concat(skippedInvisible, ", max: ").concat(_this.max));
|
|
953
|
+
console.groupEnd();
|
|
914
954
|
return output;
|
|
915
955
|
};
|
|
916
956
|
this.visible = function (el, r, height) {
|
|
@@ -2310,6 +2350,11 @@ var Visualizer = /** @class */ (function () {
|
|
|
2310
2350
|
this._state = null;
|
|
2311
2351
|
this.renderTime = 0;
|
|
2312
2352
|
this.hashFoundTime = -1;
|
|
2353
|
+
this._excludeClassNames = [];
|
|
2354
|
+
this.configure = function (opts) {
|
|
2355
|
+
_this._excludeClassNames = opts.excludeClassNames || [];
|
|
2356
|
+
helper.selector.configure(_this._excludeClassNames);
|
|
2357
|
+
};
|
|
2313
2358
|
this.dom = function (event) { return __awaiter(_this, void 0, void 0, function () {
|
|
2314
2359
|
return __generator(this, function (_a) {
|
|
2315
2360
|
switch (_a.label) {
|
|
@@ -2430,6 +2475,7 @@ var Visualizer = /** @class */ (function () {
|
|
|
2430
2475
|
decoded = decoded.sort(_this.sortPayloads);
|
|
2431
2476
|
// Re-initialize enrich class if someone ends up calling merge function directly
|
|
2432
2477
|
_this.enrich = _this.enrich || new EnrichHelper();
|
|
2478
|
+
helper.selector.configure(_this._excludeClassNames);
|
|
2433
2479
|
_this.enrich.reset();
|
|
2434
2480
|
// Walk through payloads and generate merged payload from an array of decoded payloads
|
|
2435
2481
|
for (var _i = 0, decoded_1 = decoded; _i < decoded_1.length; _i++) {
|
|
@@ -2468,6 +2514,10 @@ var Visualizer = /** @class */ (function () {
|
|
|
2468
2514
|
switch (_a.label) {
|
|
2469
2515
|
case 0:
|
|
2470
2516
|
this.reset();
|
|
2517
|
+
if (options.excludeClassNames) {
|
|
2518
|
+
this._excludeClassNames = options.excludeClassNames;
|
|
2519
|
+
helper.selector.configure(options.excludeClassNames);
|
|
2520
|
+
}
|
|
2471
2521
|
// Infer options
|
|
2472
2522
|
options.pointer = "pointer" in options ? options.pointer : true;
|
|
2473
2523
|
options.canvas = "canvas" in options ? options.canvas : true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gemx-dev/clarity-visualize",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.63",
|
|
4
4
|
"description": "Clarity visualize",
|
|
5
5
|
"author": "Microsoft Corp.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"unpkg": "build/clarity.visualize.min.js",
|
|
10
10
|
"types": "types/index.d.ts",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@gemx-dev/clarity-decode": "^0.8.
|
|
12
|
+
"@gemx-dev/clarity-decode": "^0.8.63"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@rollup/plugin-commonjs": "^24.0.0",
|
package/src/heatmap.ts
CHANGED
|
@@ -276,11 +276,31 @@ export class HeatmapHelper {
|
|
|
276
276
|
let points: { [key: string]: number } = {};
|
|
277
277
|
let localMax = 0;
|
|
278
278
|
let height = this.state.window && this.state.window.document ? this.state.window.document.documentElement.clientHeight : 0;
|
|
279
|
+
|
|
280
|
+
// DEBUG LOG
|
|
281
|
+
console.group(`[Heatmap] transform — total elements: ${this.data.length}, height: ${height}, max: ${this.max}`);
|
|
282
|
+
|
|
279
283
|
for (let element of this.data) {
|
|
280
284
|
let el = this.layout.get(element.hash) as HTMLElement;
|
|
285
|
+
if (!el) {
|
|
286
|
+
console.warn(`[Heatmap] SKIP hash="${element.hash}" — element not found in DOM`);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (typeof el.getBoundingClientRect !== "function") {
|
|
290
|
+
console.warn(`[Heatmap] SKIP hash="${element.hash}" — getBoundingClientRect not available`, el);
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
let r = el.getBoundingClientRect();
|
|
295
|
+
let v = this.visible(el, r, height);
|
|
296
|
+
|
|
297
|
+
if (!v && this.max !== null) {
|
|
298
|
+
console.warn(`[Heatmap] SKIP hash="${element.hash}" — not visible on re-render`, { rect: { top: r.top, left: r.left, width: r.width, height: r.height }, maxIsSet: this.max !== null });
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
|
|
281
302
|
if (el && typeof el.getBoundingClientRect === "function") {
|
|
282
|
-
|
|
283
|
-
let v = this.visible(el, r, height);
|
|
303
|
+
console.log(`[Heatmap] PROCESS hash="${element.hash}" visible=${v} points=${element.points}`, { rect: { top: r.top, left: r.left, width: r.width, height: r.height } });
|
|
284
304
|
// Process clicks for only visible elements
|
|
285
305
|
if (this.max === null || v) {
|
|
286
306
|
for(let i = 0; i < element.points; i++) {
|
|
@@ -289,6 +309,7 @@ export class HeatmapHelper {
|
|
|
289
309
|
let k = `${x}${Constant.Separator}${y}${Constant.Separator}${v ? 1 : 0}`;
|
|
290
310
|
points[k] = k in points ? points[k] + element.clicks[i] : element.clicks[i];
|
|
291
311
|
localMax = Math.max(points[k], localMax);
|
|
312
|
+
console.log(` point[${i}] x=${x} y=${y} visible=${v} clicks=${element.clicks[i]}`);
|
|
292
313
|
}
|
|
293
314
|
}
|
|
294
315
|
}
|
|
@@ -298,12 +319,20 @@ export class HeatmapHelper {
|
|
|
298
319
|
this.max = this.max ? this.max : localMax;
|
|
299
320
|
|
|
300
321
|
// Once all points are accounted for, convert everything into absolute (x, y)
|
|
322
|
+
let skippedInvisible = 0;
|
|
301
323
|
for (let coordinates of Object.keys(points)) {
|
|
302
324
|
let parts = coordinates.split(Constant.Separator);
|
|
303
325
|
let alpha = Math.min((points[coordinates] / this.max) + Setting.AlphaBoost, 1);
|
|
304
|
-
if (parts[2] === "1") {
|
|
326
|
+
if (parts[2] === "1") {
|
|
327
|
+
output.push({ x: parseInt(parts[0], 10), y: parseInt(parts[1], 10), a: alpha });
|
|
328
|
+
} else {
|
|
329
|
+
skippedInvisible++;
|
|
330
|
+
}
|
|
305
331
|
}
|
|
306
332
|
|
|
333
|
+
console.log(`[Heatmap] Result — drawn: ${output.length}, skipped invisible: ${skippedInvisible}, max: ${this.max}`);
|
|
334
|
+
console.groupEnd();
|
|
335
|
+
|
|
307
336
|
return output;
|
|
308
337
|
}
|
|
309
338
|
|
package/src/visualizer.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Activity, Constant, ErrorLogger, LinkHandler, MergedPayload, Options, PlaybackState, ScrollMapInfo, Setting, ShortCircuitStrategy, Visualizer as VisualizerType } from "@clarity-types/visualize";
|
|
2
|
-
import { Data } from "@gemx-dev/clarity-js";
|
|
3
1
|
import type { Data as DecodedData, Interaction, Layout } from "@gemx-dev/clarity-decode";
|
|
2
|
+
|
|
3
|
+
import { Activity, Constant, ErrorLogger, LinkHandler, MergedPayload, Options, PlaybackState, ScrollMapInfo, Setting, ShortCircuitStrategy, Visualizer as VisualizerType } from "@clarity-types/visualize";
|
|
4
|
+
import { Data, helper } from "@gemx-dev/clarity-js";
|
|
4
5
|
import { DataHelper } from "./data";
|
|
5
6
|
import { EnrichHelper } from "./enrich";
|
|
6
7
|
import { HeatmapHelper } from "./heatmap";
|
|
@@ -12,6 +13,7 @@ export class Visualizer implements VisualizerType {
|
|
|
12
13
|
_state: PlaybackState = null;
|
|
13
14
|
renderTime = 0;
|
|
14
15
|
hashFoundTime = -1;
|
|
16
|
+
_excludeClassNames: string[] = [];
|
|
15
17
|
|
|
16
18
|
enrich: EnrichHelper;
|
|
17
19
|
layout: LayoutHelper;
|
|
@@ -23,6 +25,11 @@ export class Visualizer implements VisualizerType {
|
|
|
23
25
|
return this._state;
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
public configure = (opts: { excludeClassNames?: string[] }): void => {
|
|
29
|
+
this._excludeClassNames = opts.excludeClassNames || [];
|
|
30
|
+
helper.selector.configure(this._excludeClassNames);
|
|
31
|
+
}
|
|
32
|
+
|
|
26
33
|
public dom = async (event: Layout.DomEvent): Promise<void> => {
|
|
27
34
|
await this.layout.dom(event);
|
|
28
35
|
}
|
|
@@ -116,6 +123,7 @@ export class Visualizer implements VisualizerType {
|
|
|
116
123
|
decoded = decoded.sort(this.sortPayloads);
|
|
117
124
|
// Re-initialize enrich class if someone ends up calling merge function directly
|
|
118
125
|
this.enrich = this.enrich || new EnrichHelper();
|
|
126
|
+
helper.selector.configure(this._excludeClassNames);
|
|
119
127
|
this.enrich.reset();
|
|
120
128
|
// Walk through payloads and generate merged payload from an array of decoded payloads
|
|
121
129
|
for (let payload of decoded) {
|
|
@@ -148,6 +156,10 @@ export class Visualizer implements VisualizerType {
|
|
|
148
156
|
|
|
149
157
|
public setup = async (target: Window, options: Options): Promise<Visualizer> => {
|
|
150
158
|
this.reset();
|
|
159
|
+
if (options.excludeClassNames) {
|
|
160
|
+
this._excludeClassNames = options.excludeClassNames;
|
|
161
|
+
helper.selector.configure(options.excludeClassNames);
|
|
162
|
+
}
|
|
151
163
|
// Infer options
|
|
152
164
|
options.pointer = "pointer" in options ? options.pointer : true;
|
|
153
165
|
options.canvas = "canvas" in options ? options.canvas : true;
|
package/types/visualize.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export class Visualizer {
|
|
|
27
27
|
merge: (decoded: Data.DecodedPayload[]) => MergedPayload;
|
|
28
28
|
render: (events: Data.DecodedEvent[]) => void;
|
|
29
29
|
setup: (target: Window, options: Options) => Promise<Visualizer>;
|
|
30
|
+
configure: (opts: { excludeClassNames?: string[] }) => void;
|
|
30
31
|
time: () => number;
|
|
31
32
|
get: (hash: string) => HTMLElement;
|
|
32
33
|
}
|
|
@@ -70,6 +71,7 @@ export interface Options {
|
|
|
70
71
|
mobile?: boolean;
|
|
71
72
|
vNext?: boolean;
|
|
72
73
|
locale?: string;
|
|
74
|
+
excludeClassNames?: string[];
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
export interface NodeData {
|