@aeriz/chart 1.0.1 → 2.0.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 (73) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +64 -431
  3. package/dist/aeriz.cjs +2781 -2338
  4. package/dist/aeriz.esm.js +1859 -1459
  5. package/dist/aeriz.esm.min.js +278 -1
  6. package/dist/aeriz.js +2781 -2338
  7. package/dist/aeriz.min.cjs +282 -1
  8. package/dist/aeriz.min.js +282 -1
  9. package/dist/esm/base.js +84 -0
  10. package/dist/esm/cartesian.js +13 -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 +14 -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 +5 -0
  35. package/dist/esm/math.js +32 -0
  36. package/dist/esm/react.js +4 -0
  37. package/dist/esm/shallow.js +5 -0
  38. package/dist/esm/svelte.js +4 -0
  39. package/dist/esm/vue.js +4 -0
  40. package/dist/index.d.ts +323 -0
  41. package/dist/jquery.d.ts +21 -0
  42. package/dist/react.d.ts +59 -0
  43. package/dist/svelte.d.ts +56 -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 +63 -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.d.ts +0 -517
  72. package/src/index.js +0 -18
  73. package/src/math.js +0 -178
@@ -0,0 +1,84 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Engine}from'./engine.js';import*as M from'./math.js';const charts=new Set();let rafId=null;function tick(now){let active=false;for(const c of charts){try{if(c._tick(now))active=true;}catch(e){console.error(e);}}
3
+ rafId=charts.size>0&&active?requestAnimationFrame(tick):null;}
4
+ export function schedule(chart){if(chart._destroyed)return;charts.add(chart);if(rafId===null)rafId=requestAnimationFrame(tick);}
5
+ 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._theme();schedule(c);}});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;}
6
+ return'#ffffff';}
7
+ 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',};}
8
+ 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);}
9
+ return out.length?out:M.PALETTE;}
10
+ function div(parent,css,tag='div'){const el=document.createElement(tag);if(css)el.style.cssText=css;parent.appendChild(el);return el;}
11
+ 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(', ');}
12
+ 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');}
13
+ 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);}}
14
+ 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';}
15
+ hide(){this.el.style.opacity='0';}
16
+ destroy(){this.el.remove();}}
17
+ 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');}
18
+ 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';}
19
+ if(msg)div(el,'opacity:.85').textContent=msg;}
20
+ hide(){this.el.style.display='none';this.el.removeAttribute('role');}
21
+ destroy(){this.el.remove();}}
22
+ class HitIndex{constructor(){this.regions=[];}
23
+ clear(){this.regions.length=0;}
24
+ add(r){this.regions.push(r);}
25
+ 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;}
26
+ return null;}}
27
+ const isList=(d)=>Array.isArray(d)||ArrayBuffer.isView(d);const isObj=(d)=>d!=null&&typeof d==='object'&&!isList(d);export function toRows(data){if(isList(data)){if(!data.length||Array.isArray(data[0]))return[];return typeof data[0]==='number'?Array.from(data,(v,i)=>({label:String(i+1),value:v})):data;}
28
+ return isObj(data)?Object.keys(data).map((k)=>({label:k,value:+data[k]})):[];}
29
+ const said=new Set();export function warn(msg){if(said.has(msg))return;said.add(msg);console.warn('[aeriz] '+msg);}
30
+ export const COMMON=['data','title','colors','format','legend','animate','emptyText','onClick','onLegend'];const SHAPES={rows:'an array of numbers, an object like { Jan: 64 }, or rows like [{ label, value }]',series:'{ categories: [...], series: [{ name, values: [...] }] }',stream:'{ series: [{ name }] } (values arrive with chart.push())',points:'points like [{ x, y }]',matrix:'{ x: [...], y: [...], values: [[...], ...] } with values[y][x]',boxes:'rows like [{ label, min, q1, median, q3, max }]',steps:'rows like [{ label, value }] (add total: true for a total bar)',number:'a number',};const DATA='data: { categories, series }';const RENAMED={ariaLabel:'title',palette:'colors',color:'colors',xTickFormat:'labelFormat (category axis) or format (value axis)',yTickFormat:'format',yLeftTickFormat:'format on the series',yRightTickFormat:'format on the series',categories:DATA,series:DATA,layers:DATA,axes:DATA,xLabels:'data: { x, y, values }',yLabels:'data: { x, y, values }',interpolation:'curve',windowSize:'window',yMin:'min',yMax:'max',waitingText:'emptyText',colorLow:'colors: { low, high }',colorHigh:'colors: { low, high }',colorUp:'colors: { up, down, total }',colorDown:'colors: { up, down, total }',colorTotal:'colors: { up, down, total }',minRadius:'radius: [min, max]',maxRadius:'radius: [min, max]',centerLabel:'center',totalLabel:'center',type:'the chart class itself',};const FUNCS=['label','value','labelFormat','onClick','onLegend'];function distance(a,b){const d=Array.from({length:b.length+1},(_,j)=>j);for(let i=1;i<=a.length;i++){let p=d[0];d[0]=i;for(let j=1;j<=b.length;j++){const t=d[j];d[j]=Math.min(d[j]+1,d[j-1]+1,p+(a[i-1]===b[j-1]?0:1));p=t;}}
31
+ return d[b.length];}
32
+ function checkOptions(meta,o){const name=meta.name;const allowed=[...COMMON,...meta.options];for(const k of Object.keys(o)){const v=o[k];if(!allowed.includes(k)){const hint=meta.renamed?.[k]||RENAMED[k];if(hint){warn(`${name}: "${k}" is a 1.x option. In 2.0 use ${hint}.`);continue;}
33
+ let best=null,bd=3;for(const a of allowed){const dd=a.toLowerCase()===k.toLowerCase()?0:distance(k,a);if(dd<bd){bd=dd;best=a;}}
34
+ warn(`${name}: unknown option "${k}"${best ? ` — did you mean "${best}"?` : `. Options: ${allowed.join(', ')}`}`);continue;}
35
+ if(v==null)continue;if(FUNCS.includes(k)&&typeof v!=='function')warn(`${name}: "${k}" must be a function${k === 'label' || k === 'value' ? `, like ${k}: (row) => row.${k === 'label' ? 'name' : 'amount'}` : ''}.`);const en=meta.enums?.[k];if(en&&!en.includes(v))warn(`${name}: ${k} must be ${en.map((x) => `'${x}'`).join(' | ')}, got ${JSON.stringify(v)}.`);if(k==='format'&&typeof v!=='function'&&!isObj(v))warn(`${name}: format must be Intl.NumberFormat options like { style: 'percent' } or a function (v) => string.`);if(k==='colors'&&(meta.colors?!isObj(v):!Array.isArray(v)))warn(`${name}: colors must be ${meta.colors || 'an array like [\'#64c8ff\', \'#10b981\']'}.`);}
36
+ if('data'in o)checkData(meta,o.data,o);}
37
+ function checkData(meta,d,o){if(d==null)return;const kind=meta.data;const bad=(why)=>warn(`${meta.name}: data must be ${SHAPES[kind]}${why ? ` — ${why}` : ''}.`);const first=isList(d)?d[0]:undefined;switch(kind){case'rows':if(isList(d)){if(Array.isArray(first))return bad(`pairs like ['Jan', 64] were removed in 2.0`);if(first!=null&&typeof first!=='number'&&!isObj(first))return bad(`got ${typeof first} items`);if(isObj(first)&&!o.value&&!('value'in first))warn(`${meta.name}: rows need a "value" field, or pass value: (row) => row.amount.`);if(isObj(first)&&!o.label&&!('label'in first))warn(`${meta.name}: rows need a "label" field, or pass label: (row) => row.name.`);}else if(!isObj(d))bad(`got ${typeof d}`);return;case'series':case'stream':{if(!isObj(d)||!Array.isArray(d.series)||(kind==='series'&&!isList(d.categories)))return bad(isList(d)?'got an array':'');const n=kind==='series'?d.categories.length:0;d.series.forEach((s,i)=>{const who=`series[${i}]${s?.name ? ` "${s.name}"` : ''}`;if(kind==='stream')return;if(!s||!isList(s.values))return warn(`${meta.name}: ${who} needs values: [...].`);if(s.values.length!==n)warn(`${meta.name}: ${who} has ${s.values.length} values but there are ${n} categories.`);});return;}
38
+ case'matrix':if(!isObj(d)||!isList(d.x)||!isList(d.y)||!Array.isArray(d.values))return bad(isList(d)?'got an array':'');if(d.values.length!==d.y.length)warn(`${meta.name}: values has ${d.values.length} rows but y has ${d.y.length} labels.`);return;case'number':if(typeof d!=='number')bad(`got ${typeof d}`);return;default:{if(!Array.isArray(d))return bad(`got ${isObj(d) ? 'an object' : typeof d}`);if(!first)return;const need={points:['x','y'],boxes:['q1','median','q3'],steps:['value']}[kind];const miss=need.filter((k)=>!(k in first));if(miss.length)bad(`the first item has no ${miss.join(', ')}`);if(kind==='steps'&&'isTotal'in first)warn(`${meta.name}: "isTotal" is 1.x. In 2.0 use total: true.`);if(kind==='points'&&('r'in first||'z'in first))warn(`${meta.name}: point "${'r' in first ? 'r' : 'z'}" is 1.x. In 2.0 use size.`);}}}
39
+ const STATES=['loading','empty','error'];export class Chart{static get meta(){return null;}
40
+ constructor(container,options={}){const meta=this.constructor.meta;const el=typeof container==='string'?document.querySelector(container):container;if(!el)throw new Error(`aeriz: ${meta ? meta.name + ': ' : ''}container not found${typeof container === 'string' ? ': ' + container : ''}`);this.container=el;this.options={...options};if(meta)checkOptions(meta,this.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._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._theme();this._label();this._setupEvents();this._observe();this._resize();if(options.animate===false||reducedMotion?.matches)this._anim.progress=1;schedule(this);}
41
+ update(patch={}){const meta=this.constructor.meta;if(meta)checkOptions(meta,patch);Object.assign(this.options,patch);this._cache=null;if('colors'in patch)this._theme();if(this._auto){this._state=null;this._auto=false;}
42
+ this._label();this._restart();return this;}
43
+ setState(state,text){if(state!=null&&!STATES.includes(state)){throw new Error(`aeriz: setState() takes 'loading', 'empty', 'error' or null — got ${JSON.stringify(state)}`);}
44
+ this._state=state||null;this._stateMsg=text??(state==='empty'?this.options.emptyText||'No data':state==='error'?'Error':'');this._auto=false;this._dirty=true;schedule(this);return this;}
45
+ render(){if(this._destroyed)return this;this._theme();this._anim.progress=1;this._resize();this._dirty=false;this._draw();return this;}
46
+ 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;}
47
+ _theme(){const o=this.options;this.theme=resolveTheme(this.container);this.palette=Array.isArray(o.colors)&&o.colors.length?o.colors:readPalette(this.container);this.engine._gradCache.clear();this._legendKey='';this._dirty=true;}
48
+ _empty(msg=this.options.emptyText||'No data'){this._state='empty';this._stateMsg=msg;this._auto=true;this.overlay.show('empty',msg,this.theme);}
49
+ _restart(){const instant=this.options.animate===false||reducedMotion?.matches;this._anim.start=0;this._anim.progress=instant?1:0;this._dirty=true;schedule(this);}
50
+ _label(){this.canvas.setAttribute('aria-label',this.options.title||this.constructor.meta?.aria||'Chart');}
51
+ _fmt(v){return M.formatNumber(v,this.options.format);}
52
+ _tickFmt(v){return M.formatNumber(v,this.options.format,true);}
53
+ _lab(l,time){const f=this._fn('labelFormat');return f?String(f(l)):time?new Date(+l).toLocaleString():String(l);}
54
+ _xFmt(v){const f=this._fn('labelFormat');return f?String(f(v)):this._fmt(v);}
55
+ _isOff(i){return this._off.has(i);}
56
+ _fn(k){const f=this.options[k];return typeof f==='function'?f:null;}
57
+ _hit(h){if(!h)return null;const out={index:h.index,label:h.label};if('value'in h)out.value=h.value;if('series'in h)out.series=h.series;if('row'in h)out.row=h.row;return out;}
58
+ _click(h){const hit=this._hit(h);if(hit)this.options.onClick?.(hit,this);}
59
+ _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;}
60
+ _observe(){if(typeof ResizeObserver!=='undefined'){this._ro=new ResizeObserver(()=>{if(this._resize())schedule(this);});this._ro.observe(this.plot);}
61
+ 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);}}
62
+ _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);}
63
+ _removeEvents(){for(const k in this._handlers)this.canvas.removeEventListener(k,this._handlers[k]);this.legendEl.removeEventListener('click',this._legendClick);}
64
+ _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();}
65
+ _ptr(e){const r=this.canvas.getBoundingClientRect();return{x:e.clientX-r.left,y:e.clientY-r.top};}
66
+ _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();}}
67
+ if(changed||this._follow){this._dirty=true;schedule(this);}
68
+ if(typeof this._onMoveX==='function')this._onMoveX(p);return changed;}
69
+ _onMove(e){const p=this._ptr(e);this._cursor=p;this._setHover(this.hits.find(p.x,p.y),p);}
70
+ _onLeave(){this._cursor=null;this._hover=null;this._key=-1;this.tooltip.hide();this._dirty=true;schedule(this);}
71
+ _onClick(e){const p=this._ptr(e);this._click(this.hits.find(p.x,p.y)||(this._follow?this._hover:null));}
72
+ _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._click(this._hover);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);}}
73
+ _tick(now){if(this._destroyed)return false;if(!this._visible){if(this._anim.progress<1)this._anim.start=0;return false;}
74
+ 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;}
75
+ if(this._dirty){this._dirty=false;this._draw();}
76
+ return active;}
77
+ _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});}
78
+ if(this._state&&!this._auto){this.overlay.show(this._state,this._stateMsg,this.theme);return;}
79
+ this._state=null;this._auto=false;this.overlay.hide();if(typeof this._render==='function')this._render();}
80
+ _legend(){return null;}
81
+ _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));});}
82
+ _seriesLegend(series){return series.map((s,i)=>({label:s.name||'Series '+(i+1),color:this._colorOf(i,s)}));}
83
+ _colorOf(idx,item){if(item&&item.color)return item.color;return this.palette[idx%this.palette.length];}}
84
+ const GONE={setData:'update({ data })',setLoading:"setState('loading', text)",setEmpty:"setState('empty', text)",setError:"setState('error', text)",clearState:'setState(null)',invalidate:'render()',setValue:'update({ data: value })',};for(const k in GONE){Chart.prototype[k]=function(){throw new Error(`aeriz 2.0: chart.${k}() was removed. Use chart.${GONE[k]}.`);};}
@@ -0,0 +1,13 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import*as M from'./math.js';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;}
3
+ export 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);}
4
+ export 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);}
5
+ const yFmt=spec.yFormat?(v)=>M.formatNumber(v,spec.yFormat,true):(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.yFormat?(v)=>M.formatNumber(v,right.yFormat,true):(v)=>chart._tickFmt(v);rText=rTicks.map((v)=>rf(v));for(const s of rText)rMaxW=Math.max(rMaxW,e.measure(s,font));}
6
+ 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);}
7
+ 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;const lf=chart._fn('labelFormat');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=lf?(v)=>String(lf(v)):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=lf?(v)=>String(lf(v)):(v)=>chart._tickFmt(v);}else if(spec.xLabels){const n=spec.xLabels.length||1;xToPx=(i)=>px+(i+0.5)*(pw/n);xFmt=(i)=>chart._lab(spec.xLabels[i]);const guess=Math.max(e.measure(xFmt(0),font),e.measure(xFmt(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);}
8
+ if(xToPx){let lastEnd=-Infinity;for(const v of xTicks){const txt=xFmt(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'});}}
9
+ 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};}
10
+ export 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;}}
11
+ if(lo===Infinity)return[0,1];return[lo,hi];}
12
+ export function valueDomain(o,lo,hi){const[a,b]=M.niceDomain(o.min??lo,o.max??hi,5);return[o.min??a,o.max??b];}
13
+ export const AXIS=['labelFormat','min','max'];
@@ -0,0 +1,9 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart}from'../base.js';import{drawCartesian,range,valueDomain}from'../cartesian.js';import*as M from'../math.js';export class AreaChart extends Chart{static get meta(){return{id:'area',name:'AreaChart',aria:'Area chart',data:'series',options:['labelFormat','min','max','stacked']};}
3
+ _legend(){return this._seriesLegend(this.options.data?.series||[]);}
4
+ _render(){const o=this.options;const{categories:cats=[],series=[]}=o.data||{};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);}
5
+ const[yLo,yHi]=valueDomain(o,yMin,yMax);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLabels:cats});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;}
6
+ 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);}
7
+ 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});}
8
+ 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,label:cats[i],tooltip:{title:this._lab(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])})),},});}
9
+ 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]});}}}
@@ -0,0 +1,4 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart,toRows}from'../base.js';import{drawCartesian,range,valueDomain}from'../cartesian.js';export class BarChart extends Chart{static get meta(){return{id:'bar',name:'BarChart',aria:'Bar chart',data:'rows',options:['label','value','labelFormat','min','max','dataLabels']};}
3
+ _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=this._fn('label')||((d)=>d.label);const valueOf=this._fn('value')||((d)=>d.value);const values=data.map((d,i)=>+valueOf(d,i)||0);const labels=data.map((d,i)=>labelOf(d,i));const[lo,hi]=range([values],0,0);const[yLo,yHi]=valueDomain(o,lo,hi);const dl=o.dataLabels;const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLabels:labels,pad:dl?{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(Math.max(yLo,0));const groups=new Map();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(i,data[i]);if(!groups.has(color))groups.set(color,[]);groups.get(color).push({x,y:top,w:barW,h,radius:t.barRadius});const label=this._lab(labels[i]);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:`${label}: ${this._fmt(v)}`,index:i,label:labels[i],value:v,row:data[i],});if(dl&&prog>0.95){const txt=typeof dl==='function'?dl(v,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',});}}
4
+ 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});}}}
@@ -0,0 +1,4 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart}from'../base.js';import{drawCartesian,valueDomain}from'../cartesian.js';import*as M from'../math.js';export class BoxplotChart extends Chart{static get meta(){return{id:'boxplot',name:'BoxplotChart',aria:'Box plot',data:'boxes',options:['labelFormat','min','max']};}
3
+ _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;}}
4
+ const[yLo,yHi]=valueDomain(o,yMin,yMax);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLabels:data.map((d)=>d.label)});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),index:i,label:d.label,value:d.median,row:d,tooltip:{title:this._lab(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)})),},});}}}
@@ -0,0 +1,3 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart}from'../base.js';import{drawCartesian,valueDomain}from'../cartesian.js';import{xyExtent}from'./scatter.js';import*as M from'../math.js';export class BubbleChart extends Chart{static get meta(){return{id:'bubble',name:'BubbleChart',aria:'Bubble chart',data:'points',options:['labelFormat','min','max','radius']};}
3
+ _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.size||0)>zMax)zMax=d.size;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]=valueDomain(o,yMin-py,yMax+py);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLinear:{min:xLo,max:xHi},});const prog=this._anim.progress;const[minR,maxR]=Array.isArray(o.radius)?o.radius:[4,32];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.size||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,index:i,label:d.label,value:d.y,row:d,tooltip:`${d.label ? d.label + ' ' : ''}(${this._xFmt(d.x)}, ${this._fmt(d.y)}) · ${this._fmt(d.size)}`,});}}}
@@ -0,0 +1,11 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart,warn}from'../base.js';import{drawCartesian,valueDomain}from'../cartesian.js';import*as M from'../math.js';const TYPES=['bar','line','area','scatter'];export class CompositeChart extends Chart{static get meta(){return{id:'composite',name:'CompositeChart',aria:'Composite chart',data:'series',options:['labelFormat','min','max','xScale'],enums:{xScale:['category','time']}};}
3
+ constructor(container,options){super(container,options);this._follow=true;}
4
+ _legend(){return(this.options.data?.series||[]).map((l,i)=>({label:(l.name||l.type)+(l.axis==='right'?' (R)':''),color:this._colorOf(i,l),}));}
5
+ _render(){const o=this.options;const{categories:cats=[],series:all=[]}=o.data||{};if(!all.length||!cats.length)return this._empty();const e=this.engine;const t=this.theme;all.forEach((l,i)=>{if(!TYPES.includes(l.type))warn(`CompositeChart: series[${i}] type must be ${TYPES.map((x) => `'${x}'`).join(' | ')} — got ${JSON.stringify(l.type)}.`);});const layers=all.map((l,i)=>({...l,i,color:this._colorOf(i,l)})).filter((l)=>!this._isOff(l.i));const domain=(ll)=>{let mn=0,mx=-Infinity;for(const l of ll)for(const v of l.values){if(v==null)continue;const y=+v;if(y<mn)mn=y;if(y>mx)mx=y;}
6
+ 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');let[lLo,lHi]=domain(left);if(o.min!=null||o.max!=null)[lLo,lHi]=valueDomain(o,o.min??lLo,o.max??lHi);const fmtOf=(ll)=>ll.find((l)=>l.format)?.format;const useTime=o.xScale==='time';const ax=drawCartesian(this,{yMin:lLo,yMax:lHi,yFormat:fmtOf(left),right:rightL.length?(([a,b])=>({yMin:a,yMax:b,yFormat:fmtOf(rightL)}))(domain(rightL)):null,xLabels:useTime?null:cats,xTime:useTime?{min:+cats[0],max:+cats[cats.length-1]}:null,});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});}
7
+ 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});}
8
+ 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);}
9
+ e.unclip();for(const l of layers.filter((x)=>x.type==='scatter')){const toPx=yPx(l);const items=[];l.values.forEach((v,i)=>{if(v!=null)items.push({x:xAt(i),y:toPx(+v)});});e.withAlpha(0.8,()=>e.circles(items,l.color,t.pointRadius+1));}
10
+ 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){const v=l.values[idx];if(v==null)continue;rows.push({color:l.color,name:l.name||l.type,value:M.formatNumber(+v,l.format??o.format)});}
11
+ this._tipNow={title:this._lab(cats[idx],useTime),rows};this._hover={_id:idx,index:idx,label:cats[idx]};this.tooltip.show(x,ax.py+8,this._tipNow,t);}else if(this._cursor){this.tooltip.hide();}}}
@@ -0,0 +1,3 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart,toRows}from'../base.js';export class FunnelChart extends Chart{static get meta(){return{id:'funnel',name:'FunnelChart',aria:'Funnel chart',data:'rows',options:['label','value','labelColor']};}
3
+ _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=this._fn('label')||((d)=>d.label);const valueOf=this._fn('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,index:i,label:labelOf(data[i],i),value:v,row:data[i],tooltip:`${labelOf(data[i], i)}: ${this._fmt(v)} (${((v / first) * 100).toFixed(1)}%)`,});}}}
@@ -0,0 +1,5 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart}from'../base.js';export class GaugeChart extends Chart{static get meta(){return{id:'gauge',name:'GaugeChart',aria:'Gauge',data:'number',options:['min','max','unit'],renamed:{value:'data',label:'title'},};}
3
+ update(patch={}){if('data'in patch)this._from=this._shown;return super.update(patch);}
4
+ _render(){const e=this.engine;const t=this.theme;const o=this.options;const min=o.min??0;const max=o.max??100;const target=Math.max(min,Math.min(max,+o.data||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=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.title){e.text(o.title,cx,cy+R*0.22,{fill:t.muted,font:`${t.fontSize}px ${t.font}`,align:'center',baseline:'top'});}
5
+ this.canvas.setAttribute('aria-label',`${o.title || 'Gauge'}: ${txt}`);}}
@@ -0,0 +1,5 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart}from'../base.js';import{drawCartesian,range,valueDomain}from'../cartesian.js';export class GroupedBarChart extends Chart{static get meta(){return{id:'grouped-bar',name:'GroupedBarChart',aria:'Grouped bar chart',data:'series',options:['labelFormat','min','max']};}
3
+ _legend(){return this._seriesLegend(this.options.data?.series||[]);}
4
+ _render(){const o=this.options;const{categories:cats=[],series=[]}=o.data||{};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]=valueDomain(o,lo,hi);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLabels:cats});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,label:cats[i],series:s,value:v,row:series[s],tooltip:{title:this._lab(cats[i]),rows:[{color,name:series[s].name||'Series '+(s+1),value:this._fmt(v)}]},});});}
5
+ 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});}}}
@@ -0,0 +1,6 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart,toRows}from'../base.js';import{range,valueDomain}from'../cartesian.js';import*as M from'../math.js';export class HBarChart extends Chart{static get meta(){return{id:'hbar',name:'HBarChart',aria:'Horizontal bar chart',data:'rows',options:['label','value','labelFormat','min','max']};}
3
+ _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=this._fn('label')||((d)=>d.label);const valueOf=this._fn('value')||((d)=>d.value);const values=data.map((d,i)=>+valueOf(d,i)||0);const raw=data.map((d,i)=>labelOf(d,i));const labels=raw.map((l)=>this._lab(l));const[lo,hi]=range([values],0,0);const[xLo,xHi]=valueDomain(o,lo,hi);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);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);}
4
+ e.ctx.stroke();for(const v of xTicks){e.text(this._tickFmt(v),xToPx(v),py+ph+6,{fill:t.muted,font,align:'center',baseline:'top'});}
5
+ 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(Math.max(xLo,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(i,data[i]);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)}`,index:i,label:raw[i],value:v,row:data[i],});e.text(labels[i],px-6,yMid,{fill:t.muted,font,align:'right',baseline:'middle'});}
6
+ 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});}}}
@@ -0,0 +1,7 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart}from'../base.js';import*as M from'../math.js';export class HeatmapChart extends Chart{static get meta(){return{id:'heatmap',name:'HeatmapChart',aria:'Heatmap',data:'matrix',options:['labelFormat'],colors:'an object like { low: \'#eef\', high: \'#36c\' }'};}
3
+ _render(){const o=this.options;const{x:xLabels=[],y:yLabels=[],values=[]}=o.data||{};if(!values.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;const yText=Array.from(yLabels,(l)=>this._lab(l));for(const s of yText)labelW=Math.max(labelW,e.measure(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 row of values){for(const v of row||[]){if(v>vMax)vMax=v;if(v<vMin)vMin=v;}}
4
+ const cellW=pw/xLabels.length;const cellH=ph/yLabels.length;const c=o.colors||{};const hi=c.high||this.palette[0];const lo=c.low||M.colorAlpha(hi,0.08);const gap=cellW>6&&cellH>6?1:0;const prog=this._anim.progress;for(let cy=0;cy<yLabels.length;cy++){const row=values[cy]||[];for(let cx=0;cx<xLabels.length;cx++){const v=row[cx];if(v==null||v!==v)continue;const x=px+cx*cellW;const y=py+cy*cellH;const n=vMax>vMin?(v-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:cy*xLabels.length+cx,x,y,w:cellW,h:cellH,tipX:x+cellW/2,tipY:y,index:cx,series:cy,label:xLabels[cx],value:v,row:{x:xLabels[cx],y:yLabels[cy],value:v},tooltip:`${this._lab(xLabels[cx])} · ${yText[cy]}: ${this._fmt(v)}`,});}}
5
+ let lastEnd=-Infinity;for(let i=0;i<xLabels.length;i++){const txt=this._lab(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'});}
6
+ const step=Math.ceil((t.fontSize+2)/cellH);for(let i=0;i<yLabels.length;i+=step){e.text(yText[i],px-6,py+(i+0.5)*cellH,{fill:t.muted,font,align:'right',baseline:'middle'});}
7
+ 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});}}}
@@ -0,0 +1,9 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart,toRows}from'../base.js';import{drawCartesian,range,valueDomain}from'../cartesian.js';import*as M from'../math.js';const LABEL=(d)=>d.label;const VALUE=(d)=>d.value;export class LineChart extends Chart{static get meta(){return{id:'line',name:'LineChart',aria:'Line chart',data:'rows',options:['label','value','labelFormat','min','max','curve','fill','points','xScale','yScale'],enums:{curve:['smooth','linear','step'],xScale:['category','time'],yScale:['linear','log']},};}
3
+ _render(){const o=this.options;const labelOf=this._fn('label')||LABEL;const valueOf=this._fn('value')||VALUE;let c=this._cache;if(!c||c.data!==o.data||c.valueOf!==valueOf||c.labelOf!==labelOf){const d=o.data;const nums=d&&typeof d[0]==='number'&&(Array.isArray(d)||ArrayBuffer.isView(d))&&!this._fn('value')&&!this._fn('label');const rows=nums?d:toRows(d);const values=nums?Float64Array.from(d):new Float64Array(rows.length);if(!nums)for(let i=0;i<rows.length;i++)values[i]=+valueOf(rows[i],i);const labels=nums?Array.from({length:d.length},(_,i)=>i+1):null;c=this._cache={data:o.data,rows,valueOf,labelOf,values,idx:null,idxW:0,xs:null,labels,range:null};}
4
+ const data=c.rows;if(!data.length)return this._empty();const e=this.engine;const t=this.theme;const n=data.length;const values=c.values;const log=o.yScale==='log';const[lo,hi]=(c.range||=range([values],log?Infinity:0));const[yLo,yHi]=log?[o.min??lo,o.max??hi]:valueDomain(o,lo,hi);const useTime=o.xScale==='time';const spec={yMin:yLo,yMax:yHi,yScale:o.yScale};const xs=useTime?(c.xs||=Array.from(data,(d,i)=>+labelOf(d,i))):null;if(useTime)spec.xTime={min:xs[0],max:xs[n-1]};else spec.xLabels=c.labels||=Array.from(data,(d,i)=>labelOf(d,i));const ax=drawCartesian(this,spec);const color=this.palette[0];const interp=o.curve||'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]);}
5
+ 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};}
6
+ 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});}
7
+ if(cps)e.smooth(points,cps,{stroke:color,lineWidth:lineW});else e.poly(points,{stroke:color,lineWidth:lineW,cap:'round',join:'round'});}else{e.step(points,'before',{stroke:color,lineWidth:lineW});}
8
+ e.unclip();const labelAt=(i)=>(useTime?xs[i]:c.labels[i]);const tip=(i)=>`${this._lab(labelAt(i), useTime)}: ${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),index:p.i,label:labelAt(p.i),value:values[p.i],row:data[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),index:p.i,label:labelAt(p.i),value:values[p.i],row:data[p.i],});}}
9
+ 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});}}}
@@ -0,0 +1,8 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart}from'../base.js';import{drawCartesian,range,valueDomain}from'../cartesian.js';import*as M from'../math.js';export class MultiLineChart extends Chart{static get meta(){return{id:'multi-line',name:'MultiLineChart',aria:'Multi line chart',data:'series',options:['labelFormat','min','max','curve','xScale'],enums:{curve:['smooth','linear','step'],xScale:['category','time']}};}
3
+ constructor(container,options){super(container,options);this._follow=true;}
4
+ _legend(){return this._seriesLegend(this.options.data?.series||[]);}
5
+ _render(){const o=this.options;const{categories:cats=[],series=[]}=o.data||{};if(!series.length||!cats.length)return this._empty();const e=this.engine;const t=this.theme;const interp=o.curve||'smooth';const on=series.map((s,i)=>i).filter((i)=>!this._isOff(i));const[lo,hi]=range(on.map((s)=>series[s].values),0);const[yLo,yHi]=valueDomain(o,lo,hi);const useTime=o.xScale==='time';const spec={yMin:yLo,yMax:yHi};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,'before',{stroke:color,lineWidth:lineW});if(pts.length<=50)e.circles(pts,color,t.pointRadius);}
6
+ 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;}}
7
+ 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])});}
8
+ const title=this._lab(cats[idx],useTime);this._tipNow={title,rows};this._hover={_id:idx,index:idx,label:cats[idx]};this.tooltip.show(xPx,ax.py+8,this._tipNow,t);}else if(this._cursor){this.tooltip.hide();}}}
@@ -0,0 +1,6 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart,toRows,warn}from'../base.js';import{TAU}from'../math.js';export 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;};}
3
+ export class PieChart extends Chart{static get meta(){return{id:'pie',name:'PieChart',aria:'Pie chart',data:'rows',options:['label','value','innerRadius','center']};}
4
+ _legend(){const o=this.options;const labelOf=this._fn('label')||((d)=>d.label);return toRows(o.data).map((d,i)=>({label:String(labelOf(d,i)),color:this._colorOf(i,d)}));}
5
+ _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=this._fn('label')||((d)=>d.label);const valueOf=this._fn('value')||((d)=>d.value);const raw=data.map((d,i)=>+valueOf(d,i)||0);const neg=raw.findIndex((v)=>v<0);if(neg>=0)warn(`PieChart: values can't be negative — "${labelOf(data[neg], neg)}" is ${raw[neg]}. It is drawn as 0.`);const vals=raw.map((v,i)=>(this._isOff(i)?0:Math.max(0,v)));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||0;if(ir>0.9)warn(`PieChart: innerRadius is a ratio from 0 to 0.9 (0.6 = donut) — got ${ir}.`);const innerR=R*Math.min(0.9,Math.max(0,ir));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,index:i,label:labelOf(data[i],i),value:v,row:data[i],tooltip:`${this._lab(labelOf(data[i], i))}: ${this._fmt(v)} (${((v / total) * 100).toFixed(1)}%)`,});a0=a1;}
6
+ if(innerR>0&&o.center!==false){const h=this._hover;const center=h?`${((vals[h._id] / total) * 100).toFixed(0)}%`:o.center??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',});}}}
@@ -0,0 +1,3 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart,toRows}from'../base.js';import{inSector}from'./pie.js';import{TAU}from'../math.js';export class PolarChart extends Chart{static get meta(){return{id:'polar',name:'PolarChart',aria:'Polar chart',data:'rows',options:['label','value','scale'],enums:{scale:['sqrt','linear']}};}
3
+ _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=this._fn('label')||((d)=>d.label);const valueOf=this._fn('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,index:i,label:labelOf(data[i],i),value:v,row:data[i],tooltip:`${labelOf(data[i], i)}: ${this._fmt(v)}`,});}}}
@@ -0,0 +1,10 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart}from'../base.js';import{range}from'../cartesian.js';import*as M from'../math.js';import{TAU}from'../math.js';export class RadarChart extends Chart{static get meta(){return{id:'radar',name:'RadarChart',aria:'Radar chart',data:'series',options:['labelFormat','max']};}
3
+ _legend(){return this._seriesLegend(this.options.data?.series||[]);}
4
+ _render(){const o=this.options;const{categories:axes=[],series=[]}=o.data||{};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;const names=axes.map((a)=>this._lab(a));for(const a of names)labelW=Math.max(labelW,e.measure(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=o.max??(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);}
5
+ ctx.stroke();}
6
+ 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);}
7
+ ctx.stroke();for(let i=0;i<N;i++){const a=angle(i);e.text(names[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',});}
8
+ 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=Math.min(1,(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,label:axes[i],series:s,value:v,row:ser,tooltip:{title:names[i],rows:[{color,name:ser.name||'Series '+(s+1),value:this._fmt(v)}]},});}
9
+ e.poly(pts,{fill:M.colorAlpha(color,0.2),stroke:color,lineWidth:2,close:true,join:'round'});e.circles(pts,color,t.pointRadius);}
10
+ if(this._hover){const h=this._hover;e.circle(h.x+7,h.y+7,t.pointRadius+3,{stroke:t.text,lineWidth:1.5});}}}
@@ -0,0 +1,14 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart,schedule}from'../base.js';import{drawCartesian,range,valueDomain}from'../cartesian.js';export class RealtimeChart extends Chart{static get meta(){return{id:'realtime',name:'RealtimeChart',aria:'Realtime chart',data:'stream',options:['min','max','window']};}
3
+ constructor(container,options={}){super(container,options);this._series=[];this._sync();this._follow=true;}
4
+ _sync(){const list=this.options.data?.series||[{name:'Series'}];this._series=list.map((s,i)=>({name:s.name||'Series '+(i+1),color:s.color,values:this._series[i]?.values||[]}));}
5
+ update(patch={}){super.update(patch);if('data'in patch)this._sync();return this;}
6
+ _legend(){return this._series.length>1?this._seriesLegend(this._series):null;}
7
+ push(values){const arr=Array.isArray(values)?values:[values];const N=this.options.window||200;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);}
8
+ if(this._auto){this._state=null;this._auto=false;}
9
+ this._dirty=true;this._anim.progress=1;schedule(this);return this;}
10
+ clear(){for(const s of this._series)s.values.length=0;this._dirty=true;schedule(this);return this;}
11
+ _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:o.min??0,yMax:o.max??100});e.text(o.emptyText||'Waiting for data…',ax.px+ax.pw/2,ax.py+ax.ph/2,{fill:t.muted,font:`${t.fontSize}px ${t.font}`,align:'center',baseline:'middle',});return;}
12
+ const[lo,hi]=range(on.map((s)=>this._series[s].values.filter((v)=>v===v)),0);const[yLo,yHi]=valueDomain(o,lo,hi);const N=o.window||200;const ax=drawCartesian(this,{yMin:yLo,yMax:yHi});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;}});}
13
+ 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])});}}
14
+ 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._hover={_id:k,index:k,label:k+1-len};this.tooltip.show(x,ax.py+8,this._tipNow,t);}}}
@@ -0,0 +1,6 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart}from'../base.js';import{drawCartesian,valueDomain}from'../cartesian.js';import*as M from'../math.js';export 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;}
3
+ return[xMin,xMax,yMin,yMax];}
4
+ export class ScatterChart extends Chart{static get meta(){return{id:'scatter',name:'ScatterChart',aria:'Scatter chart',data:'points',options:['labelFormat','min','max']};}
5
+ _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]=valueDomain(o,yMin,yMax);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLinear:{min:xLo,max:xHi},});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.size||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,index:i,label:d.label,value:d.y,row:d,tooltip:`${d.label ? d.label + ' ' : ''}(${this._xFmt(d.x)}, ${this._fmt(d.y)})`,});}}
6
+ 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});}}}
@@ -0,0 +1,5 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart,toRows}from'../base.js';import{range}from'../cartesian.js';import*as M from'../math.js';export class SparklineChart extends Chart{static get meta(){return{id:'sparkline',name:'SparklineChart',aria:'Sparkline',data:'rows',options:['label','value','min','max','fill','lastDot']};}
3
+ _render(){const o=this.options;const data=toRows(o.data);if(!data.length)return;const e=this.engine;const t=this.theme;const valueOf=this._fn('value')||((d)=>d.value);const labelOf=this._fn('label')||((d)=>d.label);const values=data.map((d,i)=>+valueOf(d,i));const[lo,hi]=range([values]);const yMin=o.min??lo,yMax=o.max??hi;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=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);}
4
+ 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});}
5
+ 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;const label=labelOf(data[i],i);this.hits.add({_id:i,x:l,y:0,w:r-l,h:e.h,tipX:p.x,tipY:p.y-4,index:i,label,value:values[i],row:data[i],tooltip:`${label}: ${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});}}}
@@ -0,0 +1,7 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart}from'../base.js';import{drawCartesian,valueDomain}from'../cartesian.js';export class StackedBarChart extends Chart{static get meta(){return{id:'stacked-bar',name:'StackedBarChart',aria:'Stacked bar chart',data:'series',options:['labelFormat','min','max']};}
3
+ _legend(){return this._seriesLegend(this.options.data?.series||[]);}
4
+ _render(){const o=this.options;const{categories:cats=[],series=[]}=o.data||{};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;}
5
+ if(pos>hi)hi=pos;if(neg<lo)lo=neg;}
6
+ const[yLo,yHi]=valueDomain(o,lo,hi);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLabels:cats});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,label:cats[i],series:s,value:v,row:series[s],tooltip:{title:this._lab(cats[i]),rows:[{color,name:series[s].name||'Series '+(s+1),value:this._fmt(v)}]},});}}
7
+ 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});}}}
@@ -0,0 +1,10 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart,toRows}from'../base.js';export 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);}
3
+ if(worst>best)break;best=worst;sum=s;}
4
+ if(j===i){sum=items[i].value;j=i+1;}
5
+ 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;}
6
+ if(w>=h){x+=thick;w-=thick;}else{y+=thick;h-=thick;}
7
+ rest-=sum;i=j;}}
8
+ export class TreemapChart extends Chart{static get meta(){return{id:'treemap',name:'TreemapChart',aria:'Treemap',data:'rows',options:['label','value','labelColor']};}
9
+ _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=this._fn('label')||((d)=>d.label);const valueOf=this._fn('value')||((d)=>d.value);const items=data.map((d,i)=>({raw:labelOf(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'}));}
10
+ this.hits.add({_id:it._id,x,y,w,h,tipX:x+w/2,tipY:y,index:it._id,label:it.raw,value:it.value,row:it.data,tooltip:`${it.label}: ${this._fmt(it.value)}`,});});}}
@@ -0,0 +1,5 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{Chart}from'../base.js';import{drawCartesian,range,valueDomain}from'../cartesian.js';export class WaterfallChart extends Chart{static get meta(){return{id:'waterfall',name:'WaterfallChart',aria:'Waterfall chart',data:'steps',options:['labelFormat','min','max'],colors:'an object like { up, down, total }'};}
3
+ _render(){const o=this.options;const data=Array.isArray(o.data)?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.total?{from:0,to:v}:{from:cum,to:cum+v};cum=seg.to;return{...seg,value:v,total:!!d.total,label:d.label};});const[lo,hi]=range([segs.map((s)=>s.from),segs.map((s)=>s.to)],0,0);const[yLo,yHi]=valueDomain(o,lo,hi);const ax=drawCartesian(this,{yMin:yLo,yMax:yHi,xLabels:segs.map((s)=>s.label)});const slot=ax.pw/segs.length;const barW=Math.max(1,Math.min(60,slot*0.7));const prog=this._anim.progress;const c=o.colors||{};const up=c.up||'#10b981';const down=c.down||'#ef4444';const total=c.total||'#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.total?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]});}
4
+ this.hits.add({_id:i,x,y:top,w:barW,h:Math.max(h,4),tipX:x+barW/2,tipY:top,index:i,label:s.label,value:s.value,row:data[i],tooltip:`${this._lab(s.label)}: ${s.value > 0 && !s.total ? '+' : ''}${this._fmt(s.value)}`,});}
5
+ if(this._hover){const r=this._hover;e.rect(r.x,r.y,r.w,r.h,{stroke:t.text,lineWidth:1.5});}}}
@@ -0,0 +1,33 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{TAU}from'./math.js';export 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;}
3
+ 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();}
4
+ 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);}}
5
+ 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);}
6
+ if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}
7
+ if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1;ctx.stroke();}}
8
+ 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);}}
9
+ ctx.fill();}
10
+ 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();}
11
+ if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1;ctx.stroke();}}
12
+ 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);}
13
+ ctx.fill();}
14
+ 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);}
15
+ if(opts.close)ctx.closePath();if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}
16
+ 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([]);}}
17
+ 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);}
18
+ if(opts.fill){ctx.fillStyle=opts.fill;ctx.fill();}
19
+ if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||2;ctx.lineCap=opts.cap||'round';ctx.lineJoin=opts.join||'round';ctx.stroke();}}
20
+ 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);}
21
+ ctx.lineTo(points[points.length-1].x,y0);ctx.closePath();ctx.fillStyle=fill;ctx.fill();}
22
+ 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);}}
23
+ if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1.5;ctx.stroke();}}
24
+ _setFont(font){font=font||'11px ui-monospace, monospace';if(this._font!==font){this.ctx.font=font;this._font=font;}}
25
+ 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);}}
26
+ measure(str,font){this._setFont(font);return this.ctx.measureText(str).width;}
27
+ 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();}
28
+ if(opts.stroke){ctx.strokeStyle=opts.stroke;ctx.lineWidth=opts.lineWidth||1;ctx.stroke();}}
29
+ 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);}
30
+ this._gradCache.set(key,g);return g;}
31
+ clip(x,y,w,h){const ctx=this.ctx;ctx.save();ctx.beginPath();ctx.rect(x,y,w,h);ctx.clip();}
32
+ unclip(){this.ctx.restore();this._font=null;}
33
+ withAlpha(a,fn){const ctx=this.ctx;const prev=ctx.globalAlpha;ctx.globalAlpha=prev*a;fn();ctx.globalAlpha=prev;}}
@@ -0,0 +1,2 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ export{Chart}from'./base.js';export{PALETTE}from'./math.js';export{BarChart}from'./charts/bar.js';export{HBarChart}from'./charts/hbar.js';export{LineChart}from'./charts/line.js';export{AreaChart}from'./charts/area.js';export{StackedBarChart}from'./charts/stacked-bar.js';export{GroupedBarChart}from'./charts/grouped-bar.js';export{MultiLineChart}from'./charts/multi-line.js';export{ScatterChart}from'./charts/scatter.js';export{BubbleChart}from'./charts/bubble.js';export{PieChart}from'./charts/pie.js';export{RadarChart}from'./charts/radar.js';export{PolarChart}from'./charts/polar.js';export{GaugeChart}from'./charts/gauge.js';export{HeatmapChart}from'./charts/heatmap.js';export{BoxplotChart}from'./charts/boxplot.js';export{FunnelChart}from'./charts/funnel.js';export{TreemapChart}from'./charts/treemap.js';export{SparklineChart}from'./charts/sparkline.js';export{RealtimeChart}from'./charts/realtime.js';export{WaterfallChart}from'./charts/waterfall.js';export{CompositeChart}from'./charts/composite.js';
@@ -0,0 +1,5 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ const METHODS=['update','setState','render','destroy','push','clear'];export function install($,charts){if(!$||!$.fn)throw new Error('aeriz: install($, { BarChart }) needs jQuery as the first argument');const types=$.fn.aerizChart?.types||{};for(const T of Object.values(charts||{}))if(T&&T.meta)types[T.meta.id]=T;function aerizChart(cmd,...args){if(cmd==='instance')return this.first().data('aeriz')||null;if(METHODS.includes(cmd)){return this.each(function(){const c=$(this).data('aeriz');if(!c)throw new Error(`aeriz: .aerizChart('${cmd}') — there is no chart here. Create one first, e.g. .aerizChart('bar', { data })`);if(typeof c[cmd]!=='function')throw new Error(`aeriz: ${c.constructor.meta.name} has no ${cmd}()`);c[cmd](...args);if(cmd==='destroy')$(this).removeData('aeriz');});}
3
+ const T=types[cmd];if(!T){const ids=Object.keys(types);throw new Error(typeof cmd==='function'?`aeriz 2.0: pass the chart id, not the class — install($, { ${cmd.meta?.name || 'BarChart'} }) then .aerizChart('${cmd.meta?.id || 'bar'}', options)`:`aeriz: unknown chart '${cmd}'. ${ids.length ? `Installed: ${ids.join(', ')}` : 'Call install($, { BarChart }) first'}.`);}
4
+ return this.each(function(){$(this).data('aeriz')?.destroy();$(this).data('aeriz',new T(this,args[0]||{}));});}
5
+ aerizChart.types=types;$.fn.aerizChart=aerizChart;return $;}
@@ -0,0 +1,32 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ export const TAU=Math.PI*2;export const clamp=(v,lo,hi)=>v<lo?lo:v>hi?hi:v;export const lerp=(a,b,t)=>a+(b-a)*t;export 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
+ export 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
+ export 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};export 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
+ export 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
+ export 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
+ export 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
+ export function shouldDecimate(n,pxWidth){return n>pxWidth*2;}
23
+ export 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
+ export 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
+ export 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
+ export function colorAlpha(c,a){const[r,g,b]=colorParse(c);return`rgba(${r|0},${g|0},${b|0},${a})`;}
30
+ export 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
+ export 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
+ export 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);}
@@ -0,0 +1,4 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{createElement,forwardRef,useEffect,useRef}from'react';import{apply,isDomProp}from'./shallow.js';import{BarChart as Bar}from'./charts/bar.js';import{HBarChart as HBar}from'./charts/hbar.js';import{LineChart as Line}from'./charts/line.js';import{AreaChart as Area}from'./charts/area.js';import{StackedBarChart as StackedBar}from'./charts/stacked-bar.js';import{GroupedBarChart as GroupedBar}from'./charts/grouped-bar.js';import{MultiLineChart as MultiLine}from'./charts/multi-line.js';import{ScatterChart as Scatter}from'./charts/scatter.js';import{BubbleChart as Bubble}from'./charts/bubble.js';import{PieChart as Pie}from'./charts/pie.js';import{RadarChart as Radar}from'./charts/radar.js';import{PolarChart as Polar}from'./charts/polar.js';import{GaugeChart as Gauge}from'./charts/gauge.js';import{HeatmapChart as Heatmap}from'./charts/heatmap.js';import{BoxplotChart as Boxplot}from'./charts/boxplot.js';import{FunnelChart as Funnel}from'./charts/funnel.js';import{TreemapChart as Treemap}from'./charts/treemap.js';import{SparklineChart as Sparkline}from'./charts/sparkline.js';import{RealtimeChart as Realtime}from'./charts/realtime.js';import{WaterfallChart as Waterfall}from'./charts/waterfall.js';import{CompositeChart as Composite}from'./charts/composite.js';function setRef(ref,v){if(typeof ref==='function')ref(v);else if(ref)ref.current=v;}
3
+ function make(Type){const C=forwardRef(function AerizChart(props,ref){const el=useRef(null);const chart=useRef(null);const last=useRef(null);const div={};const next={};for(const k in props)if(k!=='children')(isDomProp(k)?div:next)[k]=props[k];useEffect(()=>{const c=new Type(el.current,next);chart.current=c;last.current=next;setRef(ref,c);return()=>{c.destroy();chart.current=null;setRef(ref,null);};},[]);useEffect(()=>{const c=chart.current;if(!c||last.current===next)return;apply(c,last.current,next);last.current=next;});div.ref=el;return createElement('div',div);});C.displayName=Type.meta.name;return C;}
4
+ export const BarChart=/*#__PURE__*/make(Bar);export const HBarChart=/*#__PURE__*/make(HBar);export const LineChart=/*#__PURE__*/make(Line);export const AreaChart=/*#__PURE__*/make(Area);export const StackedBarChart=/*#__PURE__*/make(StackedBar);export const GroupedBarChart=/*#__PURE__*/make(GroupedBar);export const MultiLineChart=/*#__PURE__*/make(MultiLine);export const ScatterChart=/*#__PURE__*/make(Scatter);export const BubbleChart=/*#__PURE__*/make(Bubble);export const PieChart=/*#__PURE__*/make(Pie);export const RadarChart=/*#__PURE__*/make(Radar);export const PolarChart=/*#__PURE__*/make(Polar);export const GaugeChart=/*#__PURE__*/make(Gauge);export const HeatmapChart=/*#__PURE__*/make(Heatmap);export const BoxplotChart=/*#__PURE__*/make(Boxplot);export const FunnelChart=/*#__PURE__*/make(Funnel);export const TreemapChart=/*#__PURE__*/make(Treemap);export const SparklineChart=/*#__PURE__*/make(Sparkline);export const RealtimeChart=/*#__PURE__*/make(Realtime);export const WaterfallChart=/*#__PURE__*/make(Waterfall);export const CompositeChart=/*#__PURE__*/make(Composite);
@@ -0,0 +1,5 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ const plain=(v)=>v&&typeof v==='object'&&Object.getPrototypeOf(v)===Object.prototype;export function same(a,b,deep=true){if(a===b)return true;if(!a||!b)return false;const ka=Object.keys(a);if(ka.length!==Object.keys(b).length)return false;for(const k of ka){const x=a[k],y=b[k];if(x===y||(typeof x==='function'&&typeof y==='function'))continue;if(deep&&plain(x)&&plain(y)&&same(x,y,false))continue;return false;}
3
+ return true;}
4
+ export function apply(chart,last,next){if(same(last,next))return Object.assign(chart.options,next);const patch={...next};for(const k in last)if(!(k in next))patch[k]=undefined;chart.update(patch);}
5
+ export const isDomProp=(k)=>/^(class|className|style|id|role|tabIndex|aria-.+|data-.+)$/.test(k);
@@ -0,0 +1,4 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{apply}from'./shallow.js';import{BarChart as Bar}from'./charts/bar.js';import{HBarChart as HBar}from'./charts/hbar.js';import{LineChart as Line}from'./charts/line.js';import{AreaChart as Area}from'./charts/area.js';import{StackedBarChart as StackedBar}from'./charts/stacked-bar.js';import{GroupedBarChart as GroupedBar}from'./charts/grouped-bar.js';import{MultiLineChart as MultiLine}from'./charts/multi-line.js';import{ScatterChart as Scatter}from'./charts/scatter.js';import{BubbleChart as Bubble}from'./charts/bubble.js';import{PieChart as Pie}from'./charts/pie.js';import{RadarChart as Radar}from'./charts/radar.js';import{PolarChart as Polar}from'./charts/polar.js';import{GaugeChart as Gauge}from'./charts/gauge.js';import{HeatmapChart as Heatmap}from'./charts/heatmap.js';import{BoxplotChart as Boxplot}from'./charts/boxplot.js';import{FunnelChart as Funnel}from'./charts/funnel.js';import{TreemapChart as Treemap}from'./charts/treemap.js';import{SparklineChart as Sparkline}from'./charts/sparkline.js';import{RealtimeChart as Realtime}from'./charts/realtime.js';import{WaterfallChart as Waterfall}from'./charts/waterfall.js';import{CompositeChart as Composite}from'./charts/composite.js';const live=new WeakMap();export function chartOf(node){return live.get(node)||null;}
3
+ function action(Type){return function(node,params={}){let last={...params};const chart=new Type(node,last);live.set(node,chart);return{update(next={}){next={...next};apply(chart,last,next);last=next;},destroy(){chart.destroy();live.delete(node);},};};}
4
+ export const barChart=/*#__PURE__*/action(Bar);export const hbarChart=/*#__PURE__*/action(HBar);export const lineChart=/*#__PURE__*/action(Line);export const areaChart=/*#__PURE__*/action(Area);export const stackedBarChart=/*#__PURE__*/action(StackedBar);export const groupedBarChart=/*#__PURE__*/action(GroupedBar);export const multiLineChart=/*#__PURE__*/action(MultiLine);export const scatterChart=/*#__PURE__*/action(Scatter);export const bubbleChart=/*#__PURE__*/action(Bubble);export const pieChart=/*#__PURE__*/action(Pie);export const radarChart=/*#__PURE__*/action(Radar);export const polarChart=/*#__PURE__*/action(Polar);export const gaugeChart=/*#__PURE__*/action(Gauge);export const heatmapChart=/*#__PURE__*/action(Heatmap);export const boxplotChart=/*#__PURE__*/action(Boxplot);export const funnelChart=/*#__PURE__*/action(Funnel);export const treemapChart=/*#__PURE__*/action(Treemap);export const sparklineChart=/*#__PURE__*/action(Sparkline);export const realtimeChart=/*#__PURE__*/action(Realtime);export const waterfallChart=/*#__PURE__*/action(Waterfall);export const compositeChart=/*#__PURE__*/action(Composite);
@@ -0,0 +1,4 @@
1
+ /*! @aeriz/chart v2.0.0 | Apache-2.0 | https://aeriz.dev/chart/ */
2
+ import{defineComponent,h,onBeforeUnmount,onMounted,shallowRef,toRaw,watch}from'vue';import{COMMON}from'./base.js';import{apply,isDomProp}from'./shallow.js';import{BarChart as Bar}from'./charts/bar.js';import{HBarChart as HBar}from'./charts/hbar.js';import{LineChart as Line}from'./charts/line.js';import{AreaChart as Area}from'./charts/area.js';import{StackedBarChart as StackedBar}from'./charts/stacked-bar.js';import{GroupedBarChart as GroupedBar}from'./charts/grouped-bar.js';import{MultiLineChart as MultiLine}from'./charts/multi-line.js';import{ScatterChart as Scatter}from'./charts/scatter.js';import{BubbleChart as Bubble}from'./charts/bubble.js';import{PieChart as Pie}from'./charts/pie.js';import{RadarChart as Radar}from'./charts/radar.js';import{PolarChart as Polar}from'./charts/polar.js';import{GaugeChart as Gauge}from'./charts/gauge.js';import{HeatmapChart as Heatmap}from'./charts/heatmap.js';import{BoxplotChart as Boxplot}from'./charts/boxplot.js';import{FunnelChart as Funnel}from'./charts/funnel.js';import{TreemapChart as Treemap}from'./charts/treemap.js';import{SparklineChart as Sparkline}from'./charts/sparkline.js';import{RealtimeChart as Realtime}from'./charts/realtime.js';import{WaterfallChart as Waterfall}from'./charts/waterfall.js';import{CompositeChart as Composite}from'./charts/composite.js';function make(Type){const keys=[...COMMON,...Type.meta.options].filter((k)=>k!=='onClick'&&k!=='onLegend');const props={};for(const k of keys)props[k]={type:null,default:undefined};return defineComponent({name:Type.meta.name,props,emits:['click','legend'],inheritAttrs:false,setup(p,{emit,attrs,expose}){const el=shallowRef(null);const chart=shallowRef(null);let last=null;const opts=()=>{const o={};for(const k of keys){const v=toRaw(p[k]);if(v!==undefined)o[k]=v===''?true:v;}
3
+ for(const k in attrs)if(!isDomProp(k)&&!/^on[A-Z]/.test(k))o[k]=attrs[k];o.onClick=(hit,c)=>emit('click',hit,c);o.onLegend=(i,visible,c)=>emit('legend',i,visible,c);return o;};onMounted(()=>{last=opts();chart.value=new Type(el.value,last);});watch(()=>keys.map((k)=>p[k]),()=>{if(!chart.value)return;const o=opts();apply(chart.value,last,o);last=o;});onBeforeUnmount(()=>{chart.value?.destroy();chart.value=null;});expose({chart});return()=>{const div={ref:el};for(const k in attrs)if(isDomProp(k))div[k]=attrs[k];return h('div',div);};},});}
4
+ export const BarChart=/*#__PURE__*/make(Bar);export const HBarChart=/*#__PURE__*/make(HBar);export const LineChart=/*#__PURE__*/make(Line);export const AreaChart=/*#__PURE__*/make(Area);export const StackedBarChart=/*#__PURE__*/make(StackedBar);export const GroupedBarChart=/*#__PURE__*/make(GroupedBar);export const MultiLineChart=/*#__PURE__*/make(MultiLine);export const ScatterChart=/*#__PURE__*/make(Scatter);export const BubbleChart=/*#__PURE__*/make(Bubble);export const PieChart=/*#__PURE__*/make(Pie);export const RadarChart=/*#__PURE__*/make(Radar);export const PolarChart=/*#__PURE__*/make(Polar);export const GaugeChart=/*#__PURE__*/make(Gauge);export const HeatmapChart=/*#__PURE__*/make(Heatmap);export const BoxplotChart=/*#__PURE__*/make(Boxplot);export const FunnelChart=/*#__PURE__*/make(Funnel);export const TreemapChart=/*#__PURE__*/make(Treemap);export const SparklineChart=/*#__PURE__*/make(Sparkline);export const RealtimeChart=/*#__PURE__*/make(Realtime);export const WaterfallChart=/*#__PURE__*/make(Waterfall);export const CompositeChart=/*#__PURE__*/make(Composite);