chartai 1.0.0 → 1.1.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 (87) hide show
  1. package/dist/chart-library.d.ts +1 -0
  2. package/dist/chart-library.d.ts.map +1 -1
  3. package/dist/chart-library.js +43 -11
  4. package/dist/chart-library.min.js +1 -1
  5. package/dist/charts/area.js +1 -1
  6. package/dist/charts/area.min.js +1 -1
  7. package/dist/charts/bar.js +1 -1
  8. package/dist/charts/bar.min.js +1 -1
  9. package/dist/charts/boids.js +9 -9
  10. package/dist/charts/boids.min.js +1 -1
  11. package/dist/charts/candlestick.js +5 -127
  12. package/dist/charts/candlestick.min.js +1 -32
  13. package/dist/charts/experimental/baseline-area.js +70 -0
  14. package/dist/charts/experimental/baseline-area.min.js +1 -0
  15. package/dist/charts/experimental/bubble.js +48 -0
  16. package/dist/charts/experimental/bubble.min.js +1 -0
  17. package/dist/charts/experimental/error-band.js +111 -0
  18. package/dist/charts/experimental/error-band.min.js +1 -0
  19. package/dist/charts/experimental/heatmap.js +69 -0
  20. package/dist/charts/experimental/heatmap.min.js +1 -0
  21. package/dist/charts/experimental/histogram.js +139 -0
  22. package/dist/charts/experimental/histogram.min.js +7 -0
  23. package/dist/charts/experimental/ohlc.js +132 -0
  24. package/dist/charts/experimental/ohlc.min.js +32 -0
  25. package/dist/charts/experimental/step.js +67 -0
  26. package/dist/charts/experimental/step.min.js +1 -0
  27. package/dist/charts/experimental/waterfall.js +121 -0
  28. package/dist/charts/experimental/waterfall.min.js +7 -0
  29. package/dist/charts/line.js +1 -1
  30. package/dist/charts/line.min.js +1 -1
  31. package/dist/charts/scatter.js +1 -1
  32. package/dist/charts/scatter.min.js +1 -1
  33. package/dist/{chunk-e7d3zgw5.min.js → chunk-0eh4rzy9.min.js} +1 -1
  34. package/dist/{chunk-a27be8p9.js → chunk-1ngxm8t2.js} +25 -1
  35. package/dist/chunk-50bcv2hw.min.js +2 -0
  36. package/dist/{chunk-dmaxrg6s.min.js → chunk-64q9a7nw.min.js} +1 -1
  37. package/dist/{chunk-me3qaz3m.min.js → chunk-bbyt23tw.min.js} +1 -1
  38. package/dist/chunk-cbydth3q.min.js +2 -0
  39. package/dist/chunk-cvtt04m6.min.js +2 -0
  40. package/dist/chunk-g2qmt43n.min.js +33 -0
  41. package/dist/{chunk-1p45ex5n.min.js → chunk-gm0d4cgx.min.js} +2 -2
  42. package/dist/chunk-mmsy3yqt.js +27 -0
  43. package/dist/{chunk-g6m56ptf.js → chunk-n8ew0z0e.js} +38 -10
  44. package/dist/chunk-t0kdz02m.js +129 -0
  45. package/dist/{chunk-bfyv7z27.min.js → chunk-wdfq2fpx.min.js} +1 -1
  46. package/dist/gpu-worker.js +8 -3
  47. package/dist/gpu-worker.min.js +1 -1
  48. package/dist/plugins/experimental/annotations.js +164 -0
  49. package/dist/plugins/experimental/annotations.min.js +1 -0
  50. package/dist/plugins/experimental/crosshair.js +82 -0
  51. package/dist/plugins/experimental/crosshair.min.js +1 -0
  52. package/dist/plugins/experimental/minimap.js +190 -0
  53. package/dist/plugins/experimental/minimap.min.js +1 -0
  54. package/dist/plugins/experimental/range-selector.js +220 -0
  55. package/dist/plugins/experimental/range-selector.min.js +1 -0
  56. package/dist/plugins/experimental/ruler.js +434 -0
  57. package/dist/plugins/experimental/ruler.min.js +59 -0
  58. package/dist/plugins/experimental/stats.js +229 -0
  59. package/dist/plugins/experimental/stats.min.js +8 -0
  60. package/dist/plugins/experimental/threshold.js +96 -0
  61. package/dist/plugins/experimental/threshold.min.js +1 -0
  62. package/dist/plugins/experimental/tooltip-pin.js +177 -0
  63. package/dist/plugins/experimental/tooltip-pin.min.js +1 -0
  64. package/dist/plugins/experimental/watermark.js +76 -0
  65. package/dist/plugins/experimental/watermark.min.js +1 -0
  66. package/dist/plugins/hover.d.ts.map +1 -1
  67. package/dist/plugins/hover.js +12 -30
  68. package/dist/plugins/hover.min.js +1 -1
  69. package/dist/plugins/labels-panel.js +3 -3
  70. package/dist/plugins/labels-panel.min.js +1 -1
  71. package/dist/plugins/labels.d.ts.map +1 -1
  72. package/dist/plugins/labels.js +3 -3
  73. package/dist/plugins/labels.min.js +1 -1
  74. package/dist/plugins/legend.d.ts.map +1 -1
  75. package/dist/plugins/legend.js +87 -16
  76. package/dist/plugins/legend.min.js +23 -7
  77. package/dist/plugins/zoom.js +2 -2
  78. package/dist/plugins/zoom.min.js +1 -1
  79. package/dist/types.d.ts +8 -0
  80. package/dist/types.d.ts.map +1 -1
  81. package/dist/worker-inline.d.ts +1 -1
  82. package/dist/worker-inline.d.ts.map +1 -1
  83. package/package.json +1 -1
  84. package/readme.md +6 -3
  85. package/dist/chunk-94kc81rr.min.js +0 -2
  86. package/dist/chunk-qr6mweck.min.js +0 -2
  87. /package/dist/{chunk-m17t3vjq.js → chunk-5gtx3pza.js} +0 -0
@@ -0,0 +1 @@
1
+ import{q as w}from"../../chunk-50bcv2hw.js";import"../../chunk-wdfq2fpx.js";import"../../chunk-bbyt23tw.js";var x=new WeakMap;function R(e,c){let{canvas:X,ctx:s}=c,d=X.width,b=X.height,t=w.isDark,C=e.config,i=C.bgColor??(t?[0.11,0.11,0.12]:[0.98,0.98,0.98]);s.fillStyle=`rgb(${i.map((o)=>Math.round(o*255)).join(",")})`,s.fillRect(0,0,d,b);let{bounds:f}=e,M=f.maxX-f.minX||1,u=f.maxY-f.minY||1;for(let o of e.series){if(o.rawX.length===0)continue;let{r:v,g:z,b:y}=o.color;s.strokeStyle=`rgba(${Math.round(v*255)},${Math.round(z*255)},${Math.round(y*255)},0.7)`,s.lineWidth=1,s.beginPath();let P=Math.max(1,Math.floor(o.rawX.length/d));for(let p=0;p<o.rawX.length;p+=P){let S=(o.rawX[p]-f.minX)/M*d,D=b-(o.rawY[p]-f.minY)/u*b;if(p===0)s.moveTo(S,D);else s.lineTo(S,D)}s.stroke()}let{view:n}=e,r=e.homeView,m=1/r.zoomX,a=(n.panX-r.panX)/m*d,g=(n.panX-r.panX+1/n.zoomX)/m*d,l=C.brushColor??(t?"rgba(255,255,255,0.12)":"rgba(0,100,255,0.1)"),h=t?"rgba(255,255,255,0.35)":"rgba(0,100,255,0.5)";s.fillStyle=t?"rgba(0,0,0,0.35)":"rgba(255,255,255,0.5)",s.fillRect(0,0,a,b),s.fillRect(g,0,d-g,b),s.fillStyle=l,s.fillRect(a,0,g-a,b),s.strokeStyle=h,s.lineWidth=1.5,s.strokeRect(a,0.75,g-a,b-1.5)}var L={name:"range-selector",install(e,c){let X=e.config,s=X.rangeSelectorHeight??60,d=X.rangeSelectorMargin??4,b=new AbortController,t=document.createElement("canvas");if(t.style.cssText=`display:block;margin-top:${d}px;`,t.height=s,t.width=c.offsetWidth||400,c.parentElement)c.parentElement.insertBefore(t,c.nextSibling);let C=t.getContext("2d"),i={canvas:t,ctx:C,abort:b,resizeObserver:null,brushDrag:null};x.set(e,i);let f=(u)=>{let n=t.getBoundingClientRect();return Math.max(0,Math.min(1,(u.clientX-n.left)/n.width))};t.addEventListener("mousemove",(u)=>{if(i.brushDrag)return;let n=f(u),{view:r}=e,m=e.homeView,a=1/m.zoomX,g=(r.panX-m.panX)/a,l=g+m.zoomX/r.zoomX,h=5/t.getBoundingClientRect().width;if(Math.abs(n-g)<h||Math.abs(n-l)<h)t.style.cursor="ew-resize";else if(n>g&&n<l)t.style.cursor="grab";else t.style.cursor="default"},{signal:b.signal}),t.addEventListener("mousedown",(u)=>{let n=f(u),{view:r}=e,m=e.homeView,a=1/m.zoomX,g=(r.panX-m.panX)/a,l=g+m.zoomX/r.zoomX,h=5/t.getBoundingClientRect().width;if(Math.abs(n-g)<h)i.brushDrag={type:"left",startX:n,startPanX:r.panX,startZoomX:r.zoomX};else if(Math.abs(n-l)<h)i.brushDrag={type:"right",startX:n,startPanX:r.panX,startZoomX:r.zoomX};else if(n>g&&n<l)i.brushDrag={type:"move",startX:n,startPanX:r.panX,startZoomX:r.zoomX},t.style.cursor="grabbing";else{let o=e.homeView,v=1/o.zoomX,z=1/r.zoomX,y=Math.max(o.panX,Math.min(o.panX+v-z,o.panX+n*v-z/2));e.view.panX=y,w.requestRender(e.id),w.drawChart(e),R(e,i)}u.preventDefault()},{signal:b.signal}),window.addEventListener("mousemove",(u)=>{if(!i.brushDrag)return;let n=t.getBoundingClientRect(),m=(u.clientX-n.left)/n.width-i.brushDrag.startX,{startPanX:a,startZoomX:g}=i.brushDrag,l=e.homeView,h=1/l.zoomX;if(i.brushDrag.type==="move")e.view.panX=Math.max(l.panX,Math.min(l.panX+h-1/g,a+m*h));else if(i.brushDrag.type==="left"){let o=Math.max(l.panX,a+m*h),v=a+1/g;if(o<v-0.01)e.view.panX=o,e.view.zoomX=Math.max(l.zoomX,1/(v-o))}else if(i.brushDrag.type==="right"){let o=Math.min(l.panX+h,a+1/g+m*h);if(o>a+0.01)e.view.zoomX=Math.max(l.zoomX,1/(o-a))}w.requestRender(e.id),w.drawChart(e),R(e,i)},{signal:b.signal}),window.addEventListener("mouseup",(u)=>{i.brushDrag=null;let n=t.getBoundingClientRect(),r=Math.max(0,Math.min(1,(u.clientX-n.left)/n.width)),{view:m}=e,a=e.homeView,g=1/a.zoomX,l=(m.panX-a.panX)/g,h=l+a.zoomX/m.zoomX,o=5/t.getBoundingClientRect().width;if(Math.abs(r-l)<o||Math.abs(r-h)<o)t.style.cursor="ew-resize";else if(r>l&&r<h)t.style.cursor="grab";else t.style.cursor="default"},{signal:b.signal});let M=new ResizeObserver(()=>{let u=c.offsetWidth;if(u>0)t.width=u;R(e,i)});M.observe(c),i.resizeObserver=M,R(e,i)},afterDraw(e,c){let X=x.get(c);if(!X)return;let s=c.el.offsetWidth;if(s>0&&X.canvas.width!==s)X.canvas.width=s;R(c,X)},uninstall(e){let c=x.get(e);if(c)c.abort.abort(),c.resizeObserver.disconnect(),c.canvas.remove(),x.delete(e)}};export{L as rangeSelectorPlugin};
@@ -0,0 +1,434 @@
1
+ import {
2
+ dataToScreen,
3
+ screenToData
4
+ } from "../../chunk-mmsy3yqt.js";
5
+ import {
6
+ DEFAULT_FONT
7
+ } from "../../chunk-1ngxm8t2.js";
8
+ import {
9
+ MARGIN
10
+ } from "../../chunk-831dem4f.js";
11
+ import {
12
+ ChartManager
13
+ } from "../../chunk-n8ew0z0e.js";
14
+ import"../../chunk-93yrr7er.js";
15
+ import"../../chunk-5gtx3pza.js";
16
+
17
+ // src/plugins/experimental/ruler.ts
18
+ var states = new WeakMap;
19
+ var ENDPOINT_HIT_PX = 12;
20
+ function showClearBtn(btn, visible) {
21
+ const isVisible = btn.hasAttribute("data-visible");
22
+ if (visible === isVisible)
23
+ return;
24
+ btn.style.transform = "";
25
+ if (visible) {
26
+ btn.setAttribute("data-visible", "");
27
+ } else {
28
+ btn.removeAttribute("data-visible");
29
+ }
30
+ }
31
+ function injectRulerStyles() {
32
+ if (document.getElementById("chart-ruler-styles"))
33
+ return;
34
+ const style = document.createElement("style");
35
+ style.id = "chart-ruler-styles";
36
+ style.textContent = `
37
+ @layer chartai.ruler {
38
+ .chart-ruler-wrapper {
39
+ position: absolute;
40
+ z-index: 1000;
41
+ pointer-events: none;
42
+ display: inline-block;
43
+ }
44
+ .chart-ruler-btn {
45
+ position: relative;
46
+ width: 28px;
47
+ height: 28px;
48
+ border-radius: 50%;
49
+ border: 1.5px solid var(--ruler-btn-border);
50
+ background: var(--ruler-btn-bg);
51
+ color: var(--ruler-btn-color);
52
+ z-index: 100;
53
+ cursor: pointer;
54
+ pointer-events: auto;
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: center;
58
+ padding: 0;
59
+ box-shadow: 0 1px 4px rgba(0,0,0,0.18);
60
+ }
61
+ .chart-ruler-btn[data-active] {
62
+ --ruler-btn-border: var(--ruler-active-border);
63
+ --ruler-btn-bg: var(--ruler-active-bg);
64
+ --ruler-btn-color: var(--ruler-active-color);
65
+ }
66
+ .chart-ruler-clear {
67
+ position: absolute;
68
+ top: -6px;
69
+ right: -6px;
70
+ width: 18px;
71
+ height: 18px;
72
+ border-radius: 50%;
73
+ border: 1px solid oklch(from var(--ruler-active-color) calc(l * 0.7) c h / 1);
74
+ background: oklch(from var(--ruler-active-color) calc(l * 0.22) c h / 1);
75
+ color: var(--ruler-active-color);
76
+ z-index: 101;
77
+ cursor: pointer;
78
+ pointer-events: none;
79
+ display: flex;
80
+ align-items: center;
81
+ justify-content: center;
82
+ padding: 0;
83
+ box-shadow: 0 1px 3px rgba(0,0,0,0.22);
84
+ opacity: 0;
85
+ transform: scale(0.5);
86
+ transition: opacity 0.18s ease, transform 0.18s cubic-bezier(0.4, 0, 1, 1);
87
+ }
88
+ .chart-ruler-clear[data-visible] {
89
+ opacity: 1;
90
+ transform: scale(1);
91
+ pointer-events: auto;
92
+ transition: opacity 0.22s ease, transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
93
+ }
94
+ }`;
95
+ document.head.appendChild(style);
96
+ }
97
+ function applyTheme(el, dark) {
98
+ const s = el.style;
99
+ if (dark) {
100
+ s.setProperty("--ruler-btn-border", "rgba(255,255,255,0.2)");
101
+ s.setProperty("--ruler-btn-bg", "rgba(30,30,32,0.88)");
102
+ s.setProperty("--ruler-btn-color", "rgba(200,200,200,0.9)");
103
+ s.setProperty("--ruler-active-border", "rgba(255,200,50,0.8)");
104
+ s.setProperty("--ruler-active-bg", "rgba(255,200,50,0.2)");
105
+ s.setProperty("--ruler-active-color", "rgba(255,200,50,0.95)");
106
+ s.setProperty("--ruler-clear-border", "rgba(255,255,255,0.18)");
107
+ s.setProperty("--ruler-clear-bg", "rgba(30,30,32,0.95)");
108
+ s.setProperty("--ruler-clear-color", "rgba(200,200,200,0.9)");
109
+ } else {
110
+ s.setProperty("--ruler-btn-border", "rgba(0,0,0,0.15)");
111
+ s.setProperty("--ruler-btn-bg", "rgba(255,255,255,0.92)");
112
+ s.setProperty("--ruler-btn-color", "rgba(80,80,80,0.9)");
113
+ s.setProperty("--ruler-active-border", "rgba(180,100,0,0.8)");
114
+ s.setProperty("--ruler-active-bg", "rgba(180,100,0,0.12)");
115
+ s.setProperty("--ruler-active-color", "rgba(180,100,0,0.95)");
116
+ s.setProperty("--ruler-clear-border", "rgba(0,0,0,0.13)");
117
+ s.setProperty("--ruler-clear-bg", "rgba(255,255,255,0.97)");
118
+ s.setProperty("--ruler-clear-color", "rgba(80,80,80,0.9)");
119
+ }
120
+ }
121
+ function setWrapperPosition(el, pos = "bottom-right") {
122
+ const m = MARGIN;
123
+ const pad = 8;
124
+ el.style.removeProperty("top");
125
+ el.style.removeProperty("bottom");
126
+ el.style.removeProperty("left");
127
+ el.style.removeProperty("right");
128
+ switch (pos) {
129
+ case "top-left":
130
+ el.style.top = `${m.top + pad}px`;
131
+ el.style.left = `${m.left + pad}px`;
132
+ break;
133
+ case "top-right":
134
+ el.style.top = `${m.top + pad}px`;
135
+ el.style.right = `${m.right + pad}px`;
136
+ break;
137
+ case "bottom-right":
138
+ el.style.bottom = `${m.bottom + pad}px`;
139
+ el.style.right = `${m.right + pad}px`;
140
+ break;
141
+ default:
142
+ el.style.bottom = `${m.bottom + pad}px`;
143
+ el.style.left = `${m.left + pad}px`;
144
+ }
145
+ }
146
+ function drawPill(ctx, text, cx, cy, color, fontFamily, dark) {
147
+ ctx.font = `500 11px ${fontFamily}`;
148
+ const tw = ctx.measureText(text).width;
149
+ const pw = tw + 16;
150
+ const ph = 20;
151
+ const px = cx - pw / 2;
152
+ const py = cy - ph / 2;
153
+ ctx.beginPath();
154
+ ctx.roundRect(px, py, pw, ph, 5);
155
+ ctx.fillStyle = dark ? "rgba(20,20,22,0.90)" : "rgba(255,255,255,0.95)";
156
+ ctx.fill();
157
+ ctx.strokeStyle = color;
158
+ ctx.lineWidth = 1.5;
159
+ ctx.stroke();
160
+ ctx.fillStyle = color;
161
+ ctx.textAlign = "center";
162
+ ctx.textBaseline = "middle";
163
+ ctx.fillText(text, cx, cy);
164
+ }
165
+ function drawEndpoint(ctx, x, y, color, dark) {
166
+ ctx.beginPath();
167
+ ctx.arc(x, y, 5, 0, Math.PI * 2);
168
+ ctx.fillStyle = color;
169
+ ctx.fill();
170
+ ctx.strokeStyle = dark ? "rgba(0,0,0,0.5)" : "rgba(255,255,255,0.8)";
171
+ ctx.lineWidth = 1.5;
172
+ ctx.stroke();
173
+ }
174
+ function drawRulerShape(ctx, a, b, axis, chart, w, h, color, formatX, formatY, fontFamily, dark) {
175
+ const sa = dataToScreen(a.dataX, a.dataY, chart, w, h);
176
+ const sb = dataToScreen(b.dataX, b.dataY, chart, w, h);
177
+ ctx.save();
178
+ ctx.strokeStyle = color;
179
+ ctx.lineWidth = 1.5;
180
+ ctx.setLineDash([5, 4]);
181
+ const TICK = 6;
182
+ if (axis === "x") {
183
+ const midY = (sa.y + sb.y) / 2;
184
+ ctx.beginPath();
185
+ ctx.moveTo(sa.x, midY);
186
+ ctx.lineTo(sb.x, midY);
187
+ ctx.stroke();
188
+ ctx.setLineDash([]);
189
+ ctx.beginPath();
190
+ ctx.moveTo(sa.x, midY - TICK);
191
+ ctx.lineTo(sa.x, midY + TICK);
192
+ ctx.moveTo(sb.x, midY - TICK);
193
+ ctx.lineTo(sb.x, midY + TICK);
194
+ ctx.stroke();
195
+ drawEndpoint(ctx, sa.x, sa.y, color, dark);
196
+ drawEndpoint(ctx, sb.x, sb.y, color, dark);
197
+ const labelX = (sa.x + sb.x) / 2;
198
+ const labelY = midY - 16;
199
+ const dx = Math.abs(b.dataX - a.dataX);
200
+ drawPill(ctx, `ΔX: ${formatX(dx)}`, labelX, labelY, color, fontFamily, dark);
201
+ } else if (axis === "y") {
202
+ const midX = (sa.x + sb.x) / 2;
203
+ ctx.beginPath();
204
+ ctx.moveTo(midX, sa.y);
205
+ ctx.lineTo(midX, sb.y);
206
+ ctx.stroke();
207
+ ctx.setLineDash([]);
208
+ ctx.beginPath();
209
+ ctx.moveTo(midX - TICK, sa.y);
210
+ ctx.lineTo(midX + TICK, sa.y);
211
+ ctx.moveTo(midX - TICK, sb.y);
212
+ ctx.lineTo(midX + TICK, sb.y);
213
+ ctx.stroke();
214
+ drawEndpoint(ctx, sa.x, sa.y, color, dark);
215
+ drawEndpoint(ctx, sb.x, sb.y, color, dark);
216
+ const labelX = midX + 20;
217
+ const labelY = (sa.y + sb.y) / 2;
218
+ const dy = Math.abs(b.dataY - a.dataY);
219
+ drawPill(ctx, `ΔY: ${formatY(dy)}`, labelX, labelY, color, fontFamily, dark);
220
+ } else {
221
+ ctx.beginPath();
222
+ ctx.moveTo(sa.x, sa.y);
223
+ ctx.lineTo(sb.x, sb.y);
224
+ ctx.stroke();
225
+ drawEndpoint(ctx, sa.x, sa.y, color, dark);
226
+ drawEndpoint(ctx, sb.x, sb.y, color, dark);
227
+ const labelX = (sa.x + sb.x) / 2;
228
+ const labelY = (sa.y + sb.y) / 2 - 18;
229
+ const dx = Math.abs(b.dataX - a.dataX);
230
+ const dy = Math.abs(b.dataY - a.dataY);
231
+ drawPill(ctx, `ΔX: ${formatX(dx)} ΔY: ${formatY(dy)}`, labelX, labelY, color, fontFamily, dark);
232
+ }
233
+ ctx.restore();
234
+ }
235
+ var rulerPlugin = {
236
+ name: "ruler",
237
+ install(chart, el) {
238
+ injectRulerStyles();
239
+ const ac = new AbortController;
240
+ const cfg = chart.config;
241
+ const dark = ChartManager.isDark;
242
+ const wrapper = document.createElement("div");
243
+ wrapper.className = "chart-ruler-wrapper";
244
+ setWrapperPosition(wrapper, cfg.rulerPosition);
245
+ applyTheme(wrapper, dark);
246
+ const button = document.createElement("button");
247
+ button.type = "button";
248
+ button.className = "chart-ruler-btn";
249
+ button.title = "Toggle ruler tool";
250
+ button.innerHTML = `<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="4.5" width="12" height="5" rx="1" stroke="currentColor" stroke-width="1.3"/><line x1="3.5" y1="4.5" x2="3.5" y2="7" stroke="currentColor" stroke-width="1.2"/><line x1="5.5" y1="4.5" x2="5.5" y2="6" stroke="currentColor" stroke-width="1.2"/><line x1="7" y1="4.5" x2="7" y2="7" stroke="currentColor" stroke-width="1.2"/><line x1="8.5" y1="4.5" x2="8.5" y2="6" stroke="currentColor" stroke-width="1.2"/><line x1="10.5" y1="4.5" x2="10.5" y2="7" stroke="currentColor" stroke-width="1.2"/></svg>`;
251
+ const clearBtn = document.createElement("button");
252
+ clearBtn.type = "button";
253
+ clearBtn.className = "chart-ruler-clear";
254
+ clearBtn.title = "Clear all rulers";
255
+ clearBtn.innerHTML = `<svg width="5" height="5" viewBox="0 0 6 6" fill="none" xmlns="http://www.w3.org/2000/svg"><line x1="1" y1="1" x2="5" y2="5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><line x1="5" y1="1" x2="1" y2="5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>`;
256
+ wrapper.appendChild(button);
257
+ wrapper.appendChild(clearBtn);
258
+ el.appendChild(wrapper);
259
+ const state = {
260
+ rulers: [],
261
+ pending: null,
262
+ cursorDataX: 0,
263
+ cursorDataY: 0,
264
+ active: false,
265
+ justToggledButton: false,
266
+ abort: ac,
267
+ button,
268
+ clearBtn,
269
+ wrapper
270
+ };
271
+ states.set(chart, state);
272
+ button.addEventListener("pointerdown", (e) => {
273
+ e.stopPropagation();
274
+ e.stopImmediatePropagation();
275
+ e.preventDefault();
276
+ state.active = !state.active;
277
+ if (!state.active)
278
+ state.pending = null;
279
+ state.justToggledButton = true;
280
+ if (state.active)
281
+ button.dataset.active = "";
282
+ else
283
+ delete button.dataset.active;
284
+ ChartManager.drawChart(chart);
285
+ }, { signal: ac.signal });
286
+ clearBtn.addEventListener("pointerdown", (e) => {
287
+ e.stopPropagation();
288
+ e.stopImmediatePropagation();
289
+ e.preventDefault();
290
+ clearBtn.style.transform = "scale(0.78)";
291
+ state.rulers = [];
292
+ state.pending = null;
293
+ state.justToggledButton = true;
294
+ ChartManager.drawChart(chart);
295
+ }, { signal: ac.signal });
296
+ clearBtn.addEventListener("click", (e) => {
297
+ e.stopPropagation();
298
+ e.stopImmediatePropagation();
299
+ e.preventDefault();
300
+ }, { signal: ac.signal });
301
+ el.addEventListener("mousemove", (e) => {
302
+ const r = el.getBoundingClientRect();
303
+ const { x, y } = screenToData(e.clientX - r.left, e.clientY - r.top, chart, r.width, r.height);
304
+ state.cursorDataX = x;
305
+ state.cursorDataY = y;
306
+ if (state.active || state.pending !== null) {
307
+ ChartManager.drawChart(chart);
308
+ }
309
+ e.preventDefault();
310
+ }, { signal: ac.signal });
311
+ el.addEventListener("click", (e) => {
312
+ if (!state.active)
313
+ return;
314
+ if (state.justToggledButton) {
315
+ state.justToggledButton = false;
316
+ return;
317
+ }
318
+ if (chart.dragging)
319
+ return;
320
+ e.preventDefault();
321
+ const r = el.getBoundingClientRect();
322
+ const sx = e.clientX - r.left;
323
+ const sy = e.clientY - r.top;
324
+ const { x: dataX, y: dataY } = screenToData(sx, sy, chart, r.width, r.height);
325
+ for (let i = state.rulers.length - 1;i >= 0; i--) {
326
+ const ruler = state.rulers[i];
327
+ const sa = dataToScreen(ruler.a.dataX, ruler.a.dataY, chart, r.width, r.height);
328
+ const sb = dataToScreen(ruler.b.dataX, ruler.b.dataY, chart, r.width, r.height);
329
+ const dA = Math.hypot(sa.x - sx, sa.y - sy);
330
+ const dB = Math.hypot(sb.x - sx, sb.y - sy);
331
+ if (dA <= ENDPOINT_HIT_PX || dB <= ENDPOINT_HIT_PX) {
332
+ state.rulers.splice(i, 1);
333
+ ChartManager.drawChart(chart);
334
+ return;
335
+ }
336
+ }
337
+ if (state.pending === null) {
338
+ state.pending = { dataX, dataY };
339
+ } else {
340
+ const rulerMax = chart.config.rulerMax ?? 10;
341
+ if (state.rulers.length >= rulerMax)
342
+ state.rulers.shift();
343
+ state.rulers.push({ a: state.pending, b: { dataX, dataY } });
344
+ state.pending = null;
345
+ ChartManager.drawChart(chart);
346
+ }
347
+ }, { signal: ac.signal });
348
+ el.addEventListener("contextmenu", (e) => {
349
+ e.preventDefault();
350
+ if (state.pending !== null) {
351
+ state.pending = null;
352
+ } else {
353
+ const r = el.getBoundingClientRect();
354
+ const sx = e.clientX - r.left;
355
+ const sy = e.clientY - r.top;
356
+ let bestIdx = -1;
357
+ let bestDist = Infinity;
358
+ for (let i = 0;i < state.rulers.length; i++) {
359
+ const ruler = state.rulers[i];
360
+ const sa = dataToScreen(ruler.a.dataX, ruler.a.dataY, chart, r.width, r.height);
361
+ const sb = dataToScreen(ruler.b.dataX, ruler.b.dataY, chart, r.width, r.height);
362
+ const dA = Math.hypot(sa.x - sx, sa.y - sy);
363
+ const dB = Math.hypot(sb.x - sx, sb.y - sy);
364
+ const d = Math.min(dA, dB);
365
+ if (d < bestDist) {
366
+ bestDist = d;
367
+ bestIdx = i;
368
+ }
369
+ }
370
+ if (bestIdx !== -1)
371
+ state.rulers.splice(bestIdx, 1);
372
+ }
373
+ ChartManager.drawChart(chart);
374
+ }, { signal: ac.signal });
375
+ window.addEventListener("keydown", (e) => {
376
+ if (e.key === "Escape" && state.active) {
377
+ state.pending = null;
378
+ ChartManager.drawChart(chart);
379
+ }
380
+ }, { signal: ac.signal });
381
+ },
382
+ afterDraw(ctx, chart) {
383
+ const state = states.get(chart);
384
+ if (!state)
385
+ return;
386
+ const w = chart.width;
387
+ const h = chart.height;
388
+ const dark = ChartManager.isDark;
389
+ const cfg = chart.config;
390
+ const axis = cfg.rulerAxis ?? "x";
391
+ const color = cfg.rulerColor ?? (dark ? "rgba(255,200,50,0.9)" : "rgba(180,100,0,0.9)");
392
+ const formatX = cfg.formatX ?? String;
393
+ const formatY = cfg.formatY ?? String;
394
+ const fontFamily = cfg.fontFamily ?? DEFAULT_FONT;
395
+ setWrapperPosition(state.wrapper, cfg.rulerPosition);
396
+ applyTheme(state.wrapper, dark);
397
+ if (state.active)
398
+ state.button.dataset.active = "";
399
+ else
400
+ delete state.button.dataset.active;
401
+ showClearBtn(state.clearBtn, state.rulers.length > 0);
402
+ ctx.save();
403
+ for (const ruler of state.rulers) {
404
+ drawRulerShape(ctx, ruler.a, ruler.b, axis, chart, w, h, color, formatX, formatY, fontFamily, dark);
405
+ }
406
+ if (state.pending !== null) {
407
+ const cursor = {
408
+ dataX: state.cursorDataX,
409
+ dataY: state.cursorDataY
410
+ };
411
+ drawRulerShape(ctx, state.pending, cursor, axis, chart, w, h, color, formatX, formatY, fontFamily, dark);
412
+ const sc = dataToScreen(state.cursorDataX, state.cursorDataY, chart, w, h);
413
+ ctx.save();
414
+ ctx.beginPath();
415
+ ctx.arc(sc.x, sc.y, 5, 0, Math.PI * 2);
416
+ ctx.strokeStyle = color;
417
+ ctx.lineWidth = 1.5;
418
+ ctx.stroke();
419
+ ctx.restore();
420
+ }
421
+ ctx.restore();
422
+ },
423
+ uninstall(chart) {
424
+ const state = states.get(chart);
425
+ if (!state)
426
+ return;
427
+ state.abort.abort();
428
+ state.wrapper.remove();
429
+ states.delete(chart);
430
+ }
431
+ };
432
+ export {
433
+ rulerPlugin
434
+ };
@@ -0,0 +1,59 @@
1
+ import{j as w,k as R}from"../../chunk-cbydth3q.js";import{l as L}from"../../chunk-cvtt04m6.js";import{o as E}from"../../chunk-0eh4rzy9.js";import{q as v}from"../../chunk-50bcv2hw.js";import"../../chunk-wdfq2fpx.js";import"../../chunk-bbyt23tw.js";var P=new WeakMap,X=12;function A(e,n){let t=e.hasAttribute("data-visible");if(n===t)return;if(e.style.transform="",n)e.setAttribute("data-visible","");else e.removeAttribute("data-visible")}function I(){if(document.getElementById("chart-ruler-styles"))return;let e=document.createElement("style");e.id="chart-ruler-styles",e.textContent=`
2
+ @layer chartai.ruler {
3
+ .chart-ruler-wrapper {
4
+ position: absolute;
5
+ z-index: 1000;
6
+ pointer-events: none;
7
+ display: inline-block;
8
+ }
9
+ .chart-ruler-btn {
10
+ position: relative;
11
+ width: 28px;
12
+ height: 28px;
13
+ border-radius: 50%;
14
+ border: 1.5px solid var(--ruler-btn-border);
15
+ background: var(--ruler-btn-bg);
16
+ color: var(--ruler-btn-color);
17
+ z-index: 100;
18
+ cursor: pointer;
19
+ pointer-events: auto;
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ padding: 0;
24
+ box-shadow: 0 1px 4px rgba(0,0,0,0.18);
25
+ }
26
+ .chart-ruler-btn[data-active] {
27
+ --ruler-btn-border: var(--ruler-active-border);
28
+ --ruler-btn-bg: var(--ruler-active-bg);
29
+ --ruler-btn-color: var(--ruler-active-color);
30
+ }
31
+ .chart-ruler-clear {
32
+ position: absolute;
33
+ top: -6px;
34
+ right: -6px;
35
+ width: 18px;
36
+ height: 18px;
37
+ border-radius: 50%;
38
+ border: 1px solid oklch(from var(--ruler-active-color) calc(l * 0.7) c h / 1);
39
+ background: oklch(from var(--ruler-active-color) calc(l * 0.22) c h / 1);
40
+ color: var(--ruler-active-color);
41
+ z-index: 101;
42
+ cursor: pointer;
43
+ pointer-events: none;
44
+ display: flex;
45
+ align-items: center;
46
+ justify-content: center;
47
+ padding: 0;
48
+ box-shadow: 0 1px 3px rgba(0,0,0,0.22);
49
+ opacity: 0;
50
+ transform: scale(0.5);
51
+ transition: opacity 0.18s ease, transform 0.18s cubic-bezier(0.4, 0, 1, 1);
52
+ }
53
+ .chart-ruler-clear[data-visible] {
54
+ opacity: 1;
55
+ transform: scale(1);
56
+ pointer-events: auto;
57
+ transition: opacity 0.22s ease, transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
58
+ }
59
+ }`,document.head.appendChild(e)}function Y(e,n){let t=e.style;if(n)t.setProperty("--ruler-btn-border","rgba(255,255,255,0.2)"),t.setProperty("--ruler-btn-bg","rgba(30,30,32,0.88)"),t.setProperty("--ruler-btn-color","rgba(200,200,200,0.9)"),t.setProperty("--ruler-active-border","rgba(255,200,50,0.8)"),t.setProperty("--ruler-active-bg","rgba(255,200,50,0.2)"),t.setProperty("--ruler-active-color","rgba(255,200,50,0.95)"),t.setProperty("--ruler-clear-border","rgba(255,255,255,0.18)"),t.setProperty("--ruler-clear-bg","rgba(30,30,32,0.95)"),t.setProperty("--ruler-clear-color","rgba(200,200,200,0.9)");else t.setProperty("--ruler-btn-border","rgba(0,0,0,0.15)"),t.setProperty("--ruler-btn-bg","rgba(255,255,255,0.92)"),t.setProperty("--ruler-btn-color","rgba(80,80,80,0.9)"),t.setProperty("--ruler-active-border","rgba(180,100,0,0.8)"),t.setProperty("--ruler-active-bg","rgba(180,100,0,0.12)"),t.setProperty("--ruler-active-color","rgba(180,100,0,0.95)"),t.setProperty("--ruler-clear-border","rgba(0,0,0,0.13)"),t.setProperty("--ruler-clear-bg","rgba(255,255,255,0.97)"),t.setProperty("--ruler-clear-color","rgba(80,80,80,0.9)")}function M(e,n="bottom-right"){let t=E,c=8;switch(e.style.removeProperty("top"),e.style.removeProperty("bottom"),e.style.removeProperty("left"),e.style.removeProperty("right"),n){case"top-left":e.style.top=`${t.top+8}px`,e.style.left=`${t.left+8}px`;break;case"top-right":e.style.top=`${t.top+8}px`,e.style.right=`${t.right+8}px`;break;case"bottom-right":e.style.bottom=`${t.bottom+8}px`,e.style.right=`${t.right+8}px`;break;default:e.style.bottom=`${t.bottom+8}px`,e.style.left=`${t.left+8}px`}}function x(e,n,t,c,h,p,g){e.font=`500 11px ${p}`;let r=e.measureText(n).width+16,o=20,s=t-r/2,d=c-o/2;e.beginPath(),e.roundRect(s,d,r,o,5),e.fillStyle=g?"rgba(20,20,22,0.90)":"rgba(255,255,255,0.95)",e.fill(),e.strokeStyle=h,e.lineWidth=1.5,e.stroke(),e.fillStyle=h,e.textAlign="center",e.textBaseline="middle",e.fillText(n,t,c)}function C(e,n,t,c,h){e.beginPath(),e.arc(n,t,5,0,Math.PI*2),e.fillStyle=c,e.fill(),e.strokeStyle=h?"rgba(0,0,0,0.5)":"rgba(255,255,255,0.8)",e.lineWidth=1.5,e.stroke()}function B(e,n,t,c,h,p,g,u,r,o,s,d){let a=w(n.dataX,n.dataY,h,p,g),i=w(t.dataX,t.dataY,h,p,g);e.save(),e.strokeStyle=u,e.lineWidth=1.5,e.setLineDash([5,4]);let y=6;if(c==="x"){let l=(a.y+i.y)/2;e.beginPath(),e.moveTo(a.x,l),e.lineTo(i.x,l),e.stroke(),e.setLineDash([]),e.beginPath(),e.moveTo(a.x,l-y),e.lineTo(a.x,l+y),e.moveTo(i.x,l-y),e.lineTo(i.x,l+y),e.stroke(),C(e,a.x,a.y,u,d),C(e,i.x,i.y,u,d);let b=(a.x+i.x)/2,f=l-16,m=Math.abs(t.dataX-n.dataX);x(e,`ΔX: ${r(m)}`,b,f,u,s,d)}else if(c==="y"){let l=(a.x+i.x)/2;e.beginPath(),e.moveTo(l,a.y),e.lineTo(l,i.y),e.stroke(),e.setLineDash([]),e.beginPath(),e.moveTo(l-y,a.y),e.lineTo(l+y,a.y),e.moveTo(l-y,i.y),e.lineTo(l+y,i.y),e.stroke(),C(e,a.x,a.y,u,d),C(e,i.x,i.y,u,d);let b=l+20,f=(a.y+i.y)/2,m=Math.abs(t.dataY-n.dataY);x(e,`ΔY: ${o(m)}`,b,f,u,s,d)}else{e.beginPath(),e.moveTo(a.x,a.y),e.lineTo(i.x,i.y),e.stroke(),C(e,a.x,a.y,u,d),C(e,i.x,i.y,u,d);let l=(a.x+i.x)/2,b=(a.y+i.y)/2-18,f=Math.abs(t.dataX-n.dataX),m=Math.abs(t.dataY-n.dataY);x(e,`ΔX: ${r(f)} ΔY: ${o(m)}`,l,b,u,s,d)}e.restore()}var N={name:"ruler",install(e,n){I();let t=new AbortController,c=e.config,h=v.isDark,p=document.createElement("div");p.className="chart-ruler-wrapper",M(p,c.rulerPosition),Y(p,h);let g=document.createElement("button");g.type="button",g.className="chart-ruler-btn",g.title="Toggle ruler tool",g.innerHTML='<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="4.5" width="12" height="5" rx="1" stroke="currentColor" stroke-width="1.3"/><line x1="3.5" y1="4.5" x2="3.5" y2="7" stroke="currentColor" stroke-width="1.2"/><line x1="5.5" y1="4.5" x2="5.5" y2="6" stroke="currentColor" stroke-width="1.2"/><line x1="7" y1="4.5" x2="7" y2="7" stroke="currentColor" stroke-width="1.2"/><line x1="8.5" y1="4.5" x2="8.5" y2="6" stroke="currentColor" stroke-width="1.2"/><line x1="10.5" y1="4.5" x2="10.5" y2="7" stroke="currentColor" stroke-width="1.2"/></svg>';let u=document.createElement("button");u.type="button",u.className="chart-ruler-clear",u.title="Clear all rulers",u.innerHTML='<svg width="5" height="5" viewBox="0 0 6 6" fill="none" xmlns="http://www.w3.org/2000/svg"><line x1="1" y1="1" x2="5" y2="5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><line x1="5" y1="1" x2="1" y2="5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>',p.appendChild(g),p.appendChild(u),n.appendChild(p);let r={rulers:[],pending:null,cursorDataX:0,cursorDataY:0,active:!1,justToggledButton:!1,abort:t,button:g,clearBtn:u,wrapper:p};P.set(e,r),g.addEventListener("pointerdown",(o)=>{if(o.stopPropagation(),o.stopImmediatePropagation(),o.preventDefault(),r.active=!r.active,!r.active)r.pending=null;if(r.justToggledButton=!0,r.active)g.dataset.active="";else delete g.dataset.active;v.drawChart(e)},{signal:t.signal}),u.addEventListener("pointerdown",(o)=>{o.stopPropagation(),o.stopImmediatePropagation(),o.preventDefault(),u.style.transform="scale(0.78)",r.rulers=[],r.pending=null,r.justToggledButton=!0,v.drawChart(e)},{signal:t.signal}),u.addEventListener("click",(o)=>{o.stopPropagation(),o.stopImmediatePropagation(),o.preventDefault()},{signal:t.signal}),n.addEventListener("mousemove",(o)=>{let s=n.getBoundingClientRect(),{x:d,y:a}=R(o.clientX-s.left,o.clientY-s.top,e,s.width,s.height);if(r.cursorDataX=d,r.cursorDataY=a,r.active||r.pending!==null)v.drawChart(e);o.preventDefault()},{signal:t.signal}),n.addEventListener("click",(o)=>{if(!r.active)return;if(r.justToggledButton){r.justToggledButton=!1;return}if(e.dragging)return;o.preventDefault();let s=n.getBoundingClientRect(),d=o.clientX-s.left,a=o.clientY-s.top,{x:i,y}=R(d,a,e,s.width,s.height);for(let l=r.rulers.length-1;l>=0;l--){let b=r.rulers[l],f=w(b.a.dataX,b.a.dataY,e,s.width,s.height),m=w(b.b.dataX,b.b.dataY,e,s.width,s.height),k=Math.hypot(f.x-d,f.y-a),T=Math.hypot(m.x-d,m.y-a);if(k<=X||T<=X){r.rulers.splice(l,1),v.drawChart(e);return}}if(r.pending===null)r.pending={dataX:i,dataY:y};else{let l=e.config.rulerMax??10;if(r.rulers.length>=l)r.rulers.shift();r.rulers.push({a:r.pending,b:{dataX:i,dataY:y}}),r.pending=null,v.drawChart(e)}},{signal:t.signal}),n.addEventListener("contextmenu",(o)=>{if(o.preventDefault(),r.pending!==null)r.pending=null;else{let s=n.getBoundingClientRect(),d=o.clientX-s.left,a=o.clientY-s.top,i=-1,y=1/0;for(let l=0;l<r.rulers.length;l++){let b=r.rulers[l],f=w(b.a.dataX,b.a.dataY,e,s.width,s.height),m=w(b.b.dataX,b.b.dataY,e,s.width,s.height),k=Math.hypot(f.x-d,f.y-a),T=Math.hypot(m.x-d,m.y-a),D=Math.min(k,T);if(D<y)y=D,i=l}if(i!==-1)r.rulers.splice(i,1)}v.drawChart(e)},{signal:t.signal}),window.addEventListener("keydown",(o)=>{if(o.key==="Escape"&&r.active)r.pending=null,v.drawChart(e)},{signal:t.signal})},afterDraw(e,n){let t=P.get(n);if(!t)return;let{width:c,height:h}=n,p=v.isDark,g=n.config,u=g.rulerAxis??"x",r=g.rulerColor??(p?"rgba(255,200,50,0.9)":"rgba(180,100,0,0.9)"),o=g.formatX??String,s=g.formatY??String,d=g.fontFamily??L;if(M(t.wrapper,g.rulerPosition),Y(t.wrapper,p),t.active)t.button.dataset.active="";else delete t.button.dataset.active;A(t.clearBtn,t.rulers.length>0),e.save();for(let a of t.rulers)B(e,a.a,a.b,u,n,c,h,r,o,s,d,p);if(t.pending!==null){let a={dataX:t.cursorDataX,dataY:t.cursorDataY};B(e,t.pending,a,u,n,c,h,r,o,s,d,p);let i=w(t.cursorDataX,t.cursorDataY,n,c,h);e.save(),e.beginPath(),e.arc(i.x,i.y,5,0,Math.PI*2),e.strokeStyle=r,e.lineWidth=1.5,e.stroke(),e.restore()}e.restore()},uninstall(e){let n=P.get(e);if(!n)return;n.abort.abort(),n.wrapper.remove(),P.delete(e)}};export{N as rulerPlugin};