@graphit/cli 0.1.0 → 0.1.1
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/dist/api/client.js +7 -7
- package/dist/api/client.js.map +1 -1
- package/dist/auth/login.js +2 -1
- package/dist/auth/login.js.map +1 -1
- package/dist/commands/dashboard.js +4 -4
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +10 -5
- package/skill/references/chart-patterns.md +125 -10
package/dist/api/client.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { getValidIdToken } from "../auth/token.js";
|
|
2
|
-
|
|
2
|
+
import { getApiBaseUrl } from "../config.js";
|
|
3
3
|
const REQUEST_TIMEOUT_MS = 30_000;
|
|
4
|
-
function getBaseUrl() {
|
|
5
|
-
return process.env.GRAPHIT_API_URL || DEFAULT_BASE_URL;
|
|
6
|
-
}
|
|
7
4
|
class ApiClient {
|
|
8
5
|
async request(method, path, body) {
|
|
9
6
|
const { id_token } = await getValidIdToken();
|
|
@@ -27,7 +24,7 @@ class ApiClient {
|
|
|
27
24
|
return this.request("DELETE", path);
|
|
28
25
|
}
|
|
29
26
|
async _fetch(method, path, body, extraHeaders) {
|
|
30
|
-
const url = `${
|
|
27
|
+
const url = `${getApiBaseUrl()}${path}`;
|
|
31
28
|
const headers = {
|
|
32
29
|
"Content-Type": "application/json",
|
|
33
30
|
"X-Graphit-Source": "cli",
|
|
@@ -44,11 +41,12 @@ class ApiClient {
|
|
|
44
41
|
try {
|
|
45
42
|
const errBody = (await resp.json());
|
|
46
43
|
detail = errBody.detail || detail;
|
|
47
|
-
if (resp.status === 429
|
|
44
|
+
if (resp.status === 429) {
|
|
45
|
+
const retryAfter = parseInt(resp.headers.get("Retry-After") || "60", 10);
|
|
48
46
|
const err = {
|
|
49
47
|
status: 429,
|
|
50
48
|
detail,
|
|
51
|
-
retry_after_seconds:
|
|
49
|
+
retry_after_seconds: retryAfter,
|
|
52
50
|
};
|
|
53
51
|
throw err;
|
|
54
52
|
}
|
|
@@ -60,6 +58,8 @@ class ApiClient {
|
|
|
60
58
|
const err = { status: resp.status, detail };
|
|
61
59
|
throw err;
|
|
62
60
|
}
|
|
61
|
+
if (resp.status === 204)
|
|
62
|
+
return undefined;
|
|
63
63
|
return (await resp.json());
|
|
64
64
|
}
|
|
65
65
|
}
|
package/dist/api/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
|
package/dist/auth/login.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createServer } from "node:http";
|
|
|
2
2
|
import { randomBytes, createHash } from "node:crypto";
|
|
3
3
|
import { writeCredentials } from "./credentials.js";
|
|
4
4
|
import { apiClient } from "../api/client.js";
|
|
5
|
+
import { getFrontendUrl } from "../config.js";
|
|
5
6
|
import open from "open";
|
|
6
7
|
const AUTH_TIMEOUT_MS = 120_000;
|
|
7
8
|
function escapeHtml(str) {
|
|
@@ -141,7 +142,7 @@ export async function performLogin() {
|
|
|
141
142
|
code_challenge: codeChallenge,
|
|
142
143
|
callback_url: callbackUrl,
|
|
143
144
|
});
|
|
144
|
-
const frontendUrl =
|
|
145
|
+
const frontendUrl = getFrontendUrl();
|
|
145
146
|
const authUrl = `${frontendUrl}/auth/cli?state=${state}&callback=${encodeURIComponent(callbackUrl)}`;
|
|
146
147
|
console.log("\nOpening browser to log in to Graphit...");
|
|
147
148
|
console.log(` -> ${authUrl}\n`);
|
package/dist/auth/login.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAA0B,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,eAAe,GAAG,OAAO,CAAC;AAEhC,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,GAAG;SACP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,kBAAkB,CACzB,OAA4B,EAC5B,KAAa,EACb,OAAe;IAEf,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAExC,MAAM,UAAU,GACd,OAAO,KAAK,SAAS;QACnB,CAAC,CAAC;;;gBAGQ;QACV,CAAC,CAAC;;;gBAGQ,CAAC;IAEf,OAAO;;;;;WAKE,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAqFM,UAAU;YACxB,SAAS;WACV,WAAW;;;;QAId,CAAC;AACT,CAAC;AAED,SAAS,oBAAoB;IAC3B,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,oBAAoB,CAAC,QAAgB;IAC5C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,aAAa;IACpB,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAqBD,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAC9B,MAAM,YAAY,GAAG,oBAAoB,EAAE,CAAC;IAC5C,MAAM,aAAa,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAC;IAEzD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IACxE,MAAM,WAAW,GAAG,oBAAoB,IAAI,WAAW,CAAC;IAExD,MAAM,SAAS,CAAC,YAAY,CAC1B,uBAAuB,EACvB;QACE,KAAK;QACL,cAAc,EAAE,aAAa;QAC7B,YAAY,EAAE,WAAW;KAC1B,CACF,CAAC;IAEF,MAAM,WAAW,GAAG,
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { apiClient } from "../api/client.js";
|
|
3
|
+
import { getFrontendUrl } from "../config.js";
|
|
3
4
|
import { output, errorOutput } from "../output/format.js";
|
|
4
5
|
function dashboardUrl(id) {
|
|
5
|
-
|
|
6
|
-
return `${base}/custom-dashboards/${id}`;
|
|
6
|
+
return `${getFrontendUrl()}/custom-dashboards/${id}`;
|
|
7
7
|
}
|
|
8
8
|
export function registerDashboardCommands(program) {
|
|
9
9
|
const dashboard = program.command("dashboard").description("Custom dashboard management");
|
|
@@ -88,11 +88,11 @@ export function registerDashboardCommands(program) {
|
|
|
88
88
|
.action(async function (id) {
|
|
89
89
|
try {
|
|
90
90
|
const resp = await apiClient.get(`/api/v1/cli/dashboards/${id}?include_html=true`);
|
|
91
|
-
if (!resp.
|
|
91
|
+
if (!resp.html) {
|
|
92
92
|
console.log("<!-- Canvas is empty - no HTML content yet -->");
|
|
93
93
|
}
|
|
94
94
|
else {
|
|
95
|
-
console.log(resp.
|
|
95
|
+
console.log(resp.html);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/commands/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAE1D,SAAS,YAAY,CAAC,EAAU;IAC9B,
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/commands/dashboard.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
package/dist/config.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const DEFAULT_API_BASE_URL = "https://api.graphit-app.com";
|
|
2
|
+
export const DEFAULT_FRONTEND_URL = "https://graphit-app.com";
|
|
3
|
+
export function getApiBaseUrl() {
|
|
4
|
+
return process.env.GRAPHIT_API_URL || DEFAULT_API_BASE_URL;
|
|
5
|
+
}
|
|
6
|
+
export function getFrontendUrl() {
|
|
7
|
+
return process.env.GRAPHIT_FRONTEND_URL || DEFAULT_FRONTEND_URL;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,6BAA6B,CAAC;AAClE,MAAM,CAAC,MAAM,oBAAoB,GAAG,yBAAyB,CAAC;AAE9D,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,oBAAoB,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,oBAAoB,CAAC;AAClE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ const program = new Command();
|
|
|
11
11
|
program
|
|
12
12
|
.name("graphit")
|
|
13
13
|
.description("Graphit CLI - Build custom dashboards from any AI coding assistant")
|
|
14
|
-
.version("0.1.
|
|
14
|
+
.version("0.1.1")
|
|
15
15
|
.option("--output <format>", "Output format: json (default) or table", "json");
|
|
16
16
|
registerAuthCommands(program);
|
|
17
17
|
registerKBCommands(program);
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -21,7 +21,7 @@ Your HTML must NEVER contain:
|
|
|
21
21
|
- `<img src="https://...">` - no external images
|
|
22
22
|
|
|
23
23
|
If you use ANY `src=` or `href=` pointing to a URL, the dashboard will be blank.
|
|
24
|
-
Build charts with: CSS width% bars
|
|
24
|
+
Build charts with: CSS width% bars or inline SVG (preferred - crisp at any DPI in the iframe).
|
|
25
25
|
All CSS in `<style>`, all JS in `<script>`, all fonts from system stack.
|
|
26
26
|
|
|
27
27
|
### 2. ALWAYS query through data sources
|
|
@@ -132,19 +132,24 @@ Found **12 metrics** and **8 dimensions** on table MARKETING_UA:
|
|
|
132
132
|
|
|
133
133
|
## Chart Type Quick Reference
|
|
134
134
|
|
|
135
|
+
**NEVER use `<canvas>`.** The dashboard renders in a sandboxed iframe where canvas `devicePixelRatio` handling breaks, producing blurry charts. Use SVG (`viewBox` scales perfectly at any DPI) or CSS-based techniques instead.
|
|
136
|
+
|
|
135
137
|
| Data shape | Chart | Inline technique |
|
|
136
138
|
|---|---|---|
|
|
137
|
-
| 1 temporal + 1 numeric | Line | SVG
|
|
139
|
+
| 1 temporal + 1 numeric | Line | SVG `<polyline>` with `viewBox` |
|
|
138
140
|
| 1 categorical + 1 numeric | Bar | CSS width% divs |
|
|
139
141
|
| Single number | KPI card | Styled div |
|
|
140
|
-
| Part-whole (max 5) | Donut |
|
|
142
|
+
| Part-whole (max 5) | Donut | SVG arc paths |
|
|
143
|
+
| 2 numerics + optional size | Scatter / Bubble | SVG `<circle>` elements |
|
|
141
144
|
| Matrix / cohort | Heatmap | CSS grid + opacity |
|
|
142
145
|
| Stages | Funnel | CSS width% bars, narrowing |
|
|
143
|
-
| Time sparkline | Sparkline | Inline SVG
|
|
146
|
+
| Time sparkline | Sparkline | Inline SVG `<polyline>` |
|
|
144
147
|
| Detail / raw data | Table | HTML table + hover rows |
|
|
145
148
|
|
|
149
|
+
**SVG tooltip pattern:** Add a shared tooltip `<div>` (position:fixed, pointer-events:none). Attach `mouseenter`/`mouseleave` on SVG `<g>` groups wrapping each data element. For line charts, overlay invisible `<rect>` columns per x-tick as hover targets, with a dashed vertical guide `<line>` toggled on hover.
|
|
150
|
+
|
|
146
151
|
For more chart types (scatter, stacked bar, gauge, multi-series) see `references/chart-selection.md`.
|
|
147
|
-
For inline implementations of each, see `references/
|
|
152
|
+
For inline implementations of each, see `references/chart-patterns.md`.
|
|
148
153
|
|
|
149
154
|
---
|
|
150
155
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Chart Patterns
|
|
2
2
|
|
|
3
|
-
Inline CSS/SVG
|
|
3
|
+
Inline CSS/SVG implementations for dashboards. No external libraries - the iframe CSP blocks all external resources.
|
|
4
|
+
|
|
5
|
+
**NEVER use `<canvas>`.** Canvas produces blurry charts inside the sandboxed iframe due to DPI scaling issues. Always use SVG with `viewBox` (resolution-independent) or CSS-based techniques.
|
|
6
|
+
|
|
7
|
+
**Tooltips:** Every chart with data points should include hover tooltips. Use a shared tooltip div and attach DOM events on SVG elements or HTML rows. See the Tooltip Pattern section at the bottom.
|
|
4
8
|
|
|
5
9
|
## CSS Horizontal Bar
|
|
6
10
|
```js
|
|
@@ -84,20 +88,64 @@ var pts = vals.map(function(v,i){
|
|
|
84
88
|
sparkSvg.innerHTML = '<path d="M' + pts + '" fill="none" stroke="var(--graphit-accent)" stroke-width="1.5"/>';
|
|
85
89
|
```
|
|
86
90
|
|
|
87
|
-
##
|
|
91
|
+
## SVG Donut Chart
|
|
88
92
|
```js
|
|
89
|
-
var
|
|
90
|
-
var slices = [{v:40,c:'var(--graphit-accent)'},{v:25,c:'#80CBC4'},{v:20,c:'#26A69A'},{v:15,c:'#009688'}];
|
|
93
|
+
var slices = [{v:40,c:'var(--graphit-accent)',label:'Category A'},{v:25,c:'#80CBC4',label:'Category B'},{v:20,c:'#26A69A',label:'Category C'},{v:15,c:'#009688',label:'Category D'}];
|
|
91
94
|
var total = slices.reduce(function(a,s){return a+s.v},0);
|
|
92
|
-
var cx=
|
|
95
|
+
var cx=100, cy=100, r=80, hole=50, angle=-Math.PI/2;
|
|
96
|
+
svg.setAttribute('viewBox','0 0 200 200');
|
|
93
97
|
slices.forEach(function(s) {
|
|
94
98
|
var arc = (s.v/total)*Math.PI*2;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
99
|
+
var x1 = cx + r*Math.cos(angle), y1 = cy + r*Math.sin(angle);
|
|
100
|
+
var x2 = cx + r*Math.cos(angle+arc), y2 = cy + r*Math.sin(angle+arc);
|
|
101
|
+
var large = arc > Math.PI ? 1 : 0;
|
|
102
|
+
var path = 'M'+x1+','+y1+' A'+r+','+r+' 0 '+large+' 1 '+x2+','+y2+' L'+cx+','+cy+' Z';
|
|
103
|
+
var el = document.createElementNS('http://www.w3.org/2000/svg','path');
|
|
104
|
+
el.setAttribute('d', path); el.setAttribute('fill', s.c);
|
|
105
|
+
el.style.cursor = 'pointer';
|
|
106
|
+
el.addEventListener('mouseenter', function(e) { showTooltip(e, s.label+': '+s.v+' ('+(s.v/total*100).toFixed(1)+'%)'); });
|
|
107
|
+
el.addEventListener('mouseleave', hideTooltip);
|
|
108
|
+
svg.appendChild(el);
|
|
109
|
+
angle += arc;
|
|
110
|
+
});
|
|
111
|
+
// Center hole
|
|
112
|
+
var circle = document.createElementNS('http://www.w3.org/2000/svg','circle');
|
|
113
|
+
circle.setAttribute('cx',cx); circle.setAttribute('cy',cy); circle.setAttribute('r',hole);
|
|
114
|
+
circle.setAttribute('fill','var(--graphit-surface-raised)');
|
|
115
|
+
svg.appendChild(circle);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## SVG Scatter / Bubble Chart
|
|
119
|
+
```js
|
|
120
|
+
// data: [{label, x, y, size}] x/y are raw values, size is relative (0-1)
|
|
121
|
+
var svgW=500, svgH=300, pad={top:16,right:16,bottom:36,left:50};
|
|
122
|
+
var pW=svgW-pad.left-pad.right, pH=svgH-pad.top-pad.bottom;
|
|
123
|
+
svg.setAttribute('viewBox','0 0 '+svgW+' '+svgH);
|
|
124
|
+
var maxX=Math.max.apply(null,data.map(function(d){return d.x}))*1.1;
|
|
125
|
+
var maxY=Math.max.apply(null,data.map(function(d){return d.y}))*1.15;
|
|
126
|
+
// Sort large-to-small so small bubbles render on top
|
|
127
|
+
data.sort(function(a,b){return b.size-a.size});
|
|
128
|
+
data.forEach(function(d) {
|
|
129
|
+
var cx = pad.left + (d.x/maxX)*pW;
|
|
130
|
+
var cy = svgH - pad.bottom - (d.y/maxY)*pH;
|
|
131
|
+
var r = Math.max(5, Math.sqrt(d.size)*28);
|
|
132
|
+
var g = document.createElementNS('http://www.w3.org/2000/svg','g');
|
|
133
|
+
g.style.cursor = 'pointer';
|
|
134
|
+
var c = document.createElementNS('http://www.w3.org/2000/svg','circle');
|
|
135
|
+
c.setAttribute('cx',cx); c.setAttribute('cy',cy); c.setAttribute('r',r);
|
|
136
|
+
c.setAttribute('fill','var(--graphit-accent)'); c.setAttribute('fill-opacity','0.55');
|
|
137
|
+
c.setAttribute('stroke','var(--graphit-accent)'); c.setAttribute('stroke-width','1.5');
|
|
138
|
+
g.appendChild(c);
|
|
139
|
+
var t = document.createElementNS('http://www.w3.org/2000/svg','text');
|
|
140
|
+
t.setAttribute('x',cx); t.setAttribute('y',cy-r-4);
|
|
141
|
+
t.setAttribute('text-anchor','middle'); t.setAttribute('font-size','10');
|
|
142
|
+
t.setAttribute('fill','var(--graphit-fg-subtle)');
|
|
143
|
+
t.textContent = d.label; g.appendChild(t);
|
|
144
|
+
g.addEventListener('mouseenter', function(e){ showTooltip(e, d.tooltipHtml); });
|
|
145
|
+
g.addEventListener('mousemove', function(e){ showTooltip(e, tooltip.innerHTML); });
|
|
146
|
+
g.addEventListener('mouseleave', hideTooltip);
|
|
147
|
+
svg.appendChild(g);
|
|
98
148
|
});
|
|
99
|
-
ctx.beginPath(); ctx.arc(cx,cy,hole,0,Math.PI*2);
|
|
100
|
-
ctx.fillStyle='white'; ctx.fill();
|
|
101
149
|
```
|
|
102
150
|
|
|
103
151
|
## CSS Heatmap Grid
|
|
@@ -140,3 +188,70 @@ data.forEach(function(d, i) {
|
|
|
140
188
|
</div>
|
|
141
189
|
```
|
|
142
190
|
Dynamic: set `background: conic-gradient(var(--graphit-accent) 0% ${pct}%, var(--graphit-surface-sunken) ${pct}% 100%)`.
|
|
191
|
+
|
|
192
|
+
## Tooltip Pattern
|
|
193
|
+
|
|
194
|
+
Every chart with data points should have hover tooltips. Add ONE shared tooltip div to the page and reuse it from all charts.
|
|
195
|
+
|
|
196
|
+
### HTML (add once, before `<script>`)
|
|
197
|
+
```html
|
|
198
|
+
<div id="tooltip" style="position:fixed;pointer-events:none;z-index:100;
|
|
199
|
+
background:var(--graphit-surface-raised);color:var(--graphit-fg);
|
|
200
|
+
border:1px solid var(--graphit-border);border-radius:8px;
|
|
201
|
+
padding:10px 14px;font-size:13px;line-height:1.5;
|
|
202
|
+
box-shadow:0 4px 12px rgba(0,0,0,0.15);opacity:0;transition:opacity 0.15s;
|
|
203
|
+
max-width:240px"></div>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### JS helpers (add once)
|
|
207
|
+
```js
|
|
208
|
+
var tooltip = document.getElementById('tooltip');
|
|
209
|
+
function showTooltip(e, html) {
|
|
210
|
+
tooltip.innerHTML = html;
|
|
211
|
+
tooltip.style.opacity = '1';
|
|
212
|
+
var tx = Math.min(e.clientX + 12, window.innerWidth - 260);
|
|
213
|
+
var ty = e.clientY - tooltip.offsetHeight - 8;
|
|
214
|
+
tooltip.style.left = tx + 'px';
|
|
215
|
+
tooltip.style.top = (ty < 4 ? e.clientY + 16 : ty) + 'px';
|
|
216
|
+
}
|
|
217
|
+
function hideTooltip() { tooltip.style.opacity = '0'; }
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Tooltip row layout
|
|
221
|
+
```html
|
|
222
|
+
<!-- Inside tooltip content -->
|
|
223
|
+
<div style="font-weight:700;margin-bottom:4px">Title</div>
|
|
224
|
+
<div style="display:flex;justify-content:space-between;gap:16px">
|
|
225
|
+
<span style="color:var(--graphit-fg-muted)">Label</span>
|
|
226
|
+
<span style="font-weight:600">Value</span>
|
|
227
|
+
</div>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### SVG event attachment patterns
|
|
231
|
+
```js
|
|
232
|
+
// Bubble/scatter/donut: attach on each SVG <g> or <path>
|
|
233
|
+
element.addEventListener('mouseenter', function(e) { showTooltip(e, htmlContent); });
|
|
234
|
+
element.addEventListener('mousemove', function(e) { showTooltip(e, tooltip.innerHTML); });
|
|
235
|
+
element.addEventListener('mouseleave', hideTooltip);
|
|
236
|
+
|
|
237
|
+
// Line chart: invisible rect columns per x-tick + vertical guide line
|
|
238
|
+
weekData.forEach(function(w, i) {
|
|
239
|
+
var rect = document.createElementNS(NS, 'rect');
|
|
240
|
+
// x, y, width=colWidth, height=plotHeight, fill='transparent', cursor='pointer'
|
|
241
|
+
var vLine = document.createElementNS(NS, 'line');
|
|
242
|
+
// vertical dashed line at xPos(i), opacity=0
|
|
243
|
+
rect.addEventListener('mouseenter', function(e) {
|
|
244
|
+
vLine.setAttribute('opacity', '1');
|
|
245
|
+
showTooltip(e, buildWeekTooltip(w));
|
|
246
|
+
});
|
|
247
|
+
rect.addEventListener('mouseleave', function() {
|
|
248
|
+
vLine.setAttribute('opacity', '0');
|
|
249
|
+
hideTooltip();
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// CSS bar rows: attach on each bar <div> row
|
|
254
|
+
row.addEventListener('mouseenter', function(e) { showTooltip(e, htmlContent); });
|
|
255
|
+
row.addEventListener('mousemove', function(e) { showTooltip(e, tooltip.innerHTML); });
|
|
256
|
+
row.addEventListener('mouseleave', hideTooltip);
|
|
257
|
+
```
|