@aeriz/chart 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 (72) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +54 -447
  3. package/dist/aeriz.cjs +2575 -2387
  4. package/dist/aeriz.esm.js +1576 -1423
  5. package/dist/aeriz.esm.min.js +264 -1
  6. package/dist/aeriz.js +2575 -2387
  7. package/dist/aeriz.min.cjs +266 -1
  8. package/dist/aeriz.min.js +266 -1
  9. package/dist/esm/base.js +73 -0
  10. package/dist/esm/cartesian.js +11 -0
  11. package/dist/esm/charts/area.js +9 -0
  12. package/dist/esm/charts/bar.js +4 -0
  13. package/dist/esm/charts/boxplot.js +4 -0
  14. package/dist/esm/charts/bubble.js +3 -0
  15. package/dist/esm/charts/composite.js +11 -0
  16. package/dist/esm/charts/funnel.js +3 -0
  17. package/dist/esm/charts/gauge.js +5 -0
  18. package/dist/esm/charts/grouped-bar.js +5 -0
  19. package/dist/esm/charts/hbar.js +6 -0
  20. package/dist/esm/charts/heatmap.js +7 -0
  21. package/dist/esm/charts/line.js +9 -0
  22. package/dist/esm/charts/multi-line.js +8 -0
  23. package/dist/esm/charts/pie.js +6 -0
  24. package/dist/esm/charts/polar.js +3 -0
  25. package/dist/esm/charts/radar.js +10 -0
  26. package/dist/esm/charts/realtime.js +12 -0
  27. package/dist/esm/charts/scatter.js +6 -0
  28. package/dist/esm/charts/sparkline.js +5 -0
  29. package/dist/esm/charts/stacked-bar.js +7 -0
  30. package/dist/esm/charts/treemap.js +10 -0
  31. package/dist/esm/charts/waterfall.js +5 -0
  32. package/dist/esm/engine.js +33 -0
  33. package/dist/esm/index.js +2 -0
  34. package/dist/esm/jquery.js +4 -0
  35. package/dist/esm/math.js +32 -0
  36. package/dist/esm/react.js +4 -0
  37. package/dist/esm/shallow.js +3 -0
  38. package/dist/esm/svelte.js +4 -0
  39. package/dist/esm/vue.js +2 -0
  40. package/{src → dist}/index.d.ts +160 -30
  41. package/dist/jquery.d.ts +10 -0
  42. package/dist/react.d.ts +22 -0
  43. package/dist/svelte.d.ts +17 -0
  44. package/dist/types/area.d.ts +1 -0
  45. package/dist/types/bar.d.ts +1 -0
  46. package/dist/types/boxplot.d.ts +1 -0
  47. package/dist/types/bubble.d.ts +1 -0
  48. package/dist/types/composite.d.ts +1 -0
  49. package/dist/types/funnel.d.ts +1 -0
  50. package/dist/types/gauge.d.ts +1 -0
  51. package/dist/types/grouped-bar.d.ts +1 -0
  52. package/dist/types/hbar.d.ts +1 -0
  53. package/dist/types/heatmap.d.ts +1 -0
  54. package/dist/types/line.d.ts +1 -0
  55. package/dist/types/multi-line.d.ts +1 -0
  56. package/dist/types/pie.d.ts +1 -0
  57. package/dist/types/polar.d.ts +1 -0
  58. package/dist/types/radar.d.ts +1 -0
  59. package/dist/types/realtime.d.ts +1 -0
  60. package/dist/types/scatter.d.ts +1 -0
  61. package/dist/types/sparkline.d.ts +1 -0
  62. package/dist/types/stacked-bar.d.ts +1 -0
  63. package/dist/types/treemap.d.ts +1 -0
  64. package/dist/types/waterfall.d.ts +1 -0
  65. package/dist/vue.d.ts +13 -0
  66. package/package.json +60 -29
  67. package/src/base.js +0 -562
  68. package/src/charts.js +0 -1365
  69. package/src/composite.js +0 -187
  70. package/src/engine.js +0 -287
  71. package/src/index.js +0 -18
  72. package/src/math.js +0 -178
package/dist/aeriz.min.js CHANGED
@@ -1 +1,266 @@
1
- var Aeriz=(function(){"use strict";const M=(function(){const TAU=Math.PI*2;const clamp=(v,lo,hi)=>v<lo?lo:v>hi?hi:v;const lerp=(a,b,t)=>a+(b-a)*t;function extent(arr,fn=(x)=>x){let mn=Infinity,mx=-Infinity;for(let i=0;i<arr.length;i++){const v=fn(arr[i],i);if(v<mn)mn=v;if(v>mx)mx=v;}return[mn===Infinity?0:mn,mx===-Infinity?1:mx];}function niceTicks(min,max,count=5){if(min===max){min-=0.5;max+=0.5;}const span=max-min;const step0=span/Math.max(1,count);const mag=Math.pow(10,Math.floor(Math.log10(step0)));const norm=step0/mag;let step;if(norm<1.5)step=mag;else if(norm<3)step=2*mag;else if(norm<7)step=5*mag;else step=10*mag;const lo=Math.ceil(min/step)*step;const hi=Math.floor(max/step)*step;const ticks=[];for(let v=lo;v<=hi+step/2;v+=step){ticks.push(Math.round(v/step)*step);}return ticks;}function niceDomain(min,max,count=5){if(min===max){min-=0.5;max+=0.5;}const span=max-min;const step0=span/Math.max(1,count);const mag=Math.pow(10,Math.floor(Math.log10(step0)));const norm=step0/mag;let step;if(norm<1.5)step=mag;else if(norm<3)step=2*mag;else if(norm<7)step=5*mag;else step=10*mag;return[Math.floor(min/step)*step,Math.ceil(max/step)*step];}const T={sec:1e3,min:6e4,hour:36e5,day:864e5,week:6.048e8,month:2.628e9};function timeNiceTicks(minMs,maxMs,count=6){if(minMs===maxMs){minMs-=1000;maxMs+=1000;}const span=maxMs-minMs;const target=span/count;const units=[[T.sec,1],[T.sec,5],[T.sec,15],[T.sec,30],[T.min,1],[T.min,5],[T.min,15],[T.min,30],[T.hour,1],[T.hour,3],[T.hour,6],[T.hour,12],[T.day,1],[T.day,2],[T.week,1],[T.month,1],[T.month,3],[T.month,6],[T.month,12],];let step=T.sec;for(const[u,n]of units)if(u*n<=target*1.5)step=u*n;const lo=Math.ceil(minMs/step)*step;const ticks=[];for(let v=lo;v<=maxMs;v+=step)ticks.push(v);return ticks;}function timeFormat(step){if(step<T.min)return(t)=>new Date(t).toISOString().slice(11,19);if(step<T.hour)return(t)=>new Date(t).toISOString().slice(11,16);if(step<T.day)return(t)=>new Date(t).toISOString().slice(11,16);if(step<T.month)return(t)=>new Date(t).toISOString().slice(5,10);return(t)=>new Date(t).toISOString().slice(0,7);}function minMaxDecimate(data,targetCount,fn=(d)=>d.value){if(data.length<=targetCount)return data;const bucket=data.length/targetCount;const out=[];for(let i=0;i<targetCount;i++){const lo=Math.floor(i*bucket);const hi=Math.min(data.length,Math.floor((i+1)*bucket));let mnI=lo,mxI=lo;let mn=fn(data[lo],lo),mx=mn;for(let j=lo+1;j<hi;j++){const v=fn(data[j],j);if(v<mn){mn=v;mnI=j;}if(v>mx){mx=v;mxI=j;}}if(mnI<mxI){out.push(data[mnI]);out.push(data[mxI]);}else{out.push(data[mxI]);out.push(data[mnI]);}}return out;}function shouldDecimate(n,pxWidth){return n>pxWidth*2;}function bezierControlPoints(pts,tension=1/6){const n=pts.length;if(n<2)return[];const cps=[];for(let i=0;i<n-1;i++){const p0=pts[i-1]||pts[i];const p1=pts[i];const p2=pts[i+1];const p3=pts[i+2]||p2;cps.push({c1x:p1.x+(p2.x-p0.x)*tension,c1y:p1.y+(p2.y-p0.y)*tension,c2x:p2.x-(p3.x-p1.x)*tension,c2y:p2.y-(p3.y-p1.y)*tension,});}return cps;}function springStep(v,tgt,vel,cfg={}){const stiffness=cfg.stiffness||170;const damping=cfg.damping||26;const dt=1/60;const F=-stiffness*(v-tgt);const D=-damping*vel;const a=F+D;vel=vel+a*dt;v=v+vel*dt;const done=Math.abs(vel)<0.05&&Math.abs(v-tgt)<0.05;if(done){v=tgt;vel=0;}return{value:v,velocity:vel,done};}function colorParse(c){if(!c)return[0,0,0,1];if(c[0]==='#'){const h=c.slice(1);if(h.length===3)return[parseInt(h[0]+h[0],16),parseInt(h[1]+h[1],16),parseInt(h[2]+h[2],16),1];if(h.length===6)return[parseInt(h.slice(0,2),16),parseInt(h.slice(2,4),16),parseInt(h.slice(4,6),16),1];if(h.length===8)return[parseInt(h.slice(0,2),16),parseInt(h.slice(2,4),16),parseInt(h.slice(4,6),16),parseInt(h.slice(6,8),16)/255];}const m=c.match(/rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)(?:[,\s/]+([\d.]+))?\s*\)/);if(m)return[+m[1],+m[2],+m[3],m[4]!==undefined?+m[4]:1];return[0,0,0,1];}function colorAlpha(c,a){const[r,g,b]=colorParse(c);return`rgba(${r|0},${g|0},${b|0},${a})`;}function colorMix(a,b,t){const[r1,g1,b1,a1]=colorParse(a);const[r2,g2,b2,a2]=colorParse(b);return`rgba(${(r1+(r2-r1)*t)|0},${(g1+(g2-g1)*t)|0},${(b1+(b2-b1)*t)|0},${a1+(a2-a1)*t})`;}const PALETTE=['#64c8ff','#10b981','#f59e0b','#ef4444','#8b5cf6','#ec4899','#84cc16','#6366f1',];return{TAU,clamp,lerp,extent,niceTicks,niceDomain,timeNiceTicks,timeFormat,minMaxDecimate,shouldDecimate,bezierControlPoints,springStep,colorParse,colorAlpha,colorMix,PALETTE};})();const TAU=M.TAU;const PALETTE=M.PALETTE;class Engine{constructor(canvas){this.canvas=canvas;this.ctx=canvas.getContext('2d');this.w=0;this.h=0;this.dpr=1;this._gradCache=new Map();this._gradCacheMax=32;this._snap=true;}resize(w,h){const dpr=(typeof window!=='undefined'&&window.devicePixelRatio)||1;this.canvas.width=Math.round(w*dpr);this.canvas.height=Math.round(h*dpr);this.canvas.style.width=w+'px';this.canvas.style.height=h+'px';this.ctx.setTransform(dpr,0,0,dpr,0,0);this.w=w;this.h=h;this.dpr=dpr;this._gradCache.clear();}clear(color){if(color){this.ctx.fillStyle=color;this.ctx.fillRect(0,0,this.w,this.h);}else{this.ctx.clearRect(0,0,this.w,this.h);}}rect(x,y,w,h,opts={}){if(!(w>0)||!(h>0))return;const ctx=this.ctx;if(opts.radius&&opts.radius>0){const r=Math.max(0,Math.min(opts.radius,w/2,h/2));ctx.beginPath();ctx.moveTo(x+r,y);ctx.arcTo(x+w,y,x+w,y+h,r);ctx.arcTo(x+w,y+h,x,y+h,r);ctx.arcTo(x,y+h,x,y,r);ctx.arcTo(x,y,x+w,y,r);ctx.closePath();}else{ctx.beginPath();ctx.rect(x,y,w,h);}if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1;ctx.stroke();}}rects(items,fill){const ctx=this.ctx;ctx.fillStyle=fill;ctx.beginPath();for(const r of items){if(!(r.w>0)||!(r.h>0))continue;if(r.radius&&r.radius>0){const rad=Math.max(0,Math.min(r.radius,r.w/2,r.h/2));ctx.moveTo(r.x+rad,r.y);ctx.arcTo(r.x+r.w,r.y,r.x+r.w,r.y+r.h,rad);ctx.arcTo(r.x+r.w,r.y+r.h,r.x,r.y+r.h,rad);ctx.arcTo(r.x,r.y+r.h,r.x,r.y,rad);ctx.arcTo(r.x,r.y,r.x+r.w,r.y,rad);ctx.closePath();}else{ctx.rect(r.x,r.y,r.w,r.h);}}ctx.fill();}circle(x,y,r,opts={}){if(!(r>0))return;const ctx=this.ctx;ctx.beginPath();ctx.arc(x,y,r,0,TAU);if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1;ctx.stroke();}}circles(items,fill,fixedRadius){const ctx=this.ctx;ctx.fillStyle=fill;ctx.beginPath();for(const c of items){const r=fixedRadius!=null?fixedRadius:c.r;if(!(r>0))continue;ctx.moveTo(c.x+r,c.y);ctx.arc(c.x,c.y,r,0,TAU);}ctx.fill();}poly(points,opts={}){if(points.length<2)return;const ctx=this.ctx;const snap=this._snap&&opts.lineWidth===1;const adj=snap?0.5:0;ctx.beginPath();ctx.moveTo(points[0].x+adj,points[0].y+adj);for(let i=1;i<points.length;i++){ctx.lineTo(points[i].x+adj,points[i].y+adj);}if(opts.close)ctx.closePath();if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1.5;ctx.lineCap=opts.cap||'butt';ctx.lineJoin=opts.join||'miter';if(opts.dash)ctx.setLineDash(opts.dash);ctx.stroke();if(opts.dash)ctx.setLineDash([]);}}smooth(points,cps,opts={}){if(points.length<2)return;const ctx=this.ctx;ctx.beginPath();ctx.moveTo(points[0].x,points[0].y);for(let i=0;i<cps.length;i++){const c=cps[i];const p=points[i+1];ctx.bezierCurveTo(c.c1x,c.c1y,c.c2x,c.c2y,p.x,p.y);}if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||2;ctx.lineCap=opts.cap||'round';ctx.lineJoin=opts.join||'round';ctx.stroke();}}smoothArea(points,cps,y0,fill){if(points.length<2)return;const ctx=this.ctx;ctx.beginPath();ctx.moveTo(points[0].x,y0);ctx.lineTo(points[0].x,points[0].y);for(let i=0;i<cps.length;i++){const c=cps[i];const p=points[i+1];ctx.bezierCurveTo(c.c1x,c.c1y,c.c2x,c.c2y,p.x,p.y);}ctx.lineTo(points[points.length-1].x,y0);ctx.closePath();ctx.fillStyle=fill;ctx.fill();}step(points,mode,opts={}){if(points.length<2)return;const ctx=this.ctx;ctx.beginPath();ctx.moveTo(points[0].x,points[0].y);for(let i=1;i<points.length;i++){const a=points[i-1],b=points[i];if(mode==='after'){ctx.lineTo(b.x,a.y);ctx.lineTo(b.x,b.y);}else if(mode==='middle'){const mx=(a.x+b.x)/2;ctx.lineTo(mx,a.y);ctx.lineTo(mx,b.y);ctx.lineTo(b.x,b.y);}else{ctx.lineTo(a.x,b.y);ctx.lineTo(b.x,b.y);}}if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1.5;ctx.stroke();}}text(str,x,y,opts={}){const ctx=this.ctx;ctx.font=opts.font||'11px ui-monospace, monospace';ctx.fillStyle=opts.fill||'#000';ctx.textAlign=opts.align||'left';ctx.textBaseline=opts.baseline||'alphabetic';if(opts.rotate){ctx.save();ctx.translate(x,y);ctx.rotate(opts.rotate);ctx.fillText(str,0,0);ctx.restore();}else{ctx.fillText(str,x,y);}}measure(str,font){this.ctx.font=font||'11px ui-monospace, monospace';return this.ctx.measureText(str).width;}arc(cx,cy,rIn,rOut,a0,a1,opts={}){const ri=Math.max(0,rIn);const ro=Math.max(0,rOut);if(ro<=0)return;const ctx=this.ctx;ctx.beginPath();ctx.arc(cx,cy,ro,a0,a1);ctx.arc(cx,cy,ri,a1,a0,true);ctx.closePath();if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1;ctx.stroke();}}vGradient(key,x0,y0,y1,stops){if(this._gradCache.has(key))return this._gradCache.get(key);const g=this.ctx.createLinearGradient(x0,y0,x0,y1);for(const[pos,color]of stops)g.addColorStop(pos,color);if(this._gradCache.size>=this._gradCacheMax){const first=this._gradCache.keys().next().value;this._gradCache.delete(first);}this._gradCache.set(key,g);return g;}clip(x,y,w,h){const ctx=this.ctx;ctx.save();ctx.beginPath();ctx.rect(x,y,w,h);ctx.clip();}unclip(){this.ctx.restore();}withAlpha(a,fn){const ctx=this.ctx;const prev=ctx.globalAlpha;ctx.globalAlpha=prev*a;fn();ctx.globalAlpha=prev;}}const charts=new Set();let rafId=null;let lastTime=0;let fps=60;let frames=0;let fpsT=0;function tick(now){if(now-fpsT>500){fps=Math.round((1000*frames)/(now-fpsT));frames=0;fpsT=now;}frames++;let active=false;for(const c of charts){try{const stillActive=c._tick(now);if(stillActive)active=true;}catch(e){console.error(e);}}rafId=(charts.size>0&&active)?requestAnimationFrame(tick):null;}function schedule(chart){charts.add(chart);if(rafId===null){fpsT=lastTime=performance.now();rafId=requestAnimationFrame(tick);}}const scheduler={schedule,fps:()=>fps,count:()=>charts.size};function getFPS(){return fps;}function getActiveCount(){return charts.size;}const TOKENS={bg:['--aeriz-bg','#ffffff'],surface:['--aeriz-surface','transparent'],text:['--aeriz-text','#222'],muted:['--aeriz-muted','#888'],grid:['--aeriz-grid','rgba(0,0,0,0.08)'],axis:['--aeriz-axis','rgba(0,0,0,0.3)'],accent:['--aeriz-accent','#64c8ff'],font:['--aeriz-font','ui-monospace, SFMono-Regular, Menlo, monospace'],fontSize:['--aeriz-font-size','11','number'],lineWidth:['--aeriz-line-width','2','number'],pointRadius:['--aeriz-point-radius','3','number'],barRadius:['--aeriz-bar-radius','3','number'],tooltipBg:['--aeriz-tooltip-bg','rgba(20,20,28,0.95)'],tooltipFg:['--aeriz-tooltip-fg','#fff'],};function resolveTheme(el){const cs=getComputedStyle(el);const out={};for(const[k,[varName,fallback,kind]]of Object.entries(TOKENS)){const raw=(cs.getPropertyValue(varName)||'').trim();const v=raw||fallback;out[k]=kind==='number'?parseFloat(v):v;}return out;}function readPalette(el){const cs=getComputedStyle(el);const out=[];for(let i=1;i<=12;i++){const v=(cs.getPropertyValue(`--aeriz-color-${i}`)||'').trim();if(v)out.push(v);}return out.length?out:M.PALETTE;}class Tooltip{constructor(parent){this.parent=parent;this.el=document.createElement('div');Object.assign(this.el.style,{position:'absolute',pointerEvents:'none',padding:'6px 10px',borderRadius:'4px',fontSize:'11px',fontFamily:'inherit',lineHeight:'1.5',whiteSpace:'nowrap',opacity:'0',transform:'translate(-50%, calc(-100% - 8px))',transition:'opacity 0.1s',zIndex:'20',boxShadow:'0 4px 16px rgba(0,0,0,0.3)',});parent.appendChild(this.el);}show(x,y,content,theme){this.el.style.background=theme.tooltipBg;this.el.style.color=theme.tooltipFg;if(typeof content==='string'){this.el.textContent=content;}else{this.el.innerHTML='';if(content.title){const t=document.createElement('div');t.style.cssText='opacity:0.7;font-size:10px;margin-bottom:4px';t.textContent=content.title;this.el.appendChild(t);}for(const r of content.rows){const row=document.createElement('div');row.style.cssText='display:flex;align-items:center;gap:6px;line-height:1.4';if(r.color){const sw=document.createElement('span');sw.style.cssText=`width:8px;height:8px;border-radius:2px;background:${r.color};display:inline-block`;row.appendChild(sw);}const nm=document.createElement('span');nm.style.cssText='opacity:0.8;flex:1';nm.textContent=r.name||'';row.appendChild(nm);const val=document.createElement('span');val.style.cssText='font-weight:700;margin-left:12px';val.textContent=r.value==null?'—':String(r.value);row.appendChild(val);this.el.appendChild(row);}}this.el.style.left=x+'px';this.el.style.top=y+'px';this.el.style.opacity='1';}hide(){this.el.style.opacity='0';}destroy(){this.el.remove();}}class Overlay{constructor(parent){this.parent=parent;this.el=document.createElement('div');Object.assign(this.el.style,{position:'absolute',inset:'0',display:'none',alignItems:'center',justifyContent:'center',flexDirection:'column',gap:'8px',fontSize:'12px',fontFamily:'inherit',pointerEvents:'none',zIndex:'15',});parent.appendChild(this.el);}show(kind,msg,theme){const el=this.el;el.style.display='flex';el.style.background=M.colorAlpha(theme.bg,0.85);el.style.color=theme.text;el.innerHTML='';const icon=document.createElement('div');icon.style.fontSize='20px';icon.style.opacity='0.6';if(kind==='loading'){icon.innerHTML=`<svg width="22" height="22" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9" fill="none" stroke="${theme.accent}" stroke-width="2.5" stroke-linecap="round" stroke-dasharray="14 50"><animateTransform attributeName="transform" type="rotate" from="0 12 12" to="360 12 12" dur="1s" repeatCount="indefinite"/></circle></svg>`;}else if(kind==='empty'){icon.textContent='◌';}else if(kind==='error'){icon.textContent='!';icon.style.color='#ef4444';}el.appendChild(icon);if(msg){const t=document.createElement('div');t.style.opacity='0.85';t.textContent=msg;el.appendChild(t);}}hide(){this.el.style.display='none';}destroy(){this.el.remove();}}class HitIndex{constructor(){this.regions=[];}clear(){this.regions.length=0;}add(r){this.regions.push(r);}find(x,y){for(let i=this.regions.length-1;i>=0;i--){const r=this.regions[i];if(x>=r.x&&x<=r.x+r.w&&y>=r.y&&y<=r.y+r.h)return r;}return null;}}class Chart{constructor(container,options={}){if(!container)throw new Error('Chart: container is required');this.container=typeof container==='string'?document.querySelector(container):container;this.options=options;this.container.classList.add('aeriz');if(getComputedStyle(this.container).position==='static'){this.container.style.position='relative';}if(!this.container.style.height&&this.container.offsetHeight===0){this.container.style.height='320px';}this.canvas=document.createElement('canvas');this.canvas.style.cssText='display:block;width:100%;height:100%;touch-action:none';this.container.appendChild(this.canvas);this.engine=new Engine(this.canvas);this.tooltip=new Tooltip(this.container);this.overlay=new Overlay(this.container);this.hits=new HitIndex();this.theme=resolveTheme(this.container);this.palette=options.palette||readPalette(this.container);this._cursor=null;this._hover=null;this._dirty=true;this._destroyed=false;this._mounted=false;this._anim={progress:0,start:0,duration:600};this._state=null;this._setupEvents();this._observeResize();this._resize();schedule(this);}invalidate(){this.theme=resolveTheme(this.container);this.palette=this.options.palette||readPalette(this.container);this.engine._gradCache.clear();this._dirty=true;schedule(this);}setData(data){this.options.data=data;if(this._state==='empty')this._state=null;this._anim.start=performance.now();this._anim.progress=0;this._dirty=true;schedule(this);return this;}setLoading(msg){this._state='loading';this._stateMsg=msg||'';this._dirty=true;schedule(this);return this;}setEmpty(msg){this._state='empty';this._stateMsg=msg||'No data';this._dirty=true;schedule(this);return this;}setError(msg){this._state='error';this._stateMsg=msg||'Error';this._dirty=true;schedule(this);return this;}clearState(){this._state=null;this._dirty=true;schedule(this);return this;}destroy(){this._destroyed=true;charts.delete(this);this._ro&&this._ro.disconnect();this._removeEvents();this.tooltip.destroy();this.overlay.destroy();this.canvas.remove();this.container.classList.remove('aeriz');}_resize(){const w=this.container.clientWidth;const h=this.container.clientHeight;if(w===this.engine.w&&h===this.engine.h)return;this.engine.resize(Math.max(50,w),Math.max(50,h));this.theme=resolveTheme(this.container);this._dirty=true;}_observeResize(){if(typeof ResizeObserver==='undefined')return;this._ro=new ResizeObserver(()=>{this._resize();schedule(this);});this._ro.observe(this.container);}_setupEvents(){const c=this.canvas;this._handlers={move:(e)=>this._onMove(e),leave:()=>this._onLeave(),click:(e)=>this._onClick(e),};c.addEventListener('mousemove',this._handlers.move);c.addEventListener('mouseleave',this._handlers.leave);c.addEventListener('click',this._handlers.click);}_removeEvents(){const c=this.canvas;c.removeEventListener('mousemove',this._handlers.move);c.removeEventListener('mouseleave',this._handlers.leave);c.removeEventListener('click',this._handlers.click);}_ptr(e){const r=this.canvas.getBoundingClientRect();return{x:e.clientX-r.left,y:e.clientY-r.top};}_onMove(e){const p=this._ptr(e);this._cursor=p;const hit=this.hits.find(p.x,p.y);const changed=(hit?._id??null)!==(this._hover?._id??null);this._hover=hit;if(changed){if(hit&&hit.tooltip){this.tooltip.show(hit.tipX!=null?hit.tipX:hit.x+hit.w/2,hit.tipY!=null?hit.tipY:hit.y,hit.tooltip,this.theme,);}else if(!hit&&!this._sharedTooltip){this.tooltip.hide();}this._dirty=true;schedule(this);}if(typeof this._onMoveX==='function')this._onMoveX(p);}_onLeave(){this._cursor=null;this._hover=null;this.tooltip.hide();this._dirty=true;schedule(this);}_onClick(e){const p=this._ptr(e);const hit=this.hits.find(p.x,p.y);if(this.options.onClick)this.options.onClick(hit,p,this);}_tick(now){if(this._destroyed)return false;let active=false;if(this._anim.progress<1){if(!this._anim.start)this._anim.start=now;this._anim.progress=Math.min(1,(now-this._anim.start)/this._anim.duration);this._dirty=true;if(this._anim.progress<1)active=true;}if(this._dirty){this._draw();this._dirty=false;}return active;}_draw(){const e=this.engine;e.clear();if(this.theme.surface&&this.theme.surface!=='transparent'){e.rect(0,0,e.w,e.h,{fill:this.theme.surface});}if(this._state){this.overlay.show(this._state,this._stateMsg,this.theme);this.hits.clear();return;}else{this.overlay.hide();}this.hits.clear();if(typeof this._render==='function')this._render();}_axisPad(opts={}){const t=this.theme;return{l:opts.l!=null?opts.l:50,r:opts.r!=null?opts.r:16,t:opts.t!=null?opts.t:16,b:opts.b!=null?opts.b:32,};}_drawCartesian(spec){const e=this.engine;const t=this.theme;const pad=this._axisPad(spec.pad);const px=pad.l,py=pad.t;const pw=e.w-pad.l-pad.r;const ph=e.h-pad.t-pad.b;const yMin=spec.yMin!=null?spec.yMin:0;const yMax=spec.yMax;const yScale=spec.yScale||'linear';const font=`${t.fontSize}px ${t.font}`;let yTicks,yToPx;if(yScale==='log'){const lo=Math.max(1e-10,spec.yMin||1);const hi=yMax;const lLo=Math.log10(lo),lHi=Math.log10(hi);yToPx=v=>py+ph*(1-(Math.log10(Math.max(lo,v))-lLo)/(lHi-lLo));yTicks=[];for(let p=Math.floor(lLo);p<=Math.ceil(lHi);p++){const v=Math.pow(10,p);if(v>=lo&&v<=hi)yTicks.push(v);}}else{yTicks=M.niceTicks(yMin,yMax,5);yToPx=v=>py+ph*(1-(v-yMin)/(yMax-yMin));}e.ctx.strokeStyle=t.grid;e.ctx.lineWidth=1;e.ctx.beginPath();for(const v of yTicks){const y=Math.round(yToPx(v))+0.5;e.ctx.moveTo(px,y);e.ctx.lineTo(px+pw,y);}e.ctx.stroke();const yFmt=spec.yTickFormat||(v=>yScale==='log'&&v>=1000?v.toExponential(0):String(Math.round(v*100)/100));for(const v of yTicks){e.text(yFmt(v),px-6,yToPx(v),{fill:t.muted,font,align:'right',baseline:'middle',});}let xToPx,xTicks,xFmt;if(spec.xTime){const{min,max}=spec.xTime;xToPx=v=>px+pw*(v-min)/(max-min||1);xTicks=M.timeNiceTicks(min,max,6);const step=xTicks.length>1?xTicks[1]-xTicks[0]:(max-min);xFmt=spec.xTickFormat||M.timeFormat(step);}else if(spec.xLinear){const{min,max}=spec.xLinear;xToPx=v=>px+pw*(v-min)/(max-min||1);xTicks=M.niceTicks(min,max,6);xFmt=spec.xTickFormat||(v=>String(v));}else if(spec.xLabels){const n=spec.xLabels.length;xToPx=i=>px+(i+0.5)*(pw/n);xTicks=spec.xLabels.map((_,i)=>i);xFmt=spec.xTickFormat||(i=>String(spec.xLabels[i]));}if(xToPx){let lastEnd=-Infinity;for(const v of xTicks){const x=xToPx(v);const txt=xFmt(v);const w=e.measure(txt,font);if(x-w/2<lastEnd+4)continue;lastEnd=x+w/2;e.text(txt,x,py+ph+6,{fill:t.muted,font,align:'center',baseline:'top',});}}e.poly([{x:px,y:py+ph},{x:px+pw,y:py+ph}],{stroke:t.axis,lineWidth:1,});return{px,py,pw,ph,yToPx,xToPx,yTicks,xTicks,yMin,yMax};}_drawLegend(items,pw){const e=this.engine;const t=this.theme;const font=`${t.fontSize}px ${t.font}`;const swatch=10,gap=16,rowH=18;let x=0,y=0,maxW=pw;for(const it of items){const tw=e.measure(it.label,font);const w=swatch+6+tw;if(x+w>maxW&&x>0){x=0;y+=rowH;}e.rect(x,y+4,swatch,swatch,{fill:it.hidden?t.muted:it.color,radius:2});e.text(it.label,x+swatch+4,y+swatch/2+4,{fill:it.hidden?t.muted:t.text,font,align:'left',baseline:'middle',});this.hits.add({_id:'legend-'+it.idx,x,y,w:swatch+4+tw,h:rowH,legendIdx:it.idx,});x+=w+gap;}return y+rowH+6;}_colorOf(idx,item){if(item&&item.color)return item.color;return this.palette[idx%this.palette.length];}}class BarChart extends Chart{_render(){const data=this.options.data||[];if(data.length===0){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const labelOf=this.options.label||(d=>d.label);const valueOf=this.options.value||(d=>d.value);const values=data.map((d,i)=>valueOf(d,i));const yMin=Math.min(0,...values);const yMax=Math.max(...values);const[yLo,yHi]=M.niceDomain(yMin,yMax,5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLabels:data.map(labelOf),yTickFormat:this.options.yTickFormat,xTickFormat:this.options.xTickFormat,});const slot=ax.pw/data.length;const barW=Math.max(2,Math.min(60,slot-8));const prog=this._anim.progress;const radius=t.barRadius;const groups=new Map();const fmt=this.options.dataLabels;for(let i=0;i<data.length;i++){const v=values[i];const x=ax.px+(i+0.5)*slot-barW/2;const y0=ax.yToPx(0);const y1=ax.yToPx(v);const top=Math.min(y0,y1);const h=Math.abs(y1-y0)*prog;const animTop=v>=0?y0-h:y0;const color=this._colorOf(i,data[i]);if(!groups.has(color))groups.set(color,[]);groups.get(color).push({x,y:animTop,w:barW,h,radius});this.hits.add({_id:i,x,y:Math.min(animTop,animTop+h),w:barW,h:Math.abs(h),tipX:x+barW/2,tipY:animTop,tooltip:`${labelOf(data[i], i)}: ${v}`,});if(fmt&&prog>0.95){const txt=typeof fmt==='function'?fmt(data[i],i):String(v);e.text(txt,x+barW/2,animTop-4,{fill:t.text,font:`${t.fontSize}px ${t.font}`,align:'center',baseline:'bottom',});}}for(const[color,items]of groups)e.rects(items,color);if(this._hover){const r=this._hover;e.rect(r.x,r.y,r.w,r.h,{stroke:t.text,lineWidth:1.5});}}}class HBarChart extends Chart{_axisPad(){return{l:80,r:16,t:16,b:32};}_render(){const data=this.options.data||[];if(data.length===0){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const labelOf=this.options.label||(d=>d.label);const valueOf=this.options.value||(d=>d.value);const values=data.map((d,i)=>valueOf(d,i));const xMin=Math.min(0,...values);const xMax=Math.max(...values);const[xLo,xHi]=M.niceDomain(xMin,xMax,5);const pad=this._axisPad();const px=pad.l,py=pad.t;const pw=e.w-pad.l-pad.r;const ph=e.h-pad.t-pad.b;const font=`${t.fontSize}px ${t.font}`;const xTicks=M.niceTicks(xLo,xHi,5);const xToPx=v=>px+pw*(v-xLo)/(xHi-xLo);e.ctx.strokeStyle=t.grid;e.ctx.lineWidth=1;e.ctx.beginPath();for(const v of xTicks){const x=Math.round(xToPx(v))+0.5;e.ctx.moveTo(x,py);e.ctx.lineTo(x,py+ph);}e.ctx.stroke();for(const v of xTicks){e.text(String(v),xToPx(v),py+ph+6,{fill:t.muted,font,align:'center',baseline:'top',});}e.poly([{x:px,y:py+ph},{x:px+pw,y:py+ph}],{stroke:t.axis,lineWidth:1});const slot=ph/data.length;const barH=Math.max(2,Math.min(40,slot-6));const prog=this._anim.progress;const groups=new Map();for(let i=0;i<data.length;i++){const v=values[i];const yMid=py+(i+0.5)*slot;const y=yMid-barH/2;const x0=xToPx(0);const x1=xToPx(v);const start=Math.min(x0,x1);const w=Math.abs(x1-x0)*prog;const animLeft=v>=0?x0:x0-w;const color=this._colorOf(i,data[i]);if(!groups.has(color))groups.set(color,[]);groups.get(color).push({x:animLeft,y,w,h:barH,radius:t.barRadius});this.hits.add({_id:i,x:animLeft,y,w,h:barH,tipX:animLeft+w/2,tipY:y,tooltip:`${labelOf(data[i], i)}: ${v}`,});e.text(labelOf(data[i],i),px-6,yMid,{fill:t.muted,font,align:'right',baseline:'middle',});}for(const[color,items]of groups)e.rects(items,color);if(this._hover){const r=this._hover;e.rect(r.x,r.y,r.w,r.h,{stroke:t.text,lineWidth:1.5});}}}class LineChart extends Chart{_render(){const data=this.options.data||[];if(data.length===0){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const labelOf=this.options.label||(d=>d.label);const valueOf=this.options.value||(d=>d.value);const values=data.map((d,i)=>valueOf(d,i));const yMin=Math.min(0,...values);const yMax=Math.max(...values);const[yLo,yHi]=M.niceDomain(yMin,yMax,5);const useTime=this.options.xScale==='time';let spec={yMin:yLo,yMax:yHi,yScale:this.options.yScale,yTickFormat:this.options.yTickFormat,xTickFormat:this.options.xTickFormat,};if(useTime){const xs=data.map((d,i)=>+labelOf(d,i));spec.xTime={min:xs[0],max:xs[xs.length-1]};}else{spec.xLabels=data.map(labelOf);}const ax=this._drawCartesian(spec);const color=this.options.color||this.palette[0];const interp=this.options.interpolation||'smooth';const lineW=t.lineWidth;const prog=this._anim.progress;let renderData=data;if(M.shouldDecimate(data.length,ax.pw)){renderData=M.minMaxDecimate(data,Math.floor(ax.pw),valueOf);}const points=renderData.map((d,i)=>{const xv=useTime?+labelOf(d,i):i;const idx=useTime?null:i;const x=useTime?ax.xToPx(xv):ax.px+((idx+0.5)/renderData.length)*ax.pw;const y=ax.yToPx(valueOf(d,i));return{x,y,d,i};});e.clip(ax.px,ax.py,ax.pw*prog,ax.ph);if(interp==='smooth'||interp==='linear'){const cps=interp==='smooth'?M.bezierControlPoints(points):null;const grad=e.vGradient(`area-${color}`,ax.px,ax.py,ax.py+ax.ph,[[0,M.colorAlpha(color,0.25)],[1,M.colorAlpha(color,0)],]);if(cps)e.smoothArea(points,cps,ax.py+ax.ph,grad);else{const areaPts=points.concat([{x:points[points.length-1].x,y:ax.py+ax.ph},{x:points[0].x,y:ax.py+ax.ph},]);e.poly(areaPts,{fill:grad});}if(cps)e.smooth(points,cps,{stroke:color,lineWidth:lineW});else e.poly(points,{stroke:color,lineWidth:lineW,cap:'round',join:'round'});}else if(interp==='step'||interp.startsWith('step-')){const mode=interp==='step'?'before':interp.split('-')[1];e.step(points,mode,{stroke:color,lineWidth:lineW});}e.unclip();if(renderData===data&&data.length<=50){const pr=t.pointRadius;e.circles(points,color,pr);for(const p of points){this.hits.add({_id:p.i,x:p.x-8,y:p.y-8,w:16,h:16,tipX:p.x,tipY:p.y-pr,tooltip:`${labelOf(p.d, p.i)}: ${valueOf(p.d, p.i)}`,});}}else{const colW=ax.pw/renderData.length;for(let i=0;i<renderData.length;i++){const p=points[i];this.hits.add({_id:p.i,x:p.x-colW/2,y:ax.py,w:colW,h:ax.ph,tipX:p.x,tipY:p.y-8,tooltip:`${labelOf(p.d, p.i)}: ${valueOf(p.d, p.i)}`,});}}if(this._hover){const p=points.find(pp=>pp.i===this._hover._id);if(p)e.circle(p.x,p.y,t.pointRadius+3,{stroke:color,lineWidth:2,fill:t.bg,});}}}class AreaChart extends Chart{_render(){const series=this.options.series||[];const cats=this.options.categories||[];if(!series.length||!cats.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const stacked=this.options.stacked===true;let yMax;if(stacked){yMax=0;for(let i=0;i<cats.length;i++){let sum=0;for(const s of series)sum+=+s.values[i]||0;if(sum>yMax)yMax=sum;}}else{yMax=-Infinity;for(const s of series)for(const v of s.values)if(+v>yMax)yMax=+v;}const[yLo,yHi]=M.niceDomain(0,yMax,5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLabels:cats,yTickFormat:this.options.yTickFormat,});const lineW=t.lineWidth;const prog=this._anim.progress;e.clip(ax.px,ax.py,ax.pw*prog,ax.ph);if(stacked){const cum=new Array(cats.length).fill(0);const seriesPts=[];for(let s=0;s<series.length;s++){const ser=series[s];const ptsTop=[],ptsBot=[];for(let i=0;i<cats.length;i++){const v=+ser.values[i]||0;const x=ax.px+((i+0.5)/cats.length)*ax.pw;const yBot=ax.yToPx(cum[i]);const yTop=ax.yToPx(cum[i]+v);ptsTop.push({x,y:yTop});ptsBot.push({x,y:yBot});cum[i]+=v;}const color=this._colorOf(s,ser);const cps=M.bezierControlPoints(ptsTop);const ctx=e.ctx;ctx.beginPath();ctx.moveTo(ptsTop[0].x,ptsTop[0].y);for(let i=0;i<cps.length;i++){ctx.bezierCurveTo(cps[i].c1x,cps[i].c1y,cps[i].c2x,cps[i].c2y,ptsTop[i+1].x,ptsTop[i+1].y);}for(let i=ptsBot.length-1;i>=0;i--)ctx.lineTo(ptsBot[i].x,ptsBot[i].y);ctx.closePath();ctx.fillStyle=M.colorAlpha(color,0.7);ctx.fill();ctx.strokeStyle=color;ctx.lineWidth=lineW;ctx.stroke();seriesPts.push({ser,color,ptsTop});}}else{for(let s=0;s<series.length;s++){const ser=series[s];const pts=[];for(let i=0;i<cats.length;i++){pts.push({x:ax.px+((i+0.5)/cats.length)*ax.pw,y:ax.yToPx(+ser.values[i]||0),});}const color=this._colorOf(s,ser);const cps=M.bezierControlPoints(pts);const grad=e.vGradient(`area-${s}-${color}`,ax.px,ax.py,ax.py+ax.ph,[[0,M.colorAlpha(color,0.4)],[1,M.colorAlpha(color,0)],]);e.smoothArea(pts,cps,ax.py+ax.ph,grad);e.smooth(pts,cps,{stroke:color,lineWidth:lineW});}}e.unclip();this._drawLegend(series.map((s,i)=>({label:s.name||'Series '+(i+1),color:this._colorOf(i,s),idx:i,hidden:false,})),ax.pw);}}class StackedBarChart extends Chart{_render(){const series=this.options.series||[];const cats=this.options.categories||[];if(!series.length||!cats.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const totals=cats.map((_,i)=>series.reduce((a,s)=>a+(+s.values[i]||0),0));const[yLo,yHi]=M.niceDomain(0,Math.max(...totals),5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLabels:cats,yTickFormat:this.options.yTickFormat,});const slot=ax.pw/cats.length;const barW=Math.max(2,Math.min(50,slot-10));const prog=this._anim.progress;const groups=new Map();for(let i=0;i<cats.length;i++){let cum=0;const x=ax.px+(i+0.5)*slot-barW/2;for(let s=0;s<series.length;s++){const v=+series[s].values[i]||0;if(v<=0)continue;const segH=((v/yHi)*ax.ph)*prog;const y=ax.yToPx(cum+v);cum+=v;const color=this._colorOf(s,series[s]);if(!groups.has(color))groups.set(color,[]);const isTop=s===series.length-1;groups.get(color).push({x,y,w:barW,h:segH,radius:isTop?t.barRadius:0,});this.hits.add({_id:`${i}-${s}`,x,y,w:barW,h:segH,tipX:x+barW/2,tipY:y,tooltip:{title:cats[i],rows:[{color,name:series[s].name||'Series '+(s+1),value:v,}],},});}}for(const[color,items]of groups)e.rects(items,color);this._drawLegend(series.map((s,i)=>({label:s.name||'Series '+(i+1),color:this._colorOf(i,s),idx:i,hidden:false,})),ax.pw);}}class GroupedBarChart extends Chart{_render(){const series=this.options.series||[];const cats=this.options.categories||[];if(!series.length||!cats.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;let yMax=-Infinity;for(const s of series)for(const v of s.values)if(+v>yMax)yMax=+v;const[yLo,yHi]=M.niceDomain(0,yMax,5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLabels:cats,yTickFormat:this.options.yTickFormat,});const slot=ax.pw/cats.length;const inner=slot*0.8;const barW=Math.max(2,inner/series.length);const prog=this._anim.progress;const groups=new Map();for(let i=0;i<cats.length;i++){const xCenter=ax.px+(i+0.5)*slot;const left=xCenter-inner/2;for(let s=0;s<series.length;s++){const v=+series[s].values[i]||0;const x=left+s*barW;const h=((v/yHi)*ax.ph)*prog;const y=ax.yToPx(v);const color=this._colorOf(s,series[s]);if(!groups.has(color))groups.set(color,[]);groups.get(color).push({x,y,w:barW-1,h,radius:t.barRadius});this.hits.add({_id:`${i}-${s}`,x,y,w:barW-1,h,tipX:x+barW/2,tipY:y,tooltip:`${cats[i]} · ${series[s].name || s}: ${v}`,});}}for(const[color,items]of groups)e.rects(items,color);this._drawLegend(series.map((s,i)=>({label:s.name||'Series '+(i+1),color:this._colorOf(i,s),idx:i,hidden:false,})),ax.pw);}}class MultiLineChart extends Chart{_render(){const series=this.options.series||[];const cats=this.options.categories||[];if(!series.length||!cats.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const interp=this.options.interpolation||'smooth';let yMax=-Infinity,yMin=Infinity;for(const s of series)for(const v of s.values){if(+v>yMax)yMax=+v;if(+v<yMin)yMin=+v;}const[yLo,yHi]=M.niceDomain(Math.min(0,yMin),yMax,5);const useTime=this.options.xScale==='time';let spec={yMin:yLo,yMax:yHi,yTickFormat:this.options.yTickFormat,xTickFormat:this.options.xTickFormat,};if(useTime){spec.xTime={min:+cats[0],max:+cats[cats.length-1]};}else{spec.xLabels=cats;}const ax=this._drawCartesian(spec);const lineW=t.lineWidth;const prog=this._anim.progress;e.clip(ax.px,ax.py,ax.pw*prog,ax.ph);const allPts=[];for(let s=0;s<series.length;s++){const ser=series[s];if(ser.hidden)continue;const color=this._colorOf(s,ser);const pts=ser.values.map((v,i)=>({x:useTime?ax.xToPx(+cats[i]):ax.px+((i+0.5)/cats.length)*ax.pw,y:ax.yToPx(+v),i,s,v,}));allPts.push({pts,color,s,ser});if(interp==='smooth'){const cps=M.bezierControlPoints(pts);e.smooth(pts,cps,{stroke:color,lineWidth:lineW});}else if(interp==='linear'){e.poly(pts,{stroke:color,lineWidth:lineW,cap:'round',join:'round'});}else{const mode=interp==='step'?'before':interp.split('-')[1];e.step(pts,mode,{stroke:color,lineWidth:lineW});}if(ser.values.length<=50)e.circles(pts,color,t.pointRadius);}e.unclip();if(this._cursor&&allPts.length){const cx=this._cursor.x;if(cx>=ax.px&&cx<=ax.px+ax.pw){const i=useTime?Math.round(((cx-ax.px)/ax.pw)*(cats.length-1)):Math.floor((cx-ax.px)/(ax.pw/cats.length));const idx=Math.max(0,Math.min(cats.length-1,i));const xPx=useTime?ax.xToPx(+cats[idx]):ax.px+(idx+0.5)*(ax.pw/cats.length);e.poly([{x:xPx,y:ax.py},{x:xPx,y:ax.py+ax.ph}],{stroke:t.muted,lineWidth:1,dash:[3,3],});const rows=[];for(const sp of allPts){const p=sp.pts[idx];if(!p)continue;e.circle(p.x,p.y,t.pointRadius+2,{fill:t.bg,stroke:sp.color,lineWidth:2,});rows.push({color:sp.color,name:sp.ser.name||'Series '+(sp.s+1),value:sp.ser.values[idx],});}const title=useTime?new Date(+cats[idx]).toISOString().slice(0,19).replace('T',' '):String(cats[idx]);this.tooltip.show(xPx,ax.py+8,{title,rows},t);this._sharedTooltip=true;}}this._drawLegend(series.map((s,i)=>({label:s.name||'Series '+(i+1),color:this._colorOf(i,s),idx:i,hidden:!!s.hidden,})),ax.pw);}}class ScatterChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;let xMin=Infinity,xMax=-Infinity,yMin=Infinity,yMax=-Infinity;for(const d of data){if(d.x<xMin)xMin=d.x;if(d.x>xMax)xMax=d.x;if(d.y<yMin)yMin=d.y;if(d.y>yMax)yMax=d.y;}const[xLo,xHi]=M.niceDomain(xMin,xMax,6);const[yLo,yHi]=M.niceDomain(yMin,yMax,5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLinear:{min:xLo,max:xHi},});const prog=this._anim.progress;const groups=new Map();const defaultR=t.pointRadius+1;for(let i=0;i<data.length;i++){const d=data[i];const cx=ax.xToPx(d.x);const cy=ax.yToPx(d.y);const r=(d.r||defaultR)*prog;const g=d.group!=null?d.group:0;const color=this._colorOf(g,{color:d.color});const key=color+':'+r.toFixed(1);if(!groups.has(key))groups.set(key,{color,r,items:[]});groups.get(key).items.push({x:cx,y:cy});this.hits.add({_id:i,x:cx-r-2,y:cy-r-2,w:r*2+4,h:r*2+4,tipX:cx,tipY:cy-r,tooltip:`(${d.x}, ${d.y})${d.label ? ` · ${d.label}` : ''}`,});}e.withAlpha(0.7,()=>{for(const{color,r,items}of groups.values())e.circles(items,color,r);});}}class BubbleChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;let xMin=Infinity,xMax=-Infinity,yMin=Infinity,yMax=-Infinity,zMax=0;for(const d of data){if(d.x<xMin)xMin=d.x;if(d.x>xMax)xMax=d.x;if(d.y<yMin)yMin=d.y;if(d.y>yMax)yMax=d.y;if((d.z||0)>zMax)zMax=d.z;}const[xLo,xHi]=M.niceDomain(xMin,xMax,6);const[yLo,yHi]=M.niceDomain(yMin,yMax,5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLinear:{min:xLo,max:xHi},});const prog=this._anim.progress;const maxR=32,minR=4;for(let i=0;i<data.length;i++){const d=data[i];const cx=ax.xToPx(d.x);const cy=ax.yToPx(d.y);const r=(minR+(Math.sqrt((d.z||0)/Math.max(1,zMax)))*(maxR-minR))*prog;const color=d.color||this._colorOf(d.group!=null?d.group:i);e.circle(cx,cy,r,{fill:M.colorAlpha(color,0.5),stroke:color,lineWidth:1.5,});this.hits.add({_id:i,x:cx-r,y:cy-r,w:r*2,h:r*2,tipX:cx,tipY:cy-r,tooltip:`${d.label || ''} (${d.x}, ${d.y}) · z=${d.z}`,});}}}class PieChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const labelOf=this.options.label||(d=>d.label);const valueOf=this.options.value||(d=>d.value);const total=data.reduce((a,d,i)=>a+valueOf(d,i),0);if(total<=0){this.setEmpty('No data');return;}const cx=e.w/2,cy=e.h/2+4;const R=Math.min(e.w,e.h)/2-24;const innerR=this.options.innerRadius!=null?(typeof this.options.innerRadius==='number'?this.options.innerRadius:R*0.55):0;const prog=this._anim.progress;let a0=-Math.PI/2;for(let i=0;i<data.length;i++){const v=valueOf(data[i],i);const sweep=(v/total)*TAU*prog;const a1=a0+sweep;const color=this._colorOf(i,data[i]);const isHover=this._hover&&this._hover._id===i;const offset=isHover?6:0;const mid=(a0+a1)/2;const ox=Math.cos(mid)*offset,oy=Math.sin(mid)*offset;e.arc(cx+ox,cy+oy,innerR,R,a0,a1,{fill:color});const midR=(innerR+R)/2;const hx=cx+Math.cos(mid)*midR;const hy=cy+Math.sin(mid)*midR;this.hits.add({_id:i,x:hx-12,y:hy-12,w:24,h:24,tipX:hx,tipY:hy-8,tooltip:`${labelOf(data[i], i)}: ${v} (${(v / total * 100).toFixed(1)}%)`,});a0=a1;}if(innerR>0&&this.options.centerLabel!==false){const center=this._hover?`${(valueOf(data[this._hover._id], this._hover._id) / total * 100).toFixed(0)}%`:(this.options.totalLabel||total.toLocaleString());e.text(center,cx,cy,{fill:t.text,font:`600 ${t.fontSize * 1.6}px ${t.font}`,align:'center',baseline:'middle',});}}}class RadarChart extends Chart{_render(){const series=this.options.series||[];const axes=this.options.axes||[];if(!series.length||!axes.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const cx=e.w/2;const cy=e.h/2+4;const R=Math.min(e.w,e.h)/2-32;const N=axes.length;const angle=i=>-Math.PI/2+(i/N)*TAU;let max=-Infinity;for(const s of series)for(const v of s.values)if(+v>max)max=+v;const yLo=0;const niceMax=M.niceDomain(0,max,5)[1];const rings=M.niceTicks(0,niceMax,4).filter(v=>v>0);e.ctx.strokeStyle=t.grid;e.ctx.lineWidth=1;for(const v of rings){const r=(v/niceMax)*R;e.ctx.beginPath();for(let i=0;i<=N;i++){const a=angle(i%N);const x=cx+Math.cos(a)*r;const y=cy+Math.sin(a)*r;if(i===0)e.ctx.moveTo(x,y);else e.ctx.lineTo(x,y);}e.ctx.stroke();}e.ctx.strokeStyle=t.grid;for(let i=0;i<N;i++){const a=angle(i);e.ctx.beginPath();e.ctx.moveTo(cx,cy);e.ctx.lineTo(cx+Math.cos(a)*R,cy+Math.sin(a)*R);e.ctx.stroke();}const font=`${t.fontSize}px ${t.font}`;for(let i=0;i<N;i++){const a=angle(i);const lx=cx+Math.cos(a)*(R+14);const ly=cy+Math.sin(a)*(R+14);e.text(axes[i],lx,ly,{fill:t.muted,font,align:Math.cos(a)>0.3?'left':Math.cos(a)<-0.3?'right':'center',baseline:Math.sin(a)>0.3?'top':Math.sin(a)<-0.3?'bottom':'middle',});}const prog=this._anim.progress;for(let s=0;s<series.length;s++){const ser=series[s];const color=this._colorOf(s,ser);const pts=[];for(let i=0;i<N;i++){const v=(+ser.values[i]||0)*prog;const r=(v/niceMax)*R;const a=angle(i);pts.push({x:cx+Math.cos(a)*r,y:cy+Math.sin(a)*r});}e.poly(pts,{fill:M.colorAlpha(color,0.25),stroke:color,lineWidth:2,close:true,});e.circles(pts,color,t.pointRadius);}this._drawLegend(series.map((s,i)=>({label:s.name||'Series '+(i+1),color:this._colorOf(i,s),idx:i,hidden:false,})),e.w-16);}}class PolarChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const labelOf=this.options.label||(d=>d.label);const valueOf=this.options.value||(d=>d.value);const N=data.length;const cx=e.w/2,cy=e.h/2+4;const R=Math.min(e.w,e.h)/2-24;const max=Math.max(...data.map((d,i)=>valueOf(d,i)));const prog=this._anim.progress;for(let i=0;i<N;i++){const v=valueOf(data[i],i);const a0=-Math.PI/2+(i/N)*TAU;const a1=a0+(TAU/N)*0.92;const r=(v/max)*R*prog;const color=this._colorOf(i,data[i]);e.arc(cx,cy,0,r,a0,a1,{fill:color});const mid=(a0+a1)/2;const lx=cx+Math.cos(mid)*(R+12);const ly=cy+Math.sin(mid)*(R+12);e.text(labelOf(data[i],i),lx,ly,{fill:t.muted,font:`${t.fontSize}px ${t.font}`,align:'center',baseline:'middle',});}}}class GaugeChart extends Chart{setValue(v){this.options.value=v;this._anim.start=performance.now();this._anim.progress=0;this._dirty=true;scheduler.schedule(this);return this;}_render(){const e=this.engine;const t=this.theme;const val=this.options.value||0;const max=this.options.max||100;const cx=e.w/2;const cy=e.h*0.7;const R=Math.min(e.w/2,e.h*0.7)-12;const r=R-18;const a0=Math.PI*1.1;const a1=Math.PI*1.9;const span=a1-a0;const prog=this._anim.progress;const color=this.options.color||this.palette[0];e.arc(cx,cy,r,R,a0,a1,{fill:t.grid});const aEnd=a0+span*(val/max)*prog;if(aEnd>a0)e.arc(cx,cy,r,R,a0,aEnd,{fill:color});const num=Math.round(val);const unit=this.options.unit||'';e.text(`${num}${unit}`,cx,cy,{fill:t.text,font:`700 ${R / 2.5}px ${t.font}`,align:'center',baseline:'middle',});if(this.options.label){e.text(this.options.label,cx,cy+R/2.5,{fill:t.muted,font:`${t.fontSize}px ${t.font}`,align:'center',baseline:'top',});}}}class HeatmapChart extends Chart{_axisPad(){return{l:60,r:16,t:16,b:40};}_render(){const data=this.options.data||[];const xLabels=this.options.xLabels||[];const yLabels=this.options.yLabels||[];if(!data.length||!xLabels.length||!yLabels.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const pad=this._axisPad();const px=pad.l,py=pad.t;const pw=e.w-pad.l-pad.r;const ph=e.h-pad.t-pad.b;const font=`${t.fontSize}px ${t.font}`;let vMax=-Infinity,vMin=Infinity;for(const d of data){if(d.value>vMax)vMax=d.value;if(d.value<vMin)vMin=d.value;}const cellW=pw/xLabels.length;const cellH=ph/yLabels.length;const lo=this.options.colorLow||M.colorAlpha(this.palette[0],0.1);const hi=this.options.colorHigh||this.palette[0];const gap=1;const prog=this._anim.progress;for(const d of data){const xi=typeof d.x==='number'?d.x:xLabels.indexOf(d.x);const yi=typeof d.y==='number'?d.y:yLabels.indexOf(d.y);if(xi<0||yi<0)continue;const x=px+xi*cellW;const y=py+yi*cellH;const tNorm=vMax>vMin?(d.value-vMin)/(vMax-vMin):0.5;const fill=M.colorMix(lo,hi,tNorm*prog);e.rect(x+gap,y+gap,cellW-gap*2,cellH-gap*2,{fill,radius:2});this.hits.add({_id:`${xi}-${yi}`,x,y,w:cellW,h:cellH,tipX:x+cellW/2,tipY:y,tooltip:`${xLabels[xi]} · ${yLabels[yi]}: ${d.value}`,});}for(let i=0;i<xLabels.length;i++){e.text(xLabels[i],px+(i+0.5)*cellW,py+ph+4,{fill:t.muted,font,align:'center',baseline:'top',});}for(let i=0;i<yLabels.length;i++){e.text(yLabels[i],px-6,py+(i+0.5)*cellH,{fill:t.muted,font,align:'right',baseline:'middle',});}}}class BoxplotChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;let yMax=-Infinity,yMin=Infinity;for(const d of data){if(d.max>yMax)yMax=d.max;if(d.min<yMin)yMin=d.min;if(d.outliers)for(const o of d.outliers){if(o>yMax)yMax=o;if(o<yMin)yMin=o;}}const[yLo,yHi]=M.niceDomain(yMin,yMax,5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLabels:data.map(d=>d.label),});const slot=ax.pw/data.length;const boxW=Math.min(40,slot*0.5);for(let i=0;i<data.length;i++){const d=data[i];const cx=ax.px+(i+0.5)*slot;const yMin=ax.yToPx(d.min);const yMax=ax.yToPx(d.max);const yQ1=ax.yToPx(d.q1);const yQ3=ax.yToPx(d.q3);const yMed=ax.yToPx(d.median);const color=this._colorOf(i,d);e.poly([{x:cx,y:yMax},{x:cx,y:yMin}],{stroke:color,lineWidth:1});e.poly([{x:cx-boxW/3,y:yMax},{x:cx+boxW/3,y:yMax}],{stroke:color,lineWidth:1});e.poly([{x:cx-boxW/3,y:yMin},{x:cx+boxW/3,y:yMin}],{stroke:color,lineWidth:1});e.rect(cx-boxW/2,yQ3,boxW,yQ1-yQ3,{fill:M.colorAlpha(color,0.4),stroke:color,lineWidth:1.5,});e.poly([{x:cx-boxW/2,y:yMed},{x:cx+boxW/2,y:yMed}],{stroke:color,lineWidth:2,});if(d.outliers){e.circles(d.outliers.map(v=>({x:cx,y:ax.yToPx(v)})),color,2);}this.hits.add({_id:i,x:cx-boxW/2,y:yQ3,w:boxW,h:yQ1-yQ3,tipX:cx,tipY:yQ3,tooltip:{title:d.label,rows:[{name:'min',value:d.min},{name:'Q1',value:d.q1},{name:'med',value:d.median},{name:'Q3',value:d.q3},{name:'max',value:d.max},],},});}}}class FunnelChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const labelOf=this.options.label||(d=>d.label);const valueOf=this.options.value||(d=>d.value);const N=data.length;const max=Math.max(...data.map((d,i)=>valueOf(d,i)));const pad=24;const w=e.w-pad*2;const segH=(e.h-pad*2)/N;const cx=e.w/2;const prog=this._anim.progress;const font=`${t.fontSize}px ${t.font}`;for(let i=0;i<N;i++){const v=valueOf(data[i],i);const wTop=(v/max)*w*prog;const wBot=i+1<N?(valueOf(data[i+1],i+1)/max)*w*prog:wTop*0.85;const yTop=pad+i*segH;const yBot=yTop+segH-2;const color=this._colorOf(i,data[i]);const trap=[{x:cx-wTop/2,y:yTop},{x:cx+wTop/2,y:yTop},{x:cx+wBot/2,y:yBot},{x:cx-wBot/2,y:yBot},];e.poly(trap,{fill:color,close:true});e.text(`${labelOf(data[i], i)} ${v}`,cx,yTop+segH/2,{fill:'#fff',font:`600 ${t.fontSize}px ${t.font}`,align:'center',baseline:'middle',});this.hits.add({_id:i,x:cx-wTop/2,y:yTop,w:wTop,h:segH,tipX:cx,tipY:yTop,tooltip:`${labelOf(data[i], i)}: ${v} (${(v / max * 100).toFixed(1)}%)`,});}}}class TreemapChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const labelOf=this.options.label||(d=>d.label);const valueOf=this.options.value||(d=>d.value);const items=data.map((d,i)=>({label:labelOf(d,i),value:valueOf(d,i),color:this._colorOf(i,d),data:d,_id:i,})).sort((a,b)=>b.value-a.value);const total=items.reduce((a,x)=>a+x.value,0);const rect={x:4,y:4,w:e.w-8,h:e.h-8};const prog=this._anim.progress;squarify(items,total,rect,(it,r)=>{const padIn=2;const x=r.x+padIn,y=r.y+padIn;const w=(r.w-padIn*2)*prog;const h=(r.h-padIn*2)*prog;if(w<1||h<1)return;e.rect(x,y,w,h,{fill:it.color,radius:2});if(w>60&&h>30){e.text(it.label,x+6,y+6,{fill:'#fff',font:`600 ${t.fontSize}px ${t.font}`,align:'left',baseline:'top',});e.text(String(it.value),x+6,y+6+t.fontSize+2,{fill:'rgba(255,255,255,0.85)',font:`${t.fontSize}px ${t.font}`,align:'left',baseline:'top',});}this.hits.add({_id:it._id,x,y,w,h,tipX:x+w/2,tipY:y,tooltip:`${it.label}: ${it.value}`,});});}}function squarify(items,total,rect,draw){if(!items.length)return;const area=rect.w*rect.h;let i=0;while(i<items.length){const remaining=items.slice(i);const remTotal=remaining.reduce((a,x)=>a+x.value,0);if(remTotal<=0)break;const remArea=(remTotal/total)*area;const w=Math.min(rect.w,rect.h);let row=[],rowSum=0;let bestRatio=Infinity;for(let j=0;j<remaining.length;j++){const next=remaining[j];const trialSum=rowSum+next.value;const trialAreaPx=(trialSum/remTotal)*remArea;const trialW=trialAreaPx/w;let worst=0;for(const r of[...row,next]){const a=(r.value/trialSum)*trialAreaPx;const ratio=Math.max(trialW/(a/trialW),(a/trialW)/trialW);if(ratio>worst)worst=ratio;}if(worst>bestRatio)break;bestRatio=worst;row.push(next);rowSum=trialSum;}if(!row.length)row=[remaining[0]],rowSum=remaining[0].value;const isHoriz=rect.w>=rect.h;const rowAreaPx=(rowSum/remTotal)*remArea;const stripT=rowAreaPx/w;let cursor=isHoriz?rect.y:rect.x;for(const it of row){const itLen=((it.value/rowSum)*rowAreaPx)/stripT;const r=isHoriz?{x:rect.x,y:cursor,w:stripT,h:itLen}:{x:cursor,y:rect.y,w:itLen,h:stripT};const r2=isHoriz?{x:rect.x,y:cursor,w:stripT,h:itLen}:{x:cursor,y:rect.y,w:itLen,h:stripT};const placedRect=isHoriz?{x:rect.x,y:cursor,w:stripT,h:itLen}:{x:cursor,y:rect.y,w:itLen,h:stripT};draw(it,placedRect);cursor+=itLen;}if(isHoriz){rect.x+=stripT;rect.w-=stripT;}else{rect.y+=stripT;rect.h-=stripT;}i+=row.length;if(rect.w<1||rect.h<1)break;}}class Sparkline extends Chart{_render(){const data=this.options.data||[];if(!data.length)return;const e=this.engine;const t=this.theme;const valueOf=this.options.value||(v=>+v);const values=data.map(valueOf);const yMin=Math.min(...values),yMax=Math.max(...values);const yRange=yMax-yMin||1;const pad=2;const w=e.w-pad*2;const h=e.h-pad*2;const points=values.map((v,i)=>({x:pad+(i/(values.length-1||1))*w,y:pad+h-((v-yMin)/yRange)*h,}));const color=this.options.color||this.palette[0];const cps=M.bezierControlPoints(points);if(this.options.fill!==false){const grad=e.vGradient(`spark-${color}`,0,pad,pad+h,[[0,M.colorAlpha(color,0.3)],[1,M.colorAlpha(color,0)],]);e.smoothArea(points,cps,pad+h,grad);}e.smooth(points,cps,{stroke:color,lineWidth:t.lineWidth});if(this.options.lastDot!==false){const last=points[points.length-1];e.circle(last.x,last.y,t.pointRadius+1,{fill:color});}}}class RealtimeChart extends Chart{constructor(container,options={}){super(container,options);this.options.windowSize=options.windowSize||200;this._series=(options.series||[]).map(s=>({name:s.name||'Series',color:s.color,values:[],hidden:false,}));this._streaming=true;}push(values){const arr=Array.isArray(values)?values:[values];for(let i=0;i<this._series.length;i++){const s=this._series[i];s.values.push(arr[i]!=null?arr[i]:0);if(s.values.length>this.options.windowSize)s.values.shift();}if(this._state==='empty')this._state=null;this._dirty=true;scheduler.schedule(this);}clear(){for(const s of this._series)s.values.length=0;this._dirty=true;scheduler.schedule(this);}_render(){const e=this.engine;const t=this.theme;const hasData=this._series.some(s=>s.values.length>0);if(!hasData){const ax=this._drawCartesian({yMin:0,yMax:100,yTickFormat:this.options.yTickFormat,});e.text('streaming…',ax.px+ax.pw/2,ax.py+ax.ph/2,{fill:t.muted,font:`${t.fontSize}px ${t.font}`,align:'center',baseline:'middle',});return;}let yMin=Infinity,yMax=-Infinity;for(const s of this._series)for(const v of s.values){if(v<yMin)yMin=v;if(v>yMax)yMax=v;}const[yLo,yHi]=M.niceDomain(Math.min(0,yMin),yMax,5);const N=this.options.windowSize;const ax=this._drawCartesian({yMin:yLo,yMax:yHi,yTickFormat:this.options.yTickFormat,});for(let s=0;s<this._series.length;s++){const ser=this._series[s];if(ser.hidden)continue;const color=this._colorOf(s,ser);const pts=ser.values.map((v,i)=>({x:ax.px+(i/(N-1))*ax.pw,y:ax.yToPx(v),}));if(pts.length<2){if(pts.length===1)e.circle(pts[0].x,pts[0].y,t.pointRadius+2,{fill:color});continue;}const cps=M.bezierControlPoints(pts);e.smooth(pts,cps,{stroke:color,lineWidth:t.lineWidth});const last=pts[pts.length-1];e.circle(last.x,last.y,t.pointRadius+2,{fill:color});}this._drawLegend(this._series.map((s,i)=>({label:s.name,color:this._colorOf(i,s),idx:i,hidden:s.hidden,})),ax.pw);}}class WaterfallChart extends Chart{_render(){const data=this.options.data||[];if(!data.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;let cum=0;const segs=data.map(d=>{if(d.isTotal){const seg={from:0,to:d.value,value:d.value,isTotal:true,label:d.label};cum=d.value;return seg;}else{const seg={from:cum,to:cum+d.value,value:d.value,label:d.label};cum+=d.value;return seg;}});const allVals=segs.flatMap(s=>[s.from,s.to]);const[yLo,yHi]=M.niceDomain(Math.min(...allVals,0),Math.max(...allVals),5);const ax=this._drawCartesian({yMin:yLo,yMax:yHi,xLabels:data.map(d=>d.label),});const slot=ax.pw/segs.length;const barW=Math.max(2,Math.min(60,slot-12));const prog=this._anim.progress;const upColor=this.options.colorUp||'#10b981';const dnColor=this.options.colorDown||'#ef4444';const totalColor=this.options.colorTotal||'#6366f1';for(let i=0;i<segs.length;i++){const s=segs[i];const x=ax.px+(i+0.5)*slot-barW/2;const y0=ax.yToPx(s.from);const y1=ax.yToPx(s.to);const top=Math.min(y0,y1);const fullH=Math.abs(y1-y0);const h=fullH*prog;const animTop=s.to>=s.from?y0-h:y0;const color=s.isTotal?totalColor:(s.value>=0?upColor:dnColor);e.rect(x,animTop,barW,h,{fill:color,radius:t.barRadius});if(i<segs.length-1&&!segs[i+1].isTotal){const nextX=ax.px+(i+1.5)*slot-barW/2;const yEnd=ax.yToPx(s.to);e.poly([{x:x+barW,y:yEnd},{x:nextX,y:yEnd}],{stroke:t.muted,lineWidth:1,dash:[3,3],});}this.hits.add({_id:i,x,y:animTop,w:barW,h,tipX:x+barW/2,tipY:animTop,tooltip:`${s.label}: ${s.value > 0 && !s.isTotal ? '+' : ''}${s.value}`,});}}}class CompositeChart extends Chart{_render(){const layers=this.options.layers||[];const cats=this.options.categories||[];if(!layers.length||!cats.length){this.setEmpty('No data');return;}const e=this.engine;const t=this.theme;const leftLayers=layers.filter(l=>l.axis!=='right');const rightLayers=layers.filter(l=>l.axis==='right');const domain=(ll)=>{let mn=Infinity,mx=-Infinity;for(const l of ll){if(l.type==='scatter'){for(const p of l.values){if(p.y<mn)mn=p.y;if(p.y>mx)mx=p.y;}}else{for(const v of l.values){if(+v<mn)mn=+v;if(+v>mx)mx=+v;}}}if(mn===Infinity)return[0,1];return M.niceDomain(Math.min(0,mn),mx,5);};const[yLLo,yLHi]=domain(leftLayers);const[yRLo,yRHi]=rightLayers.length?domain(rightLayers):[0,1];const pad={l:50,r:rightLayers.length?50:16,t:30,b:32};const px=pad.l,py=pad.t;const pw=e.w-pad.l-pad.r;const ph=e.h-pad.t-pad.b;const font=`${t.fontSize}px ${t.font}`;const yLPx=v=>py+ph*(1-(v-yLLo)/(yLHi-yLLo));const yRPx=v=>py+ph*(1-(v-yRLo)/(yRHi-yRLo));const yLTicks=M.niceTicks(yLLo,yLHi,5);const yRTicks=rightLayers.length?M.niceTicks(yRLo,yRHi,5):[];e.ctx.strokeStyle=t.grid;e.ctx.lineWidth=1;e.ctx.beginPath();for(const v of yLTicks){const y=Math.round(yLPx(v))+0.5;e.ctx.moveTo(px,y);e.ctx.lineTo(px+pw,y);}e.ctx.stroke();const yLFmt=this.options.yLeftTickFormat||(v=>String(v));const yRFmt=this.options.yRightTickFormat||(v=>String(v));for(const v of yLTicks){e.text(yLFmt(v),px-6,yLPx(v),{fill:t.muted,font,align:'right',baseline:'middle',});}for(const v of yRTicks){e.text(yRFmt(v),px+pw+6,yRPx(v),{fill:t.muted,font,align:'left',baseline:'middle',});}const useTime=this.options.xScale==='time';const xToPx=useTime?(i)=>px+((+cats[i]-+cats[0])/(+cats[cats.length-1]-+cats[0]||1))*pw:(i)=>px+((i+0.5)/cats.length)*pw;let lastEnd=-Infinity;for(let i=0;i<cats.length;i++){const x=xToPx(i);const txt=useTime?new Date(+cats[i]).toISOString().slice(0,10):String(cats[i]);const w=e.measure(txt,font);if(x-w/2<lastEnd+4)continue;lastEnd=x+w/2;e.text(txt,x,py+ph+6,{fill:t.muted,font,align:'center',baseline:'top',});}e.poly([{x:px,y:py+ph},{x:px+pw,y:py+ph}],{stroke:t.axis,lineWidth:1});const prog=this._anim.progress;const lineW=t.lineWidth;const barLayers=layers.filter(l=>l.type==='bar');const slot=pw/cats.length;const innerSlot=slot*0.7;const barW=barLayers.length?Math.max(2,innerSlot/barLayers.length):0;barLayers.forEach((layer,bi)=>{const yPx=layer.axis==='right'?yRPx:yLPx;const yBase=yPx(0);const color=layer.color||this._colorOf(layers.indexOf(layer),layer);const items=[];for(let i=0;i<cats.length;i++){const v=+layer.values[i]||0;const xC=xToPx(i);const x=xC-innerSlot/2+bi*barW;const y0=yBase;const y1=yPx(v);const top=Math.min(y0,y1);const h=Math.abs(y1-y0)*prog;const animTop=v>=0?y0-h:y0;items.push({x,y:animTop,w:barW-1,h,radius:t.barRadius});this.hits.add({_id:`${layer.name || bi}-${i}`,x,y:animTop,w:barW-1,h,tipX:x+barW/2,tipY:animTop,tooltip:`${layer.name || ''} · ${cats[i]}: ${v}`,});}e.rects(items,color);});e.clip(px,py,pw*prog,ph);layers.filter(l=>l.type==='area').forEach((layer)=>{const yPx=layer.axis==='right'?yRPx:yLPx;const color=layer.color||this._colorOf(layers.indexOf(layer),layer);const pts=layer.values.map((v,i)=>({x:xToPx(i),y:yPx(+v)}));const cps=M.bezierControlPoints(pts);const grad=e.vGradient(`area-${layer.name}-${color}`,px,py,py+ph,[[0,M.colorAlpha(color,0.4)],[1,M.colorAlpha(color,0)],]);e.smoothArea(pts,cps,py+ph,grad);e.smooth(pts,cps,{stroke:color,lineWidth:lineW});});layers.filter(l=>l.type==='line').forEach((layer)=>{const yPx=layer.axis==='right'?yRPx:yLPx;const color=layer.color||this._colorOf(layers.indexOf(layer),layer);const pts=layer.values.map((v,i)=>({x:xToPx(i),y:yPx(+v),i,v}));const cps=M.bezierControlPoints(pts);e.smooth(pts,cps,{stroke:color,lineWidth:lineW});if(pts.length<=50)e.circles(pts,color,t.pointRadius);for(const p of pts){this.hits.add({_id:`${layer.name || 'line'}-${p.i}`,x:p.x-6,y:p.y-6,w:12,h:12,tipX:p.x,tipY:p.y-t.pointRadius,tooltip:`${layer.name || ''} · ${cats[p.i]}: ${p.v}`,});}});e.unclip();layers.filter(l=>l.type==='scatter').forEach((layer)=>{const yPx=layer.axis==='right'?yRPx:yLPx;const color=layer.color||this._colorOf(layers.indexOf(layer),layer);const items=layer.values.map((p)=>({x:xToPx(typeof p.x==='number'?p.x:cats.indexOf(p.x)),y:yPx(p.y),}));e.withAlpha(0.7,()=>e.circles(items,color,layer.radius||t.pointRadius+1));});this._drawLegend(layers.map((l,i)=>({label:(l.name||l.type)+(l.axis==='right'?' (R)':''),color:l.color||this._colorOf(i,l),idx:i,hidden:false,})),pw);}}return{Chart,Engine,getFPS,getActiveCount,scheduler,math:M,PALETTE,BarChart,HBarChart,LineChart,AreaChart,StackedBarChart,GroupedBarChart,MultiLineChart,ScatterChart,BubbleChart,PieChart,RadarChart,PolarChart,GaugeChart,HeatmapChart,BoxplotChart,FunnelChart,TreemapChart,Sparkline,RealtimeChart,WaterfallChart,CompositeChart,};})();if(typeof module!=='undefined'&&module.exports)module.exports=Aeriz;if(typeof window!=='undefined')window.Aeriz=Aeriz;
1
+ /*! @aeriz/chart v1.1.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ /*! @aeriz/chart v1.1.0 | Apache-2.0 | https://aeriz.dev/chart/ */var Aeriz=(function(){'use strict';const TAU=Math.PI*2;const clamp=(v,lo,hi)=>v<lo?lo:v>hi?hi:v;const lerp=(a,b,t)=>a+(b-a)*t;function extent(arr,fn=(x)=>x){let mn=Infinity,mx=-Infinity;for(let i=0;i<arr.length;i++){const v=fn(arr[i],i);if(v<mn)mn=v;if(v>mx)mx=v;}
3
+ return[mn===Infinity?0:mn,mx===-Infinity?1:mx];}
4
+ function niceTicks(min,max,count=5){if(min===max){min-=0.5;max+=0.5;}
5
+ const span=max-min;const step0=span/Math.max(1,count);const mag=Math.pow(10,Math.floor(Math.log10(step0)));const norm=step0/mag;let step;if(norm<1.5)step=mag;else if(norm<3)step=2*mag;else if(norm<7)step=5*mag;else step=10*mag;const lo=Math.ceil(min/step-1e-9)*step;const hi=Math.floor(max/step+1e-9)*step;const ticks=[];for(let i=0,v=lo;v<=hi+step/2;v=lo+ ++i*step){ticks.push(+v.toPrecision(12));}
6
+ return ticks;}
7
+ function niceDomain(min,max,count=5){if(min===max){min-=0.5;max+=0.5;}
8
+ const span=max-min;const step0=span/Math.max(1,count);const mag=Math.pow(10,Math.floor(Math.log10(step0)));const norm=step0/mag;let step;if(norm<1.5)step=mag;else if(norm<3)step=2*mag;else if(norm<7)step=5*mag;else step=10*mag;return[+(Math.floor(min/step+1e-9)*step).toPrecision(12),+(Math.ceil(max/step-1e-9)*step).toPrecision(12)];}
9
+ const T={sec:1e3,min:6e4,hour:36e5,day:864e5,week:6.048e8,month:2.628e9};function timeNiceTicks(minMs,maxMs,count=6){if(minMs===maxMs){minMs-=1000;maxMs+=1000;}
10
+ const span=maxMs-minMs;const target=span/count;const units=[[T.sec,1],[T.sec,5],[T.sec,15],[T.sec,30],[T.min,1],[T.min,5],[T.min,15],[T.min,30],[T.hour,1],[T.hour,3],[T.hour,6],[T.hour,12],[T.day,1],[T.day,2],[T.week,1],[T.month,1],[T.month,3],[T.month,6],[T.month,12],];let step=T.sec;for(const[u,n]of units)if(u*n<=target*1.5)step=u*n;const lo=Math.ceil(minMs/step)*step;const ticks=[];for(let v=lo;v<=maxMs;v+=step)ticks.push(v);return ticks;}
11
+ function timeFormat(step){if(step<T.min)return(t)=>new Date(t).toISOString().slice(11,19);if(step<T.hour)return(t)=>new Date(t).toISOString().slice(11,16);if(step<T.day)return(t)=>new Date(t).toISOString().slice(11,16);if(step<T.month)return(t)=>new Date(t).toISOString().slice(5,10);return(t)=>new Date(t).toISOString().slice(0,7);}
12
+ function minMaxDecimate(data,targetCount,fn=(d)=>d.value){if(data.length<=targetCount)return data;const bucket=data.length/targetCount;const out=[];for(let i=0;i<targetCount;i++){const lo=Math.floor(i*bucket);const hi=Math.min(data.length,Math.floor((i+1)*bucket));let mnI=lo,mxI=lo;let mn=fn(data[lo],lo),mx=mn;for(let j=lo+1;j<hi;j++){const v=fn(data[j],j);if(v<mn){mn=v;mnI=j;}
13
+ if(v>mx){mx=v;mxI=j;}}
14
+ if(mnI<mxI){out.push(data[mnI]);out.push(data[mxI]);}
15
+ else{out.push(data[mxI]);out.push(data[mnI]);}}
16
+ return out;}
17
+ function minMaxDecimateIdx(n,targetCount,valueAt){const out=[];if(n<=targetCount){for(let i=0;i<n;i++)out.push(i);return out;}
18
+ const bucket=n/targetCount;for(let b=0;b<targetCount;b++){const lo=Math.floor(b*bucket);const hi=Math.min(n,Math.floor((b+1)*bucket));let mnI=lo,mxI=lo,mn=valueAt(lo),mx=mn;for(let j=lo+1;j<hi;j++){const v=valueAt(j);if(v<mn){mn=v;mnI=j;}
19
+ if(v>mx){mx=v;mxI=j;}}
20
+ if(mnI===mxI)out.push(mnI);else if(mnI<mxI)out.push(mnI,mxI);else out.push(mxI,mnI);}
21
+ return out;}
22
+ function shouldDecimate(n,pxWidth){return n>pxWidth*2;}
23
+ function bezierControlPoints(pts,tension=1/6){const n=pts.length;if(n<2)return[];const cps=[];for(let i=0;i<n-1;i++){const p0=pts[i-1]||pts[i];const p1=pts[i];const p2=pts[i+1];const p3=pts[i+2]||p2;const lo=Math.min(p1.y,p2.y),hi=Math.max(p1.y,p2.y);cps.push({c1x:p1.x+(p2.x-p0.x)*tension,c1y:clamp(p1.y+(p2.y-p0.y)*tension,lo,hi),c2x:p2.x-(p3.x-p1.x)*tension,c2y:clamp(p2.y-(p3.y-p1.y)*tension,lo,hi),});}
24
+ return cps;}
25
+ function springStep(v,tgt,vel,cfg={}){const stiffness=cfg.stiffness||170;const damping=cfg.damping||26;const dt=1/60;const F=-stiffness*(v-tgt);const D=-damping*vel;const a=F+D;vel=vel+a*dt;v=v+vel*dt;const done=Math.abs(vel)<0.05&&Math.abs(v-tgt)<0.05;if(done){v=tgt;vel=0;}
26
+ return{value:v,velocity:vel,done};}
27
+ function colorParse(c){if(!c)return[0,0,0,1];if(c[0]==='#'){const h=c.slice(1);if(h.length===3)return[parseInt(h[0]+h[0],16),parseInt(h[1]+h[1],16),parseInt(h[2]+h[2],16),1];if(h.length===6)return[parseInt(h.slice(0,2),16),parseInt(h.slice(2,4),16),parseInt(h.slice(4,6),16),1];if(h.length===8)return[parseInt(h.slice(0,2),16),parseInt(h.slice(2,4),16),parseInt(h.slice(4,6),16),parseInt(h.slice(6,8),16)/255];}
28
+ const m=c.match(/rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)(?:[,\s/]+([\d.]+)(%?))?\s*\)/);if(m)return[+m[1],+m[2],+m[3],m[4]===undefined?1:m[5]?m[4]/100:+m[4]];return[0,0,0,1];}
29
+ function colorAlpha(c,a){const[r,g,b]=colorParse(c);return`rgba(${r|0},${g|0},${b|0},${a})`;}
30
+ function colorMix(a,b,t){const[r1,g1,b1,a1]=colorParse(a);const[r2,g2,b2,a2]=colorParse(b);return`rgba(${(r1+(r2-r1)*t)|0},${(g1+(g2-g1)*t)|0},${(b1+(b2-b1)*t)|0},${a1+(a2-a1)*t})`;}
31
+ const PALETTE=['#64c8ff','#10b981','#f59e0b','#ef4444','#8b5cf6','#ec4899','#84cc16','#6366f1',];const NF=new Map();function nf(opts){const key=JSON.stringify(opts);let f=NF.get(key);if(!f)NF.set(key,(f=new Intl.NumberFormat(undefined,opts)));return f;}
32
+ function formatNumber(v,fmt,axis){if(typeof fmt==='function')return String(fmt(v));if(typeof v!=='number'||!isFinite(v))return v==null?'—':String(v);if(fmt&&typeof fmt==='object')return nf(fmt).format(v);return nf(axis&&Math.abs(v)>=1e4?{notation:'compact',maximumFractionDigits:1}:{maximumFractionDigits:2}).format(v);}
33
+ class Engine{constructor(canvas){this.canvas=canvas;this.ctx=canvas.getContext('2d');this.w=0;this.h=0;this.dpr=1;this._gradCache=new Map();this._gradCacheMax=32;this._snap=true;}
34
+ resize(w,h){const dpr=(typeof window!=='undefined'&&window.devicePixelRatio)||1;this.canvas.width=Math.round(w*dpr);this.canvas.height=Math.round(h*dpr);this.ctx.setTransform(dpr,0,0,dpr,0,0);this.w=w;this.h=h;this.dpr=dpr;this._font=null;this._gradCache.clear();}
35
+ clear(color){if(color){this.ctx.fillStyle=color;this.ctx.fillRect(0,0,this.w,this.h);}else{this.ctx.clearRect(0,0,this.w,this.h);}}
36
+ rect(x,y,w,h,opts={}){if(!(w>0)||!(h>0))return;const ctx=this.ctx;if(opts.radius&&opts.radius>0){const r=Math.max(0,Math.min(opts.radius,w/2,h/2));ctx.beginPath();ctx.moveTo(x+r,y);ctx.arcTo(x+w,y,x+w,y+h,r);ctx.arcTo(x+w,y+h,x,y+h,r);ctx.arcTo(x,y+h,x,y,r);ctx.arcTo(x,y,x+w,y,r);ctx.closePath();}else{ctx.beginPath();ctx.rect(x,y,w,h);}
37
+ if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}
38
+ if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1;ctx.stroke();}}
39
+ rects(items,fill){const ctx=this.ctx;ctx.fillStyle=fill;ctx.beginPath();for(const r of items){if(!(r.w>0)||!(r.h>0))continue;if(r.radius&&r.radius>0){const rad=Math.max(0,Math.min(r.radius,r.w/2,r.h/2));ctx.moveTo(r.x+rad,r.y);ctx.arcTo(r.x+r.w,r.y,r.x+r.w,r.y+r.h,rad);ctx.arcTo(r.x+r.w,r.y+r.h,r.x,r.y+r.h,rad);ctx.arcTo(r.x,r.y+r.h,r.x,r.y,rad);ctx.arcTo(r.x,r.y,r.x+r.w,r.y,rad);ctx.closePath();}else{ctx.rect(r.x,r.y,r.w,r.h);}}
40
+ ctx.fill();}
41
+ circle(x,y,r,opts={}){if(!(r>0))return;const ctx=this.ctx;ctx.beginPath();ctx.arc(x,y,r,0,TAU);if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}
42
+ if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1;ctx.stroke();}}
43
+ circles(items,fill,fixedRadius){const ctx=this.ctx;ctx.fillStyle=fill;ctx.beginPath();for(const c of items){const r=fixedRadius!=null?fixedRadius:c.r;if(!(r>0))continue;ctx.moveTo(c.x+r,c.y);ctx.arc(c.x,c.y,r,0,TAU);}
44
+ ctx.fill();}
45
+ poly(points,opts={}){if(points.length<2)return;const ctx=this.ctx;const snap=this._snap&&opts.lineWidth===1;const adj=snap?0.5:0;ctx.beginPath();ctx.moveTo(points[0].x+adj,points[0].y+adj);for(let i=1;i<points.length;i++){ctx.lineTo(points[i].x+adj,points[i].y+adj);}
46
+ if(opts.close)ctx.closePath();if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}
47
+ if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1.5;ctx.lineCap=opts.cap||'butt';ctx.lineJoin=opts.join||'miter';if(opts.dash)ctx.setLineDash(opts.dash);ctx.stroke();if(opts.dash)ctx.setLineDash([]);}}
48
+ smooth(points,cps,opts={}){if(points.length<2)return;const ctx=this.ctx;ctx.beginPath();ctx.moveTo(points[0].x,points[0].y);for(let i=0;i<cps.length;i++){const c=cps[i];const p=points[i+1];ctx.bezierCurveTo(c.c1x,c.c1y,c.c2x,c.c2y,p.x,p.y);}
49
+ if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}
50
+ if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||2;ctx.lineCap=opts.cap||'round';ctx.lineJoin=opts.join||'round';ctx.stroke();}}
51
+ smoothArea(points,cps,y0,fill){if(points.length<2)return;const ctx=this.ctx;ctx.beginPath();ctx.moveTo(points[0].x,y0);ctx.lineTo(points[0].x,points[0].y);for(let i=0;i<cps.length;i++){const c=cps[i];const p=points[i+1];ctx.bezierCurveTo(c.c1x,c.c1y,c.c2x,c.c2y,p.x,p.y);}
52
+ ctx.lineTo(points[points.length-1].x,y0);ctx.closePath();ctx.fillStyle=fill;ctx.fill();}
53
+ step(points,mode,opts={}){if(points.length<2)return;const ctx=this.ctx;ctx.beginPath();ctx.moveTo(points[0].x,points[0].y);for(let i=1;i<points.length;i++){const a=points[i-1],b=points[i];if(mode==='after'){ctx.lineTo(b.x,a.y);ctx.lineTo(b.x,b.y);}else if(mode==='middle'){const mx=(a.x+b.x)/2;ctx.lineTo(mx,a.y);ctx.lineTo(mx,b.y);ctx.lineTo(b.x,b.y);}else{ctx.lineTo(a.x,b.y);ctx.lineTo(b.x,b.y);}}
54
+ if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1.5;ctx.stroke();}}
55
+ _setFont(font){font=font||'11px ui-monospace, monospace';if(this._font!==font){this.ctx.font=font;this._font=font;}}
56
+ text(str,x,y,opts={}){const ctx=this.ctx;this._setFont(opts.font);ctx.fillStyle=opts.fill||'#000';ctx.textAlign=opts.align||'left';ctx.textBaseline=opts.baseline||'alphabetic';if(opts.rotate){ctx.save();ctx.translate(x,y);ctx.rotate(opts.rotate);ctx.fillText(str,0,0);ctx.restore();}else{ctx.fillText(str,x,y);}}
57
+ measure(str,font){this._setFont(font);return this.ctx.measureText(str).width;}
58
+ arc(cx,cy,rIn,rOut,a0,a1,opts={}){const ri=Math.max(0,rIn);const ro=Math.max(0,rOut);if(ro<=0)return;const ctx=this.ctx;ctx.beginPath();ctx.arc(cx,cy,ro,a0,a1);ctx.arc(cx,cy,ri,a1,a0,true);ctx.closePath();if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}
59
+ if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1;ctx.stroke();}}
60
+ vGradient(key,x0,y0,y1,stops){if(this._gradCache.has(key))return this._gradCache.get(key);const g=this.ctx.createLinearGradient(x0,y0,x0,y1);for(const[pos,color]of stops)g.addColorStop(pos,color);if(this._gradCache.size>=this._gradCacheMax){const first=this._gradCache.keys().next().value;this._gradCache.delete(first);}
61
+ this._gradCache.set(key,g);return g;}
62
+ clip(x,y,w,h){const ctx=this.ctx;ctx.save();ctx.beginPath();ctx.rect(x,y,w,h);ctx.clip();}
63
+ unclip(){this.ctx.restore();this._font=null;}
64
+ withAlpha(a,fn){const ctx=this.ctx;const prev=ctx.globalAlpha;ctx.globalAlpha=prev*a;fn();ctx.globalAlpha=prev;}}
65
+ const M={TAU,clamp,lerp,extent,niceTicks,niceDomain,timeNiceTicks,timeFormat,minMaxDecimate,minMaxDecimateIdx,shouldDecimate,bezierControlPoints,springStep,colorParse,colorAlpha,colorMix,PALETTE,formatNumber};const charts=new Set();let rafId=null;let fps=60;let frames=0;let fpsT=0;function tick(now){if(now-fpsT>500){fps=Math.round((1000*frames)/(now-fpsT));frames=0;fpsT=now;}
66
+ frames++;let active=false;for(const c of charts){try{if(c._tick(now))active=true;}catch(e){console.error(e);}}
67
+ rafId=charts.size>0&&active?requestAnimationFrame(tick):null;}
68
+ function schedule(chart){if(chart._destroyed)return;charts.add(chart);if(rafId===null){fpsT=performance.now();rafId=requestAnimationFrame(tick);}}
69
+ const scheduler={schedule,fps:()=>fps,count:()=>charts.size};function getFPS(){return fps;}
70
+ function getActiveCount(){return charts.size;}
71
+ const mq=(q)=>(typeof matchMedia==='function'?matchMedia(q):null);const reducedMotion=mq('(prefers-reduced-motion: reduce)');const darkScheme=mq('(prefers-color-scheme: dark)');darkScheme?.addEventListener?.('change',()=>{for(const c of charts)c.invalidate();});const TOKENS=['bg','surface','text','muted','grid','axis','accent','font','fontSize','lineWidth','pointRadius','barRadius','tooltipBg','tooltipFg'];const cssVar=(k)=>'--aeriz-'+k.replace(/[A-Z]/g,(c)=>'-'+c.toLowerCase());function backgroundOf(el){for(let n=el;n&&n.nodeType===1;n=n.parentElement){const c=getComputedStyle(n).backgroundColor;if(c&&M.colorParse(c)[3]>0.5)return c;}
72
+ return'#ffffff';}
73
+ function resolveTheme(el){const cs=getComputedStyle(el);const v={};for(const k of TOKENS)v[k]=cs.getPropertyValue(cssVar(k)).trim();const text=v.text||cs.color||'#222';const num=(s,d)=>(s&&!isNaN(parseFloat(s))?parseFloat(s):d);return{bg:v.bg||backgroundOf(el),surface:v.surface||'transparent',text,muted:v.muted||M.colorAlpha(text,0.62),grid:v.grid||M.colorAlpha(text,0.09),axis:v.axis||M.colorAlpha(text,0.3),accent:v.accent||'#64c8ff',font:v.font||cs.fontFamily||'system-ui, sans-serif',fontSize:num(v.fontSize,11),lineWidth:num(v.lineWidth,2),pointRadius:num(v.pointRadius,3),barRadius:num(v.barRadius,3),tooltipBg:v.tooltipBg||'rgba(20,22,30,0.94)',tooltipFg:v.tooltipFg||'#fff',};}
74
+ function readPalette(el){const cs=getComputedStyle(el);const out=[];for(let i=1;i<=12;i++){const v=(cs.getPropertyValue(`--aeriz-color-${i}`)||'').trim();if(v)out.push(v);}
75
+ return out.length?out:M.PALETTE;}
76
+ function div(parent,css,tag='div'){const el=document.createElement(tag);if(css)el.style.cssText=css;parent.appendChild(el);return el;}
77
+ function tipText(content){if(typeof content==='string')return content;return[content.title,...content.rows.map((r)=>`${r.name ? r.name + ' ' : ''}${r.value == null ? '—' : r.value}`)].filter(Boolean).join(', ');}
78
+ class Tooltip{constructor(parent){this.parent=parent;this.el=div(parent,'position:absolute;left:0;top:0;pointer-events:none;padding:6px 10px;border-radius:4px;'+'font:inherit;font-size:11px;line-height:1.5;white-space:nowrap;opacity:0;transition:opacity .1s;'+'z-index:20;box-shadow:0 4px 16px rgba(0,0,0,.3)');this.el.setAttribute('aria-hidden','true');}
79
+ show(x,y,content,theme){const el=this.el;el.style.background=theme.tooltipBg;el.style.color=theme.tooltipFg;if(typeof content==='string'){el.textContent=content;}else{el.textContent='';if(content.title)div(el,'opacity:.7;font-size:10px;margin-bottom:4px').textContent=content.title;for(const r of content.rows){const row=div(el,'display:flex;align-items:center;gap:6px;line-height:1.4');if(r.color)div(row,`width:8px;height:8px;border-radius:2px;background:${r.color}`,'span');div(row,'opacity:.8;flex:1','span').textContent=r.name||'';div(row,'font-weight:700;margin-left:12px','span').textContent=r.value==null?'—':String(r.value);}}
80
+ const w=el.offsetWidth,h=el.offsetHeight;const pw=this.parent.clientWidth;const left=Math.max(0,Math.min(pw-w,x-w/2));const top=y-h-8<0?y+12:y-h-8;el.style.transform=`translate(${Math.round(left)}px,${Math.round(top)}px)`;el.style.opacity='1';}
81
+ hide(){this.el.style.opacity='0';}
82
+ destroy(){this.el.remove();}}
83
+ class Overlay{constructor(parent){this.el=div(parent,'position:absolute;inset:0;display:none;align-items:center;justify-content:center;flex-direction:column;'+'gap:8px;font:inherit;font-size:12px;pointer-events:none;z-index:15');}
84
+ show(kind,msg,theme){const el=this.el;el.style.display='flex';el.style.background=M.colorAlpha(theme.bg,0.85);el.style.color=theme.text;el.textContent='';el.setAttribute('role',kind==='error'?'alert':'status');const icon=div(el,'font-size:20px;opacity:.6;line-height:1');if(kind==='loading'){icon.innerHTML=`<svg width="22" height="22" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-dasharray="14 50"><animateTransform attributeName="transform" type="rotate" from="0 12 12" to="360 12 12" dur="1s" repeatCount="indefinite"/></circle></svg>`;icon.style.color=theme.accent;}else{icon.textContent=kind==='error'?'!':'◌';if(kind==='error')icon.style.color='#ef4444';}
85
+ if(msg)div(el,'opacity:.85').textContent=msg;}
86
+ hide(){this.el.style.display='none';this.el.removeAttribute('role');}
87
+ destroy(){this.el.remove();}}
88
+ class HitIndex{constructor(){this.regions=[];}
89
+ clear(){this.regions.length=0;}
90
+ add(r){this.regions.push(r);}
91
+ find(x,y){for(let i=this.regions.length-1;i>=0;i--){const r=this.regions[i];if(r.test?r.test(x,y):x>=r.x&&x<=r.x+r.w&&y>=r.y&&y<=r.y+r.h)return r;}
92
+ return null;}}
93
+ function toRows(data){if(!data)return[];if(!Array.isArray(data)){return typeof data==='object'?Object.keys(data).map((k)=>({label:k,value:+data[k]})):[];}
94
+ const first=data[0];if(typeof first==='number')return data.map((v,i)=>({label:String(i+1),value:v}));if(Array.isArray(first))return data.map((d)=>({label:d[0],value:+d[1]}));return data;}
95
+ class Chart{constructor(container,options={}){const el=typeof container==='string'?document.querySelector(container):container;if(!el)throw new Error(`aeriz: container not found${typeof container === 'string' ? ': ' + container : ''}`);this.container=el;this.options=options;this._restore={position:el.style.position,height:el.style.height};el.classList.add('aeriz');if(getComputedStyle(el).position==='static')el.style.position='relative';if(!el.style.height&&el.offsetHeight===0)el.style.height='320px';this.wrap=div(el,'display:flex;flex-direction:column;width:100%;height:100%');this.legendEl=div(this.wrap,'display:none;flex:none;flex-wrap:wrap;gap:2px 12px;padding:0 0 6px;font:inherit;font-size:11px');this.plot=div(this.wrap,'position:relative;flex:1 1 auto;min-height:0');this.canvas=document.createElement('canvas');this.canvas.style.cssText='position:absolute;inset:0;display:block;width:100%;height:100%;touch-action:pan-y;outline:none';this.canvas.tabIndex=0;this.canvas.setAttribute('role','img');this.plot.appendChild(this.canvas);this.live=div(this.plot,'position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap');this.live.setAttribute('aria-live','polite');this.engine=new Engine(this.canvas);this.tooltip=new Tooltip(this.plot);this.overlay=new Overlay(this.plot);this.hits=new HitIndex();this.theme=resolveTheme(el);this.palette=options.palette||readPalette(el);this._off=new Set();this._legendKey='';this._cursor=null;this._hover=null;this._dirty=true;this._destroyed=false;this._visible=typeof IntersectionObserver==='undefined';this._anim={progress:0,start:0,duration:600};this._state=null;this._auto=false;this._label();this._setupEvents();this._observe();this._resize();if(options.animate===false||reducedMotion?.matches)this._anim.progress=1;schedule(this);}
96
+ invalidate(){this.theme=resolveTheme(this.container);this.palette=this.options.palette||readPalette(this.container);this.engine._gradCache.clear();this._legendKey='';this._dirty=true;schedule(this);}
97
+ update(patch={}){Object.assign(this.options,patch);this._cache=null;if('palette'in patch)this.palette=patch.palette||readPalette(this.container);if(this._auto){this._state=null;this._auto=false;}
98
+ this._label();this._restart();return this;}
99
+ setData(data){return this.update({data});}
100
+ setLoading(msg){return this._setState('loading',msg||'');}
101
+ setEmpty(msg){return this._setState('empty',msg||'No data');}
102
+ setError(msg){return this._setState('error',msg||'Error');}
103
+ clearState(){return this._setState(null);}
104
+ destroy(){if(this._destroyed)return;this._destroyed=true;charts.delete(this);this._ro?.disconnect();this._io?.disconnect();this._removeEvents();this.wrap.remove();const el=this.container;el.classList.remove('aeriz');el.style.position=this._restore.position;el.style.height=this._restore.height;}
105
+ _setState(kind,msg){this._state=kind;this._stateMsg=msg;this._auto=false;this._dirty=true;schedule(this);return this;}
106
+ _empty(msg=this.options.emptyText||'No data'){this._state='empty';this._stateMsg=msg;this._auto=true;this.overlay.show('empty',msg,this.theme);}
107
+ _restart(){const instant=this.options.animate===false||reducedMotion?.matches;this._anim.start=0;this._anim.progress=instant?1:0;this._dirty=true;schedule(this);}
108
+ _label(){const o=this.options;this.canvas.setAttribute('aria-label',o.ariaLabel||o.title||this._kind||'Chart');}
109
+ _fmt(v){return M.formatNumber(v,this.options.format);}
110
+ _tickFmt(v){return M.formatNumber(v,this.options.format,true);}
111
+ _isOff(i){return this._off.has(i);}
112
+ _resize(){const w=this.plot.clientWidth;const h=this.plot.clientHeight;if(w===this.engine.w&&h===this.engine.h)return false;this.engine.resize(Math.max(50,w),Math.max(50,h));this._dirty=true;return true;}
113
+ _observe(){if(typeof ResizeObserver!=='undefined'){this._ro=new ResizeObserver(()=>{if(this._resize())schedule(this);});this._ro.observe(this.plot);}
114
+ if(typeof IntersectionObserver!=='undefined'){this._io=new IntersectionObserver((entries)=>{this._visible=entries[entries.length-1].isIntersecting;if(this._visible&&this._dirty)schedule(this);});this._io.observe(this.container);}}
115
+ _setupEvents(){const c=this.canvas;this._handlers={pointermove:(e)=>this._onMove(e),pointerdown:(e)=>{if(e.pointerType!=='mouse')this._onMove(e);},pointerleave:(e)=>{if(e.pointerType==='mouse')this._onLeave();},click:(e)=>this._onClick(e),keydown:(e)=>this._onKey(e),blur:()=>this._onLeave(),};for(const k in this._handlers)c.addEventListener(k,this._handlers[k]);this._legendClick=(e)=>{const b=e.target.closest('button[data-i]');if(b)this._toggle(+b.dataset.i);};this.legendEl.addEventListener('click',this._legendClick);}
116
+ _removeEvents(){for(const k in this._handlers)this.canvas.removeEventListener(k,this._handlers[k]);this.legendEl.removeEventListener('click',this._legendClick);}
117
+ _toggle(i){const items=this._legend()||[];if(this._off.has(i))this._off.delete(i);else if(items.length-this._off.size>1)this._off.add(i);this._legendKey='';this.options.onLegend?.(i,!this._off.has(i),this);this._restart();}
118
+ _ptr(e){const r=this.canvas.getBoundingClientRect();return{x:e.clientX-r.left,y:e.clientY-r.top};}
119
+ _setHover(hit,p){const changed=(hit?._id??null)!==(this._hover?._id??null);this._hover=hit;if(changed){if(hit&&hit.tooltip){this.tooltip.show(hit.tipX??hit.x+hit.w/2,hit.tipY??hit.y,hit.tooltip,this.theme);}else if(!this._follow){this.tooltip.hide();}}
120
+ if(changed||this._follow){this._dirty=true;schedule(this);}
121
+ if(typeof this._onMoveX==='function')this._onMoveX(p);return changed;}
122
+ _onMove(e){const p=this._ptr(e);this._cursor=p;this._setHover(this.hits.find(p.x,p.y),p);}
123
+ _onLeave(){this._cursor=null;this._hover=null;this._key=-1;this.tooltip.hide();this._dirty=true;schedule(this);}
124
+ _onClick(e){const p=this._ptr(e);const hit=this.hits.find(p.x,p.y)||(this._follow?this._hover:null);this.options.onClick?.(hit,p,this);}
125
+ _onKey(e){const k=e.key;const fwd=k==='ArrowRight'||k==='ArrowDown';const back=k==='ArrowLeft'||k==='ArrowUp';if(k==='Escape')return this._onLeave();if(k==='Enter'&&this._hover)return this.options.onClick?.(this._hover,this._cursor,this);if(!fwd&&!back&&k!=='Home'&&k!=='End')return;const steps=this._steps?.length?this._steps.map((x)=>({x,y:this.engine.h/2})):this.hits.regions.filter((r)=>r.tooltip);if(!steps.length)return;e.preventDefault();const n=steps.length;const cur=this._key??-1;this._key=k==='Home'?0:k==='End'?n-1:cur<0?(fwd?0:n-1):(cur+(fwd?1:n-1))%n;const s=steps[this._key];if(this._steps?.length){this._cursor={x:s.x,y:s.y};this._setHover(null,this._cursor);this._dirty=true;this._tick(performance.now());this.live.textContent=this._tipNow?tipText(this._tipNow):'';}else{this._cursor={x:s.tipX??s.x+s.w/2,y:s.tipY??s.y};this._setHover(s,this._cursor);this.live.textContent=tipText(s.tooltip);}}
126
+ _tick(now){if(this._destroyed)return false;if(!this._visible){if(this._anim.progress<1)this._anim.start=0;return false;}
127
+ let active=false;const a=this._anim;if(a.progress<1){if(!a.start)a.start=now;const t=Math.min(1,(now-a.start)/a.duration);a.progress=t===1?1:1-Math.pow(1-t,3);this._dirty=true;if(t<1)active=true;}
128
+ if(this._dirty){this._dirty=false;this._draw();}
129
+ return active;}
130
+ _draw(){const e=this.engine;this._renderLegend();this._resize();e.clear();this.hits.clear();this._steps=null;if(this.theme.surface&&this.theme.surface!=='transparent'){e.rect(0,0,e.w,e.h,{fill:this.theme.surface});}
131
+ if(this._state&&!this._auto){this.overlay.show(this._state,this._stateMsg,this.theme);return;}
132
+ this._state=null;this._auto=false;this.overlay.hide();if(typeof this._render==='function')this._render();}
133
+ _legend(){return null;}
134
+ _renderLegend(){const items=this.options.legend===false?null:this._legend();const key=items?items.map((it,i)=>`${it.label}|${it.color}|${this._off.has(i)}`).join(','):'';if(key===this._legendKey)return;this._legendKey=key;const el=this.legendEl;el.textContent='';el.style.display=items&&items.length?'flex':'none';if(!items)return;el.style.color=this.theme.text;items.forEach((it,i)=>{const off=this._off.has(i);const b=div(el,'display:inline-flex;align-items:center;gap:6px;padding:2px 0;border:0;background:none;color:inherit;'+`font:inherit;letter-spacing:normal;text-transform:none;border-radius:0;box-shadow:none;min-width:0;min-height:0;cursor:pointer;opacity:${off ? 0.4 : 1}`,'button');b.type='button';b.dataset.i=i;b.setAttribute('aria-pressed',String(!off));div(b,`width:10px;height:10px;border-radius:2px;background:${it.color}`,'span');b.appendChild(document.createTextNode(it.label));});}
135
+ _seriesLegend(series){return series.map((s,i)=>({label:s.name||'Series '+(i+1),color:this._colorOf(i,s)}));}
136
+ _colorOf(idx,item){if(item&&item.color)return item.color;return this.palette[idx%this.palette.length];}}
137
+ const DTF=new Map();function dateFmt(opts){const k=JSON.stringify(opts);let f=DTF.get(k);if(!f)DTF.set(k,(f=new Intl.DateTimeFormat(undefined,opts)));return f;}
138
+ function localTimeFormat(step){const o=step<6e4?{hour:'2-digit',minute:'2-digit',second:'2-digit',hour12:false}:step<864e5?{hour:'2-digit',minute:'2-digit',hour12:false}:step<2.5e9?{month:'short',day:'numeric'}:step<3e10?{year:'2-digit',month:'short'}:{year:'numeric'};const f=dateFmt(o);return(t)=>f.format(t);}
139
+ function drawCartesian(chart,spec){const e=chart.engine;const t=chart.theme;const font=`${t.fontSize}px ${t.font}`;const yMin=spec.yMin!=null?spec.yMin:0;const yMax=spec.yMax;const yScale=spec.yScale||'linear';let yTicks,yNorm;if(yScale==='log'){const lo=Math.max(1e-10,spec.yMin||1);const lLo=Math.log10(lo),lHi=Math.log10(Math.max(lo*10,yMax));yNorm=(v)=>(Math.log10(Math.max(lo,v))-lLo)/(lHi-lLo);yTicks=[];for(let p=Math.floor(lLo);p<=Math.ceil(lHi);p++){const v=Math.pow(10,p);if(v>=lo&&v<=yMax)yTicks.push(v);}}else{yTicks=M.niceTicks(yMin,yMax,5);yNorm=(v)=>(v-yMin)/(yMax-yMin||1);}
140
+ const yFmt=spec.yTickFormat||((v)=>chart._tickFmt(v));const yText=yTicks.map((v)=>yFmt(v));let maxW=0;for(const s of yText)maxW=Math.max(maxW,e.measure(s,font));const right=spec.right;let rTicks=[],rText=[],rNorm=null,rMaxW=0;if(right){rTicks=M.niceTicks(right.yMin,right.yMax,5);rNorm=(v)=>(v-right.yMin)/(right.yMax-right.yMin||1);const rf=right.yTickFormat||((v)=>chart._tickFmt(v));rText=rTicks.map((v)=>rf(v));for(const s of rText)rMaxW=Math.max(rMaxW,e.measure(s,font));}
141
+ const p=spec.pad||{};const pad={l:p.l!=null?p.l:Math.ceil(maxW)+12,r:p.r!=null?p.r:right?Math.ceil(rMaxW)+12:16,t:p.t!=null?p.t:12,b:p.b!=null?p.b:t.fontSize+16,};const px=pad.l,py=pad.t;const pw=Math.max(1,e.w-pad.l-pad.r);const ph=Math.max(1,e.h-pad.t-pad.b);const yToPx=(v)=>py+ph*(1-yNorm(v));const yRToPx=rNorm?(v)=>py+ph*(1-rNorm(v)):null;const ctx=e.ctx;ctx.strokeStyle=t.grid;ctx.lineWidth=1;ctx.beginPath();for(const v of yTicks){const y=Math.round(yToPx(v))+0.5;ctx.moveTo(px,y);ctx.lineTo(px+pw,y);}
142
+ ctx.stroke();yTicks.forEach((v,i)=>e.text(yText[i],px-6,yToPx(v),{fill:t.muted,font,align:'right',baseline:'middle'}));rTicks.forEach((v,i)=>e.text(rText[i],px+pw+6,yRToPx(v),{fill:t.muted,font,align:'left',baseline:'middle'}));let xToPx,xTicks,xFmt;if(spec.xTime){const{min,max}=spec.xTime;xToPx=(v)=>px+(pw*(v-min))/(max-min||1);xTicks=M.timeNiceTicks(min,max,Math.max(2,Math.floor(pw/90)));const step=xTicks.length>1?xTicks[1]-xTicks[0]:max-min;xFmt=spec.xTickFormat||localTimeFormat(step);}else if(spec.xLinear){const{min,max}=spec.xLinear;xToPx=(v)=>px+(pw*(v-min))/(max-min||1);xTicks=M.niceTicks(min,max,6);xFmt=spec.xTickFormat||((v)=>chart._tickFmt(v));}else if(spec.xLabels){const n=spec.xLabels.length||1;xToPx=(i)=>px+(i+0.5)*(pw/n);xFmt=spec.xTickFormat||((i)=>String(spec.xLabels[i]));const guess=Math.max(e.measure(xFmt(0,0),font),e.measure(xFmt(n-1,n-1),font))+6;const k=Math.max(1,Math.ceil((n*guess)/pw));xTicks=[];for(let i=0;i<n;i+=k)xTicks.push(i);}
143
+ if(xToPx){let lastEnd=-Infinity;for(const v of xTicks){const txt=xFmt(v,v);const w=e.measure(txt,font);const x=Math.max(w/2,Math.min(e.w-w/2,xToPx(v)));if(x-w/2<lastEnd+6)continue;lastEnd=x+w/2;e.text(txt,x,py+ph+6,{fill:t.muted,font,align:'center',baseline:'top'});}}
144
+ e.poly([{x:px,y:py+ph},{x:px+pw,y:py+ph}],{stroke:t.axis,lineWidth:1});return{px,py,pw,ph,yToPx,yRToPx,xToPx,yTicks,xTicks,yMin,yMax};}
145
+ function range(arrays,lo=Infinity,hi=-Infinity){for(const arr of arrays){for(let i=0;i<arr.length;i++){const v=+arr[i];if(v<lo)lo=v;if(v>hi)hi=v;}}
146
+ if(lo===Infinity)return[0,1];return[lo,hi];}
147
+ class BarChart extends Chart{get _kind(){return'Bar chart';}
148
+ _render(){const data=toRows(this.options.data);if(!data.length)return this._empty();const e=this.engine;const t=this.theme;const o=this.options;const labelOf=o.label||((d)=>d.label);const valueOf=o.value||((d)=>d.value);const values=data.map((d,i)=>+valueOf(d,i)||0);const[lo,hi]=range([values],0,0);const[yLo,yHi]=M.niceDomain(lo,hi,5);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLabels:data.map((d,i)=>labelOf(d,i)),yTickFormat:o.yTickFormat,xTickFormat:o.xTickFormat,pad:o.dataLabels?{t:t.fontSize+10}:undefined,});const slot=ax.pw/data.length;const barW=Math.max(1,Math.min(60,slot*0.8,slot-2));const prog=this._anim.progress;const y0=ax.yToPx(0);const groups=new Map();const dl=o.dataLabels;const font=`${t.fontSize}px ${t.font}`;for(let i=0;i<data.length;i++){const v=values[i];const x=ax.px+(i+0.5)*slot-barW/2;const h=Math.abs(ax.yToPx(v)-y0)*prog;const top=v>=0?y0-h:y0;const color=this._colorOf(o.color?0:i,data[i].color?data[i]:{color:o.color});if(!groups.has(color))groups.set(color,[]);groups.get(color).push({x,y:top,w:barW,h,radius:t.barRadius});this.hits.add({_id:i,x,y:Math.min(top,y0-2),w:barW,h:Math.max(h,4),tipX:x+barW/2,tipY:top,tooltip:`${labelOf(data[i], i)}: ${this._fmt(v)}`,data:data[i],index:i,value:v,});if(dl&&prog>0.95){const txt=typeof dl==='function'?dl(data[i],i):this._fmt(v);e.text(txt,x+barW/2,v>=0?top-4:top+h+4,{fill:t.text,font,align:'center',baseline:v>=0?'bottom':'top',});}}
149
+ for(const[color,items]of groups)e.rects(items,color);if(this._hover){const r=this._hover;e.rect(r.x,r.y,r.w,r.h,{stroke:t.text,lineWidth:1.5});}}}
150
+ class HBarChart extends Chart{get _kind(){return'Horizontal bar chart';}
151
+ _render(){const data=toRows(this.options.data);if(!data.length)return this._empty();const e=this.engine;const t=this.theme;const o=this.options;const labelOf=o.label||((d)=>d.label);const valueOf=o.value||((d)=>d.value);const values=data.map((d,i)=>+valueOf(d,i)||0);const labels=data.map((d,i)=>String(labelOf(d,i)));const[lo,hi]=range([values],0,0);const[xLo,xHi]=M.niceDomain(lo,hi,5);const font=`${t.fontSize}px ${t.font}`;let labelW=0;for(const s of labels)labelW=Math.max(labelW,e.measure(s,font));const px=Math.min(Math.ceil(labelW)+12,e.w*0.4),py=12;const pw=Math.max(1,e.w-px-20);const ph=Math.max(1,e.h-py-t.fontSize-16);const xTicks=M.niceTicks(xLo,xHi,Math.max(2,Math.min(6,Math.floor(pw/70))));const xToPx=(v)=>px+(pw*(v-xLo))/(xHi-xLo||1);const xFmt=o.xTickFormat||((v)=>this._tickFmt(v));e.ctx.strokeStyle=t.grid;e.ctx.lineWidth=1;e.ctx.beginPath();for(const v of xTicks){const x=Math.round(xToPx(v))+0.5;e.ctx.moveTo(x,py);e.ctx.lineTo(x,py+ph);}
152
+ e.ctx.stroke();for(const v of xTicks){e.text(xFmt(v),xToPx(v),py+ph+6,{fill:t.muted,font,align:'center',baseline:'top'});}
153
+ e.poly([{x:px,y:py+ph},{x:px+pw,y:py+ph}],{stroke:t.axis,lineWidth:1});const slot=ph/data.length;const barH=Math.max(1,Math.min(40,slot*0.75,slot-2));const prog=this._anim.progress;const x0=xToPx(0);const groups=new Map();for(let i=0;i<data.length;i++){const v=values[i];const yMid=py+(i+0.5)*slot;const y=yMid-barH/2;const w=Math.abs(xToPx(v)-x0)*prog;const left=v>=0?x0:x0-w;const color=this._colorOf(o.color?0:i,data[i].color?data[i]:{color:o.color});if(!groups.has(color))groups.set(color,[]);groups.get(color).push({x:left,y,w,h:barH,radius:t.barRadius});this.hits.add({_id:i,x:Math.min(left,x0-2),y,w:Math.max(w,4),h:barH,tipX:left+w/2,tipY:y,tooltip:`${labels[i]}: ${this._fmt(v)}`,data:data[i],index:i,value:v,});e.text(labels[i],px-6,yMid,{fill:t.muted,font,align:'right',baseline:'middle'});}
154
+ for(const[color,items]of groups)e.rects(items,color);if(this._hover){const r=this._hover;e.rect(r.x,r.y,r.w,r.h,{stroke:t.text,lineWidth:1.5});}}}
155
+ const LABEL=(d)=>d.label;const VALUE=(d)=>d.value;class LineChart extends Chart{get _kind(){return'Line chart';}
156
+ _render(){const data=toRows(this.options.data);if(!data.length)return this._empty();const e=this.engine;const t=this.theme;const o=this.options;const labelOf=o.label||LABEL;const valueOf=o.value||VALUE;const n=data.length;let c=this._cache;if(!c||c.data!==o.data||c.valueOf!==valueOf||c.labelOf!==labelOf){const values=new Float64Array(n);for(let i=0;i<n;i++)values[i]=+valueOf(data[i],i);c=this._cache={data:o.data,valueOf,labelOf,values,idx:null,idxW:0,xs:null,labels:null,range:null};}
157
+ const values=c.values;const[lo,hi]=(c.range||=range([values],o.yScale==='log'?Infinity:0));const[yLo,yHi]=o.yScale==='log'?[lo,hi]:M.niceDomain(lo,hi,5);const useTime=o.xScale==='time';const spec={yMin:yLo,yMax:yHi,yScale:o.yScale,yTickFormat:o.yTickFormat,xTickFormat:o.xTickFormat,};const xs=useTime?(c.xs||=data.map((d,i)=>+labelOf(d,i))):null;if(useTime)spec.xTime={min:xs[0],max:xs[n-1]};else spec.xLabels=c.labels||=data.map((d,i)=>labelOf(d,i));const ax=drawCartesian(this,spec);const color=o.color||this.palette[0];const interp=o.interpolation||'smooth';const lineW=t.lineWidth;const prog=this._anim.progress;const xAt=useTime?(i)=>ax.xToPx(xs[i]):(i)=>ax.px+((i+0.5)/n)*ax.pw;const decimated=M.shouldDecimate(n,ax.pw);if(decimated&&(!c.idx||c.idxW!==Math.floor(ax.pw))){c.idxW=Math.floor(ax.pw);c.idx=M.minMaxDecimateIdx(n,c.idxW,(i)=>values[i]);}
158
+ const idx=decimated?c.idx:null;const count=idx?idx.length:n;const points=new Array(count);for(let k=0;k<count;k++){const i=idx?idx[k]:k;points[k]={x:xAt(i),y:ax.yToPx(values[i]),i};}
159
+ e.clip(ax.px-lineW,ax.py-lineW,(ax.pw+lineW*2)*prog,ax.ph+lineW*2);const smooth=interp==='smooth'&&!decimated;const base=ax.py+ax.ph;if(interp==='smooth'||interp==='linear'){const cps=smooth?M.bezierControlPoints(points):null;if(o.fill!==false){const grad=e.vGradient(`area-${color}`,ax.px,ax.py,base,[[0,M.colorAlpha(color,0.25)],[1,M.colorAlpha(color,0)],]);if(cps)e.smoothArea(points,cps,base,grad);else e.poly(points.concat([{x:points[count-1].x,y:base},{x:points[0].x,y:base}]),{fill:grad});}
160
+ if(cps)e.smooth(points,cps,{stroke:color,lineWidth:lineW});else e.poly(points,{stroke:color,lineWidth:lineW,cap:'round',join:'round'});}else if(interp.startsWith('step')){e.step(points,interp==='step'?'before':interp.split('-')[1],{stroke:color,lineWidth:lineW});}
161
+ e.unclip();const tip=(i)=>`${useTime ? new Date(xs[i]).toLocaleString() : labelOf(data[i], i)}: ${this._fmt(values[i])}`;if(!decimated&&n<=50){if(o.points!==false)e.circles(points,color,t.pointRadius);for(const p of points){this.hits.add({_id:p.i,x:p.x-8,y:p.y-8,w:16,h:16,tipX:p.x,tipY:p.y-t.pointRadius,tooltip:tip(p.i),data:data[p.i],index:p.i,value:values[p.i],});}}else{for(let k=0;k<count;k++){const p=points[k];const l=k?(points[k-1].x+p.x)/2:ax.px;const r=k<count-1?(points[k+1].x+p.x)/2:ax.px+ax.pw;if(r<=l&&k<count-1)continue;this.hits.add({_id:p.i,x:l,y:ax.py,w:Math.max(1,r-l),h:ax.ph,tipX:p.x,tipY:p.y-8,tooltip:tip(p.i),data:data[p.i],index:p.i,value:values[p.i],});}}
162
+ if(this._hover){const p=points.find((pp)=>pp.i===this._hover._id);if(p)e.circle(p.x,p.y,t.pointRadius+3,{stroke:color,lineWidth:2,fill:t.bg});}}}
163
+ class AreaChart extends Chart{get _kind(){return'Area chart';}
164
+ _legend(){return this._seriesLegend(this.options.series||[]);}
165
+ _render(){const o=this.options;const series=o.series||[];const cats=o.categories||[];if(!series.length||!cats.length)return this._empty();const e=this.engine;const t=this.theme;const stacked=o.stacked===true;const on=series.map((s,i)=>i).filter((i)=>!this._isOff(i));let yMin=0,yMax;if(stacked){yMax=0;for(let i=0;i<cats.length;i++){let sum=0;for(const s of on)sum+=+series[s].values[i]||0;if(sum>yMax)yMax=sum;}}else{[yMin,yMax]=range(on.map((s)=>series[s].values),0,0);}
166
+ const[yLo,yHi]=M.niceDomain(yMin,yMax,5);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLabels:cats,yTickFormat:o.yTickFormat,xTickFormat:o.xTickFormat});const lineW=t.lineWidth;const prog=this._anim.progress;const xAt=(i)=>ax.px+((i+0.5)/cats.length)*ax.pw;const ctx=e.ctx;e.clip(ax.px,ax.py-lineW,ax.pw*prog,ax.ph+lineW*2);const cum=new Array(cats.length).fill(0);for(const s of on){const ser=series[s];const color=this._colorOf(s,ser);const top=[],bot=[];for(let i=0;i<cats.length;i++){const v=+ser.values[i]||0;bot.push({x:xAt(i),y:ax.yToPx(stacked?cum[i]:Math.max(yLo,0))});top.push({x:xAt(i),y:ax.yToPx(stacked?cum[i]+v:v)});if(stacked)cum[i]+=v;}
167
+ const cps=M.bezierControlPoints(top);ctx.beginPath();ctx.moveTo(top[0].x,top[0].y);for(let i=0;i<cps.length;i++){ctx.bezierCurveTo(cps[i].c1x,cps[i].c1y,cps[i].c2x,cps[i].c2y,top[i+1].x,top[i+1].y);}
168
+ for(let i=bot.length-1;i>=0;i--)ctx.lineTo(bot[i].x,bot[i].y);ctx.closePath();ctx.fillStyle=stacked?M.colorAlpha(color,0.7):e.vGradient(`area-${s}-${color}`,ax.px,ax.py,ax.py+ax.ph,[[0,M.colorAlpha(color,0.4)],[1,M.colorAlpha(color,0)]]);ctx.fill();e.smooth(top,cps,{stroke:color,lineWidth:lineW});}
169
+ e.unclip();const slot=ax.pw/cats.length;for(let i=0;i<cats.length;i++){this.hits.add({_id:i,x:ax.px+i*slot,y:ax.py,w:slot,h:ax.ph,tipX:xAt(i),tipY:ax.py+8,index:i,tooltip:{title:String(cats[i]),rows:on.map((s)=>({color:this._colorOf(s,series[s]),name:series[s].name||'Series '+(s+1),value:this._fmt(+series[s].values[i])})),},});}
170
+ if(this._hover){const x=xAt(this._hover._id);e.poly([{x,y:ax.py},{x,y:ax.py+ax.ph}],{stroke:t.muted,lineWidth:1,dash:[3,3]});}}}
171
+ class StackedBarChart extends Chart{get _kind(){return'Stacked bar chart';}
172
+ _legend(){return this._seriesLegend(this.options.series||[]);}
173
+ _render(){const o=this.options;const series=o.series||[];const cats=o.categories||[];if(!series.length||!cats.length)return this._empty();const e=this.engine;const t=this.theme;const on=series.map((s,i)=>i).filter((i)=>!this._isOff(i));let lo=0,hi=0;for(let i=0;i<cats.length;i++){let pos=0,neg=0;for(const s of on){const v=+series[s].values[i]||0;if(v>=0)pos+=v;else neg+=v;}
174
+ if(pos>hi)hi=pos;if(neg<lo)lo=neg;}
175
+ const[yLo,yHi]=M.niceDomain(lo,hi,5);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLabels:cats,yTickFormat:o.yTickFormat,xTickFormat:o.xTickFormat});const slot=ax.pw/cats.length;const barW=Math.max(1,Math.min(50,slot*0.8,slot-2));const prog=this._anim.progress;const groups=new Map();const y0=ax.yToPx(0);const lastPos=[],lastNeg=[];for(let i=0;i<cats.length;i++){let pos=0,neg=0;const x=ax.px+(i+0.5)*slot-barW/2;for(const s of on){const v=+series[s].values[i]||0;if(!v)continue;const from=v>0?pos:neg;const to=from+v;if(v>0)pos=to;else neg=to;const ya=y0+(ax.yToPx(from)-y0)*prog;const yb=y0+(ax.yToPx(to)-y0)*prog;const y=Math.min(ya,yb),h=Math.abs(yb-ya);const color=this._colorOf(s,series[s]);if(!groups.has(color))groups.set(color,[]);const item={x,y,w:barW,h,radius:0};groups.get(color).push(item);(v>0?lastPos:lastNeg)[i]=item;this.hits.add({_id:`${i}-${s}`,x,y,w:barW,h,tipX:x+barW/2,tipY:y,index:i,series:s,value:v,tooltip:{title:String(cats[i]),rows:[{color,name:series[s].name||'Series '+(s+1),value:this._fmt(v)}]},});}}
176
+ for(const it of lastPos)if(it)it.radius=t.barRadius;for(const it of lastNeg)if(it)it.radius=t.barRadius;for(const[color,items]of groups)e.rects(items,color);if(this._hover){const r=this._hover;e.rect(r.x,r.y,r.w,r.h,{stroke:t.text,lineWidth:1.5});}}}
177
+ class GroupedBarChart extends Chart{get _kind(){return'Grouped bar chart';}
178
+ _legend(){return this._seriesLegend(this.options.series||[]);}
179
+ _render(){const o=this.options;const series=o.series||[];const cats=o.categories||[];if(!series.length||!cats.length)return this._empty();const e=this.engine;const t=this.theme;const on=series.map((s,i)=>i).filter((i)=>!this._isOff(i));const[lo,hi]=range(on.map((s)=>series[s].values),0,0);const[yLo,yHi]=M.niceDomain(lo,hi,5);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLabels:cats,yTickFormat:o.yTickFormat,xTickFormat:o.xTickFormat});const slot=ax.pw/cats.length;const inner=slot*0.8;const barW=Math.max(1,inner/on.length);const prog=this._anim.progress;const y0=ax.yToPx(0);const groups=new Map();for(let i=0;i<cats.length;i++){const left=ax.px+(i+0.5)*slot-inner/2;on.forEach((s,k)=>{const v=+series[s].values[i]||0;const x=left+k*barW;const h=Math.abs(ax.yToPx(v)-y0)*prog;const y=v>=0?y0-h:y0;const color=this._colorOf(s,series[s]);if(!groups.has(color))groups.set(color,[]);const w=Math.max(1,barW-(barW>4?1:0));groups.get(color).push({x,y,w,h,radius:t.barRadius});this.hits.add({_id:`${i}-${s}`,x,y:Math.min(y,y0-2),w,h:Math.max(h,4),tipX:x+barW/2,tipY:y,index:i,series:s,value:v,tooltip:{title:String(cats[i]),rows:[{color,name:series[s].name||'Series '+(s+1),value:this._fmt(v)}]},});});}
180
+ for(const[color,items]of groups)e.rects(items,color);if(this._hover){const r=this._hover;e.rect(r.x,r.y,r.w,r.h,{stroke:t.text,lineWidth:1.5});}}}
181
+ class MultiLineChart extends Chart{get _kind(){return'Multi line chart';}
182
+ constructor(container,options){super(container,options);this._follow=true;}
183
+ _legend(){return this._seriesLegend(this.options.series||[]);}
184
+ _render(){const o=this.options;const series=o.series||[];const cats=o.categories||[];if(!series.length||!cats.length)return this._empty();const e=this.engine;const t=this.theme;const interp=o.interpolation||'smooth';const on=series.map((s,i)=>i).filter((i)=>!this._isOff(i)&&!series[i].hidden);const[lo,hi]=range(on.map((s)=>series[s].values),0);const[yLo,yHi]=M.niceDomain(lo,hi,5);const useTime=o.xScale==='time';const spec={yMin:yLo,yMax:yHi,yTickFormat:o.yTickFormat,xTickFormat:o.xTickFormat};if(useTime)spec.xTime={min:+cats[0],max:+cats[cats.length-1]};else spec.xLabels=cats;const ax=drawCartesian(this,spec);const lineW=t.lineWidth;const prog=this._anim.progress;const xAt=useTime?(i)=>ax.xToPx(+cats[i]):(i)=>ax.px+((i+0.5)/cats.length)*ax.pw;this._steps=cats.map((_,i)=>xAt(i));e.clip(ax.px-lineW,ax.py-lineW,(ax.pw+lineW*2)*prog,ax.ph+lineW*2);const all=[];for(const s of on){const ser=series[s];const color=this._colorOf(s,ser);const pts=ser.values.map((v,i)=>({x:xAt(i),y:ax.yToPx(+v)}));all.push({pts,color,s,ser});if(interp==='smooth')e.smooth(pts,M.bezierControlPoints(pts),{stroke:color,lineWidth:lineW});else if(interp==='linear')e.poly(pts,{stroke:color,lineWidth:lineW,cap:'round',join:'round'});else e.step(pts,interp==='step'?'before':interp.split('-')[1],{stroke:color,lineWidth:lineW});if(pts.length<=50)e.circles(pts,color,t.pointRadius);}
185
+ e.unclip();this._tipNow=null;const cx=this._cursor?.x;if(cx!=null&&cx>=ax.px-4&&cx<=ax.px+ax.pw+4&&all.length){let idx=0,best=Infinity;for(let i=0;i<cats.length;i++){const d=Math.abs(this._steps[i]-cx);if(d<best){best=d;idx=i;}}
186
+ const xPx=this._steps[idx];e.poly([{x:xPx,y:ax.py},{x:xPx,y:ax.py+ax.ph}],{stroke:t.muted,lineWidth:1,dash:[3,3]});const rows=[];for(const sp of all){const p=sp.pts[idx];if(!p)continue;e.circle(p.x,p.y,t.pointRadius+2,{fill:t.bg,stroke:sp.color,lineWidth:2});rows.push({color:sp.color,name:sp.ser.name||'Series '+(sp.s+1),value:this._fmt(+sp.ser.values[idx])});}
187
+ const title=useTime?new Date(+cats[idx]).toLocaleString():String(cats[idx]);this._tipNow={title,rows};this._hover={_id:idx,index:idx};this.tooltip.show(xPx,ax.py+8,this._tipNow,t);}else if(this._cursor){this.tooltip.hide();}}}
188
+ function xyExtent(data){let xMin=Infinity,xMax=-Infinity,yMin=Infinity,yMax=-Infinity;for(const d of data){if(d.x<xMin)xMin=d.x;if(d.x>xMax)xMax=d.x;if(d.y<yMin)yMin=d.y;if(d.y>yMax)yMax=d.y;}
189
+ return[xMin,xMax,yMin,yMax];}
190
+ class ScatterChart extends Chart{get _kind(){return'Scatter chart';}
191
+ _render(){const o=this.options;const data=o.data||[];if(!data.length)return this._empty();const e=this.engine;const t=this.theme;const[xMin,xMax,yMin,yMax]=xyExtent(data);const[xLo,xHi]=M.niceDomain(xMin,xMax,6);const[yLo,yHi]=M.niceDomain(yMin,yMax,5);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLinear:{min:xLo,max:xHi},yTickFormat:o.yTickFormat,xTickFormat:o.xTickFormat,});const prog=this._anim.progress;const groups=new Map();const defaultR=t.pointRadius+1;const hitsOn=data.length<=20000;for(let i=0;i<data.length;i++){const d=data[i];const cx=ax.xToPx(d.x);const cy=ax.yToPx(d.y);const r=(d.r||defaultR)*prog;const color=d.color||this._colorOf(d.group!=null?d.group:0);const key=color+':'+r.toFixed(1);if(!groups.has(key))groups.set(key,{color,r,items:[]});groups.get(key).items.push({x:cx,y:cy});if(hitsOn){this.hits.add({_id:i,x:cx-r-2,y:cy-r-2,w:r*2+4,h:r*2+4,tipX:cx,tipY:cy-r,data:d,index:i,tooltip:`(${this._fmt(d.x)}, ${this._fmt(d.y)})${d.label ? ` · ${d.label}` : ''}`,});}}
192
+ e.withAlpha(0.7,()=>{for(const{color,r,items}of groups.values())e.circles(items,color,r);});if(this._hover){const h=this._hover;e.circle(h.tipX,h.y+h.h/2,h.w/2,{stroke:t.text,lineWidth:1.5});}}}
193
+ class BubbleChart extends Chart{get _kind(){return'Bubble chart';}
194
+ _render(){const o=this.options;const data=o.data||[];if(!data.length)return this._empty();const e=this.engine;const t=this.theme;const[xMin,xMax,yMin,yMax]=xyExtent(data);let zMax=0;for(const d of data)if((d.z||0)>zMax)zMax=d.z;const px=(xMax-xMin||1)*0.08,py=(yMax-yMin||1)*0.1;const[xLo,xHi]=M.niceDomain(xMin-px,xMax+px,6);const[yLo,yHi]=M.niceDomain(yMin-py,yMax+py,5);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLinear:{min:xLo,max:xHi},yTickFormat:o.yTickFormat,xTickFormat:o.xTickFormat,});const prog=this._anim.progress;const maxR=o.maxRadius||32,minR=o.minRadius||4;for(let i=0;i<data.length;i++){const d=data[i];const cx=ax.xToPx(d.x);const cy=ax.yToPx(d.y);const r=(minR+Math.sqrt((d.z||0)/Math.max(1e-9,zMax))*(maxR-minR))*prog;const color=d.color||this._colorOf(d.group!=null?d.group:i);const hover=this._hover&&this._hover._id===i;e.circle(cx,cy,r,{fill:M.colorAlpha(color,hover?0.75:0.5),stroke:color,lineWidth:1.5});this.hits.add({_id:i,x:cx-r,y:cy-r,w:r*2,h:r*2,test:(x,y)=>(x-cx)**2+(y-cy)**2<=r*r,tipX:cx,tipY:cy-r,data:d,index:i,tooltip:`${d.label ? d.label + ' ' : ''}(${this._fmt(d.x)}, ${this._fmt(d.y)}) · z=${this._fmt(d.z)}`,});}}}
195
+ function inSector(cx,cy,rIn,rOut,a0,a1){return(x,y)=>{const dx=x-cx,dy=y-cy;const d=dx*dx+dy*dy;if(d<rIn*rIn||d>rOut*rOut)return false;let a=Math.atan2(dy,dx);while(a<a0)a+=TAU;return a<=a1;};}
196
+ class PieChart extends Chart{get _kind(){return'Pie chart';}
197
+ _legend(){const o=this.options;const labelOf=o.label||((d)=>d.label);return toRows(o.data).map((d,i)=>({label:String(labelOf(d,i)),color:this._colorOf(i,d)}));}
198
+ _render(){const o=this.options;const data=toRows(o.data);if(!data.length)return this._empty();const e=this.engine;const t=this.theme;const labelOf=o.label||((d)=>d.label);const valueOf=o.value||((d)=>d.value);const vals=data.map((d,i)=>(this._isOff(i)?0:Math.max(0,+valueOf(d,i)||0)));const total=vals.reduce((a,v)=>a+v,0);if(total<=0)return this._empty();const cx=e.w/2,cy=e.h/2;const R=Math.max(4,Math.min(e.w,e.h)/2-12);const ir=o.innerRadius;const innerR=ir==null||ir===false?0:typeof ir==='number'?(ir<=1?R*ir:ir):R*0.55;const prog=this._anim.progress;let a0=-Math.PI/2;for(let i=0;i<data.length;i++){const v=vals[i];if(!v)continue;const a1=a0+(v/total)*TAU*prog;const color=this._colorOf(i,data[i]);const hover=this._hover&&this._hover._id===i;const mid=(a0+a1)/2;const off=hover?6:0;e.arc(cx+Math.cos(mid)*off,cy+Math.sin(mid)*off,innerR,R,a0,a1,{fill:color,stroke:data.length>1?t.bg:null,lineWidth:1.5,});const midR=(innerR+R)/2;this.hits.add({_id:i,x:cx-R,y:cy-R,w:R*2,h:R*2,test:inSector(cx,cy,innerR,R+off,a0,a1),tipX:cx+Math.cos(mid)*midR,tipY:cy+Math.sin(mid)*midR-8,data:data[i],index:i,value:v,tooltip:`${labelOf(data[i], i)}: ${this._fmt(v)} (${((v / total) * 100).toFixed(1)}%)`,});a0=a1;}
199
+ if(innerR>0&&o.centerLabel!==false){const h=this._hover;const center=h?`${((vals[h._id] / total) * 100).toFixed(0)}%`:o.totalLabel||this._fmt(total);e.text(center,cx,cy,{fill:t.text,font:`600 ${Math.max(12, Math.min(t.fontSize * 1.8, innerR / 2.2))}px ${t.font}`,align:'center',baseline:'middle',});}}}
200
+ class RadarChart extends Chart{get _kind(){return'Radar chart';}
201
+ _legend(){return this._seriesLegend(this.options.series||[]);}
202
+ _render(){const o=this.options;const series=o.series||[];const axes=o.axes||[];if(!series.length||!axes.length)return this._empty();const e=this.engine;const t=this.theme;const font=`${t.fontSize}px ${t.font}`;let labelW=0;for(const a of axes)labelW=Math.max(labelW,e.measure(String(a),font));const cx=e.w/2;const cy=e.h/2;const R=Math.max(10,Math.min(e.w/2-labelW-18,e.h/2-t.fontSize-16));const N=axes.length;const angle=(i)=>-Math.PI/2+(i/N)*TAU;const on=series.map((s,i)=>i).filter((i)=>!this._isOff(i));const max=range(on.map((s)=>series[s].values),0)[1];const niceMax=M.niceDomain(0,max,5)[1]||1;const ctx=e.ctx;ctx.strokeStyle=t.grid;ctx.lineWidth=1;for(const v of M.niceTicks(0,niceMax,4)){if(v<=0)continue;const r=(v/niceMax)*R;ctx.beginPath();for(let i=0;i<=N;i++){const a=angle(i%N);if(i===0)ctx.moveTo(cx+Math.cos(a)*r,cy+Math.sin(a)*r);else ctx.lineTo(cx+Math.cos(a)*r,cy+Math.sin(a)*r);}
203
+ ctx.stroke();}
204
+ ctx.beginPath();for(let i=0;i<N;i++){const a=angle(i);ctx.moveTo(cx,cy);ctx.lineTo(cx+Math.cos(a)*R,cy+Math.sin(a)*R);}
205
+ ctx.stroke();for(let i=0;i<N;i++){const a=angle(i);e.text(String(axes[i]),cx+Math.cos(a)*(R+10),cy+Math.sin(a)*(R+10),{fill:t.muted,font,align:Math.cos(a)>0.3?'left':Math.cos(a)<-0.3?'right':'center',baseline:Math.sin(a)>0.3?'top':Math.sin(a)<-0.3?'bottom':'middle',});}
206
+ const prog=this._anim.progress;for(const s of on){const ser=series[s];const color=this._colorOf(s,ser);const pts=[];for(let i=0;i<N;i++){const v=+ser.values[i]||0;const r=((v*prog)/niceMax)*R;const a=angle(i);const p={x:cx+Math.cos(a)*r,y:cy+Math.sin(a)*r};pts.push(p);this.hits.add({_id:`${s}-${i}`,x:p.x-7,y:p.y-7,w:14,h:14,tipX:p.x,tipY:p.y-4,index:i,series:s,value:v,tooltip:{title:String(axes[i]),rows:[{color,name:ser.name||'Series '+(s+1),value:this._fmt(v)}]},});}
207
+ e.poly(pts,{fill:M.colorAlpha(color,0.2),stroke:color,lineWidth:2,close:true,join:'round'});e.circles(pts,color,t.pointRadius);}
208
+ if(this._hover){const h=this._hover;e.circle(h.x+7,h.y+7,t.pointRadius+3,{stroke:t.text,lineWidth:1.5});}}}
209
+ class PolarChart extends Chart{get _kind(){return'Polar chart';}
210
+ _render(){const o=this.options;const data=toRows(o.data);if(!data.length)return this._empty();const e=this.engine;const t=this.theme;const labelOf=o.label||((d)=>d.label);const valueOf=o.value||((d)=>d.value);const N=data.length;const font=`${t.fontSize}px ${t.font}`;const cx=e.w/2,cy=e.h/2;const R=Math.max(10,Math.min(e.w,e.h)/2-t.fontSize-14);let max=0;for(let i=0;i<N;i++)max=Math.max(max,+valueOf(data[i],i)||0);const prog=this._anim.progress;const sqrt=o.scale!=='linear';e.circle(cx,cy,R,{stroke:t.grid,lineWidth:1});for(let i=0;i<N;i++){const v=Math.max(0,+valueOf(data[i],i)||0);const a0=-Math.PI/2+(i/N)*TAU;const a1=a0+(TAU/N)*0.92;const ratio=max?v/max:0;const r=(sqrt?Math.sqrt(ratio):ratio)*R*prog;const color=this._colorOf(i,data[i]);const hover=this._hover&&this._hover._id===i;e.arc(cx,cy,0,r+(hover?4:0),a0,a1,{fill:color});const mid=(a0+a1)/2;const c=Math.cos(mid),s=Math.sin(mid);e.text(String(labelOf(data[i],i)),cx+c*(R+8),cy+s*(R+8),{fill:t.muted,font,align:c>0.3?'left':c<-0.3?'right':'center',baseline:s>0.3?'top':s<-0.3?'bottom':'middle',});this.hits.add({_id:i,x:cx-R,y:cy-R,w:R*2,h:R*2,test:inSector(cx,cy,0,Math.max(r,12),a0,a1),tipX:cx+c*r*0.7,tipY:cy+s*r*0.7-6,data:data[i],index:i,value:v,tooltip:`${labelOf(data[i], i)}: ${this._fmt(v)}`,});}}}
211
+ class GaugeChart extends Chart{get _kind(){return'Gauge chart';}
212
+ setValue(v){this._from=this._shown??0;return this.update({value:v});}
213
+ _render(){const e=this.engine;const t=this.theme;const o=this.options;const min=o.min||0;const max=o.max!=null?o.max:100;const target=Math.max(min,Math.min(max,+o.value||0));const from=this._from??min;const val=from+(target-from)*this._anim.progress;this._shown=val;const cx=e.w/2;const cy=e.h*0.68;const R=Math.max(10,Math.min(e.w/2-8,e.h*0.62));const r=R*0.8;const a0=Math.PI*1.1;const a1=Math.PI*1.9;const color=o.color||this.palette[0];e.arc(cx,cy,r,R,a0,a1,{fill:t.grid});const aEnd=a0+((a1-a0)*(val-min))/(max-min||1);if(aEnd>a0)e.arc(cx,cy,r,R,a0,aEnd,{fill:color});const txt=(o.format?this._fmt(val):String(Math.round(val)))+(o.unit||'');e.text(txt,cx,cy-R*0.08,{fill:t.text,font:`700 ${Math.max(12, R / 2.6)}px ${t.font}`,align:'center',baseline:'middle',});if(o.label){e.text(o.label,cx,cy+R*0.22,{fill:t.muted,font:`${t.fontSize}px ${t.font}`,align:'center',baseline:'top'});}
214
+ this.canvas.setAttribute('aria-label',`${o.ariaLabel || o.label || o.title || 'Gauge'}: ${txt}`);}}
215
+ class HeatmapChart extends Chart{get _kind(){return'Heatmap chart';}
216
+ _render(){const o=this.options;const data=o.data||[];const xLabels=o.xLabels||[];const yLabels=o.yLabels||[];if(!data.length||!xLabels.length||!yLabels.length)return this._empty();const e=this.engine;const t=this.theme;const font=`${t.fontSize}px ${t.font}`;let labelW=0;for(const s of yLabels)labelW=Math.max(labelW,e.measure(String(s),font));const px=Math.ceil(labelW)+12,py=8;const pw=Math.max(1,e.w-px-8);const ph=Math.max(1,e.h-py-t.fontSize-14);let vMax=-Infinity,vMin=Infinity;for(const d of data){if(d.value>vMax)vMax=d.value;if(d.value<vMin)vMin=d.value;}
217
+ const cellW=pw/xLabels.length;const cellH=ph/yLabels.length;const lo=o.colorLow||M.colorAlpha(this.palette[0],0.08);const hi=o.colorHigh||this.palette[0];const gap=cellW>6&&cellH>6?1:0;const prog=this._anim.progress;const xi=new Map(xLabels.map((l,i)=>[l,i]));const yi=new Map(yLabels.map((l,i)=>[l,i]));for(let k=0;k<data.length;k++){const d=data[k];const cx=typeof d.x==='number'?d.x:xi.get(d.x)??-1;const cy=typeof d.y==='number'?d.y:yi.get(d.y)??-1;if(cx<0||cy<0)continue;const x=px+cx*cellW;const y=py+cy*cellH;const n=vMax>vMin?(d.value-vMin)/(vMax-vMin):0.5;e.rect(x+gap,y+gap,cellW-gap*2,cellH-gap*2,{fill:M.colorMix(lo,hi,n*prog),radius:gap?2:0});this.hits.add({_id:k,x,y,w:cellW,h:cellH,tipX:x+cellW/2,tipY:y,data:d,index:k,value:d.value,tooltip:`${xLabels[cx]} · ${yLabels[cy]}: ${this._fmt(d.value)}`,});}
218
+ let lastEnd=-Infinity;for(let i=0;i<xLabels.length;i++){const txt=String(xLabels[i]);const w=e.measure(txt,font);const x=px+(i+0.5)*cellW;if(x-w/2<lastEnd+4)continue;lastEnd=x+w/2;e.text(txt,x,py+ph+6,{fill:t.muted,font,align:'center',baseline:'top'});}
219
+ const step=Math.ceil((t.fontSize+2)/cellH);for(let i=0;i<yLabels.length;i+=step){e.text(String(yLabels[i]),px-6,py+(i+0.5)*cellH,{fill:t.muted,font,align:'right',baseline:'middle'});}
220
+ if(this._hover){const r=this._hover;e.rect(r.x+0.5,r.y+0.5,r.w-1,r.h-1,{stroke:t.text,lineWidth:1.5});}}}
221
+ class BoxplotChart extends Chart{get _kind(){return'Boxplot chart';}
222
+ _render(){const o=this.options;const data=o.data||[];if(!data.length)return this._empty();const e=this.engine;let yMax=-Infinity,yMin=Infinity;for(const d of data){if(d.max>yMax)yMax=d.max;if(d.min<yMin)yMin=d.min;if(d.outliers)for(const v of d.outliers){if(v>yMax)yMax=v;if(v<yMin)yMin=v;}}
223
+ const[yLo,yHi]=M.niceDomain(yMin,yMax,5);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLabels:data.map((d)=>d.label),yTickFormat:o.yTickFormat});const slot=ax.pw/data.length;const boxW=Math.min(40,slot*0.5);const prog=this._anim.progress;for(let i=0;i<data.length;i++){const d=data[i];const cx=ax.px+(i+0.5)*slot;const med=ax.yToPx(d.median);const y=(v)=>med+(ax.yToPx(v)-med)*prog;const color=this._colorOf(i,d);const hover=this._hover&&this._hover._id===i;e.poly([{x:cx,y:y(d.max)},{x:cx,y:y(d.min)}],{stroke:color,lineWidth:1});e.poly([{x:cx-boxW/3,y:y(d.max)},{x:cx+boxW/3,y:y(d.max)}],{stroke:color,lineWidth:1});e.poly([{x:cx-boxW/3,y:y(d.min)},{x:cx+boxW/3,y:y(d.min)}],{stroke:color,lineWidth:1});e.rect(cx-boxW/2,y(d.q3),boxW,y(d.q1)-y(d.q3),{fill:M.colorAlpha(color,hover?0.6:0.4),stroke:color,lineWidth:1.5,});e.poly([{x:cx-boxW/2,y:med},{x:cx+boxW/2,y:med}],{stroke:color,lineWidth:2});if(d.outliers)e.circles(d.outliers.map((v)=>({x:cx,y:y(v)})),color,2);const top=ax.yToPx(d.max),bot=ax.yToPx(d.min);this.hits.add({_id:i,x:cx-boxW/2,y:top,w:boxW,h:Math.max(4,bot-top),tipX:cx,tipY:ax.yToPx(d.q3),data:d,index:i,tooltip:{title:String(d.label),rows:[['max',d.max],['Q3',d.q3],['median',d.median],['Q1',d.q1],['min',d.min]].map(([name,v])=>({name,value:this._fmt(v)})),},});}}}
224
+ class FunnelChart extends Chart{get _kind(){return'Funnel chart';}
225
+ _render(){const o=this.options;const data=toRows(o.data);if(!data.length)return this._empty();const e=this.engine;const t=this.theme;const labelOf=o.label||((d)=>d.label);const valueOf=o.value||((d)=>d.value);const N=data.length;const vals=data.map((d,i)=>Math.max(0,+valueOf(d,i)||0));const max=Math.max(1e-9,vals.reduce((a,v)=>(v>a?v:a),0));const first=vals[0]||max;const pad=12;const w=e.w-pad*2;const segH=(e.h-pad*2)/N;const cx=e.w/2;const prog=this._anim.progress;const font=`600 ${t.fontSize}px ${t.font}`;for(let i=0;i<N;i++){const v=vals[i];const wTop=(v/max)*w*prog;const wBot=i+1<N?(vals[i+1]/max)*w*prog:wTop*0.85;const yTop=pad+i*segH;const yBot=yTop+segH-2;const color=this._colorOf(i,data[i]);const hover=this._hover&&this._hover._id===i;e.withAlpha(hover?1:0.9,()=>e.poly([{x:cx-wTop/2,y:yTop},{x:cx+wTop/2,y:yTop},{x:cx+wBot/2,y:yBot},{x:cx-wBot/2,y:yBot},],{fill:color,close:true}));const txt=`${labelOf(data[i], i)} ${this._fmt(v)}`;const tw=e.measure(txt,font);const inside=tw+16<Math.min(wTop,wBot);e.text(txt,inside?cx:cx+Math.max(wTop,wBot)/2+8,yTop+segH/2,{fill:inside?o.labelColor||'#fff':t.text,font,align:inside?'center':'left',baseline:'middle',});this.hits.add({_id:i,x:cx-Math.max(wTop,8)/2,y:yTop,w:Math.max(wTop,8),h:segH,tipX:cx,tipY:yTop,data:data[i],index:i,value:v,tooltip:`${labelOf(data[i], i)}: ${this._fmt(v)} (${((v / first) * 100).toFixed(1)}%)`,});}}}
226
+ function squarify(items,rect,place){let i=0;let{x,y,w,h}=rect;let rest=items.reduce((a,it)=>a+it.value,0);while(i<items.length&&rest>0&&w>=1&&h>=1){const side=Math.min(w,h);const scale=(w*h)/rest;let sum=0,best=Infinity,j=i;for(;j<items.length;j++){const s=sum+items[j].value;const len=(s*scale)/side;let worst=0;for(let k=i;k<=j;k++){const l=(items[k].value*scale)/len;worst=Math.max(worst,len/l,l/len);}
227
+ if(worst>best)break;best=worst;sum=s;}
228
+ if(j===i){sum=items[i].value;j=i+1;}
229
+ const thick=(sum*scale)/side;let pos=0;for(let k=i;k<j;k++){const l=(items[k].value*scale)/thick;place(items[k],w>=h?{x,y:y+pos,w:thick,h:l}:{x:x+pos,y,w:l,h:thick});pos+=l;}
230
+ if(w>=h){x+=thick;w-=thick;}else{y+=thick;h-=thick;}
231
+ rest-=sum;i=j;}}
232
+ class TreemapChart extends Chart{get _kind(){return'Treemap chart';}
233
+ _render(){const o=this.options;const data=toRows(o.data);if(!data.length)return this._empty();const e=this.engine;const t=this.theme;const labelOf=o.label||((d)=>d.label);const valueOf=o.value||((d)=>d.value);const items=data.map((d,i)=>({label:String(labelOf(d,i)),value:Math.max(0,+valueOf(d,i)||0),color:this._colorOf(i,d),data:d,_id:i})).filter((it)=>it.value>0).sort((a,b)=>b.value-a.value);if(!items.length)return this._empty();const prog=this._anim.progress;const fg=o.labelColor||'#fff';squarify(items,{x:0,y:0,w:e.w,h:e.h},(it,r)=>{const gap=1;const w=(r.w-gap*2)*prog;const h=(r.h-gap*2)*prog;const x=r.x+gap+(r.w-gap*2-w)/2;const y=r.y+gap+(r.h-gap*2-h)/2;if(w<1||h<1)return;const hover=this._hover&&this._hover._id===it._id;e.withAlpha(hover?0.85:1,()=>e.rect(x,y,w,h,{fill:it.color,radius:2}));if(w>48&&h>t.fontSize*2+12){e.text(it.label,x+6,y+6,{fill:fg,font:`600 ${t.fontSize}px ${t.font}`,align:'left',baseline:'top'});e.withAlpha(0.85,()=>e.text(this._fmt(it.value),x+6,y+8+t.fontSize,{fill:fg,font:`${t.fontSize}px ${t.font}`,align:'left',baseline:'top'}));}
234
+ this.hits.add({_id:it._id,x,y,w,h,tipX:x+w/2,tipY:y,data:it.data,index:it._id,value:it.value,tooltip:`${it.label}: ${this._fmt(it.value)}`,});});}}
235
+ class Sparkline extends Chart{get _kind(){return'Sparkline';}
236
+ _render(){const o=this.options;const data=o.data||[];if(!data.length)return;const e=this.engine;const t=this.theme;const valueOf=o.value||((v)=>(typeof v==='object'&&v?+v.value:+v));const values=data.map((d,i)=>valueOf(d,i));const[yMin,yMax]=range([values]);const yRange=yMax-yMin||1;const pad=Math.max(2,t.pointRadius+1);const w=e.w-pad*2;const h=e.h-pad*2;const points=values.map((v,i)=>({x:pad+(i/(values.length-1||1))*w,y:pad+h-((v-yMin)/yRange)*h,}));const color=o.color||this.palette[0];const cps=M.bezierControlPoints(points);e.clip(0,0,e.w*this._anim.progress,e.h);if(o.fill!==false){const grad=e.vGradient(`spark-${color}`,0,pad,pad+h,[[0,M.colorAlpha(color,0.3)],[1,M.colorAlpha(color,0)]]);e.smoothArea(points,cps,pad+h,grad);}
237
+ e.smooth(points,cps,{stroke:color,lineWidth:t.lineWidth});e.unclip();if(o.lastDot!==false){const last=points[points.length-1];e.circle(last.x,last.y,t.pointRadius+1,{fill:color});}
238
+ const n=points.length;points.forEach((p,i)=>{const l=i?(points[i-1].x+p.x)/2:0;const r=i<n-1?(points[i+1].x+p.x)/2:e.w;this.hits.add({_id:i,x:l,y:0,w:r-l,h:e.h,tipX:p.x,tipY:p.y-4,index:i,value:values[i],tooltip:this._fmt(values[i])});});if(this._hover){const p=points[this._hover._id];if(p)e.circle(p.x,p.y,t.pointRadius+1,{fill:t.bg,stroke:color,lineWidth:2});}}}
239
+ class RealtimeChart extends Chart{get _kind(){return'Realtime chart';}
240
+ constructor(container,options={}){super(container,options);this.options.windowSize=options.windowSize||200;this._series=(options.series||[{name:'Series'}]).map((s)=>({name:s.name||'Series',color:s.color,values:[]}));this._follow=true;this._streaming=true;}
241
+ _legend(){return this._series.length>1?this._seriesLegend(this._series):null;}
242
+ push(values){const arr=Array.isArray(values)?values:[values];const N=this.options.windowSize;for(let i=0;i<this._series.length;i++){const v=this._series[i].values;v.push(arr[i]!=null?+arr[i]:NaN);if(v.length>N)v.splice(0,v.length-N);}
243
+ if(this._auto){this._state=null;this._auto=false;}
244
+ this._dirty=true;this._anim.progress=1;scheduler.schedule(this);return this;}
245
+ clear(){for(const s of this._series)s.values.length=0;this._dirty=true;scheduler.schedule(this);return this;}
246
+ _render(){const e=this.engine;const t=this.theme;const o=this.options;const on=this._series.map((s,i)=>i).filter((i)=>!this._isOff(i));const hasData=this._series.some((s)=>s.values.length>0);if(!hasData){const ax=drawCartesian(this,{yMin:0,yMax:100,yTickFormat:o.yTickFormat});e.text(o.waitingText||'streaming…',ax.px+ax.pw/2,ax.py+ax.ph/2,{fill:t.muted,font:`${t.fontSize}px ${t.font}`,align:'center',baseline:'middle',});return;}
247
+ const[lo,hi]=range(on.map((s)=>this._series[s].values.filter((v)=>v===v)),o.yMin!=null?o.yMin:0);const[yLo,yHi]=o.yMax!=null?[o.yMin||0,o.yMax]:M.niceDomain(lo,hi,5);const N=o.windowSize;const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,yTickFormat:o.yTickFormat});const xAt=(i,len)=>ax.px+((N-len+i)/(N-1))*ax.pw;const len=this._series[0].values.length;this._steps=[];for(let i=0;i<len;i++)this._steps.push(xAt(i,len));e.clip(ax.px,ax.py-t.lineWidth,ax.pw+t.lineWidth,ax.ph+t.lineWidth*2);const rows=[];let k=-1;if(this._cursor){let best=Infinity;this._steps.forEach((x,i)=>{const d=Math.abs(x-this._cursor.x);if(d<best){best=d;k=i;}});}
248
+ for(const s of on){const ser=this._series[s];const color=this._colorOf(s,ser);const pts=ser.values.map((v,i)=>({x:xAt(i,ser.values.length),y:ax.yToPx(v)}));if(pts.length===1)e.circle(pts[0].x,pts[0].y,t.pointRadius+2,{fill:color});if(pts.length<2)continue;e.poly(pts,{stroke:color,lineWidth:t.lineWidth,cap:'round',join:'round'});const last=pts[pts.length-1];e.circle(last.x,last.y,t.pointRadius+1,{fill:color});if(k>=0&&pts[k]){e.circle(pts[k].x,pts[k].y,t.pointRadius+2,{fill:t.bg,stroke:color,lineWidth:2});rows.push({color,name:ser.name,value:this._fmt(ser.values[k])});}}
249
+ e.unclip();this._tipNow=null;if(k>=0&&rows.length&&this._cursor.x>=ax.px-4){const x=this._steps[k];e.poly([{x,y:ax.py},{x,y:ax.py+ax.ph}],{stroke:t.muted,lineWidth:1,dash:[3,3]});this._tipNow={title:`#${k + 1 - len}`,rows};this.tooltip.show(x,ax.py+8,this._tipNow,t);}}}
250
+ class WaterfallChart extends Chart{get _kind(){return'Waterfall chart';}
251
+ _render(){const o=this.options;const data=toRows(o.data);if(!data.length)return this._empty();const e=this.engine;const t=this.theme;let cum=0;const segs=data.map((d)=>{const v=+d.value||0;const seg=d.isTotal?{from:0,to:v}:{from:cum,to:cum+v};cum=seg.to;return{...seg,value:v,isTotal:!!d.isTotal,label:d.label};});const[lo,hi]=range([segs.map((s)=>s.from),segs.map((s)=>s.to)],0,0);const[yLo,yHi]=M.niceDomain(lo,hi,5);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLabels:segs.map((s)=>s.label),yTickFormat:o.yTickFormat});const slot=ax.pw/segs.length;const barW=Math.max(1,Math.min(60,slot*0.7));const prog=this._anim.progress;const up=o.colorUp||'#10b981';const down=o.colorDown||'#ef4444';const total=o.colorTotal||'#6366f1';for(let i=0;i<segs.length;i++){const s=segs[i];const x=ax.px+(i+0.5)*slot-barW/2;const y0=ax.yToPx(s.from);const h=Math.abs(ax.yToPx(s.to)-y0)*prog;const top=s.to>=s.from?y0-h:y0;e.rect(x,top,barW,Math.max(h,1),{fill:s.isTotal?total:s.value>=0?up:down,radius:t.barRadius});if(i<segs.length-1&&prog===1){const yEnd=ax.yToPx(s.to);e.poly([{x:x+barW,y:yEnd},{x:x+slot,y:yEnd}],{stroke:t.muted,lineWidth:1,dash:[3,3]});}
252
+ this.hits.add({_id:i,x,y:top,w:barW,h:Math.max(h,4),tipX:x+barW/2,tipY:top,data:data[i],index:i,value:s.value,tooltip:`${s.label}: ${s.value > 0 && !s.isTotal ? '+' : ''}${this._fmt(s.value)}`,});}
253
+ if(this._hover){const r=this._hover;e.rect(r.x,r.y,r.w,r.h,{stroke:t.text,lineWidth:1.5});}}}
254
+ class CompositeChart extends Chart{get _kind(){return'Composite chart';}
255
+ constructor(container,options){super(container,options);this._follow=true;}
256
+ _legend(){return(this.options.layers||[]).map((l,i)=>({label:(l.name||l.type)+(l.axis==='right'?' (R)':''),color:this._colorOf(i,l),}));}
257
+ _render(){const o=this.options;const all=o.layers||[];const cats=o.categories||[];if(!all.length||!cats.length)return this._empty();const e=this.engine;const t=this.theme;const layers=all.map((l,i)=>({...l,i,color:this._colorOf(i,l)})).filter((l)=>!this._isOff(l.i));const yOf=(l,v)=>(l.type==='scatter'?v.y:+v);const domain=(ll)=>{let mn=0,mx=-Infinity;for(const l of ll)for(const v of l.values){const y=yOf(l,v);if(y<mn)mn=y;if(y>mx)mx=y;}
258
+ return mx===-Infinity?[0,1]:M.niceDomain(mn,mx,5);};const left=layers.filter((l)=>l.axis!=='right');const rightL=layers.filter((l)=>l.axis==='right');const[lLo,lHi]=domain(left);const useTime=o.xScale==='time';const ax=drawCartesian(this,{yMin:lLo,yMax:lHi,yTickFormat:o.yLeftTickFormat||o.yTickFormat,right:rightL.length?(([a,b])=>({yMin:a,yMax:b,yTickFormat:o.yRightTickFormat}))(domain(rightL)):null,xLabels:useTime?null:cats,xTime:useTime?{min:+cats[0],max:+cats[cats.length-1]}:null,xTickFormat:o.xTickFormat,});const yPx=(l)=>(l.axis==='right'&&ax.yRToPx?ax.yRToPx:ax.yToPx);const xAt=useTime?(i)=>ax.xToPx(+cats[i]):(i)=>ax.px+((i+0.5)/cats.length)*ax.pw;this._steps=cats.map((_,i)=>xAt(i));const prog=this._anim.progress;const lineW=t.lineWidth;const bars=layers.filter((l)=>l.type==='bar');const slot=ax.pw/cats.length;const inner=slot*0.7;const barW=bars.length?Math.max(1,inner/bars.length):0;bars.forEach((l,bi)=>{const toPx=yPx(l);const y0=toPx(0);const items=[];for(let i=0;i<cats.length;i++){const v=+l.values[i]||0;const h=Math.abs(toPx(v)-y0)*prog;items.push({x:xAt(i)-inner/2+bi*barW,y:v>=0?y0-h:y0,w:Math.max(1,barW-1),h,radius:t.barRadius});}
259
+ e.rects(items,l.color);});e.clip(ax.px-lineW,ax.py-lineW,(ax.pw+lineW*2)*prog,ax.ph+lineW*2);for(const l of layers.filter((x)=>x.type==='area')){const toPx=yPx(l);const pts=l.values.map((v,i)=>({x:xAt(i),y:toPx(+v)}));const cps=M.bezierControlPoints(pts);const grad=e.vGradient(`area-${l.i}-${l.color}`,ax.px,ax.py,ax.py+ax.ph,[[0,M.colorAlpha(l.color,0.4)],[1,M.colorAlpha(l.color,0)]]);e.smoothArea(pts,cps,Math.min(ax.py+ax.ph,toPx(0)),grad);e.smooth(pts,cps,{stroke:l.color,lineWidth:lineW});}
260
+ for(const l of layers.filter((x)=>x.type==='line')){const toPx=yPx(l);const pts=l.values.map((v,i)=>({x:xAt(i),y:toPx(+v)}));e.smooth(pts,M.bezierControlPoints(pts),{stroke:l.color,lineWidth:lineW});if(pts.length<=50)e.circles(pts,l.color,t.pointRadius);}
261
+ e.unclip();for(const l of layers.filter((x)=>x.type==='scatter')){const toPx=yPx(l);const items=l.values.map((p)=>({x:xAt(typeof p.x==='number'?p.x:cats.indexOf(p.x)),y:toPx(p.y)}));e.withAlpha(0.8,()=>e.circles(items,l.color,l.radius||t.pointRadius+1));}
262
+ this._tipNow=null;const cx=this._cursor?.x;if(cx!=null&&cx>=ax.px&&cx<=ax.px+ax.pw){let idx=0,best=Infinity;this._steps.forEach((x,i)=>{const d=Math.abs(x-cx);if(d<best){best=d;idx=i;}});const x=this._steps[idx];e.withAlpha(0.06,()=>e.rect(x-slot/2,ax.py,slot,ax.ph,{fill:t.text}));const rows=[];for(const l of layers){let v;if(l.type==='scatter'){const p=l.values.find((p)=>(typeof p.x==='number'?p.x:cats.indexOf(p.x))===idx);if(!p)continue;v=p.y;}else v=+l.values[idx];rows.push({color:l.color,name:l.name||l.type,value:this._fmt(v)});}
263
+ this._tipNow={title:useTime?new Date(+cats[idx]).toLocaleString():String(cats[idx]),rows};this._hover={_id:idx,index:idx};this.tooltip.show(x,ax.py+8,this._tipNow,t);}else if(this._cursor){this.tooltip.hide();}}}
264
+ const math={TAU,clamp,lerp,extent,niceTicks,niceDomain,timeNiceTicks,timeFormat,minMaxDecimate,minMaxDecimateIdx,shouldDecimate,bezierControlPoints,springStep,colorParse,colorAlpha,colorMix,PALETTE,formatNumber};function install($,registry){if(!$||!$.fn)throw new Error('aeriz: install($) needs jQuery');$.fn.aerizChart=function(type,options){const first=this.first().data('aeriz');if(type==='instance')return first;if(typeof type==='string'&&first&&typeof first[type]==='function'){const args=[].slice.call(arguments,1);return this.each(function(){const c=$(this).data('aeriz');if(!c)return;c[type](...args);if(type==='destroy')$(this).removeData('aeriz');});}
265
+ const T=typeof type==='string'?registry&&registry[type]:type;if(typeof T!=='function')throw new Error(`aeriz: unknown chart type ${type}`);return this.each(function(){$(this).data('aeriz')?.destroy();$(this).data('aeriz',new T(this,options||{}));});};return $;}
266
+ const Aeriz={Chart,getFPS,getActiveCount,scheduler,toRows,Engine,drawCartesian,math,PALETTE,BarChart,HBarChart,LineChart,AreaChart,StackedBarChart,GroupedBarChart,MultiLineChart,ScatterChart,BubbleChart,PieChart,RadarChart,PolarChart,GaugeChart,HeatmapChart,BoxplotChart,FunnelChart,TreemapChart,Sparkline,RealtimeChart,WaterfallChart,CompositeChart,jquery:($)=>install($,Aeriz)};if(typeof window!=='undefined'&&window.jQuery)install(window.jQuery,Aeriz);return Aeriz;})();if(typeof module!=='undefined'&&module.exports)module.exports=Aeriz;if(typeof window!=='undefined')window.Aeriz=Aeriz;