heatspot 0.2.2 → 0.2.4

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/README.md CHANGED
@@ -8,6 +8,28 @@
8
8
 
9
9
  `heatspot` is an ESM TypeScript library for capturing pointer heat data and rendering an embeddable heatmap web component.
10
10
 
11
+ ## Try the Demo
12
+
13
+ Live demo:
14
+
15
+ - [![heatspot demo](https://raw.githubusercontent.com/Davidhanson90/heatspot/main/assets/example.jpg)](https://davidhanson90.github.io/heatspot/)
16
+
17
+ How to try it:
18
+
19
+ 1. Open the demo URL.
20
+ 2. Move your cursor around the demo area for a few seconds.
21
+ 3. Click the toggle icon in the top-left of the component to show the heat overlay.
22
+ 4. Continue interacting to see hotspots update in real time.
23
+
24
+ To run the demo locally:
25
+
26
+ ```bash
27
+ npm install
28
+ npm start
29
+ ```
30
+
31
+ Then open the local URL shown in your terminal (Vite default is usually `http://localhost:5173`).
32
+
11
33
  ## Features
12
34
 
13
35
  - ESM package output with TypeScript declarations
@@ -59,11 +81,43 @@ Example with hidden toolbar:
59
81
  </heat-spot>
60
82
  ```
61
83
 
62
- ### Example
84
+ ### 3. Read heatmap data from a `<heat-spot>` element
63
85
 
64
- ![example.jpg](https://raw.githubusercontent.com/Davidhanson90/heatspot/main/assets/example.jpg)
86
+ ```ts
87
+ const element = document.querySelector<HeatSpotElement>('heat-spot');
88
+
89
+ const snapshot = element.getHeatmapData();
90
+
91
+ console.log(snapshot);
92
+ ```
93
+
94
+ Example data shape:
95
+
96
+ ```json
97
+ {
98
+ "totalSamples": 42,
99
+ "trackedSince": 1741351200000,
100
+ "viewport": { "width": 960, "height": 540 },
101
+ "hotspots": [
102
+ {
103
+ "id": "hs-0",
104
+ "x": 418.2,
105
+ "y": 225.6,
106
+ "count": 18,
107
+ "intensity": 1
108
+ },
109
+ {
110
+ "id": "hs-1",
111
+ "x": 701.1,
112
+ "y": 392.8,
113
+ "count": 9,
114
+ "intensity": 0.5
115
+ }
116
+ ]
117
+ }
118
+ ```
65
119
 
66
- ### 3. Optional tracking API
120
+ ### 4. Optional global tracking API
67
121
 
68
122
  ```ts
69
123
  import {
@@ -84,6 +138,33 @@ stopMouseTracking();
84
138
  resetMouseHeatmap();
85
139
  ```
86
140
 
141
+ ### 5. Export the heatmap visualization as an image
142
+
143
+ ```ts
144
+ const element = document.querySelector<HeatSpotElement>("heat-spot");
145
+
146
+ if (element) {
147
+ const imageDataUrl = element.getHeatmapImage();
148
+
149
+ if (imageDataUrl) {
150
+ console.log(imageDataUrl);
151
+ // Example prefix:
152
+ // data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...
153
+
154
+ // Optional download example
155
+ const link = document.createElement("a");
156
+ link.href = imageDataUrl;
157
+ link.download = "heatmap.png";
158
+ link.click();
159
+ }
160
+ }
161
+ ```
162
+
163
+ `getHeatmapImage()` returns:
164
+
165
+ - A `data:` URL string when the component has measurable dimensions.
166
+ - `null` if the element has no measurable surface yet (for example, hidden or not laid out).
167
+
87
168
  ## Scripts
88
169
 
89
170
  - `npm run build` - compile library to `dist/`
@@ -1,4 +1,5 @@
1
1
  import { LitElement } from "lit";
2
+ import { type HeatmapSnapshot } from "./contracts/heatmap-contracts.js";
2
3
  export declare class HeatSpotElement extends LitElement {
3
4
  static properties: {
4
5
  heatmapVisible: {
@@ -15,6 +16,17 @@ export declare class HeatSpotElement extends LitElement {
15
16
  private readonly tracker;
16
17
  static styles: import("lit").CSSResult;
17
18
  constructor();
19
+ /**
20
+ * Returns the current tracked heatmap snapshot for this element instance.
21
+ */
22
+ getHeatmapData(): HeatmapSnapshot;
23
+ /**
24
+ * Renders the current heatmap visualization into an image data URL.
25
+ */
26
+ getHeatmapImage(options?: {
27
+ type?: string;
28
+ quality?: number;
29
+ }): string | null;
18
30
  /**
19
31
  * Stops frame rendering when element leaves the document.
20
32
  */
@@ -40,14 +40,22 @@ export class HeatSpotElement extends LitElement {
40
40
  color: #ffffff;
41
41
  padding: 0;
42
42
  line-height: 1;
43
- font-size: 0.92rem;
44
- font-weight: 700;
45
43
  cursor: pointer;
46
44
  z-index: 10001;
45
+ opacity: 0.72;
46
+ transition: opacity 180ms ease;
47
+ display: grid;
48
+ place-items: center;
47
49
  }
48
50
 
49
51
  .toggle:hover {
50
- background: rgba(2, 6, 23, 0.95);
52
+ opacity: 1;
53
+ }
54
+
55
+ .toggle-icon {
56
+ width: 0.95rem;
57
+ height: 0.95rem;
58
+ fill: currentColor;
51
59
  }
52
60
 
53
61
  .overlay {
@@ -64,6 +72,36 @@ export class HeatSpotElement extends LitElement {
64
72
  this.heatmapVisible = false;
65
73
  this.toolbar = "simple";
66
74
  }
75
+ /**
76
+ * Returns the current tracked heatmap snapshot for this element instance.
77
+ */
78
+ getHeatmapData() {
79
+ return this.tracker.getSnapshot();
80
+ }
81
+ /**
82
+ * Renders the current heatmap visualization into an image data URL.
83
+ */
84
+ getHeatmapImage(options = {}) {
85
+ const surface = this.renderRoot.querySelector(".surface");
86
+ if (!surface) {
87
+ return null;
88
+ }
89
+ const width = Math.round(surface.clientWidth);
90
+ const height = Math.round(surface.clientHeight);
91
+ if (width <= 0 || height <= 0) {
92
+ return null;
93
+ }
94
+ const imageCanvas = document.createElement("canvas");
95
+ imageCanvas.width = width;
96
+ imageCanvas.height = height;
97
+ const context = imageCanvas.getContext("2d");
98
+ if (!context) {
99
+ return null;
100
+ }
101
+ const snapshot = this.tracker.getSnapshot();
102
+ renderHeatmapOverlay(context, width, height, snapshot.hotspots);
103
+ return imageCanvas.toDataURL(options.type ?? "image/png", options.quality);
104
+ }
67
105
  /**
68
106
  * Stops frame rendering when element leaves the document.
69
107
  */
@@ -165,7 +203,17 @@ export class HeatSpotElement extends LitElement {
165
203
  ${this.toolbar === "hidden"
166
204
  ? null
167
205
  : html `<button class="toggle" @click=${this.toggleHeatmap} aria-label="Toggle heatmap">
168
- ${this.heatmapVisible ? "x" : "*"}
206
+ <svg
207
+ class="toggle-icon"
208
+ viewBox="0 0 24 24"
209
+ role="img"
210
+ aria-label="Heatmap fire icon"
211
+ xmlns="http://www.w3.org/2000/svg"
212
+ >
213
+ <path
214
+ d="M12 2.5c.4 2.3-.2 3.9-1.3 5.3-1.2 1.5-2.6 2.9-2.6 5.3 0 2.2 1.8 4 3.9 4 .8 0 1.6-.2 2.2-.6-1.1-.4-1.9-1.5-1.9-2.8 0-1.9 1.6-3 2.7-4.4.8-1 .9-2.1.8-3.3 2.3 1.5 4.2 4 4.2 7.1 0 4.2-3.4 7.6-7.6 7.6S4.8 17.3 4.8 13.1c0-4.3 2.5-6.7 4.5-8.7C10.4 3.3 11.2 2.6 12 2.5z"
215
+ />
216
+ </svg>
169
217
  </button>`}
170
218
  ${this.heatmapVisible ? html `<canvas id="heatmap" class="overlay"></canvas>` : null}
171
219
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heatspot",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "A tiny ESM TypeScript library.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",