@vielzeug/prism 2.2.3 → 2.2.5

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.
@@ -1,2 +1,2 @@
1
- function e(e,t,n){for(let r=0;r<t.length;r++){let i=n.bandCenter(t[r])-n.bandwidth/2;if(e>=i&&e<i+n.bandwidth)return r}return-1}function t(e,t,n,r,i){if(r.stacked){if(r.horizontal){for(let n=0;n<i;n++)if(r.valueScale.map(r.stackedTops[n]?.[t]??0)>=e.x)return n;return i-1}for(let n=0;n<i;n++)if(r.valueScale.map(r.stackedTops[n]?.[t]??0)<=e.y)return n;return i-1}{let a=r.bandwidth/i,o=r.bandCenter(n[t])-r.bandwidth/2,s=r.horizontal?e.y-o:e.x-o,c=Math.floor(s/a);return Math.max(0,Math.min(i-1,c))}}function n(e,t,n,r,i){let a=i-1,o=r.stacked?r.stackedTops[a]?.[t]??0:Math.max(...Array.from({length:i},(e,r)=>n[r]?.[t]?.value??0)),s=r.valueScale.map(o);return r.horizontal?e.x>s:e.y<s}exports.findCatIdx=e,exports.findSeriesIdx=t,exports.isOutsideBars=n;
1
+ function e(e,t,n){for(let r=0;r<t.length;r++){let i=n.bandCenter(t[r])-n.bandwidth/2;if(e>=i&&e<i+n.bandwidth)return r}return-1}function t(e,t,n,r,i){if(r.stacked)if(r.horizontal){for(let n=0;n<i;n++)if(r.valueScale.map(r.stackedTops[n]?.[t]??0)>=e.x)return n;return i-1}else{for(let n=0;n<i;n++)if(r.valueScale.map(r.stackedTops[n]?.[t]??0)<=e.y)return n;return i-1}{let a=r.bandwidth/i,o=r.bandCenter(n[t])-r.bandwidth/2,s=r.horizontal?e.y-o:e.x-o,c=Math.floor(s/a);return Math.max(0,Math.min(i-1,c))}}function n(e,t,n,r,i){let a=i-1,o=r.stacked?r.stackedTops[a]?.[t]??0:Math.max(...Array.from({length:i},(e,r)=>n[r]?.[t]?.value??0)),s=r.valueScale.map(o);return r.horizontal?e.x>s:e.y<s}exports.findCatIdx=e,exports.findSeriesIdx=t,exports.isOutsideBars=n;
2
2
  //# sourceMappingURL=bar-hit-test.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"bar-hit-test.cjs","names":[],"sources":["../../../src/charts/bar/bar-hit-test.ts"],"sourcesContent":["import type { Datum } from '../../types';\nimport type { BarScaleContext } from './bar-scale-context';\n\nexport function findCatIdx(pos: number, categories: string[], sc: BarScaleContext): number {\n for (let i = 0; i < categories.length; i++) {\n const center = sc.bandCenter(categories[i]);\n const start = center - sc.bandwidth / 2;\n\n if (pos >= start && pos < start + sc.bandwidth) return i;\n }\n\n return -1;\n}\n\nexport function findSeriesIdx(\n pos: { x: number; y: number },\n catIdx: number,\n categories: string[],\n sc: BarScaleContext,\n seriesCount: number,\n): number {\n if (sc.stacked) {\n if (sc.horizontal) {\n for (let si = 0; si < seriesCount; si++) {\n const rightX = sc.valueScale.map(sc.stackedTops[si]?.[catIdx] ?? 0);\n\n if (rightX >= pos.x) return si;\n }\n\n return seriesCount - 1;\n } else {\n for (let si = 0; si < seriesCount; si++) {\n const topY = sc.valueScale.map(sc.stackedTops[si]?.[catIdx] ?? 0);\n\n if (topY <= pos.y) return si;\n }\n\n return seriesCount - 1;\n }\n } else {\n const subSize = sc.bandwidth / seriesCount;\n const bandStart = sc.bandCenter(categories[catIdx]) - sc.bandwidth / 2;\n const offset = sc.horizontal ? pos.y - bandStart : pos.x - bandStart;\n const subIdx = Math.floor(offset / subSize);\n\n return Math.max(0, Math.min(seriesCount - 1, subIdx));\n }\n}\n\nexport function isOutsideBars(\n pos: { x: number; y: number },\n catIdx: number,\n allData: (Datum | undefined)[][],\n sc: BarScaleContext,\n seriesCount: number,\n): boolean {\n const lastSi = seriesCount - 1;\n const maxVal = sc.stacked\n ? (sc.stackedTops[lastSi]?.[catIdx] ?? 0)\n : Math.max(...Array.from({ length: seriesCount }, (_, si) => allData[si]?.[catIdx]?.value ?? 0));\n const maxPx = sc.valueScale.map(maxVal);\n\n return sc.horizontal ? pos.x > maxPx : pos.y < maxPx;\n}\n"],"mappings":"AAGA,SAAgB,EAAW,EAAa,EAAsB,EAA6B,CACzF,IAAK,IAAI,EAAI,EAAG,EAAI,EAAW,OAAQ,IAAK,CAE1C,IAAM,EADS,EAAG,WAAW,EAAW,EAC1B,EAAS,EAAG,UAAY,EAEtC,GAAI,GAAO,GAAS,EAAM,EAAQ,EAAG,UAAW,OAAO,CACzD,CAEA,MAAO,EACT,CAEA,SAAgB,EACd,EACA,EACA,EACA,EACA,EACQ,CACR,GAAI,EAAG,QAAS,CACd,GAAI,EAAG,WAAY,CACjB,IAAK,IAAI,EAAK,EAAG,EAAK,EAAa,IAGjC,GAFe,EAAG,WAAW,IAAI,EAAG,YAAY,EAAG,GAAG,IAAW,CAE7D,GAAU,EAAI,EAAG,OAAO,EAG9B,OAAO,EAAc,CACvB,CACE,IAAK,IAAI,EAAK,EAAG,EAAK,EAAa,IAGjC,GAFa,EAAG,WAAW,IAAI,EAAG,YAAY,EAAG,GAAG,IAAW,CAE3D,GAAQ,EAAI,EAAG,OAAO,EAG5B,OAAO,EAAc,CAEzB,CAAO,CACL,IAAM,EAAU,EAAG,UAAY,EACzB,EAAY,EAAG,WAAW,EAAW,EAAO,EAAI,EAAG,UAAY,EAC/D,EAAS,EAAG,WAAa,EAAI,EAAI,EAAY,EAAI,EAAI,EACrD,EAAS,KAAK,MAAM,EAAS,CAAO,EAE1C,OAAO,KAAK,IAAI,EAAG,KAAK,IAAI,EAAc,EAAG,CAAM,CAAC,CACtD,CACF,CAEA,SAAgB,EACd,EACA,EACA,EACA,EACA,EACS,CACT,IAAM,EAAS,EAAc,EACvB,EAAS,EAAG,QACb,EAAG,YAAY,EAAO,GAAG,IAAW,EACrC,KAAK,IAAI,GAAG,MAAM,KAAK,CAAE,OAAQ,CAAY,GAAI,EAAG,IAAO,EAAQ,EAAG,GAAG,EAAO,EAAE,OAAS,CAAC,CAAC,EAC3F,EAAQ,EAAG,WAAW,IAAI,CAAM,EAEtC,OAAO,EAAG,WAAa,EAAI,EAAI,EAAQ,EAAI,EAAI,CACjD"}
1
+ {"version":3,"file":"bar-hit-test.cjs","names":[],"sources":["../../../src/charts/bar/bar-hit-test.ts"],"sourcesContent":["import type { Datum } from '../../types';\nimport type { BarScaleContext } from './bar-scale-context';\n\nexport function findCatIdx(pos: number, categories: string[], sc: BarScaleContext): number {\n for (let i = 0; i < categories.length; i++) {\n const center = sc.bandCenter(categories[i]);\n const start = center - sc.bandwidth / 2;\n\n if (pos >= start && pos < start + sc.bandwidth) return i;\n }\n\n return -1;\n}\n\nexport function findSeriesIdx(\n pos: { x: number; y: number },\n catIdx: number,\n categories: string[],\n sc: BarScaleContext,\n seriesCount: number,\n): number {\n if (sc.stacked) {\n if (sc.horizontal) {\n for (let si = 0; si < seriesCount; si++) {\n const rightX = sc.valueScale.map(sc.stackedTops[si]?.[catIdx] ?? 0);\n\n if (rightX >= pos.x) return si;\n }\n\n return seriesCount - 1;\n } else {\n for (let si = 0; si < seriesCount; si++) {\n const topY = sc.valueScale.map(sc.stackedTops[si]?.[catIdx] ?? 0);\n\n if (topY <= pos.y) return si;\n }\n\n return seriesCount - 1;\n }\n } else {\n const subSize = sc.bandwidth / seriesCount;\n const bandStart = sc.bandCenter(categories[catIdx]) - sc.bandwidth / 2;\n const offset = sc.horizontal ? pos.y - bandStart : pos.x - bandStart;\n const subIdx = Math.floor(offset / subSize);\n\n return Math.max(0, Math.min(seriesCount - 1, subIdx));\n }\n}\n\nexport function isOutsideBars(\n pos: { x: number; y: number },\n catIdx: number,\n allData: (Datum | undefined)[][],\n sc: BarScaleContext,\n seriesCount: number,\n): boolean {\n const lastSi = seriesCount - 1;\n const maxVal = sc.stacked\n ? (sc.stackedTops[lastSi]?.[catIdx] ?? 0)\n : Math.max(...Array.from({ length: seriesCount }, (_, si) => allData[si]?.[catIdx]?.value ?? 0));\n const maxPx = sc.valueScale.map(maxVal);\n\n return sc.horizontal ? pos.x > maxPx : pos.y < maxPx;\n}\n"],"mappings":"AAGA,SAAgB,EAAW,EAAa,EAAsB,EAA6B,CACzF,IAAK,IAAI,EAAI,EAAG,EAAI,EAAW,OAAQ,IAAK,CAE1C,IAAM,EADS,EAAG,WAAW,EAAW,EAC1B,EAAS,EAAG,UAAY,EAEtC,GAAI,GAAO,GAAS,EAAM,EAAQ,EAAG,UAAW,OAAO,CACzD,CAEA,MAAO,EACT,CAEA,SAAgB,EACd,EACA,EACA,EACA,EACA,EACQ,CACR,GAAI,EAAG,QACL,GAAI,EAAG,WAAY,CACjB,IAAK,IAAI,EAAK,EAAG,EAAK,EAAa,IAGjC,GAFe,EAAG,WAAW,IAAI,EAAG,YAAY,EAAG,GAAG,IAAW,CAE7D,GAAU,EAAI,EAAG,OAAO,EAG9B,OAAO,EAAc,CACvB,KAAO,CACL,IAAK,IAAI,EAAK,EAAG,EAAK,EAAa,IAGjC,GAFa,EAAG,WAAW,IAAI,EAAG,YAAY,EAAG,GAAG,IAAW,CAE3D,GAAQ,EAAI,EAAG,OAAO,EAG5B,OAAO,EAAc,CACvB,CACK,CACL,IAAM,EAAU,EAAG,UAAY,EACzB,EAAY,EAAG,WAAW,EAAW,EAAO,EAAI,EAAG,UAAY,EAC/D,EAAS,EAAG,WAAa,EAAI,EAAI,EAAY,EAAI,EAAI,EACrD,EAAS,KAAK,MAAM,EAAS,CAAO,EAE1C,OAAO,KAAK,IAAI,EAAG,KAAK,IAAI,EAAc,EAAG,CAAM,CAAC,CACtD,CACF,CAEA,SAAgB,EACd,EACA,EACA,EACA,EACA,EACS,CACT,IAAM,EAAS,EAAc,EACvB,EAAS,EAAG,QACb,EAAG,YAAY,EAAO,GAAG,IAAW,EACrC,KAAK,IAAI,GAAG,MAAM,KAAK,CAAE,OAAQ,CAAY,GAAI,EAAG,IAAO,EAAQ,EAAG,GAAG,EAAO,EAAE,OAAS,CAAC,CAAC,EAC3F,EAAQ,EAAG,WAAW,IAAI,CAAM,EAEtC,OAAO,EAAG,WAAa,EAAI,EAAI,EAAQ,EAAI,EAAI,CACjD"}
@@ -7,11 +7,10 @@ function e(e, t, n) {
7
7
  return -1;
8
8
  }
9
9
  function t(e, t, n, r, i) {
10
- if (r.stacked) {
11
- if (r.horizontal) {
12
- for (let n = 0; n < i; n++) if (r.valueScale.map(r.stackedTops[n]?.[t] ?? 0) >= e.x) return n;
13
- return i - 1;
14
- }
10
+ if (r.stacked) if (r.horizontal) {
11
+ for (let n = 0; n < i; n++) if (r.valueScale.map(r.stackedTops[n]?.[t] ?? 0) >= e.x) return n;
12
+ return i - 1;
13
+ } else {
15
14
  for (let n = 0; n < i; n++) if (r.valueScale.map(r.stackedTops[n]?.[t] ?? 0) <= e.y) return n;
16
15
  return i - 1;
17
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bar-hit-test.js","names":[],"sources":["../../../src/charts/bar/bar-hit-test.ts"],"sourcesContent":["import type { Datum } from '../../types';\nimport type { BarScaleContext } from './bar-scale-context';\n\nexport function findCatIdx(pos: number, categories: string[], sc: BarScaleContext): number {\n for (let i = 0; i < categories.length; i++) {\n const center = sc.bandCenter(categories[i]);\n const start = center - sc.bandwidth / 2;\n\n if (pos >= start && pos < start + sc.bandwidth) return i;\n }\n\n return -1;\n}\n\nexport function findSeriesIdx(\n pos: { x: number; y: number },\n catIdx: number,\n categories: string[],\n sc: BarScaleContext,\n seriesCount: number,\n): number {\n if (sc.stacked) {\n if (sc.horizontal) {\n for (let si = 0; si < seriesCount; si++) {\n const rightX = sc.valueScale.map(sc.stackedTops[si]?.[catIdx] ?? 0);\n\n if (rightX >= pos.x) return si;\n }\n\n return seriesCount - 1;\n } else {\n for (let si = 0; si < seriesCount; si++) {\n const topY = sc.valueScale.map(sc.stackedTops[si]?.[catIdx] ?? 0);\n\n if (topY <= pos.y) return si;\n }\n\n return seriesCount - 1;\n }\n } else {\n const subSize = sc.bandwidth / seriesCount;\n const bandStart = sc.bandCenter(categories[catIdx]) - sc.bandwidth / 2;\n const offset = sc.horizontal ? pos.y - bandStart : pos.x - bandStart;\n const subIdx = Math.floor(offset / subSize);\n\n return Math.max(0, Math.min(seriesCount - 1, subIdx));\n }\n}\n\nexport function isOutsideBars(\n pos: { x: number; y: number },\n catIdx: number,\n allData: (Datum | undefined)[][],\n sc: BarScaleContext,\n seriesCount: number,\n): boolean {\n const lastSi = seriesCount - 1;\n const maxVal = sc.stacked\n ? (sc.stackedTops[lastSi]?.[catIdx] ?? 0)\n : Math.max(...Array.from({ length: seriesCount }, (_, si) => allData[si]?.[catIdx]?.value ?? 0));\n const maxPx = sc.valueScale.map(maxVal);\n\n return sc.horizontal ? pos.x > maxPx : pos.y < maxPx;\n}\n"],"mappings":";AAGA,SAAgB,EAAW,GAAa,GAAsB,GAA6B;CACzF,KAAK,IAAI,IAAI,GAAG,IAAI,EAAW,QAAQ,KAAK;EAE1C,IAAM,IADS,EAAG,WAAW,EAAW,EAC1B,IAAS,EAAG,YAAY;EAEtC,IAAI,KAAO,KAAS,IAAM,IAAQ,EAAG,WAAW,OAAO;CACzD;CAEA,OAAO;AACT;AAEA,SAAgB,EACd,GACA,GACA,GACA,GACA,GACQ;CACR,IAAI,EAAG,SAAS;EACd,IAAI,EAAG,YAAY;GACjB,KAAK,IAAI,IAAK,GAAG,IAAK,GAAa,KAGjC,IAFe,EAAG,WAAW,IAAI,EAAG,YAAY,EAAG,GAAG,MAAW,CAE7D,KAAU,EAAI,GAAG,OAAO;GAG9B,OAAO,IAAc;EACvB;EACE,KAAK,IAAI,IAAK,GAAG,IAAK,GAAa,KAGjC,IAFa,EAAG,WAAW,IAAI,EAAG,YAAY,EAAG,GAAG,MAAW,CAE3D,KAAQ,EAAI,GAAG,OAAO;EAG5B,OAAO,IAAc;CAEzB;CAAO;EACL,IAAM,IAAU,EAAG,YAAY,GACzB,IAAY,EAAG,WAAW,EAAW,EAAO,IAAI,EAAG,YAAY,GAC/D,IAAS,EAAG,aAAa,EAAI,IAAI,IAAY,EAAI,IAAI,GACrD,IAAS,KAAK,MAAM,IAAS,CAAO;EAE1C,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,IAAc,GAAG,CAAM,CAAC;CACtD;AACF;AAEA,SAAgB,EACd,GACA,GACA,GACA,GACA,GACS;CACT,IAAM,IAAS,IAAc,GACvB,IAAS,EAAG,UACb,EAAG,YAAY,EAAO,GAAG,MAAW,IACrC,KAAK,IAAI,GAAG,MAAM,KAAK,EAAE,QAAQ,EAAY,IAAI,GAAG,MAAO,EAAQ,EAAG,GAAG,EAAO,EAAE,SAAS,CAAC,CAAC,GAC3F,IAAQ,EAAG,WAAW,IAAI,CAAM;CAEtC,OAAO,EAAG,aAAa,EAAI,IAAI,IAAQ,EAAI,IAAI;AACjD"}
1
+ {"version":3,"file":"bar-hit-test.js","names":[],"sources":["../../../src/charts/bar/bar-hit-test.ts"],"sourcesContent":["import type { Datum } from '../../types';\nimport type { BarScaleContext } from './bar-scale-context';\n\nexport function findCatIdx(pos: number, categories: string[], sc: BarScaleContext): number {\n for (let i = 0; i < categories.length; i++) {\n const center = sc.bandCenter(categories[i]);\n const start = center - sc.bandwidth / 2;\n\n if (pos >= start && pos < start + sc.bandwidth) return i;\n }\n\n return -1;\n}\n\nexport function findSeriesIdx(\n pos: { x: number; y: number },\n catIdx: number,\n categories: string[],\n sc: BarScaleContext,\n seriesCount: number,\n): number {\n if (sc.stacked) {\n if (sc.horizontal) {\n for (let si = 0; si < seriesCount; si++) {\n const rightX = sc.valueScale.map(sc.stackedTops[si]?.[catIdx] ?? 0);\n\n if (rightX >= pos.x) return si;\n }\n\n return seriesCount - 1;\n } else {\n for (let si = 0; si < seriesCount; si++) {\n const topY = sc.valueScale.map(sc.stackedTops[si]?.[catIdx] ?? 0);\n\n if (topY <= pos.y) return si;\n }\n\n return seriesCount - 1;\n }\n } else {\n const subSize = sc.bandwidth / seriesCount;\n const bandStart = sc.bandCenter(categories[catIdx]) - sc.bandwidth / 2;\n const offset = sc.horizontal ? pos.y - bandStart : pos.x - bandStart;\n const subIdx = Math.floor(offset / subSize);\n\n return Math.max(0, Math.min(seriesCount - 1, subIdx));\n }\n}\n\nexport function isOutsideBars(\n pos: { x: number; y: number },\n catIdx: number,\n allData: (Datum | undefined)[][],\n sc: BarScaleContext,\n seriesCount: number,\n): boolean {\n const lastSi = seriesCount - 1;\n const maxVal = sc.stacked\n ? (sc.stackedTops[lastSi]?.[catIdx] ?? 0)\n : Math.max(...Array.from({ length: seriesCount }, (_, si) => allData[si]?.[catIdx]?.value ?? 0));\n const maxPx = sc.valueScale.map(maxVal);\n\n return sc.horizontal ? pos.x > maxPx : pos.y < maxPx;\n}\n"],"mappings":";AAGA,SAAgB,EAAW,GAAa,GAAsB,GAA6B;CACzF,KAAK,IAAI,IAAI,GAAG,IAAI,EAAW,QAAQ,KAAK;EAE1C,IAAM,IADS,EAAG,WAAW,EAAW,EAC1B,IAAS,EAAG,YAAY;EAEtC,IAAI,KAAO,KAAS,IAAM,IAAQ,EAAG,WAAW,OAAO;CACzD;CAEA,OAAO;AACT;AAEA,SAAgB,EACd,GACA,GACA,GACA,GACA,GACQ;CACR,IAAI,EAAG,SACL,IAAI,EAAG,YAAY;EACjB,KAAK,IAAI,IAAK,GAAG,IAAK,GAAa,KAGjC,IAFe,EAAG,WAAW,IAAI,EAAG,YAAY,EAAG,GAAG,MAAW,CAE7D,KAAU,EAAI,GAAG,OAAO;EAG9B,OAAO,IAAc;CACvB,OAAO;EACL,KAAK,IAAI,IAAK,GAAG,IAAK,GAAa,KAGjC,IAFa,EAAG,WAAW,IAAI,EAAG,YAAY,EAAG,GAAG,MAAW,CAE3D,KAAQ,EAAI,GAAG,OAAO;EAG5B,OAAO,IAAc;CACvB;CACK;EACL,IAAM,IAAU,EAAG,YAAY,GACzB,IAAY,EAAG,WAAW,EAAW,EAAO,IAAI,EAAG,YAAY,GAC/D,IAAS,EAAG,aAAa,EAAI,IAAI,IAAY,EAAI,IAAI,GACrD,IAAS,KAAK,MAAM,IAAS,CAAO;EAE1C,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,IAAc,GAAG,CAAM,CAAC;CACtD;AACF;AAEA,SAAgB,EACd,GACA,GACA,GACA,GACA,GACS;CACT,IAAM,IAAS,IAAc,GACvB,IAAS,EAAG,UACb,EAAG,YAAY,EAAO,GAAG,MAAW,IACrC,KAAK,IAAI,GAAG,MAAM,KAAK,EAAE,QAAQ,EAAY,IAAI,GAAG,MAAO,EAAQ,EAAG,GAAG,EAAO,EAAE,SAAS,CAAC,CAAC,GAC3F,IAAQ,EAAG,WAAW,IAAI,CAAM;CAEtC,OAAO,EAAG,aAAa,EAAI,IAAI,IAAQ,EAAI,IAAI;AACjD"}
@@ -1,2 +1,2 @@
1
- const e=require("../../animation/easing.cjs"),t=require("../../animation/motion.cjs"),n=require("../../animation/tween.cjs"),r=require("../../svg/element.cjs"),i=require("../../core/chart-scaffold.cjs"),a=require("../../theme/index.cjs"),o=require("./pie-renderer.cjs");let s=require("@vielzeug/ripple");var c=2*Math.PI,l=-Math.PI/2,u=Math.PI/2;function d(e){return e===`semi`?{end:u,start:l}:{end:c,start:0}}function f(c,l){let u=l.variant??`pie`,f=l.padPixels??(u===`pie`?0:8),m=l.cornerRadius??(u===`pie`?0:8),h=r.createSvgElement(`circle`,{class:`prism-pie-bg`,"pointer-events":`none`}),g=r.createSvgElement(`g`,{class:`prism-pie-slices`,"shape-rendering":`geometricPrecision`}),_=r.createSvgElement(`g`,{class:`prism-pie-labels`,"pointer-events":`none`}),v=[],y=null;function b(e){for(;_.children.length>v.length;)_.removeChild(_.lastChild);for(let t=0;t<v.length;t++){let n=v[t],i=e[t];if(!i?.label)continue;let{x:a,y:s}=o.arcCentroid(n),c=_.children[t];c||(c=r.createSvgElement(`text`,{class:`prism-pie-label`}),_.appendChild(c)),r.setAttributes(c,{"dominant-baseline":`middle`,fill:`var(--prism-pie-label-color, #fff)`,"font-family":`var(--prism-font-family, system-ui)`,"font-size":`var(--prism-pie-label-size, 12px)`,"text-anchor":`middle`,x:a,y:s}),c.textContent=i.label}}let x=i.createRadialScaffold(c,{a11y:l.a11y,legend:l.legend,plugins:l.plugins,tooltip:l.tooltip},i=>{let{legend:x,svg:S,tooltip:C}=i;S.contains(h)||(S.appendChild(h),S.appendChild(g),S.appendChild(_));let{height:w,width:T}=i.dimensions.value,E=u===`semi`,D=T/2,O=E?w*.85:w/2,k=E?Math.min(D,O)-8:Math.min(T,w)/2-8,A=u===`pie`?0:Math.round(k*.55),j=l.innerRadius===void 0?A:l.innerRadius,M=Math.max(j+1,k),N=(0,s.isReactive)(l.data)?l.data.value:l.data,{end:P,start:F}=d(u);for(v=o.computeArcs(N,D,O,M,j,F,P,f,m,e=>a.seriesColor(e),!1),r.setAttributes(h,{cx:D,cy:O,r:j>0?j:0}),h.setAttribute(`style`,`fill:var(--prism-bg,#fff)`);g.children.length>v.length;)g.removeChild(g.lastChild);for(;_.children.length>v.length;)_.removeChild(_.lastChild);let I=t.resolveMotion(l.transition,0),L=I.duration,R=e.resolveEasing(I.easing);for(let e=0;e<v.length;e++){let t=v[e],n=g.children[e];n||(n=r.createSvgElement(`path`,{class:`prism-pie-slice`}),g.appendChild(n)),n.setAttribute(`fill`,t.color),n.setAttribute(`stroke`,`none`),n.style.cursor=l.onClick||l.onHover?`pointer`:``}if(y!==null&&(cancelAnimationFrame(y),y=null),L>0){let e=null,t=i=>{e===null&&(e=i);let a=R(Math.min(1,(i-e)/L)),s=n.tweenNumber(F,P,a);for(let e=0;e<v.length;e++){let t=v[e],n=g.children[e];if(n){if(s<=t.startAngle)r.setAttributes(n,{d:``});else{let e=Math.min(t.endAngle,s);r.setAttributes(n,{d:o.arcPath({...t,endAngle:e})})}}}a<1?y=requestAnimationFrame(t):(y=null,b(N))};y=requestAnimationFrame(t)}else{for(let e=0;e<v.length;e++){let t=v[e],n=g.children[e];n&&r.setAttributes(n,{d:o.arcPath(t)})}b(N)}return x?.update(v.map(e=>({color:e.color,name:e.slice.label??``}))),C?.hide(),{onClick:e=>{if(!l.onClick)return;let t=S.getBoundingClientRect(),n=e.clientX-t.left,r=e.clientY-t.top,i=p(v,n,r,u);i>=0&&l.onClick(v[i].slice,i)},onMouseLeave:()=>{l.onHover?.(null,null),C?.hide()},onMouseMove:e=>{let t=S.getBoundingClientRect(),n=e.clientX-t.left,r=e.clientY-t.top,i=p(v,n,r,u);if(i>=0){let e=v[i];l.onHover?.(e.slice,i);let{x:n,y:r}=o.arcCentroid(e),a=c.getBoundingClientRect();C?.show(n+(t.left-a.left),r+(t.top-a.top),{key:i,value:e.slice.value},{color:e.color,data:[],name:e.slice.label??``})}else l.onHover?.(null,null),C?.hide()}}});return{get disposalSignal(){return x.disposalSignal},dispose(){y!==null&&(cancelAnimationFrame(y),y=null),x.dispose()},get disposed(){return x.disposed},el:x.el,[Symbol.dispose](){this.dispose()}}}function p(e,t,n,r){for(let i=0;i<e.length;i++){let a=e[i],o=t-a.centerX,s=n-a.centerY,l=Math.sqrt(o*o+s*s);if(l<a.outerRadius&&l>=a.innerRadius){let e=Math.atan2(o,-s);if(r===`semi`){if(e>=a.startAngle-1e-9&&e<=a.endAngle+1e-9)return i}else{let t=e<0?e+c:e;if(t>=a.startAngle-1e-9&&t<=a.endAngle+1e-9)return i}}}return-1}exports.createPieChart=f;
1
+ const e=require("../../animation/easing.cjs"),t=require("../../animation/motion.cjs"),n=require("../../animation/tween.cjs"),r=require("../../svg/element.cjs"),i=require("../../core/chart-scaffold.cjs"),a=require("../../theme/index.cjs"),o=require("./pie-renderer.cjs");let s=require("@vielzeug/ripple");var c=2*Math.PI,l=-Math.PI/2,u=Math.PI/2;function d(e){return e===`semi`?{end:u,start:l}:{end:c,start:0}}function f(c,l){let u=l.variant??`pie`,f=l.padPixels??(u===`pie`?0:8),m=l.cornerRadius??(u===`pie`?0:8),h=r.createSvgElement(`circle`,{class:`prism-pie-bg`,"pointer-events":`none`}),g=r.createSvgElement(`g`,{class:`prism-pie-slices`,"shape-rendering":`geometricPrecision`}),_=r.createSvgElement(`g`,{class:`prism-pie-labels`,"pointer-events":`none`}),v=[],y=null;function b(e){for(;_.children.length>v.length;)_.removeChild(_.lastChild);for(let t=0;t<v.length;t++){let n=v[t],i=e[t];if(!i?.label)continue;let{x:a,y:s}=o.arcCentroid(n),c=_.children[t];c||(c=r.createSvgElement(`text`,{class:`prism-pie-label`}),_.appendChild(c)),r.setAttributes(c,{"dominant-baseline":`middle`,fill:`var(--prism-pie-label-color, #fff)`,"font-family":`var(--prism-font-family, system-ui)`,"font-size":`var(--prism-pie-label-size, 12px)`,"text-anchor":`middle`,x:a,y:s}),c.textContent=i.label}}let x=i.createRadialScaffold(c,{a11y:l.a11y,legend:l.legend,plugins:l.plugins,tooltip:l.tooltip},i=>{let{legend:x,svg:S,tooltip:C}=i;S.contains(h)||(S.appendChild(h),S.appendChild(g),S.appendChild(_));let{height:w,width:T}=i.dimensions.value,E=u===`semi`,D=T/2,O=E?w*.85:w/2,k=E?Math.min(D,O)-8:Math.min(T,w)/2-8,A=u===`pie`?0:Math.round(k*.55),j=l.innerRadius===void 0?A:l.innerRadius,M=Math.max(j+1,k),N=(0,s.isReactive)(l.data)?l.data.value:l.data,{end:P,start:F}=d(u);for(v=o.computeArcs(N,D,O,M,j,F,P,f,m,e=>a.seriesColor(e),!1),r.setAttributes(h,{cx:D,cy:O,r:j>0?j:0}),h.setAttribute(`style`,`fill:var(--prism-bg,#fff)`);g.children.length>v.length;)g.removeChild(g.lastChild);for(;_.children.length>v.length;)_.removeChild(_.lastChild);let I=t.resolveMotion(l.transition,0),L=I.duration,R=e.resolveEasing(I.easing);for(let e=0;e<v.length;e++){let t=v[e],n=g.children[e];n||(n=r.createSvgElement(`path`,{class:`prism-pie-slice`}),g.appendChild(n)),n.setAttribute(`fill`,t.color),n.setAttribute(`stroke`,`none`),n.style.cursor=l.onClick||l.onHover?`pointer`:``}if(y!==null&&(cancelAnimationFrame(y),y=null),L>0){let e=null,t=i=>{e===null&&(e=i);let a=R(Math.min(1,(i-e)/L)),s=n.tweenNumber(F,P,a);for(let e=0;e<v.length;e++){let t=v[e],n=g.children[e];if(n)if(s<=t.startAngle)r.setAttributes(n,{d:``});else{let e=Math.min(t.endAngle,s);r.setAttributes(n,{d:o.arcPath({...t,endAngle:e})})}}a<1?y=requestAnimationFrame(t):(y=null,b(N))};y=requestAnimationFrame(t)}else{for(let e=0;e<v.length;e++){let t=v[e],n=g.children[e];n&&r.setAttributes(n,{d:o.arcPath(t)})}b(N)}return x?.update(v.map(e=>({color:e.color,name:e.slice.label??``}))),C?.hide(),{onClick:e=>{if(!l.onClick)return;let t=S.getBoundingClientRect(),n=e.clientX-t.left,r=e.clientY-t.top,i=p(v,n,r,u);i>=0&&l.onClick(v[i].slice,i)},onMouseLeave:()=>{l.onHover?.(null,null),C?.hide()},onMouseMove:e=>{let t=S.getBoundingClientRect(),n=e.clientX-t.left,r=e.clientY-t.top,i=p(v,n,r,u);if(i>=0){let e=v[i];l.onHover?.(e.slice,i);let{x:n,y:r}=o.arcCentroid(e),a=c.getBoundingClientRect();C?.show(n+(t.left-a.left),r+(t.top-a.top),{key:i,value:e.slice.value},{color:e.color,data:[],name:e.slice.label??``})}else l.onHover?.(null,null),C?.hide()}}});return{get disposalSignal(){return x.disposalSignal},dispose(){y!==null&&(cancelAnimationFrame(y),y=null),x.dispose()},get disposed(){return x.disposed},el:x.el,[Symbol.dispose](){this.dispose()}}}function p(e,t,n,r){for(let i=0;i<e.length;i++){let a=e[i],o=t-a.centerX,s=n-a.centerY,l=Math.sqrt(o*o+s*s);if(l<a.outerRadius&&l>=a.innerRadius){let e=Math.atan2(o,-s);if(r===`semi`){if(e>=a.startAngle-1e-9&&e<=a.endAngle+1e-9)return i}else{let t=e<0?e+c:e;if(t>=a.startAngle-1e-9&&t<=a.endAngle+1e-9)return i}}}return-1}exports.createPieChart=f;
2
2
  //# sourceMappingURL=pie-chart.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pie-chart.cjs","names":[],"sources":["../../../src/charts/pie/pie-chart.ts"],"sourcesContent":["import { isReactive } from '@vielzeug/ripple';\nimport { resolveEasing } from '../../animation/easing';\nimport { resolveMotion } from '../../animation/motion';\nimport { tweenNumber } from '../../animation/tween';\nimport type { ChartEventHandlers } from '../../core/chart-scaffold';\nimport { createRadialScaffold } from '../../core/chart-scaffold';\nimport { createSvgElement, setAttributes } from '../../svg/element';\nimport { seriesColor } from '../../theme';\nimport type { ChartHandle, PieChartConfig, PieSliceConfig } from '../../types';\nimport { type Arc, arcCentroid, arcPath, computeArcs } from './pie-renderer';\n\nconst TWO_PI = 2 * Math.PI;\n\nconst SEMI_START = -Math.PI / 2; // -90° = 9-o'clock (left)\nconst SEMI_END = Math.PI / 2; // +90° = 3-o'clock (right) → true 180° half-circle\n\nfunction semiAngles(variant: PieChartConfig['variant']): { end: number; start: number } {\n return variant === 'semi' ? { end: SEMI_END, start: SEMI_START } : { end: TWO_PI, start: 0 };\n}\n\nexport function createPieChart(container: HTMLElement, config: PieChartConfig): ChartHandle {\n const variant = config.variant ?? 'pie';\n const padPixels = config.padPixels ?? (variant === 'pie' ? 0 : 8);\n const cornerRadius = config.cornerRadius ?? (variant === 'pie' ? 0 : 8);\n\n // Pie SVG elements live directly on the SVG (not inside chartArea groups).\n // We create them once and reuse across renders.\n const bgCircle = createSvgElement('circle', { class: 'prism-pie-bg', 'pointer-events': 'none' });\n const pieGroup = createSvgElement('g', {\n class: 'prism-pie-slices',\n 'shape-rendering': 'geometricPrecision',\n });\n const labelGroup = createSvgElement('g', { class: 'prism-pie-labels', 'pointer-events': 'none' });\n\n // Current arcs shared between renderFn and event handlers via closure.\n let currentArcs: Arc[] = [];\n let activeRaf: number | null = null;\n\n function renderLabels(slices: PieSliceConfig[]): void {\n while (labelGroup.children.length > currentArcs.length) labelGroup.removeChild(labelGroup.lastChild!);\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n const slice = slices[i];\n\n if (!slice?.label) continue;\n\n const { x, y } = arcCentroid(arc);\n let text = labelGroup.children[i] as SVGTextElement | undefined;\n\n if (!text) {\n text = createSvgElement('text', { class: 'prism-pie-label' });\n labelGroup.appendChild(text);\n }\n\n setAttributes(text, {\n 'dominant-baseline': 'middle',\n fill: 'var(--prism-pie-label-color, #fff)',\n 'font-family': 'var(--prism-font-family, system-ui)',\n 'font-size': 'var(--prism-pie-label-size, 12px)',\n 'text-anchor': 'middle',\n x,\n y,\n });\n\n text.textContent = slice.label;\n }\n }\n\n const handle = createRadialScaffold(\n container,\n {\n a11y: config.a11y,\n legend: config.legend,\n plugins: config.plugins,\n tooltip: config.tooltip,\n },\n (ctx): ChartEventHandlers => {\n const { legend, svg, tooltip } = ctx;\n\n // Append pie groups to SVG on first render (idempotent).\n if (!svg.contains(bgCircle)) {\n svg.appendChild(bgCircle);\n svg.appendChild(pieGroup);\n svg.appendChild(labelGroup);\n }\n\n const { height: h, width: w } = ctx.dimensions.value;\n const isSemi = variant === 'semi';\n const cx = w / 2;\n const cy = isSemi ? h * 0.85 : h / 2;\n const padding = 8;\n const outer = isSemi ? Math.min(cx, cy) - padding : Math.min(w, h) / 2 - padding;\n const defaultInner = variant === 'pie' ? 0 : Math.round(outer * 0.55);\n const inner = config.innerRadius !== undefined ? config.innerRadius : defaultInner;\n const outerR = Math.max(inner + 1, outer);\n\n const slices = isReactive(config.data) ? config.data.value : config.data;\n const { end, start } = semiAngles(variant);\n\n currentArcs = computeArcs(\n slices,\n cx,\n cy,\n outerR,\n inner,\n start,\n end,\n padPixels,\n cornerRadius,\n (i) => seriesColor(i),\n false,\n );\n\n setAttributes(bgCircle, { cx, cy, r: inner > 0 ? inner : 0 });\n bgCircle.setAttribute('style', 'fill:var(--prism-bg,#fff)');\n\n while (pieGroup.children.length > currentArcs.length) pieGroup.removeChild(pieGroup.lastChild!);\n\n while (labelGroup.children.length > currentArcs.length) labelGroup.removeChild(labelGroup.lastChild!);\n\n const motion = resolveMotion(config.transition, 0);\n const dur = motion.duration;\n const easing = resolveEasing(motion.easing);\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n let path = pieGroup.children[i] as SVGPathElement | undefined;\n\n if (!path) {\n path = createSvgElement('path', { class: 'prism-pie-slice' });\n pieGroup.appendChild(path);\n }\n\n path.setAttribute('fill', arc.color);\n path.setAttribute('stroke', 'none');\n path.style.cursor = config.onClick || config.onHover ? 'pointer' : '';\n }\n\n if (activeRaf !== null) {\n cancelAnimationFrame(activeRaf);\n activeRaf = null;\n }\n\n if (dur > 0) {\n let rafStart: number | null = null;\n\n const frame = (ts: number) => {\n if (rafStart === null) rafStart = ts;\n\n const t = easing(Math.min(1, (ts - rafStart) / dur));\n const revealAngle = tweenNumber(start, end, t);\n\n for (let j = 0; j < currentArcs.length; j++) {\n const a = currentArcs[j];\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n\n if (!el) continue;\n\n if (revealAngle <= a.startAngle) {\n setAttributes(el, { d: '' });\n } else {\n const visibleEnd = Math.min(a.endAngle, revealAngle);\n\n setAttributes(el, { d: arcPath({ ...a, endAngle: visibleEnd }) });\n }\n }\n\n if (t < 1) {\n activeRaf = requestAnimationFrame(frame);\n } else {\n activeRaf = null;\n renderLabels(slices);\n }\n };\n\n activeRaf = requestAnimationFrame(frame);\n } else {\n for (let j = 0; j < currentArcs.length; j++) {\n const a = currentArcs[j];\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n\n if (el) setAttributes(el, { d: arcPath(a) });\n }\n\n renderLabels(slices);\n }\n\n legend?.update(currentArcs.map((arc) => ({ color: arc.color, name: arc.slice.label ?? '' })));\n tooltip?.hide();\n\n const onMouseMove = (e: MouseEvent): void => {\n const svgRect = svg.getBoundingClientRect();\n const mx = e.clientX - svgRect.left;\n const my = e.clientY - svgRect.top;\n const hit = hitTestArc(currentArcs, mx, my, variant);\n\n if (hit >= 0) {\n const arc = currentArcs[hit];\n\n config.onHover?.(arc.slice, hit);\n\n const { x, y } = arcCentroid(arc);\n const contR = container.getBoundingClientRect();\n\n tooltip?.show(\n x + (svgRect.left - contR.left),\n y + (svgRect.top - contR.top),\n { key: hit, value: arc.slice.value },\n { color: arc.color, data: [], name: arc.slice.label ?? '' },\n );\n } else {\n config.onHover?.(null, null);\n tooltip?.hide();\n }\n };\n\n const onMouseLeave = (): void => {\n config.onHover?.(null, null);\n tooltip?.hide();\n };\n\n const onClick = (e: MouseEvent): void => {\n if (!config.onClick) return;\n\n const svgRect = svg.getBoundingClientRect();\n const mx = e.clientX - svgRect.left;\n const my = e.clientY - svgRect.top;\n const hit = hitTestArc(currentArcs, mx, my, variant);\n\n if (hit >= 0) config.onClick(currentArcs[hit].slice, hit);\n };\n\n return { onClick, onMouseLeave, onMouseMove };\n },\n );\n\n return {\n get disposalSignal(): AbortSignal {\n return handle.disposalSignal;\n },\n\n dispose() {\n if (activeRaf !== null) {\n cancelAnimationFrame(activeRaf);\n activeRaf = null;\n }\n\n handle.dispose();\n },\n\n get disposed(): boolean {\n return handle.disposed;\n },\n\n el: handle.el,\n\n [Symbol.dispose]() {\n this.dispose();\n },\n };\n}\n\nfunction hitTestArc(arcs: Arc[], mx: number, my: number, variant: PieChartConfig['variant']): number {\n for (let i = 0; i < arcs.length; i++) {\n const arc = arcs[i];\n const dx = mx - arc.centerX;\n const dy = my - arc.centerY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < arc.outerRadius && dist >= arc.innerRadius) {\n const rawAngle = Math.atan2(dx, -dy);\n\n if (variant === 'semi') {\n if (rawAngle >= arc.startAngle - 1e-9 && rawAngle <= arc.endAngle + 1e-9) return i;\n } else {\n const angle = rawAngle < 0 ? rawAngle + TWO_PI : rawAngle;\n\n if (angle >= arc.startAngle - 1e-9 && angle <= arc.endAngle + 1e-9) return i;\n }\n }\n }\n\n return -1;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pie-chart.cjs","names":[],"sources":["../../../src/charts/pie/pie-chart.ts"],"sourcesContent":["import { isReactive } from '@vielzeug/ripple';\nimport { resolveEasing } from '../../animation/easing';\nimport { resolveMotion } from '../../animation/motion';\nimport { tweenNumber } from '../../animation/tween';\nimport type { ChartEventHandlers } from '../../core/chart-scaffold';\nimport { createRadialScaffold } from '../../core/chart-scaffold';\nimport { createSvgElement, setAttributes } from '../../svg/element';\nimport { seriesColor } from '../../theme';\nimport type { ChartHandle, PieChartConfig, PieSliceConfig } from '../../types';\nimport { type Arc, arcCentroid, arcPath, computeArcs } from './pie-renderer';\n\nconst TWO_PI = 2 * Math.PI;\n\nconst SEMI_START = -Math.PI / 2; // -90° = 9-o'clock (left)\nconst SEMI_END = Math.PI / 2; // +90° = 3-o'clock (right) → true 180° half-circle\n\nfunction semiAngles(variant: PieChartConfig['variant']): { end: number; start: number } {\n return variant === 'semi' ? { end: SEMI_END, start: SEMI_START } : { end: TWO_PI, start: 0 };\n}\n\nexport function createPieChart(container: HTMLElement, config: PieChartConfig): ChartHandle {\n const variant = config.variant ?? 'pie';\n const padPixels = config.padPixels ?? (variant === 'pie' ? 0 : 8);\n const cornerRadius = config.cornerRadius ?? (variant === 'pie' ? 0 : 8);\n\n // Pie SVG elements live directly on the SVG (not inside chartArea groups).\n // We create them once and reuse across renders.\n const bgCircle = createSvgElement('circle', { class: 'prism-pie-bg', 'pointer-events': 'none' });\n const pieGroup = createSvgElement('g', {\n class: 'prism-pie-slices',\n 'shape-rendering': 'geometricPrecision',\n });\n const labelGroup = createSvgElement('g', { class: 'prism-pie-labels', 'pointer-events': 'none' });\n\n // Current arcs shared between renderFn and event handlers via closure.\n let currentArcs: Arc[] = [];\n let activeRaf: number | null = null;\n\n function renderLabels(slices: PieSliceConfig[]): void {\n while (labelGroup.children.length > currentArcs.length) labelGroup.removeChild(labelGroup.lastChild!);\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n const slice = slices[i];\n\n if (!slice?.label) continue;\n\n const { x, y } = arcCentroid(arc);\n let text = labelGroup.children[i] as SVGTextElement | undefined;\n\n if (!text) {\n text = createSvgElement('text', { class: 'prism-pie-label' });\n labelGroup.appendChild(text);\n }\n\n setAttributes(text, {\n 'dominant-baseline': 'middle',\n fill: 'var(--prism-pie-label-color, #fff)',\n 'font-family': 'var(--prism-font-family, system-ui)',\n 'font-size': 'var(--prism-pie-label-size, 12px)',\n 'text-anchor': 'middle',\n x,\n y,\n });\n\n text.textContent = slice.label;\n }\n }\n\n const handle = createRadialScaffold(\n container,\n {\n a11y: config.a11y,\n legend: config.legend,\n plugins: config.plugins,\n tooltip: config.tooltip,\n },\n (ctx): ChartEventHandlers => {\n const { legend, svg, tooltip } = ctx;\n\n // Append pie groups to SVG on first render (idempotent).\n if (!svg.contains(bgCircle)) {\n svg.appendChild(bgCircle);\n svg.appendChild(pieGroup);\n svg.appendChild(labelGroup);\n }\n\n const { height: h, width: w } = ctx.dimensions.value;\n const isSemi = variant === 'semi';\n const cx = w / 2;\n const cy = isSemi ? h * 0.85 : h / 2;\n const padding = 8;\n const outer = isSemi ? Math.min(cx, cy) - padding : Math.min(w, h) / 2 - padding;\n const defaultInner = variant === 'pie' ? 0 : Math.round(outer * 0.55);\n const inner = config.innerRadius !== undefined ? config.innerRadius : defaultInner;\n const outerR = Math.max(inner + 1, outer);\n\n const slices = isReactive(config.data) ? config.data.value : config.data;\n const { end, start } = semiAngles(variant);\n\n currentArcs = computeArcs(\n slices,\n cx,\n cy,\n outerR,\n inner,\n start,\n end,\n padPixels,\n cornerRadius,\n (i) => seriesColor(i),\n false,\n );\n\n setAttributes(bgCircle, { cx, cy, r: inner > 0 ? inner : 0 });\n bgCircle.setAttribute('style', 'fill:var(--prism-bg,#fff)');\n\n while (pieGroup.children.length > currentArcs.length) pieGroup.removeChild(pieGroup.lastChild!);\n\n while (labelGroup.children.length > currentArcs.length) labelGroup.removeChild(labelGroup.lastChild!);\n\n const motion = resolveMotion(config.transition, 0);\n const dur = motion.duration;\n const easing = resolveEasing(motion.easing);\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n let path = pieGroup.children[i] as SVGPathElement | undefined;\n\n if (!path) {\n path = createSvgElement('path', { class: 'prism-pie-slice' });\n pieGroup.appendChild(path);\n }\n\n path.setAttribute('fill', arc.color);\n path.setAttribute('stroke', 'none');\n path.style.cursor = config.onClick || config.onHover ? 'pointer' : '';\n }\n\n if (activeRaf !== null) {\n cancelAnimationFrame(activeRaf);\n activeRaf = null;\n }\n\n if (dur > 0) {\n let rafStart: number | null = null;\n\n const frame = (ts: number) => {\n if (rafStart === null) rafStart = ts;\n\n const t = easing(Math.min(1, (ts - rafStart) / dur));\n const revealAngle = tweenNumber(start, end, t);\n\n for (let j = 0; j < currentArcs.length; j++) {\n const a = currentArcs[j];\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n\n if (!el) continue;\n\n if (revealAngle <= a.startAngle) {\n setAttributes(el, { d: '' });\n } else {\n const visibleEnd = Math.min(a.endAngle, revealAngle);\n\n setAttributes(el, { d: arcPath({ ...a, endAngle: visibleEnd }) });\n }\n }\n\n if (t < 1) {\n activeRaf = requestAnimationFrame(frame);\n } else {\n activeRaf = null;\n renderLabels(slices);\n }\n };\n\n activeRaf = requestAnimationFrame(frame);\n } else {\n for (let j = 0; j < currentArcs.length; j++) {\n const a = currentArcs[j];\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n\n if (el) setAttributes(el, { d: arcPath(a) });\n }\n\n renderLabels(slices);\n }\n\n legend?.update(currentArcs.map((arc) => ({ color: arc.color, name: arc.slice.label ?? '' })));\n tooltip?.hide();\n\n const onMouseMove = (e: MouseEvent): void => {\n const svgRect = svg.getBoundingClientRect();\n const mx = e.clientX - svgRect.left;\n const my = e.clientY - svgRect.top;\n const hit = hitTestArc(currentArcs, mx, my, variant);\n\n if (hit >= 0) {\n const arc = currentArcs[hit];\n\n config.onHover?.(arc.slice, hit);\n\n const { x, y } = arcCentroid(arc);\n const contR = container.getBoundingClientRect();\n\n tooltip?.show(\n x + (svgRect.left - contR.left),\n y + (svgRect.top - contR.top),\n { key: hit, value: arc.slice.value },\n { color: arc.color, data: [], name: arc.slice.label ?? '' },\n );\n } else {\n config.onHover?.(null, null);\n tooltip?.hide();\n }\n };\n\n const onMouseLeave = (): void => {\n config.onHover?.(null, null);\n tooltip?.hide();\n };\n\n const onClick = (e: MouseEvent): void => {\n if (!config.onClick) return;\n\n const svgRect = svg.getBoundingClientRect();\n const mx = e.clientX - svgRect.left;\n const my = e.clientY - svgRect.top;\n const hit = hitTestArc(currentArcs, mx, my, variant);\n\n if (hit >= 0) config.onClick(currentArcs[hit].slice, hit);\n };\n\n return { onClick, onMouseLeave, onMouseMove };\n },\n );\n\n return {\n get disposalSignal(): AbortSignal {\n return handle.disposalSignal;\n },\n\n dispose() {\n if (activeRaf !== null) {\n cancelAnimationFrame(activeRaf);\n activeRaf = null;\n }\n\n handle.dispose();\n },\n\n get disposed(): boolean {\n return handle.disposed;\n },\n\n el: handle.el,\n\n [Symbol.dispose]() {\n this.dispose();\n },\n };\n}\n\nfunction hitTestArc(arcs: Arc[], mx: number, my: number, variant: PieChartConfig['variant']): number {\n for (let i = 0; i < arcs.length; i++) {\n const arc = arcs[i];\n const dx = mx - arc.centerX;\n const dy = my - arc.centerY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < arc.outerRadius && dist >= arc.innerRadius) {\n const rawAngle = Math.atan2(dx, -dy);\n\n if (variant === 'semi') {\n if (rawAngle >= arc.startAngle - 1e-9 && rawAngle <= arc.endAngle + 1e-9) return i;\n } else {\n const angle = rawAngle < 0 ? rawAngle + TWO_PI : rawAngle;\n\n if (angle >= arc.startAngle - 1e-9 && angle <= arc.endAngle + 1e-9) return i;\n }\n }\n }\n\n return -1;\n}\n"],"mappings":"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"}
@@ -71,15 +71,13 @@ function h(d, f) {
71
71
  let a = V(Math.min(1, (r - e) / B)), o = n(R, L, a);
72
72
  for (let e = 0; e < x.length; e++) {
73
73
  let t = x[e], n = y.children[e];
74
- if (n) {
75
- if (o <= t.startAngle) i(n, { d: "" });
76
- else {
77
- let e = Math.min(t.endAngle, o);
78
- i(n, { d: c({
79
- ...t,
80
- endAngle: e
81
- }) });
82
- }
74
+ if (n) if (o <= t.startAngle) i(n, { d: "" });
75
+ else {
76
+ let e = Math.min(t.endAngle, o);
77
+ i(n, { d: c({
78
+ ...t,
79
+ endAngle: e
80
+ }) });
83
81
  }
84
82
  }
85
83
  a < 1 ? S = requestAnimationFrame(t) : (S = null, C(I));
@@ -1 +1 @@
1
- {"version":3,"file":"pie-chart.js","names":[],"sources":["../../../src/charts/pie/pie-chart.ts"],"sourcesContent":["import { isReactive } from '@vielzeug/ripple';\nimport { resolveEasing } from '../../animation/easing';\nimport { resolveMotion } from '../../animation/motion';\nimport { tweenNumber } from '../../animation/tween';\nimport type { ChartEventHandlers } from '../../core/chart-scaffold';\nimport { createRadialScaffold } from '../../core/chart-scaffold';\nimport { createSvgElement, setAttributes } from '../../svg/element';\nimport { seriesColor } from '../../theme';\nimport type { ChartHandle, PieChartConfig, PieSliceConfig } from '../../types';\nimport { type Arc, arcCentroid, arcPath, computeArcs } from './pie-renderer';\n\nconst TWO_PI = 2 * Math.PI;\n\nconst SEMI_START = -Math.PI / 2; // -90° = 9-o'clock (left)\nconst SEMI_END = Math.PI / 2; // +90° = 3-o'clock (right) → true 180° half-circle\n\nfunction semiAngles(variant: PieChartConfig['variant']): { end: number; start: number } {\n return variant === 'semi' ? { end: SEMI_END, start: SEMI_START } : { end: TWO_PI, start: 0 };\n}\n\nexport function createPieChart(container: HTMLElement, config: PieChartConfig): ChartHandle {\n const variant = config.variant ?? 'pie';\n const padPixels = config.padPixels ?? (variant === 'pie' ? 0 : 8);\n const cornerRadius = config.cornerRadius ?? (variant === 'pie' ? 0 : 8);\n\n // Pie SVG elements live directly on the SVG (not inside chartArea groups).\n // We create them once and reuse across renders.\n const bgCircle = createSvgElement('circle', { class: 'prism-pie-bg', 'pointer-events': 'none' });\n const pieGroup = createSvgElement('g', {\n class: 'prism-pie-slices',\n 'shape-rendering': 'geometricPrecision',\n });\n const labelGroup = createSvgElement('g', { class: 'prism-pie-labels', 'pointer-events': 'none' });\n\n // Current arcs shared between renderFn and event handlers via closure.\n let currentArcs: Arc[] = [];\n let activeRaf: number | null = null;\n\n function renderLabels(slices: PieSliceConfig[]): void {\n while (labelGroup.children.length > currentArcs.length) labelGroup.removeChild(labelGroup.lastChild!);\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n const slice = slices[i];\n\n if (!slice?.label) continue;\n\n const { x, y } = arcCentroid(arc);\n let text = labelGroup.children[i] as SVGTextElement | undefined;\n\n if (!text) {\n text = createSvgElement('text', { class: 'prism-pie-label' });\n labelGroup.appendChild(text);\n }\n\n setAttributes(text, {\n 'dominant-baseline': 'middle',\n fill: 'var(--prism-pie-label-color, #fff)',\n 'font-family': 'var(--prism-font-family, system-ui)',\n 'font-size': 'var(--prism-pie-label-size, 12px)',\n 'text-anchor': 'middle',\n x,\n y,\n });\n\n text.textContent = slice.label;\n }\n }\n\n const handle = createRadialScaffold(\n container,\n {\n a11y: config.a11y,\n legend: config.legend,\n plugins: config.plugins,\n tooltip: config.tooltip,\n },\n (ctx): ChartEventHandlers => {\n const { legend, svg, tooltip } = ctx;\n\n // Append pie groups to SVG on first render (idempotent).\n if (!svg.contains(bgCircle)) {\n svg.appendChild(bgCircle);\n svg.appendChild(pieGroup);\n svg.appendChild(labelGroup);\n }\n\n const { height: h, width: w } = ctx.dimensions.value;\n const isSemi = variant === 'semi';\n const cx = w / 2;\n const cy = isSemi ? h * 0.85 : h / 2;\n const padding = 8;\n const outer = isSemi ? Math.min(cx, cy) - padding : Math.min(w, h) / 2 - padding;\n const defaultInner = variant === 'pie' ? 0 : Math.round(outer * 0.55);\n const inner = config.innerRadius !== undefined ? config.innerRadius : defaultInner;\n const outerR = Math.max(inner + 1, outer);\n\n const slices = isReactive(config.data) ? config.data.value : config.data;\n const { end, start } = semiAngles(variant);\n\n currentArcs = computeArcs(\n slices,\n cx,\n cy,\n outerR,\n inner,\n start,\n end,\n padPixels,\n cornerRadius,\n (i) => seriesColor(i),\n false,\n );\n\n setAttributes(bgCircle, { cx, cy, r: inner > 0 ? inner : 0 });\n bgCircle.setAttribute('style', 'fill:var(--prism-bg,#fff)');\n\n while (pieGroup.children.length > currentArcs.length) pieGroup.removeChild(pieGroup.lastChild!);\n\n while (labelGroup.children.length > currentArcs.length) labelGroup.removeChild(labelGroup.lastChild!);\n\n const motion = resolveMotion(config.transition, 0);\n const dur = motion.duration;\n const easing = resolveEasing(motion.easing);\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n let path = pieGroup.children[i] as SVGPathElement | undefined;\n\n if (!path) {\n path = createSvgElement('path', { class: 'prism-pie-slice' });\n pieGroup.appendChild(path);\n }\n\n path.setAttribute('fill', arc.color);\n path.setAttribute('stroke', 'none');\n path.style.cursor = config.onClick || config.onHover ? 'pointer' : '';\n }\n\n if (activeRaf !== null) {\n cancelAnimationFrame(activeRaf);\n activeRaf = null;\n }\n\n if (dur > 0) {\n let rafStart: number | null = null;\n\n const frame = (ts: number) => {\n if (rafStart === null) rafStart = ts;\n\n const t = easing(Math.min(1, (ts - rafStart) / dur));\n const revealAngle = tweenNumber(start, end, t);\n\n for (let j = 0; j < currentArcs.length; j++) {\n const a = currentArcs[j];\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n\n if (!el) continue;\n\n if (revealAngle <= a.startAngle) {\n setAttributes(el, { d: '' });\n } else {\n const visibleEnd = Math.min(a.endAngle, revealAngle);\n\n setAttributes(el, { d: arcPath({ ...a, endAngle: visibleEnd }) });\n }\n }\n\n if (t < 1) {\n activeRaf = requestAnimationFrame(frame);\n } else {\n activeRaf = null;\n renderLabels(slices);\n }\n };\n\n activeRaf = requestAnimationFrame(frame);\n } else {\n for (let j = 0; j < currentArcs.length; j++) {\n const a = currentArcs[j];\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n\n if (el) setAttributes(el, { d: arcPath(a) });\n }\n\n renderLabels(slices);\n }\n\n legend?.update(currentArcs.map((arc) => ({ color: arc.color, name: arc.slice.label ?? '' })));\n tooltip?.hide();\n\n const onMouseMove = (e: MouseEvent): void => {\n const svgRect = svg.getBoundingClientRect();\n const mx = e.clientX - svgRect.left;\n const my = e.clientY - svgRect.top;\n const hit = hitTestArc(currentArcs, mx, my, variant);\n\n if (hit >= 0) {\n const arc = currentArcs[hit];\n\n config.onHover?.(arc.slice, hit);\n\n const { x, y } = arcCentroid(arc);\n const contR = container.getBoundingClientRect();\n\n tooltip?.show(\n x + (svgRect.left - contR.left),\n y + (svgRect.top - contR.top),\n { key: hit, value: arc.slice.value },\n { color: arc.color, data: [], name: arc.slice.label ?? '' },\n );\n } else {\n config.onHover?.(null, null);\n tooltip?.hide();\n }\n };\n\n const onMouseLeave = (): void => {\n config.onHover?.(null, null);\n tooltip?.hide();\n };\n\n const onClick = (e: MouseEvent): void => {\n if (!config.onClick) return;\n\n const svgRect = svg.getBoundingClientRect();\n const mx = e.clientX - svgRect.left;\n const my = e.clientY - svgRect.top;\n const hit = hitTestArc(currentArcs, mx, my, variant);\n\n if (hit >= 0) config.onClick(currentArcs[hit].slice, hit);\n };\n\n return { onClick, onMouseLeave, onMouseMove };\n },\n );\n\n return {\n get disposalSignal(): AbortSignal {\n return handle.disposalSignal;\n },\n\n dispose() {\n if (activeRaf !== null) {\n cancelAnimationFrame(activeRaf);\n activeRaf = null;\n }\n\n handle.dispose();\n },\n\n get disposed(): boolean {\n return handle.disposed;\n },\n\n el: handle.el,\n\n [Symbol.dispose]() {\n this.dispose();\n },\n };\n}\n\nfunction hitTestArc(arcs: Arc[], mx: number, my: number, variant: PieChartConfig['variant']): number {\n for (let i = 0; i < arcs.length; i++) {\n const arc = arcs[i];\n const dx = mx - arc.centerX;\n const dy = my - arc.centerY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < arc.outerRadius && dist >= arc.innerRadius) {\n const rawAngle = Math.atan2(dx, -dy);\n\n if (variant === 'semi') {\n if (rawAngle >= arc.startAngle - 1e-9 && rawAngle <= arc.endAngle + 1e-9) return i;\n } else {\n const angle = rawAngle < 0 ? rawAngle + TWO_PI : rawAngle;\n\n if (angle >= arc.startAngle - 1e-9 && angle <= arc.endAngle + 1e-9) return i;\n }\n }\n }\n\n return -1;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pie-chart.js","names":[],"sources":["../../../src/charts/pie/pie-chart.ts"],"sourcesContent":["import { isReactive } from '@vielzeug/ripple';\nimport { resolveEasing } from '../../animation/easing';\nimport { resolveMotion } from '../../animation/motion';\nimport { tweenNumber } from '../../animation/tween';\nimport type { ChartEventHandlers } from '../../core/chart-scaffold';\nimport { createRadialScaffold } from '../../core/chart-scaffold';\nimport { createSvgElement, setAttributes } from '../../svg/element';\nimport { seriesColor } from '../../theme';\nimport type { ChartHandle, PieChartConfig, PieSliceConfig } from '../../types';\nimport { type Arc, arcCentroid, arcPath, computeArcs } from './pie-renderer';\n\nconst TWO_PI = 2 * Math.PI;\n\nconst SEMI_START = -Math.PI / 2; // -90° = 9-o'clock (left)\nconst SEMI_END = Math.PI / 2; // +90° = 3-o'clock (right) → true 180° half-circle\n\nfunction semiAngles(variant: PieChartConfig['variant']): { end: number; start: number } {\n return variant === 'semi' ? { end: SEMI_END, start: SEMI_START } : { end: TWO_PI, start: 0 };\n}\n\nexport function createPieChart(container: HTMLElement, config: PieChartConfig): ChartHandle {\n const variant = config.variant ?? 'pie';\n const padPixels = config.padPixels ?? (variant === 'pie' ? 0 : 8);\n const cornerRadius = config.cornerRadius ?? (variant === 'pie' ? 0 : 8);\n\n // Pie SVG elements live directly on the SVG (not inside chartArea groups).\n // We create them once and reuse across renders.\n const bgCircle = createSvgElement('circle', { class: 'prism-pie-bg', 'pointer-events': 'none' });\n const pieGroup = createSvgElement('g', {\n class: 'prism-pie-slices',\n 'shape-rendering': 'geometricPrecision',\n });\n const labelGroup = createSvgElement('g', { class: 'prism-pie-labels', 'pointer-events': 'none' });\n\n // Current arcs shared between renderFn and event handlers via closure.\n let currentArcs: Arc[] = [];\n let activeRaf: number | null = null;\n\n function renderLabels(slices: PieSliceConfig[]): void {\n while (labelGroup.children.length > currentArcs.length) labelGroup.removeChild(labelGroup.lastChild!);\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n const slice = slices[i];\n\n if (!slice?.label) continue;\n\n const { x, y } = arcCentroid(arc);\n let text = labelGroup.children[i] as SVGTextElement | undefined;\n\n if (!text) {\n text = createSvgElement('text', { class: 'prism-pie-label' });\n labelGroup.appendChild(text);\n }\n\n setAttributes(text, {\n 'dominant-baseline': 'middle',\n fill: 'var(--prism-pie-label-color, #fff)',\n 'font-family': 'var(--prism-font-family, system-ui)',\n 'font-size': 'var(--prism-pie-label-size, 12px)',\n 'text-anchor': 'middle',\n x,\n y,\n });\n\n text.textContent = slice.label;\n }\n }\n\n const handle = createRadialScaffold(\n container,\n {\n a11y: config.a11y,\n legend: config.legend,\n plugins: config.plugins,\n tooltip: config.tooltip,\n },\n (ctx): ChartEventHandlers => {\n const { legend, svg, tooltip } = ctx;\n\n // Append pie groups to SVG on first render (idempotent).\n if (!svg.contains(bgCircle)) {\n svg.appendChild(bgCircle);\n svg.appendChild(pieGroup);\n svg.appendChild(labelGroup);\n }\n\n const { height: h, width: w } = ctx.dimensions.value;\n const isSemi = variant === 'semi';\n const cx = w / 2;\n const cy = isSemi ? h * 0.85 : h / 2;\n const padding = 8;\n const outer = isSemi ? Math.min(cx, cy) - padding : Math.min(w, h) / 2 - padding;\n const defaultInner = variant === 'pie' ? 0 : Math.round(outer * 0.55);\n const inner = config.innerRadius !== undefined ? config.innerRadius : defaultInner;\n const outerR = Math.max(inner + 1, outer);\n\n const slices = isReactive(config.data) ? config.data.value : config.data;\n const { end, start } = semiAngles(variant);\n\n currentArcs = computeArcs(\n slices,\n cx,\n cy,\n outerR,\n inner,\n start,\n end,\n padPixels,\n cornerRadius,\n (i) => seriesColor(i),\n false,\n );\n\n setAttributes(bgCircle, { cx, cy, r: inner > 0 ? inner : 0 });\n bgCircle.setAttribute('style', 'fill:var(--prism-bg,#fff)');\n\n while (pieGroup.children.length > currentArcs.length) pieGroup.removeChild(pieGroup.lastChild!);\n\n while (labelGroup.children.length > currentArcs.length) labelGroup.removeChild(labelGroup.lastChild!);\n\n const motion = resolveMotion(config.transition, 0);\n const dur = motion.duration;\n const easing = resolveEasing(motion.easing);\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n let path = pieGroup.children[i] as SVGPathElement | undefined;\n\n if (!path) {\n path = createSvgElement('path', { class: 'prism-pie-slice' });\n pieGroup.appendChild(path);\n }\n\n path.setAttribute('fill', arc.color);\n path.setAttribute('stroke', 'none');\n path.style.cursor = config.onClick || config.onHover ? 'pointer' : '';\n }\n\n if (activeRaf !== null) {\n cancelAnimationFrame(activeRaf);\n activeRaf = null;\n }\n\n if (dur > 0) {\n let rafStart: number | null = null;\n\n const frame = (ts: number) => {\n if (rafStart === null) rafStart = ts;\n\n const t = easing(Math.min(1, (ts - rafStart) / dur));\n const revealAngle = tweenNumber(start, end, t);\n\n for (let j = 0; j < currentArcs.length; j++) {\n const a = currentArcs[j];\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n\n if (!el) continue;\n\n if (revealAngle <= a.startAngle) {\n setAttributes(el, { d: '' });\n } else {\n const visibleEnd = Math.min(a.endAngle, revealAngle);\n\n setAttributes(el, { d: arcPath({ ...a, endAngle: visibleEnd }) });\n }\n }\n\n if (t < 1) {\n activeRaf = requestAnimationFrame(frame);\n } else {\n activeRaf = null;\n renderLabels(slices);\n }\n };\n\n activeRaf = requestAnimationFrame(frame);\n } else {\n for (let j = 0; j < currentArcs.length; j++) {\n const a = currentArcs[j];\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n\n if (el) setAttributes(el, { d: arcPath(a) });\n }\n\n renderLabels(slices);\n }\n\n legend?.update(currentArcs.map((arc) => ({ color: arc.color, name: arc.slice.label ?? '' })));\n tooltip?.hide();\n\n const onMouseMove = (e: MouseEvent): void => {\n const svgRect = svg.getBoundingClientRect();\n const mx = e.clientX - svgRect.left;\n const my = e.clientY - svgRect.top;\n const hit = hitTestArc(currentArcs, mx, my, variant);\n\n if (hit >= 0) {\n const arc = currentArcs[hit];\n\n config.onHover?.(arc.slice, hit);\n\n const { x, y } = arcCentroid(arc);\n const contR = container.getBoundingClientRect();\n\n tooltip?.show(\n x + (svgRect.left - contR.left),\n y + (svgRect.top - contR.top),\n { key: hit, value: arc.slice.value },\n { color: arc.color, data: [], name: arc.slice.label ?? '' },\n );\n } else {\n config.onHover?.(null, null);\n tooltip?.hide();\n }\n };\n\n const onMouseLeave = (): void => {\n config.onHover?.(null, null);\n tooltip?.hide();\n };\n\n const onClick = (e: MouseEvent): void => {\n if (!config.onClick) return;\n\n const svgRect = svg.getBoundingClientRect();\n const mx = e.clientX - svgRect.left;\n const my = e.clientY - svgRect.top;\n const hit = hitTestArc(currentArcs, mx, my, variant);\n\n if (hit >= 0) config.onClick(currentArcs[hit].slice, hit);\n };\n\n return { onClick, onMouseLeave, onMouseMove };\n },\n );\n\n return {\n get disposalSignal(): AbortSignal {\n return handle.disposalSignal;\n },\n\n dispose() {\n if (activeRaf !== null) {\n cancelAnimationFrame(activeRaf);\n activeRaf = null;\n }\n\n handle.dispose();\n },\n\n get disposed(): boolean {\n return handle.disposed;\n },\n\n el: handle.el,\n\n [Symbol.dispose]() {\n this.dispose();\n },\n };\n}\n\nfunction hitTestArc(arcs: Arc[], mx: number, my: number, variant: PieChartConfig['variant']): number {\n for (let i = 0; i < arcs.length; i++) {\n const arc = arcs[i];\n const dx = mx - arc.centerX;\n const dy = my - arc.centerY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < arc.outerRadius && dist >= arc.innerRadius) {\n const rawAngle = Math.atan2(dx, -dy);\n\n if (variant === 'semi') {\n if (rawAngle >= arc.startAngle - 1e-9 && rawAngle <= arc.endAngle + 1e-9) return i;\n } else {\n const angle = rawAngle < 0 ? rawAngle + TWO_PI : rawAngle;\n\n if (angle >= arc.startAngle - 1e-9 && angle <= arc.endAngle + 1e-9) return i;\n }\n }\n }\n\n return -1;\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../core/layout.cjs"),t=require("../core/cartesian-model.cjs"),n=require("./events.cjs");function r(e,t,n){let r=null,i=1/0;for(let a=0;a<t.length;a++)for(let o=0;o<(t[a]?.length??0);o++){let s=e[a]?.[o],c=t[a]?.[o];if(!s||!c)continue;let l=Math.abs(c.x-n);l<i&&(i=l,r=s.key)}return r}function i(e,n,r,i){let a=t.keyId(r),o=null,s=1/0;for(let r=0;r<n.length;r++){let c=e[r]?.findIndex(e=>t.keyId(e.key)===a)??-1,l=c===-1?void 0:e[r]?.[c],u=c===-1?void 0:n[r]?.[c];if(!l||!u)continue;let d=Number.isFinite(i)?Math.abs(u.y-i):0;d<s&&(s=d,o={datum:l,point:u,seriesIndex:r})}return o}function a(a){let o=0;return{onClick:t=>{if(!a.onClick)return;let o=a.getPoints();if(o.length===0||o[0].length===0)return;let s=a.dims(),c=n.getMousePosition(a.svg,t,s.margin.left,s.margin.top),l=e.chartArea(s.width,s.height,s.margin);if(c.x<0||c.x>l.width||c.y<0||c.y>l.height)return;let u=r(a.getData(),o,c.x);if(u===null)return;let d=i(a.getData(),o,u,c.y),f=d?a.getSeriesList()[d.seriesIndex]:void 0;d&&f&&a.onClick({datum:d.datum,originalEvent:t,series:f})},onKeyDown:e=>{let n=a.getData(),r=a.getPoints(),s=[...new Map(n.flat().map(e=>[t.keyId(e.key),e.key])).values()];if(s.length!==0){if(e.key===`ArrowLeft`||e.key===`ArrowRight`){e.preventDefault(),o=Math.max(0,Math.min(s.length-1,o+(e.key===`ArrowLeft`?-1:1)));let t=s[o],c=i(n,r,t,1/0),l=c?a.getSeriesList()[c.seriesIndex]:void 0;if(!c||!l)return;a.crosshair?.show(c.point.x,c.point.y,a.dims().width,a.dims().height,`${l.name}: ${c.datum.value}`),a.tooltip?.show(c.point.x+a.dims().margin.left,c.point.y+a.dims().margin.top,c.datum,l),a.onHover?.({datum:c.datum,originalEvent:e,series:l})}if((e.key===`Enter`||e.key===` `)&&a.onClick){e.preventDefault();let t=s[o],c=i(n,r,t,1/0),l=c?a.getSeriesList()[c.seriesIndex]:void 0;c&&l&&a.onClick({datum:c.datum,originalEvent:e,series:l})}e.key===`Escape`&&(a.crosshair?.hide(),a.tooltip?.hide(),a.onHover?.(null))}},onMouseLeave:()=>{a.crosshair?.hide(),a.tooltip?.hide(),a.onHover?.(null)},onMouseMove:t=>{let o=a.getPoints();if(o.length===0||o[0].length===0)return;let s=a.dims(),c=n.getMousePosition(a.svg,t,s.margin.left,s.margin.top),l=e.chartArea(s.width,s.height,s.margin);if(c.x<0||c.x>l.width||c.y<0||c.y>l.height){a.crosshair?.hide(),a.tooltip?.hide();return}let u=r(a.getData(),o,c.x);if(u===null)return;let d=i(a.getData(),o,u,c.y),f=d?a.getSeriesList()[d.seriesIndex]:void 0,p=a.crosshair?.snap===!1?c.x:d?.point.x??c.x,m=a.crosshair?.snap===!1?c.y:d?.point.y??c.y;a.crosshair?.show(p,m,l.width,l.height,f&&d?`${f.name}: ${d.datum.value}`:void 0),d&&f&&(a.tooltip?.show(d.point.x+s.margin.left,d.point.y+s.margin.top,d.datum,f),a.onHover?.({datum:d.datum,originalEvent:t,series:f}))}}}exports.createSeriesInteraction=a;
1
+ const e=require("../core/layout.cjs"),t=require("../core/cartesian-model.cjs"),n=require("./events.cjs");function r(e,t,n){let r=null,i=1/0;for(let a=0;a<t.length;a++)for(let o=0;o<(t[a]?.length??0);o++){let s=e[a]?.[o],c=t[a]?.[o];if(!s||!c)continue;let l=Math.abs(c.x-n);l<i&&(i=l,r=s.key)}return r}function i(e,n,r,i){let a=t.keyId(r),o=null,s=1/0;for(let r=0;r<n.length;r++){let c=e[r]?.findIndex(e=>t.keyId(e.key)===a)??-1,l=c===-1?void 0:e[r]?.[c],u=c===-1?void 0:n[r]?.[c];if(!l||!u)continue;let d=Number.isFinite(i)?Math.abs(u.y-i):0;d<s&&(s=d,o={datum:l,point:u,seriesIndex:r})}return o}function a(a){let o=0;return{onClick:t=>{if(!a.onClick)return;let o=a.getPoints();if(o.length===0||o[0].length===0)return;let s=a.dims(),c=n.getMousePosition(a.svg,t,s.margin.left,s.margin.top),l=e.chartArea(s.width,s.height,s.margin);if(c.x<0||c.x>l.width||c.y<0||c.y>l.height)return;let u=r(a.getData(),o,c.x);if(u===null)return;let d=i(a.getData(),o,u,c.y),f=d?a.getSeriesList()[d.seriesIndex]:void 0;d&&f&&a.onClick({datum:d.datum,originalEvent:t,series:f})},onKeyDown:e=>{let n=a.getData(),r=a.getPoints(),s=[...new Map(n.flat().map(e=>[t.keyId(e.key),e.key])).values()];if(s.length!==0){if(e.key===`ArrowLeft`||e.key===`ArrowRight`){e.preventDefault(),o=Math.max(0,Math.min(s.length-1,o+(e.key===`ArrowLeft`?-1:1)));let t=s[o],c=i(n,r,t,1/0),l=c?a.getSeriesList()[c.seriesIndex]:void 0;if(!c||!l)return;let u=a.tooltip?void 0:`${l.name}: ${c.datum.value}`;a.crosshair?.show(c.point.x,c.point.y,a.dims().width,a.dims().height,u),a.tooltip?.show(c.point.x+a.dims().margin.left,c.point.y+a.dims().margin.top,c.datum,l),a.onHover?.({datum:c.datum,originalEvent:e,series:l})}if((e.key===`Enter`||e.key===` `)&&a.onClick){e.preventDefault();let t=s[o],c=i(n,r,t,1/0),l=c?a.getSeriesList()[c.seriesIndex]:void 0;c&&l&&a.onClick({datum:c.datum,originalEvent:e,series:l})}e.key===`Escape`&&(a.crosshair?.hide(),a.tooltip?.hide(),a.onHover?.(null))}},onMouseLeave:()=>{a.crosshair?.hide(),a.tooltip?.hide(),a.onHover?.(null)},onMouseMove:t=>{let o=a.getPoints();if(o.length===0||o[0].length===0)return;let s=a.dims(),c=n.getMousePosition(a.svg,t,s.margin.left,s.margin.top),l=e.chartArea(s.width,s.height,s.margin);if(c.x<0||c.x>l.width||c.y<0||c.y>l.height){a.crosshair?.hide(),a.tooltip?.hide();return}let u=r(a.getData(),o,c.x);if(u===null)return;let d=i(a.getData(),o,u,c.y),f=d?a.getSeriesList()[d.seriesIndex]:void 0,p=a.crosshair?.snap===!1?c.x:d?.point.x??c.x,m=a.crosshair?.snap===!1?c.y:d?.point.y??c.y,h=!a.tooltip&&f&&d?`${f.name}: ${d.datum.value}`:void 0;a.crosshair?.show(p,m,l.width,l.height,h),d&&f&&(a.tooltip?.show(d.point.x+s.margin.left,d.point.y+s.margin.top,d.datum,f),a.onHover?.({datum:d.datum,originalEvent:t,series:f}))}}}exports.createSeriesInteraction=a;
2
2
  //# sourceMappingURL=series-interaction.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"series-interaction.cjs","names":[],"sources":["../../src/interaction/series-interaction.ts"],"sourcesContent":["import { keyId } from '../core/cartesian-model';\nimport type { ChartEventHandlers } from '../core/chart-scaffold';\nimport { chartArea } from '../core/layout';\nimport type { Point } from '../svg/path';\nimport type { ChartDimensions, ChartEvent, Datum, Series } from '../types';\nimport type { CrosshairState } from './crosshair';\nimport { getMousePosition } from './events';\nimport type { TooltipState } from './tooltip';\n\nexport interface SeriesInteractionOptions {\n crosshair?: CrosshairState | null;\n dims: () => ChartDimensions;\n getData: () => Datum[][];\n getPoints: () => Point[][];\n getSeriesList: () => Series[];\n onClick?: ((event: ChartEvent) => void) | undefined;\n onHover?: ((event: ChartEvent | null) => void) | undefined;\n svg: SVGSVGElement;\n tooltip?: TooltipState | null;\n}\n\ntype SeriesPoint = { datum: Datum; point: Point; seriesIndex: number };\n\nfunction findNearestKey(allData: Datum[][], allPoints: Point[][], posX: number): Datum['key'] | null {\n let nearest: Datum['key'] | null = null;\n let minXDistance = Infinity;\n\n for (let seriesIndex = 0; seriesIndex < allPoints.length; seriesIndex++) {\n for (let datumIndex = 0; datumIndex < (allPoints[seriesIndex]?.length ?? 0); datumIndex++) {\n const datum = allData[seriesIndex]?.[datumIndex];\n const point = allPoints[seriesIndex]?.[datumIndex];\n\n if (!datum || !point) continue;\n\n const distance = Math.abs(point.x - posX);\n\n if (distance < minXDistance) {\n minXDistance = distance;\n nearest = datum.key;\n }\n }\n }\n\n return nearest;\n}\n\nfunction findNearestSeries(\n allData: Datum[][],\n allPoints: Point[][],\n key: Datum['key'],\n posY: number,\n): SeriesPoint | null {\n const id = keyId(key);\n let nearest: { datum: Datum; point: Point; seriesIndex: number } | null = null;\n let minYDist = Infinity;\n\n for (let seriesIndex = 0; seriesIndex < allPoints.length; seriesIndex++) {\n const datumIndex = allData[seriesIndex]?.findIndex((datum) => keyId(datum.key) === id) ?? -1;\n const datum = datumIndex === -1 ? undefined : allData[seriesIndex]?.[datumIndex];\n const point = datumIndex === -1 ? undefined : allPoints[seriesIndex]?.[datumIndex];\n\n if (!datum || !point) continue;\n\n const distance = Number.isFinite(posY) ? Math.abs(point.y - posY) : 0;\n\n if (distance < minYDist) {\n minYDist = distance;\n nearest = { datum, point, seriesIndex };\n }\n }\n\n return nearest;\n}\n\nexport function createSeriesInteraction(opts: SeriesInteractionOptions): ChartEventHandlers {\n let keyboardIndex = 0;\n\n const onMouseMove = (event: MouseEvent) => {\n const allPoints = opts.getPoints();\n\n if (allPoints.length === 0 || allPoints[0].length === 0) return;\n\n const dims = opts.dims();\n const pos = getMousePosition(opts.svg, event, dims.margin.left, dims.margin.top);\n const area = chartArea(dims.width, dims.height, dims.margin);\n\n if (pos.x < 0 || pos.x > area.width || pos.y < 0 || pos.y > area.height) {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n\n return;\n }\n\n const key = findNearestKey(opts.getData(), allPoints, pos.x);\n\n if (key === null) return;\n\n const nearest = findNearestSeries(opts.getData(), allPoints, key, pos.y);\n const series = nearest ? opts.getSeriesList()[nearest.seriesIndex] : undefined;\n const crosshairX = opts.crosshair?.snap === false ? pos.x : (nearest?.point.x ?? pos.x);\n const crosshairY = opts.crosshair?.snap === false ? pos.y : (nearest?.point.y ?? pos.y);\n\n opts.crosshair?.show(\n crosshairX,\n crosshairY,\n area.width,\n area.height,\n series && nearest ? `${series.name}: ${nearest.datum.value}` : undefined,\n );\n\n if (nearest && series) {\n opts.tooltip?.show(nearest.point.x + dims.margin.left, nearest.point.y + dims.margin.top, nearest.datum, series);\n opts.onHover?.({ datum: nearest.datum, originalEvent: event, series });\n }\n };\n\n const onMouseLeave = () => {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n opts.onHover?.(null);\n };\n\n const onClick = (event: MouseEvent) => {\n if (!opts.onClick) return;\n\n const allPoints = opts.getPoints();\n\n if (allPoints.length === 0 || allPoints[0].length === 0) return;\n\n const dims = opts.dims();\n const pos = getMousePosition(opts.svg, event, dims.margin.left, dims.margin.top);\n const area = chartArea(dims.width, dims.height, dims.margin);\n\n if (pos.x < 0 || pos.x > area.width || pos.y < 0 || pos.y > area.height) return;\n\n const key = findNearestKey(opts.getData(), allPoints, pos.x);\n\n if (key === null) return;\n\n const nearest = findNearestSeries(opts.getData(), allPoints, key, pos.y);\n const series = nearest ? opts.getSeriesList()[nearest.seriesIndex] : undefined;\n\n if (nearest && series) {\n opts.onClick({ datum: nearest.datum, originalEvent: event, series });\n }\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n const allData = opts.getData();\n const allPoints = opts.getPoints();\n const domain = [...new Map(allData.flat().map((datum) => [keyId(datum.key), datum.key])).values()];\n\n if (domain.length === 0) return;\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {\n event.preventDefault();\n keyboardIndex = Math.max(0, Math.min(domain.length - 1, keyboardIndex + (event.key === 'ArrowLeft' ? -1 : 1)));\n\n const key = domain[keyboardIndex]!;\n const candidate = findNearestSeries(allData, allPoints, key, Number.POSITIVE_INFINITY);\n const series = candidate ? opts.getSeriesList()[candidate.seriesIndex] : undefined;\n\n if (!candidate || !series) return;\n\n opts.crosshair?.show(\n candidate.point.x,\n candidate.point.y,\n opts.dims().width,\n opts.dims().height,\n `${series.name}: ${candidate.datum.value}`,\n );\n opts.tooltip?.show(\n candidate.point.x + opts.dims().margin.left,\n candidate.point.y + opts.dims().margin.top,\n candidate.datum,\n series,\n );\n opts.onHover?.({ datum: candidate.datum, originalEvent: event, series });\n }\n\n if ((event.key === 'Enter' || event.key === ' ') && opts.onClick) {\n event.preventDefault();\n\n const key = domain[keyboardIndex]!;\n const candidate = findNearestSeries(allData, allPoints, key, Number.POSITIVE_INFINITY);\n const series = candidate ? opts.getSeriesList()[candidate.seriesIndex] : undefined;\n\n if (candidate && series) opts.onClick({ datum: candidate.datum, originalEvent: event, series });\n }\n\n if (event.key === 'Escape') {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n opts.onHover?.(null);\n }\n };\n\n return { onClick, onKeyDown, onMouseLeave, onMouseMove };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"series-interaction.cjs","names":[],"sources":["../../src/interaction/series-interaction.ts"],"sourcesContent":["import { keyId } from '../core/cartesian-model';\nimport type { ChartEventHandlers } from '../core/chart-scaffold';\nimport { chartArea } from '../core/layout';\nimport type { Point } from '../svg/path';\nimport type { ChartDimensions, ChartEvent, Datum, Series } from '../types';\nimport type { CrosshairState } from './crosshair';\nimport { getMousePosition } from './events';\nimport type { TooltipState } from './tooltip';\n\nexport interface SeriesInteractionOptions {\n crosshair?: CrosshairState | null;\n dims: () => ChartDimensions;\n getData: () => Datum[][];\n getPoints: () => Point[][];\n getSeriesList: () => Series[];\n onClick?: ((event: ChartEvent) => void) | undefined;\n onHover?: ((event: ChartEvent | null) => void) | undefined;\n svg: SVGSVGElement;\n tooltip?: TooltipState | null;\n}\n\ntype SeriesPoint = { datum: Datum; point: Point; seriesIndex: number };\n\nfunction findNearestKey(allData: Datum[][], allPoints: Point[][], posX: number): Datum['key'] | null {\n let nearest: Datum['key'] | null = null;\n let minXDistance = Infinity;\n\n for (let seriesIndex = 0; seriesIndex < allPoints.length; seriesIndex++) {\n for (let datumIndex = 0; datumIndex < (allPoints[seriesIndex]?.length ?? 0); datumIndex++) {\n const datum = allData[seriesIndex]?.[datumIndex];\n const point = allPoints[seriesIndex]?.[datumIndex];\n\n if (!datum || !point) continue;\n\n const distance = Math.abs(point.x - posX);\n\n if (distance < minXDistance) {\n minXDistance = distance;\n nearest = datum.key;\n }\n }\n }\n\n return nearest;\n}\n\nfunction findNearestSeries(\n allData: Datum[][],\n allPoints: Point[][],\n key: Datum['key'],\n posY: number,\n): SeriesPoint | null {\n const id = keyId(key);\n let nearest: { datum: Datum; point: Point; seriesIndex: number } | null = null;\n let minYDist = Infinity;\n\n for (let seriesIndex = 0; seriesIndex < allPoints.length; seriesIndex++) {\n const datumIndex = allData[seriesIndex]?.findIndex((datum) => keyId(datum.key) === id) ?? -1;\n const datum = datumIndex === -1 ? undefined : allData[seriesIndex]?.[datumIndex];\n const point = datumIndex === -1 ? undefined : allPoints[seriesIndex]?.[datumIndex];\n\n if (!datum || !point) continue;\n\n const distance = Number.isFinite(posY) ? Math.abs(point.y - posY) : 0;\n\n if (distance < minYDist) {\n minYDist = distance;\n nearest = { datum, point, seriesIndex };\n }\n }\n\n return nearest;\n}\n\nexport function createSeriesInteraction(opts: SeriesInteractionOptions): ChartEventHandlers {\n let keyboardIndex = 0;\n\n const onMouseMove = (event: MouseEvent) => {\n const allPoints = opts.getPoints();\n\n if (allPoints.length === 0 || allPoints[0].length === 0) return;\n\n const dims = opts.dims();\n const pos = getMousePosition(opts.svg, event, dims.margin.left, dims.margin.top);\n const area = chartArea(dims.width, dims.height, dims.margin);\n\n if (pos.x < 0 || pos.x > area.width || pos.y < 0 || pos.y > area.height) {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n\n return;\n }\n\n const key = findNearestKey(opts.getData(), allPoints, pos.x);\n\n if (key === null) return;\n\n const nearest = findNearestSeries(opts.getData(), allPoints, key, pos.y);\n const series = nearest ? opts.getSeriesList()[nearest.seriesIndex] : undefined;\n const crosshairX = opts.crosshair?.snap === false ? pos.x : (nearest?.point.x ?? pos.x);\n const crosshairY = opts.crosshair?.snap === false ? pos.y : (nearest?.point.y ?? pos.y);\n\n // When a tooltip is present it already carries role=\"status\" + aria-live=\"polite\",\n // so the crosshair's own live region must stay silent to avoid double-announcing.\n const announceText = !opts.tooltip && series && nearest ? `${series.name}: ${nearest.datum.value}` : undefined;\n\n opts.crosshair?.show(crosshairX, crosshairY, area.width, area.height, announceText);\n\n if (nearest && series) {\n opts.tooltip?.show(nearest.point.x + dims.margin.left, nearest.point.y + dims.margin.top, nearest.datum, series);\n opts.onHover?.({ datum: nearest.datum, originalEvent: event, series });\n }\n };\n\n const onMouseLeave = () => {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n opts.onHover?.(null);\n };\n\n const onClick = (event: MouseEvent) => {\n if (!opts.onClick) return;\n\n const allPoints = opts.getPoints();\n\n if (allPoints.length === 0 || allPoints[0].length === 0) return;\n\n const dims = opts.dims();\n const pos = getMousePosition(opts.svg, event, dims.margin.left, dims.margin.top);\n const area = chartArea(dims.width, dims.height, dims.margin);\n\n if (pos.x < 0 || pos.x > area.width || pos.y < 0 || pos.y > area.height) return;\n\n const key = findNearestKey(opts.getData(), allPoints, pos.x);\n\n if (key === null) return;\n\n const nearest = findNearestSeries(opts.getData(), allPoints, key, pos.y);\n const series = nearest ? opts.getSeriesList()[nearest.seriesIndex] : undefined;\n\n if (nearest && series) {\n opts.onClick({ datum: nearest.datum, originalEvent: event, series });\n }\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n const allData = opts.getData();\n const allPoints = opts.getPoints();\n const domain = [...new Map(allData.flat().map((datum) => [keyId(datum.key), datum.key])).values()];\n\n if (domain.length === 0) return;\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {\n event.preventDefault();\n keyboardIndex = Math.max(0, Math.min(domain.length - 1, keyboardIndex + (event.key === 'ArrowLeft' ? -1 : 1)));\n\n const key = domain[keyboardIndex]!;\n const candidate = findNearestSeries(allData, allPoints, key, Number.POSITIVE_INFINITY);\n const series = candidate ? opts.getSeriesList()[candidate.seriesIndex] : undefined;\n\n if (!candidate || !series) return;\n\n // Same dedup as onMouseMove — tooltip announces when present.\n const announceText = !opts.tooltip ? `${series.name}: ${candidate.datum.value}` : undefined;\n\n opts.crosshair?.show(candidate.point.x, candidate.point.y, opts.dims().width, opts.dims().height, announceText);\n opts.tooltip?.show(\n candidate.point.x + opts.dims().margin.left,\n candidate.point.y + opts.dims().margin.top,\n candidate.datum,\n series,\n );\n opts.onHover?.({ datum: candidate.datum, originalEvent: event, series });\n }\n\n if ((event.key === 'Enter' || event.key === ' ') && opts.onClick) {\n event.preventDefault();\n\n const key = domain[keyboardIndex]!;\n const candidate = findNearestSeries(allData, allPoints, key, Number.POSITIVE_INFINITY);\n const series = candidate ? opts.getSeriesList()[candidate.seriesIndex] : undefined;\n\n if (candidate && series) opts.onClick({ datum: candidate.datum, originalEvent: event, series });\n }\n\n if (event.key === 'Escape') {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n opts.onHover?.(null);\n }\n };\n\n return { onClick, onKeyDown, onMouseLeave, onMouseMove };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"series-interaction.d.ts","sourceRoot":"","sources":["../../src/interaction/series-interaction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,MAAM,WAAW,wBAAwB;IACvC,SAAS,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAClC,IAAI,EAAE,MAAM,eAAe,CAAC;IAC5B,OAAO,EAAE,MAAM,KAAK,EAAE,EAAE,CAAC;IACzB,SAAS,EAAE,MAAM,KAAK,EAAE,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,MAAM,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3D,GAAG,EAAE,aAAa,CAAC;IACnB,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CAC/B;AAuDD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,wBAAwB,GAAG,kBAAkB,CA4H1F"}
1
+ {"version":3,"file":"series-interaction.d.ts","sourceRoot":"","sources":["../../src/interaction/series-interaction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,MAAM,WAAW,wBAAwB;IACvC,SAAS,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAClC,IAAI,EAAE,MAAM,eAAe,CAAC;IAC5B,OAAO,EAAE,MAAM,KAAK,EAAE,EAAE,CAAC;IACzB,SAAS,EAAE,MAAM,KAAK,EAAE,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,MAAM,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3D,GAAG,EAAE,aAAa,CAAC;IACnB,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CAC/B;AAuDD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,wBAAwB,GAAG,kBAAkB,CAuH1F"}
@@ -51,7 +51,8 @@ function a(a) {
51
51
  e.preventDefault(), o = Math.max(0, Math.min(s.length - 1, o + (e.key === "ArrowLeft" ? -1 : 1)));
52
52
  let t = s[o], c = i(n, r, t, Infinity), l = c ? a.getSeriesList()[c.seriesIndex] : void 0;
53
53
  if (!c || !l) return;
54
- a.crosshair?.show(c.point.x, c.point.y, a.dims().width, a.dims().height, `${l.name}: ${c.datum.value}`), a.tooltip?.show(c.point.x + a.dims().margin.left, c.point.y + a.dims().margin.top, c.datum, l), a.onHover?.({
54
+ let u = a.tooltip ? void 0 : `${l.name}: ${c.datum.value}`;
55
+ a.crosshair?.show(c.point.x, c.point.y, a.dims().width, a.dims().height, u), a.tooltip?.show(c.point.x + a.dims().margin.left, c.point.y + a.dims().margin.top, c.datum, l), a.onHover?.({
55
56
  datum: c.datum,
56
57
  originalEvent: e,
57
58
  series: l
@@ -82,8 +83,8 @@ function a(a) {
82
83
  }
83
84
  let u = r(a.getData(), o, c.x);
84
85
  if (u === null) return;
85
- let d = i(a.getData(), o, u, c.y), f = d ? a.getSeriesList()[d.seriesIndex] : void 0, p = a.crosshair?.snap === !1 ? c.x : d?.point.x ?? c.x, m = a.crosshair?.snap === !1 ? c.y : d?.point.y ?? c.y;
86
- a.crosshair?.show(p, m, l.width, l.height, f && d ? `${f.name}: ${d.datum.value}` : void 0), d && f && (a.tooltip?.show(d.point.x + s.margin.left, d.point.y + s.margin.top, d.datum, f), a.onHover?.({
86
+ let d = i(a.getData(), o, u, c.y), f = d ? a.getSeriesList()[d.seriesIndex] : void 0, p = a.crosshair?.snap === !1 ? c.x : d?.point.x ?? c.x, m = a.crosshair?.snap === !1 ? c.y : d?.point.y ?? c.y, h = !a.tooltip && f && d ? `${f.name}: ${d.datum.value}` : void 0;
87
+ a.crosshair?.show(p, m, l.width, l.height, h), d && f && (a.tooltip?.show(d.point.x + s.margin.left, d.point.y + s.margin.top, d.datum, f), a.onHover?.({
87
88
  datum: d.datum,
88
89
  originalEvent: t,
89
90
  series: f
@@ -1 +1 @@
1
- {"version":3,"file":"series-interaction.js","names":[],"sources":["../../src/interaction/series-interaction.ts"],"sourcesContent":["import { keyId } from '../core/cartesian-model';\nimport type { ChartEventHandlers } from '../core/chart-scaffold';\nimport { chartArea } from '../core/layout';\nimport type { Point } from '../svg/path';\nimport type { ChartDimensions, ChartEvent, Datum, Series } from '../types';\nimport type { CrosshairState } from './crosshair';\nimport { getMousePosition } from './events';\nimport type { TooltipState } from './tooltip';\n\nexport interface SeriesInteractionOptions {\n crosshair?: CrosshairState | null;\n dims: () => ChartDimensions;\n getData: () => Datum[][];\n getPoints: () => Point[][];\n getSeriesList: () => Series[];\n onClick?: ((event: ChartEvent) => void) | undefined;\n onHover?: ((event: ChartEvent | null) => void) | undefined;\n svg: SVGSVGElement;\n tooltip?: TooltipState | null;\n}\n\ntype SeriesPoint = { datum: Datum; point: Point; seriesIndex: number };\n\nfunction findNearestKey(allData: Datum[][], allPoints: Point[][], posX: number): Datum['key'] | null {\n let nearest: Datum['key'] | null = null;\n let minXDistance = Infinity;\n\n for (let seriesIndex = 0; seriesIndex < allPoints.length; seriesIndex++) {\n for (let datumIndex = 0; datumIndex < (allPoints[seriesIndex]?.length ?? 0); datumIndex++) {\n const datum = allData[seriesIndex]?.[datumIndex];\n const point = allPoints[seriesIndex]?.[datumIndex];\n\n if (!datum || !point) continue;\n\n const distance = Math.abs(point.x - posX);\n\n if (distance < minXDistance) {\n minXDistance = distance;\n nearest = datum.key;\n }\n }\n }\n\n return nearest;\n}\n\nfunction findNearestSeries(\n allData: Datum[][],\n allPoints: Point[][],\n key: Datum['key'],\n posY: number,\n): SeriesPoint | null {\n const id = keyId(key);\n let nearest: { datum: Datum; point: Point; seriesIndex: number } | null = null;\n let minYDist = Infinity;\n\n for (let seriesIndex = 0; seriesIndex < allPoints.length; seriesIndex++) {\n const datumIndex = allData[seriesIndex]?.findIndex((datum) => keyId(datum.key) === id) ?? -1;\n const datum = datumIndex === -1 ? undefined : allData[seriesIndex]?.[datumIndex];\n const point = datumIndex === -1 ? undefined : allPoints[seriesIndex]?.[datumIndex];\n\n if (!datum || !point) continue;\n\n const distance = Number.isFinite(posY) ? Math.abs(point.y - posY) : 0;\n\n if (distance < minYDist) {\n minYDist = distance;\n nearest = { datum, point, seriesIndex };\n }\n }\n\n return nearest;\n}\n\nexport function createSeriesInteraction(opts: SeriesInteractionOptions): ChartEventHandlers {\n let keyboardIndex = 0;\n\n const onMouseMove = (event: MouseEvent) => {\n const allPoints = opts.getPoints();\n\n if (allPoints.length === 0 || allPoints[0].length === 0) return;\n\n const dims = opts.dims();\n const pos = getMousePosition(opts.svg, event, dims.margin.left, dims.margin.top);\n const area = chartArea(dims.width, dims.height, dims.margin);\n\n if (pos.x < 0 || pos.x > area.width || pos.y < 0 || pos.y > area.height) {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n\n return;\n }\n\n const key = findNearestKey(opts.getData(), allPoints, pos.x);\n\n if (key === null) return;\n\n const nearest = findNearestSeries(opts.getData(), allPoints, key, pos.y);\n const series = nearest ? opts.getSeriesList()[nearest.seriesIndex] : undefined;\n const crosshairX = opts.crosshair?.snap === false ? pos.x : (nearest?.point.x ?? pos.x);\n const crosshairY = opts.crosshair?.snap === false ? pos.y : (nearest?.point.y ?? pos.y);\n\n opts.crosshair?.show(\n crosshairX,\n crosshairY,\n area.width,\n area.height,\n series && nearest ? `${series.name}: ${nearest.datum.value}` : undefined,\n );\n\n if (nearest && series) {\n opts.tooltip?.show(nearest.point.x + dims.margin.left, nearest.point.y + dims.margin.top, nearest.datum, series);\n opts.onHover?.({ datum: nearest.datum, originalEvent: event, series });\n }\n };\n\n const onMouseLeave = () => {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n opts.onHover?.(null);\n };\n\n const onClick = (event: MouseEvent) => {\n if (!opts.onClick) return;\n\n const allPoints = opts.getPoints();\n\n if (allPoints.length === 0 || allPoints[0].length === 0) return;\n\n const dims = opts.dims();\n const pos = getMousePosition(opts.svg, event, dims.margin.left, dims.margin.top);\n const area = chartArea(dims.width, dims.height, dims.margin);\n\n if (pos.x < 0 || pos.x > area.width || pos.y < 0 || pos.y > area.height) return;\n\n const key = findNearestKey(opts.getData(), allPoints, pos.x);\n\n if (key === null) return;\n\n const nearest = findNearestSeries(opts.getData(), allPoints, key, pos.y);\n const series = nearest ? opts.getSeriesList()[nearest.seriesIndex] : undefined;\n\n if (nearest && series) {\n opts.onClick({ datum: nearest.datum, originalEvent: event, series });\n }\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n const allData = opts.getData();\n const allPoints = opts.getPoints();\n const domain = [...new Map(allData.flat().map((datum) => [keyId(datum.key), datum.key])).values()];\n\n if (domain.length === 0) return;\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {\n event.preventDefault();\n keyboardIndex = Math.max(0, Math.min(domain.length - 1, keyboardIndex + (event.key === 'ArrowLeft' ? -1 : 1)));\n\n const key = domain[keyboardIndex]!;\n const candidate = findNearestSeries(allData, allPoints, key, Number.POSITIVE_INFINITY);\n const series = candidate ? opts.getSeriesList()[candidate.seriesIndex] : undefined;\n\n if (!candidate || !series) return;\n\n opts.crosshair?.show(\n candidate.point.x,\n candidate.point.y,\n opts.dims().width,\n opts.dims().height,\n `${series.name}: ${candidate.datum.value}`,\n );\n opts.tooltip?.show(\n candidate.point.x + opts.dims().margin.left,\n candidate.point.y + opts.dims().margin.top,\n candidate.datum,\n series,\n );\n opts.onHover?.({ datum: candidate.datum, originalEvent: event, series });\n }\n\n if ((event.key === 'Enter' || event.key === ' ') && opts.onClick) {\n event.preventDefault();\n\n const key = domain[keyboardIndex]!;\n const candidate = findNearestSeries(allData, allPoints, key, Number.POSITIVE_INFINITY);\n const series = candidate ? opts.getSeriesList()[candidate.seriesIndex] : undefined;\n\n if (candidate && series) opts.onClick({ datum: candidate.datum, originalEvent: event, series });\n }\n\n if (event.key === 'Escape') {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n opts.onHover?.(null);\n }\n };\n\n return { onClick, onKeyDown, onMouseLeave, onMouseMove };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"series-interaction.js","names":[],"sources":["../../src/interaction/series-interaction.ts"],"sourcesContent":["import { keyId } from '../core/cartesian-model';\nimport type { ChartEventHandlers } from '../core/chart-scaffold';\nimport { chartArea } from '../core/layout';\nimport type { Point } from '../svg/path';\nimport type { ChartDimensions, ChartEvent, Datum, Series } from '../types';\nimport type { CrosshairState } from './crosshair';\nimport { getMousePosition } from './events';\nimport type { TooltipState } from './tooltip';\n\nexport interface SeriesInteractionOptions {\n crosshair?: CrosshairState | null;\n dims: () => ChartDimensions;\n getData: () => Datum[][];\n getPoints: () => Point[][];\n getSeriesList: () => Series[];\n onClick?: ((event: ChartEvent) => void) | undefined;\n onHover?: ((event: ChartEvent | null) => void) | undefined;\n svg: SVGSVGElement;\n tooltip?: TooltipState | null;\n}\n\ntype SeriesPoint = { datum: Datum; point: Point; seriesIndex: number };\n\nfunction findNearestKey(allData: Datum[][], allPoints: Point[][], posX: number): Datum['key'] | null {\n let nearest: Datum['key'] | null = null;\n let minXDistance = Infinity;\n\n for (let seriesIndex = 0; seriesIndex < allPoints.length; seriesIndex++) {\n for (let datumIndex = 0; datumIndex < (allPoints[seriesIndex]?.length ?? 0); datumIndex++) {\n const datum = allData[seriesIndex]?.[datumIndex];\n const point = allPoints[seriesIndex]?.[datumIndex];\n\n if (!datum || !point) continue;\n\n const distance = Math.abs(point.x - posX);\n\n if (distance < minXDistance) {\n minXDistance = distance;\n nearest = datum.key;\n }\n }\n }\n\n return nearest;\n}\n\nfunction findNearestSeries(\n allData: Datum[][],\n allPoints: Point[][],\n key: Datum['key'],\n posY: number,\n): SeriesPoint | null {\n const id = keyId(key);\n let nearest: { datum: Datum; point: Point; seriesIndex: number } | null = null;\n let minYDist = Infinity;\n\n for (let seriesIndex = 0; seriesIndex < allPoints.length; seriesIndex++) {\n const datumIndex = allData[seriesIndex]?.findIndex((datum) => keyId(datum.key) === id) ?? -1;\n const datum = datumIndex === -1 ? undefined : allData[seriesIndex]?.[datumIndex];\n const point = datumIndex === -1 ? undefined : allPoints[seriesIndex]?.[datumIndex];\n\n if (!datum || !point) continue;\n\n const distance = Number.isFinite(posY) ? Math.abs(point.y - posY) : 0;\n\n if (distance < minYDist) {\n minYDist = distance;\n nearest = { datum, point, seriesIndex };\n }\n }\n\n return nearest;\n}\n\nexport function createSeriesInteraction(opts: SeriesInteractionOptions): ChartEventHandlers {\n let keyboardIndex = 0;\n\n const onMouseMove = (event: MouseEvent) => {\n const allPoints = opts.getPoints();\n\n if (allPoints.length === 0 || allPoints[0].length === 0) return;\n\n const dims = opts.dims();\n const pos = getMousePosition(opts.svg, event, dims.margin.left, dims.margin.top);\n const area = chartArea(dims.width, dims.height, dims.margin);\n\n if (pos.x < 0 || pos.x > area.width || pos.y < 0 || pos.y > area.height) {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n\n return;\n }\n\n const key = findNearestKey(opts.getData(), allPoints, pos.x);\n\n if (key === null) return;\n\n const nearest = findNearestSeries(opts.getData(), allPoints, key, pos.y);\n const series = nearest ? opts.getSeriesList()[nearest.seriesIndex] : undefined;\n const crosshairX = opts.crosshair?.snap === false ? pos.x : (nearest?.point.x ?? pos.x);\n const crosshairY = opts.crosshair?.snap === false ? pos.y : (nearest?.point.y ?? pos.y);\n\n // When a tooltip is present it already carries role=\"status\" + aria-live=\"polite\",\n // so the crosshair's own live region must stay silent to avoid double-announcing.\n const announceText = !opts.tooltip && series && nearest ? `${series.name}: ${nearest.datum.value}` : undefined;\n\n opts.crosshair?.show(crosshairX, crosshairY, area.width, area.height, announceText);\n\n if (nearest && series) {\n opts.tooltip?.show(nearest.point.x + dims.margin.left, nearest.point.y + dims.margin.top, nearest.datum, series);\n opts.onHover?.({ datum: nearest.datum, originalEvent: event, series });\n }\n };\n\n const onMouseLeave = () => {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n opts.onHover?.(null);\n };\n\n const onClick = (event: MouseEvent) => {\n if (!opts.onClick) return;\n\n const allPoints = opts.getPoints();\n\n if (allPoints.length === 0 || allPoints[0].length === 0) return;\n\n const dims = opts.dims();\n const pos = getMousePosition(opts.svg, event, dims.margin.left, dims.margin.top);\n const area = chartArea(dims.width, dims.height, dims.margin);\n\n if (pos.x < 0 || pos.x > area.width || pos.y < 0 || pos.y > area.height) return;\n\n const key = findNearestKey(opts.getData(), allPoints, pos.x);\n\n if (key === null) return;\n\n const nearest = findNearestSeries(opts.getData(), allPoints, key, pos.y);\n const series = nearest ? opts.getSeriesList()[nearest.seriesIndex] : undefined;\n\n if (nearest && series) {\n opts.onClick({ datum: nearest.datum, originalEvent: event, series });\n }\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n const allData = opts.getData();\n const allPoints = opts.getPoints();\n const domain = [...new Map(allData.flat().map((datum) => [keyId(datum.key), datum.key])).values()];\n\n if (domain.length === 0) return;\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {\n event.preventDefault();\n keyboardIndex = Math.max(0, Math.min(domain.length - 1, keyboardIndex + (event.key === 'ArrowLeft' ? -1 : 1)));\n\n const key = domain[keyboardIndex]!;\n const candidate = findNearestSeries(allData, allPoints, key, Number.POSITIVE_INFINITY);\n const series = candidate ? opts.getSeriesList()[candidate.seriesIndex] : undefined;\n\n if (!candidate || !series) return;\n\n // Same dedup as onMouseMove — tooltip announces when present.\n const announceText = !opts.tooltip ? `${series.name}: ${candidate.datum.value}` : undefined;\n\n opts.crosshair?.show(candidate.point.x, candidate.point.y, opts.dims().width, opts.dims().height, announceText);\n opts.tooltip?.show(\n candidate.point.x + opts.dims().margin.left,\n candidate.point.y + opts.dims().margin.top,\n candidate.datum,\n series,\n );\n opts.onHover?.({ datum: candidate.datum, originalEvent: event, series });\n }\n\n if ((event.key === 'Enter' || event.key === ' ') && opts.onClick) {\n event.preventDefault();\n\n const key = domain[keyboardIndex]!;\n const candidate = findNearestSeries(allData, allPoints, key, Number.POSITIVE_INFINITY);\n const series = candidate ? opts.getSeriesList()[candidate.seriesIndex] : undefined;\n\n if (candidate && series) opts.onClick({ datum: candidate.datum, originalEvent: event, series });\n }\n\n if (event.key === 'Escape') {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n opts.onHover?.(null);\n }\n };\n\n return { onClick, onKeyDown, onMouseLeave, onMouseMove };\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- require("../_dev.cjs");let e=require("@vielzeug/orbit");function t(t,n){getComputedStyle(t).position===`static`&&(t.style.position=`relative`);let r=document.createElement(`div`);r.className=`prism-tooltip`,r.style.position=`absolute`,r.style.pointerEvents=`none`,r.style.top=`0`,r.style.left=`0`,r.setAttribute(`role`,`status`),r.setAttribute(`aria-live`,`polite`),t.appendChild(r);let i=n!==!0&&n?.offset||8,a=n===!0?void 0:n?.render,o=n===!0?void 0:n?.sanitize,s=null,c=null,l=!1,u=()=>{c!==null&&(clearTimeout(c),c=null)},d=()=>{s!==null&&(cancelAnimationFrame(s),s=null)},f=()=>{d(),u(),r.remove()};return{dispose:f,el:r,hide(){d(),u(),l=!1,r.style.opacity=`0`,c=setTimeout(()=>{c=null,l||(r.textContent=``)},150)},show(n,c,d,f){if(!t.isConnected)return;if(u(),a){let e=a(d,f);o?r.innerHTML=o(e):r.textContent=e}else r.textContent=`${f.name}: ${d.value}`;let{x:p,y:m}=(0,e.computePosition)({getBoundingClientRect:()=>{let e=t.getBoundingClientRect();return{bottom:e.top+c,height:0,left:e.left+n,right:e.left+n,top:e.top+c,width:0,x:e.left+n,y:e.top+c}}},r,{containingBlock:t,middleware:[(0,e.offset)(i),(0,e.flip)(),(0,e.shift)({padding:8})],placement:`top`});if(r.style.left=`${p}px`,r.style.top=`${m}px`,l){s===null&&(r.style.opacity=`1`);return}l=!0,r.style.opacity=`0`,s=requestAnimationFrame(()=>{s=null,l&&(r.style.opacity=`1`)})},[Symbol.dispose]:f}}exports.createTooltip=t;
1
+ require("../_dev.cjs");let e=require("@vielzeug/orbit");function t(t,n){getComputedStyle(t).position===`static`&&(t.style.position=`relative`);let r=t.ownerDocument.createElement(`div`);r.className=`prism-tooltip`,r.style.position=`absolute`,r.style.pointerEvents=`none`,r.style.top=`0`,r.style.left=`0`,r.setAttribute(`role`,`status`),r.setAttribute(`aria-live`,`polite`),t.appendChild(r);let i=n!==!0&&n?.offset||8,a=n===!0?void 0:n?.render,o=n===!0?void 0:n?.sanitize,s=null,c=null,l=!1,u=()=>{c!==null&&(clearTimeout(c),c=null)},d=()=>{s!==null&&(cancelAnimationFrame(s),s=null)},f=()=>{d(),u(),r.remove()};return{dispose:f,el:r,hide(){d(),u(),l=!1,r.style.opacity=`0`,c=setTimeout(()=>{c=null,l||(r.textContent=``)},150)},show(n,c,d,f){if(!t.isConnected)return;if(u(),a){let e=a(d,f);o?r.innerHTML=o(e):r.textContent=e}else r.textContent=`${f.name}: ${d.value}`;let{x:p,y:m}=(0,e.computePosition)({getBoundingClientRect:()=>{let e=t.getBoundingClientRect();return{bottom:e.top+c,height:0,left:e.left+n,right:e.left+n,top:e.top+c,width:0,x:e.left+n,y:e.top+c}}},r,{containingBlock:t,middleware:[(0,e.offset)(i),(0,e.flip)(),(0,e.shift)({padding:8})],placement:`top`});if(r.style.left=`${p}px`,r.style.top=`${m}px`,l){s===null&&(r.style.opacity=`1`);return}l=!0,r.style.opacity=`0`,s=requestAnimationFrame(()=>{s=null,l&&(r.style.opacity=`1`)})},[Symbol.dispose]:f}}exports.createTooltip=t;
2
2
  //# sourceMappingURL=tooltip.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.cjs","names":[],"sources":["../../src/interaction/tooltip.ts"],"sourcesContent":["import { computePosition, flip, offset, shift } from '@vielzeug/orbit';\nimport { warn } from '../_dev';\nimport type { Datum, Series, TooltipConfig } from '../types';\n\nexport interface TooltipState {\n dispose(): void;\n el: HTMLDivElement | null;\n hide(): void;\n show(x: number, y: number, datum: Datum, series: Series): void;\n [Symbol.dispose](): void;\n}\n\nexport function createTooltip(container: HTMLElement, config?: TooltipConfig | true): TooltipState {\n if (getComputedStyle(container).position === 'static') {\n container.style.position = 'relative';\n }\n\n const el = document.createElement('div');\n\n el.className = 'prism-tooltip';\n el.style.position = 'absolute';\n el.style.pointerEvents = 'none';\n el.style.top = '0';\n el.style.left = '0';\n // Non-modal status text — announced by assistive tech whenever content/hide state changes.\n el.setAttribute('role', 'status');\n el.setAttribute('aria-live', 'polite');\n container.appendChild(el);\n\n const tooltipOffset: number = (config !== true && config?.offset) || 8;\n const render = config !== true ? config?.render : undefined;\n const sanitize = config !== true ? config?.sanitize : undefined;\n\n if (render && !sanitize) {\n warn(\n 'createTooltip: `render` is set without `sanitize` — falling back to plain-text rendering of the returned string to avoid an XSS risk. Pass `sanitize` to render HTML.',\n );\n }\n\n let revealFrame: number | null = null;\n let clearTimer: ReturnType<typeof setTimeout> | null = null;\n let visible = false;\n\n const cancelClear = (): void => {\n if (clearTimer === null) return;\n\n clearTimeout(clearTimer);\n clearTimer = null;\n };\n\n const cancelReveal = (): void => {\n if (revealFrame === null) return;\n\n cancelAnimationFrame(revealFrame);\n revealFrame = null;\n };\n\n const disposeHandle = (): void => {\n cancelReveal();\n cancelClear();\n el.remove();\n };\n\n return {\n dispose: disposeHandle,\n el,\n hide() {\n cancelReveal();\n cancelClear();\n visible = false;\n el.style.opacity = '0';\n clearTimer = setTimeout(() => {\n clearTimer = null;\n\n if (!visible) el.textContent = '';\n }, 150);\n },\n show(x: number, y: number, datum: Datum, series: Series) {\n if (!container.isConnected) return;\n\n cancelClear();\n\n if (render) {\n const html = render(datum, series);\n\n if (sanitize) {\n el.innerHTML = sanitize(html);\n } else {\n // No sanitizer provided — never inject the raw string as HTML. Render it as text instead.\n el.textContent = html;\n }\n } else {\n el.textContent = `${series.name}: ${datum.value}`;\n }\n\n const virtualRef = {\n getBoundingClientRect: () => {\n const rect = container.getBoundingClientRect();\n\n return {\n bottom: rect.top + y,\n height: 0,\n left: rect.left + x,\n right: rect.left + x,\n top: rect.top + y,\n width: 0,\n x: rect.left + x,\n y: rect.top + y,\n };\n },\n };\n\n const { x: positionX, y: positionY } = computePosition(virtualRef, el, {\n containingBlock: container,\n middleware: [offset(tooltipOffset), flip(), shift({ padding: 8 })],\n placement: 'top',\n });\n\n el.style.left = `${positionX}px`;\n el.style.top = `${positionY}px`;\n\n if (visible) {\n if (revealFrame === null) el.style.opacity = '1';\n\n return;\n }\n\n visible = true;\n el.style.opacity = '0';\n revealFrame = requestAnimationFrame(() => {\n revealFrame = null;\n\n if (visible) el.style.opacity = '1';\n });\n },\n [Symbol.dispose]: disposeHandle,\n };\n}\n"],"mappings":"wDAYA,SAAgB,EAAc,EAAwB,EAA6C,CAC7F,iBAAiB,CAAS,CAAC,CAAC,WAAa,WAC3C,EAAU,MAAM,SAAW,YAG7B,IAAM,EAAK,SAAS,cAAc,KAAK,EAEvC,EAAG,UAAY,gBACf,EAAG,MAAM,SAAW,WACpB,EAAG,MAAM,cAAgB,OACzB,EAAG,MAAM,IAAM,IACf,EAAG,MAAM,KAAO,IAEhB,EAAG,aAAa,OAAQ,QAAQ,EAChC,EAAG,aAAa,YAAa,QAAQ,EACrC,EAAU,YAAY,CAAE,EAExB,IAAM,EAAyB,IAAW,IAAQ,GAAQ,QAAW,EAC/D,EAAS,IAAW,GAAwB,IAAA,GAAjB,GAAQ,OACnC,EAAW,IAAW,GAA0B,IAAA,GAAnB,GAAQ,SAQvC,EAA6B,KAC7B,EAAmD,KACnD,EAAU,GAER,MAA0B,CAC1B,IAAe,OAEnB,aAAa,CAAU,EACvB,EAAa,KACf,EAEM,MAA2B,CAC3B,IAAgB,OAEpB,qBAAqB,CAAW,EAChC,EAAc,KAChB,EAEM,MAA4B,CAChC,EAAa,EACb,EAAY,EACZ,EAAG,OAAO,CACZ,EAEA,MAAO,CACL,QAAS,EACT,KACA,MAAO,CACL,EAAa,EACb,EAAY,EACZ,EAAU,GACV,EAAG,MAAM,QAAU,IACnB,EAAa,eAAiB,CAC5B,EAAa,KAER,IAAS,EAAG,YAAc,GACjC,EAAG,GAAG,CACR,EACA,KAAK,EAAW,EAAW,EAAc,EAAgB,CACvD,GAAI,CAAC,EAAU,YAAa,OAI5B,GAFA,EAAY,EAER,EAAQ,CACV,IAAM,EAAO,EAAO,EAAO,CAAM,EAE7B,EACF,EAAG,UAAY,EAAS,CAAI,EAG5B,EAAG,YAAc,CAErB,KACE,GAAG,YAAc,GAAG,EAAO,KAAK,IAAI,EAAM,QAoB5C,GAAM,CAAE,EAAG,EAAW,EAAG,IAAA,EAAc,EAAA,gBAAA,CAAgB,CAhBrD,0BAA6B,CAC3B,IAAM,EAAO,EAAU,sBAAsB,EAE7C,MAAO,CACL,OAAQ,EAAK,IAAM,EACnB,OAAQ,EACR,KAAM,EAAK,KAAO,EAClB,MAAO,EAAK,KAAO,EACnB,IAAK,EAAK,IAAM,EAChB,MAAO,EACP,EAAG,EAAK,KAAO,EACf,EAAG,EAAK,IAAM,CAChB,CACF,CAGqD,EAAY,EAAI,CACrE,gBAAiB,EACjB,WAAY,EAAC,EAAA,EAAA,OAAA,CAAO,CAAa,GAAG,EAAA,EAAA,KAAA,CAAK,GAAG,EAAA,EAAA,MAAA,CAAM,CAAE,QAAS,CAAE,CAAC,CAAC,EACjE,UAAW,KACb,CAAC,EAKD,GAHA,EAAG,MAAM,KAAO,GAAG,EAAU,IAC7B,EAAG,MAAM,IAAM,GAAG,EAAU,IAExB,EAAS,CACP,IAAgB,OAAM,EAAG,MAAM,QAAU,KAE7C,MACF,CAEA,EAAU,GACV,EAAG,MAAM,QAAU,IACnB,EAAc,0BAA4B,CACxC,EAAc,KAEV,IAAS,EAAG,MAAM,QAAU,IAClC,CAAC,CACH,GACC,OAAO,SAAU,CACpB,CACF"}
1
+ {"version":3,"file":"tooltip.cjs","names":[],"sources":["../../src/interaction/tooltip.ts"],"sourcesContent":["import { computePosition, flip, offset, shift } from '@vielzeug/orbit';\nimport { warn } from '../_dev';\nimport type { Datum, Series, TooltipConfig } from '../types';\n\nexport interface TooltipState {\n dispose(): void;\n el: HTMLDivElement | null;\n hide(): void;\n show(x: number, y: number, datum: Datum, series: Series): void;\n [Symbol.dispose](): void;\n}\n\nexport function createTooltip(container: HTMLElement, config?: TooltipConfig | true): TooltipState {\n if (getComputedStyle(container).position === 'static') {\n container.style.position = 'relative';\n }\n\n const el = container.ownerDocument.createElement('div');\n\n el.className = 'prism-tooltip';\n el.style.position = 'absolute';\n el.style.pointerEvents = 'none';\n el.style.top = '0';\n el.style.left = '0';\n // Non-modal status text — announced by assistive tech whenever content/hide state changes.\n el.setAttribute('role', 'status');\n el.setAttribute('aria-live', 'polite');\n container.appendChild(el);\n\n const tooltipOffset: number = (config !== true && config?.offset) || 8;\n const render = config !== true ? config?.render : undefined;\n const sanitize = config !== true ? config?.sanitize : undefined;\n\n if (render && !sanitize) {\n warn(\n 'createTooltip: `render` is set without `sanitize` — falling back to plain-text rendering of the returned string to avoid an XSS risk. Pass `sanitize` to render HTML.',\n );\n }\n\n let revealFrame: number | null = null;\n let clearTimer: ReturnType<typeof setTimeout> | null = null;\n let visible = false;\n\n const cancelClear = (): void => {\n if (clearTimer === null) return;\n\n clearTimeout(clearTimer);\n clearTimer = null;\n };\n\n const cancelReveal = (): void => {\n if (revealFrame === null) return;\n\n cancelAnimationFrame(revealFrame);\n revealFrame = null;\n };\n\n const disposeHandle = (): void => {\n cancelReveal();\n cancelClear();\n el.remove();\n };\n\n return {\n dispose: disposeHandle,\n el,\n hide() {\n cancelReveal();\n cancelClear();\n visible = false;\n el.style.opacity = '0';\n clearTimer = setTimeout(() => {\n clearTimer = null;\n\n if (!visible) el.textContent = '';\n }, 150);\n },\n show(x: number, y: number, datum: Datum, series: Series) {\n if (!container.isConnected) return;\n\n cancelClear();\n\n if (render) {\n const html = render(datum, series);\n\n if (sanitize) {\n el.innerHTML = sanitize(html);\n } else {\n // No sanitizer provided — never inject the raw string as HTML. Render it as text instead.\n el.textContent = html;\n }\n } else {\n el.textContent = `${series.name}: ${datum.value}`;\n }\n\n const virtualRef = {\n getBoundingClientRect: () => {\n const rect = container.getBoundingClientRect();\n\n return {\n bottom: rect.top + y,\n height: 0,\n left: rect.left + x,\n right: rect.left + x,\n top: rect.top + y,\n width: 0,\n x: rect.left + x,\n y: rect.top + y,\n };\n },\n };\n\n const { x: positionX, y: positionY } = computePosition(virtualRef, el, {\n containingBlock: container,\n middleware: [offset(tooltipOffset), flip(), shift({ padding: 8 })],\n placement: 'top',\n });\n\n el.style.left = `${positionX}px`;\n el.style.top = `${positionY}px`;\n\n if (visible) {\n if (revealFrame === null) el.style.opacity = '1';\n\n return;\n }\n\n visible = true;\n el.style.opacity = '0';\n revealFrame = requestAnimationFrame(() => {\n revealFrame = null;\n\n if (visible) el.style.opacity = '1';\n });\n },\n [Symbol.dispose]: disposeHandle,\n };\n}\n"],"mappings":"wDAYA,SAAgB,EAAc,EAAwB,EAA6C,CAC7F,iBAAiB,CAAS,CAAC,CAAC,WAAa,WAC3C,EAAU,MAAM,SAAW,YAG7B,IAAM,EAAK,EAAU,cAAc,cAAc,KAAK,EAEtD,EAAG,UAAY,gBACf,EAAG,MAAM,SAAW,WACpB,EAAG,MAAM,cAAgB,OACzB,EAAG,MAAM,IAAM,IACf,EAAG,MAAM,KAAO,IAEhB,EAAG,aAAa,OAAQ,QAAQ,EAChC,EAAG,aAAa,YAAa,QAAQ,EACrC,EAAU,YAAY,CAAE,EAExB,IAAM,EAAyB,IAAW,IAAQ,GAAQ,QAAW,EAC/D,EAAS,IAAW,GAAwB,IAAA,GAAjB,GAAQ,OACnC,EAAW,IAAW,GAA0B,IAAA,GAAnB,GAAQ,SAQvC,EAA6B,KAC7B,EAAmD,KACnD,EAAU,GAER,MAA0B,CAC1B,IAAe,OAEnB,aAAa,CAAU,EACvB,EAAa,KACf,EAEM,MAA2B,CAC3B,IAAgB,OAEpB,qBAAqB,CAAW,EAChC,EAAc,KAChB,EAEM,MAA4B,CAChC,EAAa,EACb,EAAY,EACZ,EAAG,OAAO,CACZ,EAEA,MAAO,CACL,QAAS,EACT,KACA,MAAO,CACL,EAAa,EACb,EAAY,EACZ,EAAU,GACV,EAAG,MAAM,QAAU,IACnB,EAAa,eAAiB,CAC5B,EAAa,KAER,IAAS,EAAG,YAAc,GACjC,EAAG,GAAG,CACR,EACA,KAAK,EAAW,EAAW,EAAc,EAAgB,CACvD,GAAI,CAAC,EAAU,YAAa,OAI5B,GAFA,EAAY,EAER,EAAQ,CACV,IAAM,EAAO,EAAO,EAAO,CAAM,EAE7B,EACF,EAAG,UAAY,EAAS,CAAI,EAG5B,EAAG,YAAc,CAErB,KACE,GAAG,YAAc,GAAG,EAAO,KAAK,IAAI,EAAM,QAoB5C,GAAM,CAAE,EAAG,EAAW,EAAG,IAAA,EAAc,EAAA,gBAAA,CAAgB,CAhBrD,0BAA6B,CAC3B,IAAM,EAAO,EAAU,sBAAsB,EAE7C,MAAO,CACL,OAAQ,EAAK,IAAM,EACnB,OAAQ,EACR,KAAM,EAAK,KAAO,EAClB,MAAO,EAAK,KAAO,EACnB,IAAK,EAAK,IAAM,EAChB,MAAO,EACP,EAAG,EAAK,KAAO,EACf,EAAG,EAAK,IAAM,CAChB,CACF,CAGqD,EAAY,EAAI,CACrE,gBAAiB,EACjB,WAAY,EAAC,EAAA,EAAA,OAAA,CAAO,CAAa,GAAG,EAAA,EAAA,KAAA,CAAK,GAAG,EAAA,EAAA,MAAA,CAAM,CAAE,QAAS,CAAE,CAAC,CAAC,EACjE,UAAW,KACb,CAAC,EAKD,GAHA,EAAG,MAAM,KAAO,GAAG,EAAU,IAC7B,EAAG,MAAM,IAAM,GAAG,EAAU,IAExB,EAAS,CACP,IAAgB,OAAM,EAAG,MAAM,QAAU,KAE7C,MACF,CAEA,EAAU,GACV,EAAG,MAAM,QAAU,IACnB,EAAc,0BAA4B,CACxC,EAAc,KAEV,IAAS,EAAG,MAAM,QAAU,IAClC,CAAC,CACH,GACC,OAAO,SAAU,CACpB,CACF"}
@@ -3,7 +3,7 @@ import { computePosition as e, flip as t, offset as n, shift as r } from "@vielz
3
3
  //#region src/interaction/tooltip.ts
4
4
  function i(i, a) {
5
5
  getComputedStyle(i).position === "static" && (i.style.position = "relative");
6
- let o = document.createElement("div");
6
+ let o = i.ownerDocument.createElement("div");
7
7
  o.className = "prism-tooltip", o.style.position = "absolute", o.style.pointerEvents = "none", o.style.top = "0", o.style.left = "0", o.setAttribute("role", "status"), o.setAttribute("aria-live", "polite"), i.appendChild(o);
8
8
  let s = a !== !0 && a?.offset || 8, c = a === !0 ? void 0 : a?.render, l = a === !0 ? void 0 : a?.sanitize, u = null, d = null, f = !1, p = () => {
9
9
  d !== null && (clearTimeout(d), d = null);
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.js","names":[],"sources":["../../src/interaction/tooltip.ts"],"sourcesContent":["import { computePosition, flip, offset, shift } from '@vielzeug/orbit';\nimport { warn } from '../_dev';\nimport type { Datum, Series, TooltipConfig } from '../types';\n\nexport interface TooltipState {\n dispose(): void;\n el: HTMLDivElement | null;\n hide(): void;\n show(x: number, y: number, datum: Datum, series: Series): void;\n [Symbol.dispose](): void;\n}\n\nexport function createTooltip(container: HTMLElement, config?: TooltipConfig | true): TooltipState {\n if (getComputedStyle(container).position === 'static') {\n container.style.position = 'relative';\n }\n\n const el = document.createElement('div');\n\n el.className = 'prism-tooltip';\n el.style.position = 'absolute';\n el.style.pointerEvents = 'none';\n el.style.top = '0';\n el.style.left = '0';\n // Non-modal status text — announced by assistive tech whenever content/hide state changes.\n el.setAttribute('role', 'status');\n el.setAttribute('aria-live', 'polite');\n container.appendChild(el);\n\n const tooltipOffset: number = (config !== true && config?.offset) || 8;\n const render = config !== true ? config?.render : undefined;\n const sanitize = config !== true ? config?.sanitize : undefined;\n\n if (render && !sanitize) {\n warn(\n 'createTooltip: `render` is set without `sanitize` — falling back to plain-text rendering of the returned string to avoid an XSS risk. Pass `sanitize` to render HTML.',\n );\n }\n\n let revealFrame: number | null = null;\n let clearTimer: ReturnType<typeof setTimeout> | null = null;\n let visible = false;\n\n const cancelClear = (): void => {\n if (clearTimer === null) return;\n\n clearTimeout(clearTimer);\n clearTimer = null;\n };\n\n const cancelReveal = (): void => {\n if (revealFrame === null) return;\n\n cancelAnimationFrame(revealFrame);\n revealFrame = null;\n };\n\n const disposeHandle = (): void => {\n cancelReveal();\n cancelClear();\n el.remove();\n };\n\n return {\n dispose: disposeHandle,\n el,\n hide() {\n cancelReveal();\n cancelClear();\n visible = false;\n el.style.opacity = '0';\n clearTimer = setTimeout(() => {\n clearTimer = null;\n\n if (!visible) el.textContent = '';\n }, 150);\n },\n show(x: number, y: number, datum: Datum, series: Series) {\n if (!container.isConnected) return;\n\n cancelClear();\n\n if (render) {\n const html = render(datum, series);\n\n if (sanitize) {\n el.innerHTML = sanitize(html);\n } else {\n // No sanitizer provided — never inject the raw string as HTML. Render it as text instead.\n el.textContent = html;\n }\n } else {\n el.textContent = `${series.name}: ${datum.value}`;\n }\n\n const virtualRef = {\n getBoundingClientRect: () => {\n const rect = container.getBoundingClientRect();\n\n return {\n bottom: rect.top + y,\n height: 0,\n left: rect.left + x,\n right: rect.left + x,\n top: rect.top + y,\n width: 0,\n x: rect.left + x,\n y: rect.top + y,\n };\n },\n };\n\n const { x: positionX, y: positionY } = computePosition(virtualRef, el, {\n containingBlock: container,\n middleware: [offset(tooltipOffset), flip(), shift({ padding: 8 })],\n placement: 'top',\n });\n\n el.style.left = `${positionX}px`;\n el.style.top = `${positionY}px`;\n\n if (visible) {\n if (revealFrame === null) el.style.opacity = '1';\n\n return;\n }\n\n visible = true;\n el.style.opacity = '0';\n revealFrame = requestAnimationFrame(() => {\n revealFrame = null;\n\n if (visible) el.style.opacity = '1';\n });\n },\n [Symbol.dispose]: disposeHandle,\n };\n}\n"],"mappings":";;;AAYA,SAAgB,EAAc,GAAwB,GAA6C;CACjG,AAAI,iBAAiB,CAAS,CAAC,CAAC,aAAa,aAC3C,EAAU,MAAM,WAAW;CAG7B,IAAM,IAAK,SAAS,cAAc,KAAK;CAUvC,AARA,EAAG,YAAY,iBACf,EAAG,MAAM,WAAW,YACpB,EAAG,MAAM,gBAAgB,QACzB,EAAG,MAAM,MAAM,KACf,EAAG,MAAM,OAAO,KAEhB,EAAG,aAAa,QAAQ,QAAQ,GAChC,EAAG,aAAa,aAAa,QAAQ,GACrC,EAAU,YAAY,CAAE;CAExB,IAAM,IAAyB,MAAW,MAAQ,GAAQ,UAAW,GAC/D,IAAS,MAAW,KAAwB,KAAA,IAAjB,GAAQ,QACnC,IAAW,MAAW,KAA0B,KAAA,IAAnB,GAAQ,UAQvC,IAA6B,MAC7B,IAAmD,MACnD,IAAU,IAER,UAA0B;EAC1B,MAAe,SAEnB,aAAa,CAAU,GACvB,IAAa;CACf,GAEM,UAA2B;EAC3B,MAAgB,SAEpB,qBAAqB,CAAW,GAChC,IAAc;CAChB,GAEM,UAA4B;EAGhC,AAFA,EAAa,GACb,EAAY,GACZ,EAAG,OAAO;CACZ;CAEA,OAAO;EACL,SAAS;EACT;EACA,OAAO;GAKL,AAJA,EAAa,GACb,EAAY,GACZ,IAAU,IACV,EAAG,MAAM,UAAU,KACnB,IAAa,iBAAiB;IAG5B,AAFA,IAAa,MAER,MAAS,EAAG,cAAc;GACjC,GAAG,GAAG;EACR;EACA,KAAK,GAAW,GAAW,GAAc,GAAgB;GACvD,IAAI,CAAC,EAAU,aAAa;GAI5B,IAFA,EAAY,GAER,GAAQ;IACV,IAAM,IAAO,EAAO,GAAO,CAAM;IAEjC,AAAI,IACF,EAAG,YAAY,EAAS,CAAI,IAG5B,EAAG,cAAc;GAErB,OACE,EAAG,cAAc,GAAG,EAAO,KAAK,IAAI,EAAM;GAoB5C,IAAM,EAAE,GAAG,GAAW,GAAG,MAAc,EAAgB,EAhBrD,6BAA6B;IAC3B,IAAM,IAAO,EAAU,sBAAsB;IAE7C,OAAO;KACL,QAAQ,EAAK,MAAM;KACnB,QAAQ;KACR,MAAM,EAAK,OAAO;KAClB,OAAO,EAAK,OAAO;KACnB,KAAK,EAAK,MAAM;KAChB,OAAO;KACP,GAAG,EAAK,OAAO;KACf,GAAG,EAAK,MAAM;IAChB;GACF,EAGqD,GAAY,GAAI;IACrE,iBAAiB;IACjB,YAAY;KAAC,EAAO,CAAa;KAAG,EAAK;KAAG,EAAM,EAAE,SAAS,EAAE,CAAC;IAAC;IACjE,WAAW;GACb,CAAC;GAKD,IAHA,EAAG,MAAM,OAAO,GAAG,EAAU,KAC7B,EAAG,MAAM,MAAM,GAAG,EAAU,KAExB,GAAS;IACX,AAAI,MAAgB,SAAM,EAAG,MAAM,UAAU;IAE7C;GACF;GAIA,AAFA,IAAU,IACV,EAAG,MAAM,UAAU,KACnB,IAAc,4BAA4B;IAGxC,AAFA,IAAc,MAEV,MAAS,EAAG,MAAM,UAAU;GAClC,CAAC;EACH;GACC,OAAO,UAAU;CACpB;AACF"}
1
+ {"version":3,"file":"tooltip.js","names":[],"sources":["../../src/interaction/tooltip.ts"],"sourcesContent":["import { computePosition, flip, offset, shift } from '@vielzeug/orbit';\nimport { warn } from '../_dev';\nimport type { Datum, Series, TooltipConfig } from '../types';\n\nexport interface TooltipState {\n dispose(): void;\n el: HTMLDivElement | null;\n hide(): void;\n show(x: number, y: number, datum: Datum, series: Series): void;\n [Symbol.dispose](): void;\n}\n\nexport function createTooltip(container: HTMLElement, config?: TooltipConfig | true): TooltipState {\n if (getComputedStyle(container).position === 'static') {\n container.style.position = 'relative';\n }\n\n const el = container.ownerDocument.createElement('div');\n\n el.className = 'prism-tooltip';\n el.style.position = 'absolute';\n el.style.pointerEvents = 'none';\n el.style.top = '0';\n el.style.left = '0';\n // Non-modal status text — announced by assistive tech whenever content/hide state changes.\n el.setAttribute('role', 'status');\n el.setAttribute('aria-live', 'polite');\n container.appendChild(el);\n\n const tooltipOffset: number = (config !== true && config?.offset) || 8;\n const render = config !== true ? config?.render : undefined;\n const sanitize = config !== true ? config?.sanitize : undefined;\n\n if (render && !sanitize) {\n warn(\n 'createTooltip: `render` is set without `sanitize` — falling back to plain-text rendering of the returned string to avoid an XSS risk. Pass `sanitize` to render HTML.',\n );\n }\n\n let revealFrame: number | null = null;\n let clearTimer: ReturnType<typeof setTimeout> | null = null;\n let visible = false;\n\n const cancelClear = (): void => {\n if (clearTimer === null) return;\n\n clearTimeout(clearTimer);\n clearTimer = null;\n };\n\n const cancelReveal = (): void => {\n if (revealFrame === null) return;\n\n cancelAnimationFrame(revealFrame);\n revealFrame = null;\n };\n\n const disposeHandle = (): void => {\n cancelReveal();\n cancelClear();\n el.remove();\n };\n\n return {\n dispose: disposeHandle,\n el,\n hide() {\n cancelReveal();\n cancelClear();\n visible = false;\n el.style.opacity = '0';\n clearTimer = setTimeout(() => {\n clearTimer = null;\n\n if (!visible) el.textContent = '';\n }, 150);\n },\n show(x: number, y: number, datum: Datum, series: Series) {\n if (!container.isConnected) return;\n\n cancelClear();\n\n if (render) {\n const html = render(datum, series);\n\n if (sanitize) {\n el.innerHTML = sanitize(html);\n } else {\n // No sanitizer provided — never inject the raw string as HTML. Render it as text instead.\n el.textContent = html;\n }\n } else {\n el.textContent = `${series.name}: ${datum.value}`;\n }\n\n const virtualRef = {\n getBoundingClientRect: () => {\n const rect = container.getBoundingClientRect();\n\n return {\n bottom: rect.top + y,\n height: 0,\n left: rect.left + x,\n right: rect.left + x,\n top: rect.top + y,\n width: 0,\n x: rect.left + x,\n y: rect.top + y,\n };\n },\n };\n\n const { x: positionX, y: positionY } = computePosition(virtualRef, el, {\n containingBlock: container,\n middleware: [offset(tooltipOffset), flip(), shift({ padding: 8 })],\n placement: 'top',\n });\n\n el.style.left = `${positionX}px`;\n el.style.top = `${positionY}px`;\n\n if (visible) {\n if (revealFrame === null) el.style.opacity = '1';\n\n return;\n }\n\n visible = true;\n el.style.opacity = '0';\n revealFrame = requestAnimationFrame(() => {\n revealFrame = null;\n\n if (visible) el.style.opacity = '1';\n });\n },\n [Symbol.dispose]: disposeHandle,\n };\n}\n"],"mappings":";;;AAYA,SAAgB,EAAc,GAAwB,GAA6C;CACjG,AAAI,iBAAiB,CAAS,CAAC,CAAC,aAAa,aAC3C,EAAU,MAAM,WAAW;CAG7B,IAAM,IAAK,EAAU,cAAc,cAAc,KAAK;CAUtD,AARA,EAAG,YAAY,iBACf,EAAG,MAAM,WAAW,YACpB,EAAG,MAAM,gBAAgB,QACzB,EAAG,MAAM,MAAM,KACf,EAAG,MAAM,OAAO,KAEhB,EAAG,aAAa,QAAQ,QAAQ,GAChC,EAAG,aAAa,aAAa,QAAQ,GACrC,EAAU,YAAY,CAAE;CAExB,IAAM,IAAyB,MAAW,MAAQ,GAAQ,UAAW,GAC/D,IAAS,MAAW,KAAwB,KAAA,IAAjB,GAAQ,QACnC,IAAW,MAAW,KAA0B,KAAA,IAAnB,GAAQ,UAQvC,IAA6B,MAC7B,IAAmD,MACnD,IAAU,IAER,UAA0B;EAC1B,MAAe,SAEnB,aAAa,CAAU,GACvB,IAAa;CACf,GAEM,UAA2B;EAC3B,MAAgB,SAEpB,qBAAqB,CAAW,GAChC,IAAc;CAChB,GAEM,UAA4B;EAGhC,AAFA,EAAa,GACb,EAAY,GACZ,EAAG,OAAO;CACZ;CAEA,OAAO;EACL,SAAS;EACT;EACA,OAAO;GAKL,AAJA,EAAa,GACb,EAAY,GACZ,IAAU,IACV,EAAG,MAAM,UAAU,KACnB,IAAa,iBAAiB;IAG5B,AAFA,IAAa,MAER,MAAS,EAAG,cAAc;GACjC,GAAG,GAAG;EACR;EACA,KAAK,GAAW,GAAW,GAAc,GAAgB;GACvD,IAAI,CAAC,EAAU,aAAa;GAI5B,IAFA,EAAY,GAER,GAAQ;IACV,IAAM,IAAO,EAAO,GAAO,CAAM;IAEjC,AAAI,IACF,EAAG,YAAY,EAAS,CAAI,IAG5B,EAAG,cAAc;GAErB,OACE,EAAG,cAAc,GAAG,EAAO,KAAK,IAAI,EAAM;GAoB5C,IAAM,EAAE,GAAG,GAAW,GAAG,MAAc,EAAgB,EAhBrD,6BAA6B;IAC3B,IAAM,IAAO,EAAU,sBAAsB;IAE7C,OAAO;KACL,QAAQ,EAAK,MAAM;KACnB,QAAQ;KACR,MAAM,EAAK,OAAO;KAClB,OAAO,EAAK,OAAO;KACnB,KAAK,EAAK,MAAM;KAChB,OAAO;KACP,GAAG,EAAK,OAAO;KACf,GAAG,EAAK,MAAM;IAChB;GACF,EAGqD,GAAY,GAAI;IACrE,iBAAiB;IACjB,YAAY;KAAC,EAAO,CAAa;KAAG,EAAK;KAAG,EAAM,EAAE,SAAS,EAAE,CAAC;IAAC;IACjE,WAAW;GACb,CAAC;GAKD,IAHA,EAAG,MAAM,OAAO,GAAG,EAAU,KAC7B,EAAG,MAAM,MAAM,GAAG,EAAU,KAExB,GAAS;IACX,AAAI,MAAgB,SAAM,EAAG,MAAM,UAAU;IAE7C;GACF;GAIA,AAFA,IAAU,IACV,EAAG,MAAM,UAAU,KACnB,IAAc,4BAA4B;IAGxC,AAFA,IAAc,MAEV,MAAS,EAAG,MAAM,UAAU;GAClC,CAAC;EACH;GACC,OAAO,UAAU;CACpB;AACF"}