@vielzeug/prism 26.10.2 → 26.10.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/README.md +1 -1
  2. package/dist/_dev.cjs.map +1 -1
  3. package/dist/_dev.js.map +1 -1
  4. package/dist/_tappers.cjs +2 -0
  5. package/dist/_tappers.cjs.map +1 -0
  6. package/dist/_tappers.d.ts +10 -0
  7. package/dist/_tappers.d.ts.map +1 -0
  8. package/dist/_tappers.js +2 -0
  9. package/dist/_tappers.js.map +1 -0
  10. package/dist/animation/easing.cjs +1 -1
  11. package/dist/animation/easing.cjs.map +1 -1
  12. package/dist/animation/easing.d.ts +15 -1
  13. package/dist/animation/easing.d.ts.map +1 -1
  14. package/dist/animation/easing.js +1 -1
  15. package/dist/animation/easing.js.map +1 -1
  16. package/dist/animation/motion.cjs +1 -1
  17. package/dist/animation/motion.cjs.map +1 -1
  18. package/dist/animation/motion.d.ts +17 -2
  19. package/dist/animation/motion.d.ts.map +1 -1
  20. package/dist/animation/motion.js +1 -1
  21. package/dist/animation/motion.js.map +1 -1
  22. package/dist/animation/transition.cjs +1 -1
  23. package/dist/animation/transition.cjs.map +1 -1
  24. package/dist/animation/transition.d.ts +32 -3
  25. package/dist/animation/transition.d.ts.map +1 -1
  26. package/dist/animation/transition.js +1 -1
  27. package/dist/animation/transition.js.map +1 -1
  28. package/dist/charts/area/area-chart.cjs +1 -1
  29. package/dist/charts/area/area-chart.cjs.map +1 -1
  30. package/dist/charts/area/area-chart.d.ts.map +1 -1
  31. package/dist/charts/area/area-chart.js +1 -1
  32. package/dist/charts/area/area-chart.js.map +1 -1
  33. package/dist/charts/area/area-renderer.cjs +1 -1
  34. package/dist/charts/area/area-renderer.cjs.map +1 -1
  35. package/dist/charts/area/area-renderer.d.ts +2 -2
  36. package/dist/charts/area/area-renderer.d.ts.map +1 -1
  37. package/dist/charts/area/area-renderer.js +1 -1
  38. package/dist/charts/area/area-renderer.js.map +1 -1
  39. package/dist/charts/bar/bar-chart.cjs +1 -1
  40. package/dist/charts/bar/bar-chart.cjs.map +1 -1
  41. package/dist/charts/bar/bar-chart.d.ts.map +1 -1
  42. package/dist/charts/bar/bar-chart.js +1 -1
  43. package/dist/charts/bar/bar-chart.js.map +1 -1
  44. package/dist/charts/bar/bar-renderer.cjs +1 -1
  45. package/dist/charts/bar/bar-renderer.cjs.map +1 -1
  46. package/dist/charts/bar/bar-renderer.d.ts +2 -2
  47. package/dist/charts/bar/bar-renderer.d.ts.map +1 -1
  48. package/dist/charts/bar/bar-renderer.js +1 -1
  49. package/dist/charts/bar/bar-renderer.js.map +1 -1
  50. package/dist/charts/line/line-chart.cjs +1 -1
  51. package/dist/charts/line/line-chart.cjs.map +1 -1
  52. package/dist/charts/line/line-chart.d.ts.map +1 -1
  53. package/dist/charts/line/line-chart.js +1 -1
  54. package/dist/charts/line/line-chart.js.map +1 -1
  55. package/dist/charts/line/line-renderer.cjs +1 -1
  56. package/dist/charts/line/line-renderer.cjs.map +1 -1
  57. package/dist/charts/line/line-renderer.d.ts +4 -2
  58. package/dist/charts/line/line-renderer.d.ts.map +1 -1
  59. package/dist/charts/line/line-renderer.js +1 -1
  60. package/dist/charts/line/line-renderer.js.map +1 -1
  61. package/dist/charts/pie/pie-chart.cjs +1 -1
  62. package/dist/charts/pie/pie-chart.cjs.map +1 -1
  63. package/dist/charts/pie/pie-chart.d.ts.map +1 -1
  64. package/dist/charts/pie/pie-chart.js +1 -1
  65. package/dist/charts/pie/pie-chart.js.map +1 -1
  66. package/dist/charts/radar/radar-chart.cjs +1 -1
  67. package/dist/charts/radar/radar-chart.cjs.map +1 -1
  68. package/dist/charts/radar/radar-chart.d.ts.map +1 -1
  69. package/dist/charts/radar/radar-chart.js +1 -1
  70. package/dist/charts/radar/radar-chart.js.map +1 -1
  71. package/dist/charts/sparkline/sparkline.cjs +1 -1
  72. package/dist/charts/sparkline/sparkline.cjs.map +1 -1
  73. package/dist/charts/sparkline/sparkline.d.ts.map +1 -1
  74. package/dist/charts/sparkline/sparkline.js +1 -1
  75. package/dist/charts/sparkline/sparkline.js.map +1 -1
  76. package/dist/core/chart-scaffold.cjs +1 -1
  77. package/dist/core/chart-scaffold.cjs.map +1 -1
  78. package/dist/core/chart-scaffold.d.ts.map +1 -1
  79. package/dist/core/chart-scaffold.js +1 -1
  80. package/dist/core/chart-scaffold.js.map +1 -1
  81. package/dist/core/responsive.cjs +1 -1
  82. package/dist/core/responsive.cjs.map +1 -1
  83. package/dist/core/responsive.d.ts.map +1 -1
  84. package/dist/core/responsive.js +1 -1
  85. package/dist/core/responsive.js.map +1 -1
  86. package/dist/index.d.ts +2 -2
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/interaction/crosshair.cjs +1 -1
  89. package/dist/interaction/crosshair.cjs.map +1 -1
  90. package/dist/interaction/crosshair.d.ts +7 -0
  91. package/dist/interaction/crosshair.d.ts.map +1 -1
  92. package/dist/interaction/crosshair.js +1 -1
  93. package/dist/interaction/crosshair.js.map +1 -1
  94. package/dist/interaction/series-interaction.cjs +1 -1
  95. package/dist/interaction/series-interaction.cjs.map +1 -1
  96. package/dist/interaction/series-interaction.d.ts +3 -1
  97. package/dist/interaction/series-interaction.d.ts.map +1 -1
  98. package/dist/interaction/series-interaction.js +1 -1
  99. package/dist/interaction/series-interaction.js.map +1 -1
  100. package/dist/interaction/tooltip.cjs +1 -1
  101. package/dist/interaction/tooltip.cjs.map +1 -1
  102. package/dist/interaction/tooltip.d.ts.map +1 -1
  103. package/dist/interaction/tooltip.js +1 -1
  104. package/dist/interaction/tooltip.js.map +1 -1
  105. package/dist/prism.cjs +1 -1
  106. package/dist/prism.cjs.map +1 -1
  107. package/dist/prism.iife.js +1 -1
  108. package/dist/prism.iife.js.map +1 -1
  109. package/dist/prism.js +1 -1
  110. package/dist/prism.js.map +1 -1
  111. package/dist/theme/index.cjs +1 -1
  112. package/dist/theme/index.cjs.map +1 -1
  113. package/dist/theme/index.d.ts +9 -4
  114. package/dist/theme/index.d.ts.map +1 -1
  115. package/dist/theme/index.js +1 -1
  116. package/dist/theme/index.js.map +1 -1
  117. package/dist/theme/prism.css +18 -4
  118. package/dist/types.d.ts +76 -9
  119. package/dist/types.d.ts.map +1 -1
  120. package/package.json +3 -18
  121. package/dist/animation/index.d.ts +0 -6
  122. package/dist/animation/index.d.ts.map +0 -1
  123. package/dist/axes/index.d.ts +0 -3
  124. package/dist/axes/index.d.ts.map +0 -1
  125. package/dist/core/index.d.ts +0 -7
  126. package/dist/core/index.d.ts.map +0 -1
  127. package/dist/devtools/index.d.ts +0 -52
  128. package/dist/devtools/index.d.ts.map +0 -1
  129. package/dist/devtools.cjs +0 -2
  130. package/dist/devtools.cjs.map +0 -1
  131. package/dist/devtools.js +0 -2
  132. package/dist/devtools.js.map +0 -1
  133. package/dist/interaction/index.d.ts +0 -8
  134. package/dist/interaction/index.d.ts.map +0 -1
  135. package/dist/scales/index.d.ts +0 -4
  136. package/dist/scales/index.d.ts.map +0 -1
  137. package/dist/svg/index.d.ts +0 -5
  138. package/dist/svg/index.d.ts.map +0 -1
@@ -1,2 +1,2 @@
1
- const e=require("../svg/element.cjs"),t=require("./announcer.cjs"),n=require("./tooltip.cjs"),r=require("../core/layout.cjs"),i=require("../core/cartesian-model.cjs"),a=require("./events.cjs");var keyOrder=e=>e instanceof Date?e.getTime():Number(e);function keyLabel(e){return e instanceof Date?e.toLocaleDateString():String(e)}function findNearestKey(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 createSeriesInteraction(o){let s=-1,c=!1,renderedHit=e=>{let t=e.target;if(!(t instanceof SVGElement)||t.tagName.toLowerCase()!==`circle`||!o.seriesGroup.contains(t)||!t.parentElement)return null;let n=t.parentElement;for(;n&&n.parentNode!==o.seriesGroup;)n=n.parentElement;if(!n)return null;let r=[...o.seriesGroup.children].indexOf(n),i=[...t.parentElement.children].indexOf(t),a=o.getData()[r]?.[i],s=o.getPoints()[r]?.[i];return a&&s?{datum:a,point:s,seriesIndex:r}:null},entriesAt=e=>{let t=i.keyId(e),n=o.getPoints();return o.getData().map((e,r)=>{let a=e.findIndex(e=>i.keyId(e.key)===t);return a===-1?{datum:void 0,point:void 0}:{datum:e[a],point:n[r]?.[a]}})},nearestIndex=(e,t)=>{let n=-1,r=1/0;return e.forEach(({point:e},i)=>{if(!e)return;let a=Number.isFinite(t)?Math.abs(e.y-t):0;a<r&&(r=a,n=i)}),n},highlight=(t,n)=>{let r=o.colors();o.markers.replaceChildren(),t.forEach(({point:t},n)=>{t&&o.markers.appendChild(e.createSvgElement(`circle`,{class:`prism-active-point`,cx:t.x,cy:t.y,fill:r[n],r:4}))});let i=[...o.seriesGroup.children];o.seriesGroup.classList.toggle(`prism-series-focused`,n>=0&&i.length>1);for(let[e,t]of i.entries())t.classList.toggle(`prism-series-active`,e===n)},clear=()=>{o.crosshair?.hide(),o.tooltip?.hide(),o.announcer.clear(),highlight([],-1),c=!1,o.onHover?.(null)},eventFor=(e,t,n)=>{let r=entriesAt(e),i=renderedHit(n),a=i?.seriesIndex??nearestIndex(r,t);i&&(r[a]={datum:i.datum,point:i.point});let s=o.getSeriesList(),c=r[a]?.datum;if(a<0||!c)return null;let l=r.map((e,t)=>({datum:e.datum,series:s[t]}));return{entries:r,event:{datum:c,originalEvent:n,series:s[a],values:l},index:a}},focus=(e,i,a)=>{let s=eventFor(e,i?.y??NaN,a);if(!s)return;let{entries:l,event:u,index:d}=s,f=l[d].point,p=o.dims(),m=r.chartArea(p.width,p.height,p.margin),h=o.crosshair?.snap===!1&&i,g=keyLabel(e),_=u.values??[],v=o.colors();o.crosshair?.show(h?i.x:f.x,h?i.y:f.y,m.width,m.height),highlight(l,d);let y=t.describeValues(g,_);if(o.tooltip){let e=_.flatMap(({datum:e,series:t},n)=>e?[{color:v[n],name:t.name,value:String(e.value)}]:[]);o.tooltip.show(f.x+p.margin.left,f.y+p.margin.top,u.datum,u.series,n.comparisonContent(o.svg.ownerDocument,g,e,y))}else o.announcer.announce(y);c=!0,o.onHover?.(u)},pointerKey=e=>{let t=renderedHit(e);if(t)return{key:t.datum.key,pos:t.point};let n=o.getPoints();if(n.every(e=>e.length===0))return null;let i=o.dims(),s=a.getMousePosition(o.svg,e,i.margin.left,i.margin.top),c=r.chartArea(i.width,i.height,i.margin);if(s.x<0||s.x>c.width||s.y<0||s.y>c.height)return null;let l=findNearestKey(o.getData(),n,s.x);return l===null?null:{key:l,pos:s}},sortedKeys=()=>[...new Map(o.getData().flat().map(e=>[i.keyId(e.key),e.key])).values()].sort((e,t)=>keyOrder(e)-keyOrder(t)),onMouseMove=e=>{let t=pointerKey(e);t?focus(t.key,t.pos,e):c&&clear()},onClick=e=>{if(!o.onClick)return;let t=pointerKey(e),n=t&&eventFor(t.key,t.pos.y,e);n&&o.onClick(n.event)},onKeyDown=e=>{let t=sortedKeys();if(t.length===0)return;let n=t.length-1,r={ArrowLeft:s<0?n:Math.max(0,s-1),ArrowRight:s<0?0:Math.min(n,s+1),End:n,Home:0};if(e.key in r)e.preventDefault(),s=r[e.key],focus(t[s],null,e);else if((e.key===`Enter`||e.key===` `)&&s>=0&&o.onClick){e.preventDefault();let n=eventFor(t[s],NaN,e);n&&o.onClick(n.event)}else e.key===`Escape`&&(s=-1,clear())};return{onClick,onKeyDown,onMouseLeave:clear,onMouseMove}}function ensureMarkerGroup(t,n){if(n&&t.contains(n))return n;let r=e.createSvgElement(`g`,{"aria-hidden":`true`,class:`prism-active-points`,"pointer-events":`none`});return t.appendChild(r),r}exports.createSeriesInteraction=createSeriesInteraction,exports.ensureMarkerGroup=ensureMarkerGroup,exports.keyLabel=keyLabel;
1
+ const e=require("../svg/element.cjs"),t=require("./announcer.cjs"),n=require("./tooltip.cjs"),r=require("../core/layout.cjs"),i=require("../core/cartesian-model.cjs"),a=require("./events.cjs");var keyOrder=e=>e instanceof Date?e.getTime():Number(e);function keyLabel(e){return e instanceof Date?e.toLocaleDateString():String(e)}function findNearestKey(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 createSeriesInteraction(o){let s=-1,c=!1,renderedHit=e=>{let t=e.target;if(!(t instanceof SVGElement)||t.tagName.toLowerCase()!==`circle`||!o.seriesGroup.contains(t)||!t.parentElement)return null;let n=t.parentElement;for(;n&&n.parentNode!==o.seriesGroup;)n=n.parentElement;if(!n)return null;let r=[...o.seriesGroup.children].indexOf(n),i=[...t.parentElement.children].indexOf(t),a=o.getData()[r]?.[i],s=o.getPoints()[r]?.[i];return a&&s?{datum:a,point:s,seriesIndex:r}:null},entriesAt=e=>{let t=i.keyId(e),n=o.getPoints();return o.getData().map((e,r)=>{let a=e.findIndex(e=>i.keyId(e.key)===t);return a===-1?{datum:void 0,point:void 0}:{datum:e[a],point:n[r]?.[a]}})},nearestIndex=(e,t)=>{let n=-1,r=1/0;return e.forEach(({point:e},i)=>{if(!e)return;let a=Number.isFinite(t)?Math.abs(e.y-t):0;a<r&&(r=a,n=i)}),n},highlight=(t,n)=>{let r=o.colors();o.markers.replaceChildren(),t.forEach(({point:t},n)=>{t&&o.markers.appendChild(e.createSvgElement(`circle`,{class:`prism-active-point`,cx:t.x,cy:t.y,fill:r[n],r:4}))});let i=[...o.seriesGroup.children];o.seriesGroup.classList.toggle(`prism-series-focused`,n>=0&&i.length>1);for(let[e,t]of i.entries())t.classList.toggle(`prism-series-active`,e===n)},clear=()=>{o.crosshair?.hide(),o.tooltip?.hide(),o.announcer.clear(),highlight([],-1),c=!1,o.onHover?.(null)},eventFor=(e,t,n)=>{let r=entriesAt(e),i=renderedHit(n),a=i?.seriesIndex??nearestIndex(r,t);i&&(r[a]={datum:i.datum,point:i.point});let s=o.getSeriesList(),c=r[a]?.datum;if(a<0||!c)return null;let l=r.map((e,t)=>({datum:e.datum,series:s[t]}));return{entries:r,event:{datum:c,originalEvent:n,series:s[a],values:l},index:a}},focus=(e,i,a)=>{let s=eventFor(e,i?.y??NaN,a);if(!s)return;let{entries:l,event:u,index:d}=s,f=l[d].point,p=o.dims(),m=r.chartArea(p.width,p.height,o.margin),h=o.crosshair?.snap===!1&&i,g=keyLabel(e),_=u.values??[],v=o.colors();o.crosshair?.show(h?i.x:f.x,h?i.y:f.y,m.width,m.height),highlight(l,d);let y=t.describeValues(g,_);if(o.tooltip){let e=_.flatMap(({datum:e,series:t},n)=>e?[{color:v[n],name:t.name,value:String(e.value)}]:[]);o.tooltip.show(f.x+o.margin.left,f.y+o.margin.top,u.datum,u.series,n.comparisonContent(o.svg.ownerDocument,g,e,y))}else o.announcer.announce(y);c=!0,o.onHover?.(u)},pointerKey=e=>{let t=renderedHit(e);if(t)return{key:t.datum.key,pos:t.point};let n=o.getPoints();if(n.every(e=>e.length===0))return null;let i=o.dims(),s=a.getMousePosition(o.svg,e,o.margin.left,o.margin.top),c=r.chartArea(i.width,i.height,o.margin);if(s.x<0||s.x>c.width||s.y<0||s.y>c.height)return null;let l=findNearestKey(o.getData(),n,s.x);return l===null?null:{key:l,pos:s}},sortedKeys=()=>[...new Map(o.getData().flat().map(e=>[i.keyId(e.key),e.key])).values()].sort((e,t)=>keyOrder(e)-keyOrder(t)),onMouseMove=e=>{let t=pointerKey(e);t?focus(t.key,t.pos,e):c&&clear()},onClick=e=>{if(!o.onClick)return;let t=pointerKey(e),n=t&&eventFor(t.key,t.pos.y,e);n&&o.onClick(n.event)},onKeyDown=e=>{let t=sortedKeys();if(t.length===0)return;let n=t.length-1,r={ArrowLeft:s<0?n:Math.max(0,s-1),ArrowRight:s<0?0:Math.min(n,s+1),End:n,Home:0};if(e.key in r)e.preventDefault(),s=r[e.key],focus(t[s],null,e);else if((e.key===`Enter`||e.key===` `)&&s>=0&&o.onClick){e.preventDefault();let n=eventFor(t[s],NaN,e);n&&o.onClick(n.event)}else e.key===`Escape`&&(s=-1,clear())};return{onClick,onKeyDown,onMouseLeave:clear,onMouseMove}}function ensureMarkerGroup(t,n){if(n&&t.contains(n))return n;let r=e.createSvgElement(`g`,{"aria-hidden":`true`,class:`prism-active-points`,"pointer-events":`none`});return t.appendChild(r),r}exports.createSeriesInteraction=createSeriesInteraction,exports.ensureMarkerGroup=ensureMarkerGroup,exports.keyLabel=keyLabel;
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 { createSvgElement } from '../svg/element';\nimport type { Point } from '../svg/path';\nimport type { ChartDimensions, ChartEvent, Datum, Series, SeriesValue } from '../types';\nimport { type Announcer, describeValues } from './announcer';\nimport type { CrosshairState } from './crosshair';\nimport { getMousePosition } from './events';\nimport { comparisonContent, type TooltipState } from './tooltip';\n\nexport interface SeriesInteractionOptions {\n announcer: Announcer;\n colors: () => string[];\n crosshair?: CrosshairState | null;\n dims: () => ChartDimensions;\n getData: () => Datum[][];\n getPoints: () => Point[][];\n getSeriesList: () => Series[];\n /** Receives one marker per series at the active key. */\n markers: SVGGElement;\n onClick?: ((event: ChartEvent) => void) | undefined;\n onHover?: ((event: ChartEvent | null) => void) | undefined;\n /** Parent of the per-series groups; the nearest series is emphasised and the rest dim. */\n seriesGroup: SVGGElement;\n svg: SVGSVGElement;\n tooltip?: TooltipState | null;\n}\n\ninterface KeyEntry {\n datum: Datum | undefined;\n point: Point | undefined;\n}\n\nconst keyOrder = (key: Datum['key']): number => (key instanceof Date ? key.getTime() : Number(key));\n\nexport function keyLabel(key: Datum['key']): string {\n return key instanceof Date ? key.toLocaleDateString() : String(key);\n}\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\nexport function createSeriesInteraction(opts: SeriesInteractionOptions): ChartEventHandlers {\n let keyboardIndex = -1;\n let active = false;\n\n const renderedHit = (event: Event): { datum: Datum; point: Point; seriesIndex: number } | null => {\n const target = event.target;\n if (\n !(target instanceof SVGElement) ||\n target.tagName.toLowerCase() !== 'circle' ||\n !opts.seriesGroup.contains(target) ||\n !target.parentElement\n )\n return null;\n let group: Element | null = target.parentElement;\n while (group && group.parentNode !== opts.seriesGroup) group = group.parentElement;\n if (!group) return null;\n const seriesIndex = [...opts.seriesGroup.children].indexOf(group);\n const datumIndex = [...target.parentElement.children].indexOf(target);\n const datum = opts.getData()[seriesIndex]?.[datumIndex];\n const point = opts.getPoints()[seriesIndex]?.[datumIndex];\n return datum && point ? { datum, point, seriesIndex } : null;\n };\n\n const entriesAt = (key: Datum['key']): KeyEntry[] => {\n const id = keyId(key);\n const allPoints = opts.getPoints();\n\n return opts.getData().map((data, seriesIndex) => {\n const datumIndex = data.findIndex((datum) => keyId(datum.key) === id);\n\n return datumIndex === -1\n ? { datum: undefined, point: undefined }\n : { datum: data[datumIndex], point: allPoints[seriesIndex]?.[datumIndex] };\n });\n };\n\n const nearestIndex = (entries: KeyEntry[], posY: number): number => {\n let best = -1;\n let minDistance = Infinity;\n\n entries.forEach(({ point }, index) => {\n if (!point) return;\n\n const distance = Number.isFinite(posY) ? Math.abs(point.y - posY) : 0;\n\n if (distance < minDistance) {\n minDistance = distance;\n best = index;\n }\n });\n\n return best;\n };\n\n const highlight = (entries: KeyEntry[], emphasised: number): void => {\n const colors = opts.colors();\n\n opts.markers.replaceChildren();\n entries.forEach(({ point }, index) => {\n if (!point) return;\n\n opts.markers.appendChild(\n createSvgElement('circle', {\n class: 'prism-active-point',\n cx: point.x,\n cy: point.y,\n fill: colors[index],\n r: 4,\n }),\n );\n });\n\n const groups = [...opts.seriesGroup.children];\n\n opts.seriesGroup.classList.toggle('prism-series-focused', emphasised >= 0 && groups.length > 1);\n for (const [index, group] of groups.entries()) group.classList.toggle('prism-series-active', index === emphasised);\n };\n\n const clear = (): void => {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n opts.announcer.clear();\n highlight([], -1);\n active = false;\n opts.onHover?.(null);\n };\n\n const eventFor = (\n key: Datum['key'],\n posY: number,\n originalEvent: Event,\n ): { entries: KeyEntry[]; event: ChartEvent; index: number } | null => {\n const entries = entriesAt(key);\n const hit = renderedHit(originalEvent);\n const index = hit?.seriesIndex ?? nearestIndex(entries, posY);\n if (hit) entries[index] = { datum: hit.datum, point: hit.point };\n const seriesList = opts.getSeriesList();\n const datum = entries[index]?.datum;\n\n if (index < 0 || !datum) return null;\n\n const values: SeriesValue[] = entries.map((entry, i) => ({ datum: entry.datum, series: seriesList[i] }));\n\n return { entries, event: { datum, originalEvent, series: seriesList[index], values }, index };\n };\n\n const focus = (key: Datum['key'], pos: Point | null, originalEvent: Event): void => {\n const found = eventFor(key, pos?.y ?? Number.NaN, originalEvent);\n\n if (!found) return;\n\n const { entries, event, index } = found;\n const point = entries[index].point!;\n const dims = opts.dims();\n const area = chartArea(dims.width, dims.height, dims.margin);\n const raw = opts.crosshair?.snap === false && pos;\n const label = keyLabel(key);\n const values = event.values ?? [];\n const colors = opts.colors();\n\n opts.crosshair?.show(raw ? pos.x : point.x, raw ? pos.y : point.y, area.width, area.height);\n highlight(entries, index);\n\n const spoken = describeValues(label, values);\n\n if (opts.tooltip) {\n const rows = values.flatMap(({ datum, series }, i) =>\n datum ? [{ color: colors[i], name: series.name, value: String(datum.value) }] : [],\n );\n\n opts.tooltip.show(\n point.x + dims.margin.left,\n point.y + dims.margin.top,\n event.datum,\n event.series,\n comparisonContent(opts.svg.ownerDocument, label, rows, spoken),\n );\n } else {\n opts.announcer.announce(spoken);\n }\n\n active = true;\n opts.onHover?.(event);\n };\n\n const pointerKey = (event: MouseEvent): { key: Datum['key']; pos: Point } | null => {\n const rendered = renderedHit(event);\n if (rendered) return { key: rendered.datum.key, pos: rendered.point };\n const allPoints = opts.getPoints();\n\n if (allPoints.every((points) => points.length === 0)) return null;\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 null;\n\n const key = findNearestKey(opts.getData(), allPoints, pos.x);\n\n return key === null ? null : { key, pos };\n };\n\n const sortedKeys = (): Datum['key'][] =>\n [\n ...new Map(\n opts\n .getData()\n .flat()\n .map((datum) => [keyId(datum.key), datum.key]),\n ).values(),\n ].sort((a, b) => keyOrder(a) - keyOrder(b));\n\n const onMouseMove = (event: MouseEvent): void => {\n const hit = pointerKey(event);\n\n if (hit) focus(hit.key, hit.pos, event);\n else if (active) clear();\n };\n\n const onClick = (event: MouseEvent): void => {\n if (!opts.onClick) return;\n\n const hit = pointerKey(event);\n const found = hit && eventFor(hit.key, hit.pos.y, event);\n\n if (found) opts.onClick(found.event);\n };\n\n const onKeyDown = (event: KeyboardEvent): void => {\n const keys = sortedKeys();\n\n if (keys.length === 0) return;\n\n const last = keys.length - 1;\n const target: Record<string, number> = {\n ArrowLeft: keyboardIndex < 0 ? last : Math.max(0, keyboardIndex - 1),\n ArrowRight: keyboardIndex < 0 ? 0 : Math.min(last, keyboardIndex + 1),\n End: last,\n Home: 0,\n };\n\n if (event.key in target) {\n event.preventDefault();\n keyboardIndex = target[event.key];\n focus(keys[keyboardIndex], null, event);\n } else if ((event.key === 'Enter' || event.key === ' ') && keyboardIndex >= 0 && opts.onClick) {\n event.preventDefault();\n\n const found = eventFor(keys[keyboardIndex], Number.NaN, event);\n\n if (found) opts.onClick(found.event);\n } else if (event.key === 'Escape') {\n keyboardIndex = -1;\n clear();\n }\n };\n\n return { onClick, onKeyDown, onMouseLeave: clear, onMouseMove };\n}\n\n/** Group for the per-series active markers, reused across renders. */\nexport function ensureMarkerGroup(parent: SVGGElement, current: SVGGElement | null): SVGGElement {\n if (current && parent.contains(current)) return current;\n\n const group = createSvgElement('g', {\n 'aria-hidden': 'true',\n class: 'prism-active-points',\n 'pointer-events': 'none',\n });\n\n parent.appendChild(group);\n\n return group;\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 { createSvgElement } from '../svg/element';\nimport type { Point } from '../svg/path';\nimport type { ChartDimensions, ChartEvent, ChartMargin, Datum, Series, SeriesValue } from '../types';\nimport { type Announcer, describeValues } from './announcer';\nimport type { CrosshairState } from './crosshair';\nimport { getMousePosition } from './events';\nimport { comparisonContent, type TooltipState } from './tooltip';\n\nexport interface SeriesInteractionOptions {\n announcer: Announcer;\n colors: () => string[];\n crosshair?: CrosshairState | null;\n dims: () => ChartDimensions;\n getData: () => Datum[][];\n getPoints: () => Point[][];\n getSeriesList: () => Series[];\n /** Effective chart margin (including any dual-axis widening): hit bounds and tooltip offsets use this, never the shared dimensions. */\n margin: ChartMargin;\n /** Receives one marker per series at the active key. */\n markers: SVGGElement;\n onClick?: ((event: ChartEvent) => void) | undefined;\n onHover?: ((event: ChartEvent | null) => void) | undefined;\n /** Parent of the per-series groups; the nearest series is emphasised and the rest dim. */\n seriesGroup: SVGGElement;\n svg: SVGSVGElement;\n tooltip?: TooltipState | null;\n}\n\ninterface KeyEntry {\n datum: Datum | undefined;\n point: Point | undefined;\n}\n\nconst keyOrder = (key: Datum['key']): number => (key instanceof Date ? key.getTime() : Number(key));\n\nexport function keyLabel(key: Datum['key']): string {\n return key instanceof Date ? key.toLocaleDateString() : String(key);\n}\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\nexport function createSeriesInteraction(opts: SeriesInteractionOptions): ChartEventHandlers {\n let keyboardIndex = -1;\n let active = false;\n\n const renderedHit = (event: Event): { datum: Datum; point: Point; seriesIndex: number } | null => {\n const target = event.target;\n if (\n !(target instanceof SVGElement) ||\n target.tagName.toLowerCase() !== 'circle' ||\n !opts.seriesGroup.contains(target) ||\n !target.parentElement\n )\n return null;\n let group: Element | null = target.parentElement;\n while (group && group.parentNode !== opts.seriesGroup) group = group.parentElement;\n if (!group) return null;\n const seriesIndex = [...opts.seriesGroup.children].indexOf(group);\n const datumIndex = [...target.parentElement.children].indexOf(target);\n const datum = opts.getData()[seriesIndex]?.[datumIndex];\n const point = opts.getPoints()[seriesIndex]?.[datumIndex];\n return datum && point ? { datum, point, seriesIndex } : null;\n };\n\n const entriesAt = (key: Datum['key']): KeyEntry[] => {\n const id = keyId(key);\n const allPoints = opts.getPoints();\n\n return opts.getData().map((data, seriesIndex) => {\n const datumIndex = data.findIndex((datum) => keyId(datum.key) === id);\n\n return datumIndex === -1\n ? { datum: undefined, point: undefined }\n : { datum: data[datumIndex], point: allPoints[seriesIndex]?.[datumIndex] };\n });\n };\n\n const nearestIndex = (entries: KeyEntry[], posY: number): number => {\n let best = -1;\n let minDistance = Infinity;\n\n entries.forEach(({ point }, index) => {\n if (!point) return;\n\n const distance = Number.isFinite(posY) ? Math.abs(point.y - posY) : 0;\n\n if (distance < minDistance) {\n minDistance = distance;\n best = index;\n }\n });\n\n return best;\n };\n\n const highlight = (entries: KeyEntry[], emphasised: number): void => {\n const colors = opts.colors();\n\n opts.markers.replaceChildren();\n entries.forEach(({ point }, index) => {\n if (!point) return;\n\n opts.markers.appendChild(\n createSvgElement('circle', {\n class: 'prism-active-point',\n cx: point.x,\n cy: point.y,\n fill: colors[index],\n r: 4,\n }),\n );\n });\n\n const groups = [...opts.seriesGroup.children];\n\n opts.seriesGroup.classList.toggle('prism-series-focused', emphasised >= 0 && groups.length > 1);\n for (const [index, group] of groups.entries()) group.classList.toggle('prism-series-active', index === emphasised);\n };\n\n const clear = (): void => {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n opts.announcer.clear();\n highlight([], -1);\n active = false;\n opts.onHover?.(null);\n };\n\n const eventFor = (\n key: Datum['key'],\n posY: number,\n originalEvent: Event,\n ): { entries: KeyEntry[]; event: ChartEvent; index: number } | null => {\n const entries = entriesAt(key);\n const hit = renderedHit(originalEvent);\n const index = hit?.seriesIndex ?? nearestIndex(entries, posY);\n if (hit) entries[index] = { datum: hit.datum, point: hit.point };\n const seriesList = opts.getSeriesList();\n const datum = entries[index]?.datum;\n\n if (index < 0 || !datum) return null;\n\n const values: SeriesValue[] = entries.map((entry, i) => ({ datum: entry.datum, series: seriesList[i] }));\n\n return { entries, event: { datum, originalEvent, series: seriesList[index], values }, index };\n };\n\n const focus = (key: Datum['key'], pos: Point | null, originalEvent: Event): void => {\n const found = eventFor(key, pos?.y ?? Number.NaN, originalEvent);\n\n if (!found) return;\n\n const { entries, event, index } = found;\n const point = entries[index].point!;\n const dims = opts.dims();\n const area = chartArea(dims.width, dims.height, opts.margin);\n const raw = opts.crosshair?.snap === false && pos;\n const label = keyLabel(key);\n const values = event.values ?? [];\n const colors = opts.colors();\n\n opts.crosshair?.show(raw ? pos.x : point.x, raw ? pos.y : point.y, area.width, area.height);\n highlight(entries, index);\n\n const spoken = describeValues(label, values);\n\n if (opts.tooltip) {\n const rows = values.flatMap(({ datum, series }, i) =>\n datum ? [{ color: colors[i], name: series.name, value: String(datum.value) }] : [],\n );\n\n opts.tooltip.show(\n point.x + opts.margin.left,\n point.y + opts.margin.top,\n event.datum,\n event.series,\n comparisonContent(opts.svg.ownerDocument, label, rows, spoken),\n );\n } else {\n opts.announcer.announce(spoken);\n }\n\n active = true;\n opts.onHover?.(event);\n };\n\n const pointerKey = (event: MouseEvent): { key: Datum['key']; pos: Point } | null => {\n const rendered = renderedHit(event);\n if (rendered) return { key: rendered.datum.key, pos: rendered.point };\n const allPoints = opts.getPoints();\n\n if (allPoints.every((points) => points.length === 0)) return null;\n\n const dims = opts.dims();\n const pos = getMousePosition(opts.svg, event, opts.margin.left, opts.margin.top);\n const area = chartArea(dims.width, dims.height, opts.margin);\n\n if (pos.x < 0 || pos.x > area.width || pos.y < 0 || pos.y > area.height) return null;\n\n const key = findNearestKey(opts.getData(), allPoints, pos.x);\n\n return key === null ? null : { key, pos };\n };\n\n const sortedKeys = (): Datum['key'][] =>\n [\n ...new Map(\n opts\n .getData()\n .flat()\n .map((datum) => [keyId(datum.key), datum.key]),\n ).values(),\n ].sort((a, b) => keyOrder(a) - keyOrder(b));\n\n const onMouseMove = (event: MouseEvent): void => {\n const hit = pointerKey(event);\n\n if (hit) focus(hit.key, hit.pos, event);\n else if (active) clear();\n };\n\n const onClick = (event: MouseEvent): void => {\n if (!opts.onClick) return;\n\n const hit = pointerKey(event);\n const found = hit && eventFor(hit.key, hit.pos.y, event);\n\n if (found) opts.onClick(found.event);\n };\n\n const onKeyDown = (event: KeyboardEvent): void => {\n const keys = sortedKeys();\n\n if (keys.length === 0) return;\n\n const last = keys.length - 1;\n const target: Record<string, number> = {\n ArrowLeft: keyboardIndex < 0 ? last : Math.max(0, keyboardIndex - 1),\n ArrowRight: keyboardIndex < 0 ? 0 : Math.min(last, keyboardIndex + 1),\n End: last,\n Home: 0,\n };\n\n if (event.key in target) {\n event.preventDefault();\n keyboardIndex = target[event.key];\n focus(keys[keyboardIndex], null, event);\n } else if ((event.key === 'Enter' || event.key === ' ') && keyboardIndex >= 0 && opts.onClick) {\n event.preventDefault();\n\n const found = eventFor(keys[keyboardIndex], Number.NaN, event);\n\n if (found) opts.onClick(found.event);\n } else if (event.key === 'Escape') {\n keyboardIndex = -1;\n clear();\n }\n };\n\n return { onClick, onKeyDown, onMouseLeave: clear, onMouseMove };\n}\n\n/** Group for the per-series active markers, reused across renders. */\nexport function ensureMarkerGroup(parent: SVGGElement, current: SVGGElement | null): SVGGElement {\n if (current && parent.contains(current)) return current;\n\n const group = createSvgElement('g', {\n 'aria-hidden': 'true',\n class: 'prism-active-points',\n 'pointer-events': 'none',\n });\n\n parent.appendChild(group);\n\n return group;\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import type { ChartEventHandlers } from '../core/chart-scaffold';
2
2
  import type { Point } from '../svg/path';
3
- import type { ChartDimensions, ChartEvent, Datum, Series } from '../types';
3
+ import type { ChartDimensions, ChartEvent, ChartMargin, Datum, Series } from '../types';
4
4
  import { type Announcer } from './announcer';
5
5
  import type { CrosshairState } from './crosshair';
6
6
  import { type TooltipState } from './tooltip';
@@ -12,6 +12,8 @@ export interface SeriesInteractionOptions {
12
12
  getData: () => Datum[][];
13
13
  getPoints: () => Point[][];
14
14
  getSeriesList: () => Series[];
15
+ /** Effective chart margin (including any dual-axis widening): hit bounds and tooltip offsets use this, never the shared dimensions. */
16
+ margin: ChartMargin;
15
17
  /** Receives one marker per series at the active key. */
16
18
  markers: SVGGElement;
17
19
  onClick?: ((event: ChartEvent) => void) | undefined;
@@ -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;AAGjE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAe,MAAM,UAAU,CAAC;AACxF,OAAO,EAAE,KAAK,SAAS,EAAkB,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAEjE,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,MAAM,MAAM,EAAE,CAAC;IACvB,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,wDAAwD;IACxD,OAAO,EAAE,WAAW,CAAC;IACrB,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,0FAA0F;IAC1F,WAAW,EAAE,WAAW,CAAC;IACzB,GAAG,EAAE,aAAa,CAAC;IACnB,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CAC/B;AASD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,MAAM,CAElD;AAyBD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,wBAAwB,GAAG,kBAAkB,CA2N1F;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,WAAW,CAY/F"}
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;AAGjE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAe,MAAM,UAAU,CAAC;AACrG,OAAO,EAAE,KAAK,SAAS,EAAkB,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAEjE,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,MAAM,MAAM,EAAE,CAAC;IACvB,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,uIAAuI;IACvI,MAAM,EAAE,WAAW,CAAC;IACpB,wDAAwD;IACxD,OAAO,EAAE,WAAW,CAAC;IACrB,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,0FAA0F;IAC1F,WAAW,EAAE,WAAW,CAAC;IACzB,GAAG,EAAE,aAAa,CAAC;IACnB,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CAC/B;AASD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,MAAM,CAElD;AAyBD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,wBAAwB,GAAG,kBAAkB,CA2N1F;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,WAAW,CAY/F"}
@@ -1,2 +1,2 @@
1
- import{createSvgElement as e}from"../svg/element.js";import{describeValues as t}from"./announcer.js";import{comparisonContent as n}from"./tooltip.js";import{chartArea as r}from"../core/layout.js";import{keyId as i}from"../core/cartesian-model.js";import{getMousePosition as a}from"./events.js";var keyOrder=e=>e instanceof Date?e.getTime():Number(e);function keyLabel(e){return e instanceof Date?e.toLocaleDateString():String(e)}function findNearestKey(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 createSeriesInteraction(o){let s=-1,c=!1,renderedHit=e=>{let t=e.target;if(!(t instanceof SVGElement)||t.tagName.toLowerCase()!==`circle`||!o.seriesGroup.contains(t)||!t.parentElement)return null;let n=t.parentElement;for(;n&&n.parentNode!==o.seriesGroup;)n=n.parentElement;if(!n)return null;let r=[...o.seriesGroup.children].indexOf(n),i=[...t.parentElement.children].indexOf(t),a=o.getData()[r]?.[i],s=o.getPoints()[r]?.[i];return a&&s?{datum:a,point:s,seriesIndex:r}:null},entriesAt=e=>{let t=i(e),n=o.getPoints();return o.getData().map((e,r)=>{let a=e.findIndex(e=>i(e.key)===t);return a===-1?{datum:void 0,point:void 0}:{datum:e[a],point:n[r]?.[a]}})},nearestIndex=(e,t)=>{let n=-1,r=1/0;return e.forEach(({point:e},i)=>{if(!e)return;let a=Number.isFinite(t)?Math.abs(e.y-t):0;a<r&&(r=a,n=i)}),n},highlight=(t,n)=>{let r=o.colors();o.markers.replaceChildren(),t.forEach(({point:t},n)=>{t&&o.markers.appendChild(e(`circle`,{class:`prism-active-point`,cx:t.x,cy:t.y,fill:r[n],r:4}))});let i=[...o.seriesGroup.children];o.seriesGroup.classList.toggle(`prism-series-focused`,n>=0&&i.length>1);for(let[e,t]of i.entries())t.classList.toggle(`prism-series-active`,e===n)},clear=()=>{o.crosshair?.hide(),o.tooltip?.hide(),o.announcer.clear(),highlight([],-1),c=!1,o.onHover?.(null)},eventFor=(e,t,n)=>{let r=entriesAt(e),i=renderedHit(n),a=i?.seriesIndex??nearestIndex(r,t);i&&(r[a]={datum:i.datum,point:i.point});let s=o.getSeriesList(),c=r[a]?.datum;if(a<0||!c)return null;let l=r.map((e,t)=>({datum:e.datum,series:s[t]}));return{entries:r,event:{datum:c,originalEvent:n,series:s[a],values:l},index:a}},focus=(e,i,a)=>{let s=eventFor(e,i?.y??NaN,a);if(!s)return;let{entries:l,event:u,index:d}=s,f=l[d].point,p=o.dims(),m=r(p.width,p.height,p.margin),h=o.crosshair?.snap===!1&&i,g=keyLabel(e),_=u.values??[],v=o.colors();o.crosshair?.show(h?i.x:f.x,h?i.y:f.y,m.width,m.height),highlight(l,d);let y=t(g,_);if(o.tooltip){let e=_.flatMap(({datum:e,series:t},n)=>e?[{color:v[n],name:t.name,value:String(e.value)}]:[]);o.tooltip.show(f.x+p.margin.left,f.y+p.margin.top,u.datum,u.series,n(o.svg.ownerDocument,g,e,y))}else o.announcer.announce(y);c=!0,o.onHover?.(u)},pointerKey=e=>{let t=renderedHit(e);if(t)return{key:t.datum.key,pos:t.point};let n=o.getPoints();if(n.every(e=>e.length===0))return null;let i=o.dims(),s=a(o.svg,e,i.margin.left,i.margin.top),c=r(i.width,i.height,i.margin);if(s.x<0||s.x>c.width||s.y<0||s.y>c.height)return null;let l=findNearestKey(o.getData(),n,s.x);return l===null?null:{key:l,pos:s}},sortedKeys=()=>[...new Map(o.getData().flat().map(e=>[i(e.key),e.key])).values()].sort((e,t)=>keyOrder(e)-keyOrder(t)),onMouseMove=e=>{let t=pointerKey(e);t?focus(t.key,t.pos,e):c&&clear()},onClick=e=>{if(!o.onClick)return;let t=pointerKey(e),n=t&&eventFor(t.key,t.pos.y,e);n&&o.onClick(n.event)},onKeyDown=e=>{let t=sortedKeys();if(t.length===0)return;let n=t.length-1,r={ArrowLeft:s<0?n:Math.max(0,s-1),ArrowRight:s<0?0:Math.min(n,s+1),End:n,Home:0};if(e.key in r)e.preventDefault(),s=r[e.key],focus(t[s],null,e);else if((e.key===`Enter`||e.key===` `)&&s>=0&&o.onClick){e.preventDefault();let n=eventFor(t[s],NaN,e);n&&o.onClick(n.event)}else e.key===`Escape`&&(s=-1,clear())};return{onClick,onKeyDown,onMouseLeave:clear,onMouseMove}}function ensureMarkerGroup(t,n){if(n&&t.contains(n))return n;let r=e(`g`,{"aria-hidden":`true`,class:`prism-active-points`,"pointer-events":`none`});return t.appendChild(r),r}export{createSeriesInteraction,ensureMarkerGroup,keyLabel};
1
+ import{createSvgElement as e}from"../svg/element.js";import{describeValues as t}from"./announcer.js";import{comparisonContent as n}from"./tooltip.js";import{chartArea as r}from"../core/layout.js";import{keyId as i}from"../core/cartesian-model.js";import{getMousePosition as a}from"./events.js";var keyOrder=e=>e instanceof Date?e.getTime():Number(e);function keyLabel(e){return e instanceof Date?e.toLocaleDateString():String(e)}function findNearestKey(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 createSeriesInteraction(o){let s=-1,c=!1,renderedHit=e=>{let t=e.target;if(!(t instanceof SVGElement)||t.tagName.toLowerCase()!==`circle`||!o.seriesGroup.contains(t)||!t.parentElement)return null;let n=t.parentElement;for(;n&&n.parentNode!==o.seriesGroup;)n=n.parentElement;if(!n)return null;let r=[...o.seriesGroup.children].indexOf(n),i=[...t.parentElement.children].indexOf(t),a=o.getData()[r]?.[i],s=o.getPoints()[r]?.[i];return a&&s?{datum:a,point:s,seriesIndex:r}:null},entriesAt=e=>{let t=i(e),n=o.getPoints();return o.getData().map((e,r)=>{let a=e.findIndex(e=>i(e.key)===t);return a===-1?{datum:void 0,point:void 0}:{datum:e[a],point:n[r]?.[a]}})},nearestIndex=(e,t)=>{let n=-1,r=1/0;return e.forEach(({point:e},i)=>{if(!e)return;let a=Number.isFinite(t)?Math.abs(e.y-t):0;a<r&&(r=a,n=i)}),n},highlight=(t,n)=>{let r=o.colors();o.markers.replaceChildren(),t.forEach(({point:t},n)=>{t&&o.markers.appendChild(e(`circle`,{class:`prism-active-point`,cx:t.x,cy:t.y,fill:r[n],r:4}))});let i=[...o.seriesGroup.children];o.seriesGroup.classList.toggle(`prism-series-focused`,n>=0&&i.length>1);for(let[e,t]of i.entries())t.classList.toggle(`prism-series-active`,e===n)},clear=()=>{o.crosshair?.hide(),o.tooltip?.hide(),o.announcer.clear(),highlight([],-1),c=!1,o.onHover?.(null)},eventFor=(e,t,n)=>{let r=entriesAt(e),i=renderedHit(n),a=i?.seriesIndex??nearestIndex(r,t);i&&(r[a]={datum:i.datum,point:i.point});let s=o.getSeriesList(),c=r[a]?.datum;if(a<0||!c)return null;let l=r.map((e,t)=>({datum:e.datum,series:s[t]}));return{entries:r,event:{datum:c,originalEvent:n,series:s[a],values:l},index:a}},focus=(e,i,a)=>{let s=eventFor(e,i?.y??NaN,a);if(!s)return;let{entries:l,event:u,index:d}=s,f=l[d].point,p=o.dims(),m=r(p.width,p.height,o.margin),h=o.crosshair?.snap===!1&&i,g=keyLabel(e),_=u.values??[],v=o.colors();o.crosshair?.show(h?i.x:f.x,h?i.y:f.y,m.width,m.height),highlight(l,d);let y=t(g,_);if(o.tooltip){let e=_.flatMap(({datum:e,series:t},n)=>e?[{color:v[n],name:t.name,value:String(e.value)}]:[]);o.tooltip.show(f.x+o.margin.left,f.y+o.margin.top,u.datum,u.series,n(o.svg.ownerDocument,g,e,y))}else o.announcer.announce(y);c=!0,o.onHover?.(u)},pointerKey=e=>{let t=renderedHit(e);if(t)return{key:t.datum.key,pos:t.point};let n=o.getPoints();if(n.every(e=>e.length===0))return null;let i=o.dims(),s=a(o.svg,e,o.margin.left,o.margin.top),c=r(i.width,i.height,o.margin);if(s.x<0||s.x>c.width||s.y<0||s.y>c.height)return null;let l=findNearestKey(o.getData(),n,s.x);return l===null?null:{key:l,pos:s}},sortedKeys=()=>[...new Map(o.getData().flat().map(e=>[i(e.key),e.key])).values()].sort((e,t)=>keyOrder(e)-keyOrder(t)),onMouseMove=e=>{let t=pointerKey(e);t?focus(t.key,t.pos,e):c&&clear()},onClick=e=>{if(!o.onClick)return;let t=pointerKey(e),n=t&&eventFor(t.key,t.pos.y,e);n&&o.onClick(n.event)},onKeyDown=e=>{let t=sortedKeys();if(t.length===0)return;let n=t.length-1,r={ArrowLeft:s<0?n:Math.max(0,s-1),ArrowRight:s<0?0:Math.min(n,s+1),End:n,Home:0};if(e.key in r)e.preventDefault(),s=r[e.key],focus(t[s],null,e);else if((e.key===`Enter`||e.key===` `)&&s>=0&&o.onClick){e.preventDefault();let n=eventFor(t[s],NaN,e);n&&o.onClick(n.event)}else e.key===`Escape`&&(s=-1,clear())};return{onClick,onKeyDown,onMouseLeave:clear,onMouseMove}}function ensureMarkerGroup(t,n){if(n&&t.contains(n))return n;let r=e(`g`,{"aria-hidden":`true`,class:`prism-active-points`,"pointer-events":`none`});return t.appendChild(r),r}export{createSeriesInteraction,ensureMarkerGroup,keyLabel};
2
2
  //# sourceMappingURL=series-interaction.js.map
@@ -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 { createSvgElement } from '../svg/element';\nimport type { Point } from '../svg/path';\nimport type { ChartDimensions, ChartEvent, Datum, Series, SeriesValue } from '../types';\nimport { type Announcer, describeValues } from './announcer';\nimport type { CrosshairState } from './crosshair';\nimport { getMousePosition } from './events';\nimport { comparisonContent, type TooltipState } from './tooltip';\n\nexport interface SeriesInteractionOptions {\n announcer: Announcer;\n colors: () => string[];\n crosshair?: CrosshairState | null;\n dims: () => ChartDimensions;\n getData: () => Datum[][];\n getPoints: () => Point[][];\n getSeriesList: () => Series[];\n /** Receives one marker per series at the active key. */\n markers: SVGGElement;\n onClick?: ((event: ChartEvent) => void) | undefined;\n onHover?: ((event: ChartEvent | null) => void) | undefined;\n /** Parent of the per-series groups; the nearest series is emphasised and the rest dim. */\n seriesGroup: SVGGElement;\n svg: SVGSVGElement;\n tooltip?: TooltipState | null;\n}\n\ninterface KeyEntry {\n datum: Datum | undefined;\n point: Point | undefined;\n}\n\nconst keyOrder = (key: Datum['key']): number => (key instanceof Date ? key.getTime() : Number(key));\n\nexport function keyLabel(key: Datum['key']): string {\n return key instanceof Date ? key.toLocaleDateString() : String(key);\n}\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\nexport function createSeriesInteraction(opts: SeriesInteractionOptions): ChartEventHandlers {\n let keyboardIndex = -1;\n let active = false;\n\n const renderedHit = (event: Event): { datum: Datum; point: Point; seriesIndex: number } | null => {\n const target = event.target;\n if (\n !(target instanceof SVGElement) ||\n target.tagName.toLowerCase() !== 'circle' ||\n !opts.seriesGroup.contains(target) ||\n !target.parentElement\n )\n return null;\n let group: Element | null = target.parentElement;\n while (group && group.parentNode !== opts.seriesGroup) group = group.parentElement;\n if (!group) return null;\n const seriesIndex = [...opts.seriesGroup.children].indexOf(group);\n const datumIndex = [...target.parentElement.children].indexOf(target);\n const datum = opts.getData()[seriesIndex]?.[datumIndex];\n const point = opts.getPoints()[seriesIndex]?.[datumIndex];\n return datum && point ? { datum, point, seriesIndex } : null;\n };\n\n const entriesAt = (key: Datum['key']): KeyEntry[] => {\n const id = keyId(key);\n const allPoints = opts.getPoints();\n\n return opts.getData().map((data, seriesIndex) => {\n const datumIndex = data.findIndex((datum) => keyId(datum.key) === id);\n\n return datumIndex === -1\n ? { datum: undefined, point: undefined }\n : { datum: data[datumIndex], point: allPoints[seriesIndex]?.[datumIndex] };\n });\n };\n\n const nearestIndex = (entries: KeyEntry[], posY: number): number => {\n let best = -1;\n let minDistance = Infinity;\n\n entries.forEach(({ point }, index) => {\n if (!point) return;\n\n const distance = Number.isFinite(posY) ? Math.abs(point.y - posY) : 0;\n\n if (distance < minDistance) {\n minDistance = distance;\n best = index;\n }\n });\n\n return best;\n };\n\n const highlight = (entries: KeyEntry[], emphasised: number): void => {\n const colors = opts.colors();\n\n opts.markers.replaceChildren();\n entries.forEach(({ point }, index) => {\n if (!point) return;\n\n opts.markers.appendChild(\n createSvgElement('circle', {\n class: 'prism-active-point',\n cx: point.x,\n cy: point.y,\n fill: colors[index],\n r: 4,\n }),\n );\n });\n\n const groups = [...opts.seriesGroup.children];\n\n opts.seriesGroup.classList.toggle('prism-series-focused', emphasised >= 0 && groups.length > 1);\n for (const [index, group] of groups.entries()) group.classList.toggle('prism-series-active', index === emphasised);\n };\n\n const clear = (): void => {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n opts.announcer.clear();\n highlight([], -1);\n active = false;\n opts.onHover?.(null);\n };\n\n const eventFor = (\n key: Datum['key'],\n posY: number,\n originalEvent: Event,\n ): { entries: KeyEntry[]; event: ChartEvent; index: number } | null => {\n const entries = entriesAt(key);\n const hit = renderedHit(originalEvent);\n const index = hit?.seriesIndex ?? nearestIndex(entries, posY);\n if (hit) entries[index] = { datum: hit.datum, point: hit.point };\n const seriesList = opts.getSeriesList();\n const datum = entries[index]?.datum;\n\n if (index < 0 || !datum) return null;\n\n const values: SeriesValue[] = entries.map((entry, i) => ({ datum: entry.datum, series: seriesList[i] }));\n\n return { entries, event: { datum, originalEvent, series: seriesList[index], values }, index };\n };\n\n const focus = (key: Datum['key'], pos: Point | null, originalEvent: Event): void => {\n const found = eventFor(key, pos?.y ?? Number.NaN, originalEvent);\n\n if (!found) return;\n\n const { entries, event, index } = found;\n const point = entries[index].point!;\n const dims = opts.dims();\n const area = chartArea(dims.width, dims.height, dims.margin);\n const raw = opts.crosshair?.snap === false && pos;\n const label = keyLabel(key);\n const values = event.values ?? [];\n const colors = opts.colors();\n\n opts.crosshair?.show(raw ? pos.x : point.x, raw ? pos.y : point.y, area.width, area.height);\n highlight(entries, index);\n\n const spoken = describeValues(label, values);\n\n if (opts.tooltip) {\n const rows = values.flatMap(({ datum, series }, i) =>\n datum ? [{ color: colors[i], name: series.name, value: String(datum.value) }] : [],\n );\n\n opts.tooltip.show(\n point.x + dims.margin.left,\n point.y + dims.margin.top,\n event.datum,\n event.series,\n comparisonContent(opts.svg.ownerDocument, label, rows, spoken),\n );\n } else {\n opts.announcer.announce(spoken);\n }\n\n active = true;\n opts.onHover?.(event);\n };\n\n const pointerKey = (event: MouseEvent): { key: Datum['key']; pos: Point } | null => {\n const rendered = renderedHit(event);\n if (rendered) return { key: rendered.datum.key, pos: rendered.point };\n const allPoints = opts.getPoints();\n\n if (allPoints.every((points) => points.length === 0)) return null;\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 null;\n\n const key = findNearestKey(opts.getData(), allPoints, pos.x);\n\n return key === null ? null : { key, pos };\n };\n\n const sortedKeys = (): Datum['key'][] =>\n [\n ...new Map(\n opts\n .getData()\n .flat()\n .map((datum) => [keyId(datum.key), datum.key]),\n ).values(),\n ].sort((a, b) => keyOrder(a) - keyOrder(b));\n\n const onMouseMove = (event: MouseEvent): void => {\n const hit = pointerKey(event);\n\n if (hit) focus(hit.key, hit.pos, event);\n else if (active) clear();\n };\n\n const onClick = (event: MouseEvent): void => {\n if (!opts.onClick) return;\n\n const hit = pointerKey(event);\n const found = hit && eventFor(hit.key, hit.pos.y, event);\n\n if (found) opts.onClick(found.event);\n };\n\n const onKeyDown = (event: KeyboardEvent): void => {\n const keys = sortedKeys();\n\n if (keys.length === 0) return;\n\n const last = keys.length - 1;\n const target: Record<string, number> = {\n ArrowLeft: keyboardIndex < 0 ? last : Math.max(0, keyboardIndex - 1),\n ArrowRight: keyboardIndex < 0 ? 0 : Math.min(last, keyboardIndex + 1),\n End: last,\n Home: 0,\n };\n\n if (event.key in target) {\n event.preventDefault();\n keyboardIndex = target[event.key];\n focus(keys[keyboardIndex], null, event);\n } else if ((event.key === 'Enter' || event.key === ' ') && keyboardIndex >= 0 && opts.onClick) {\n event.preventDefault();\n\n const found = eventFor(keys[keyboardIndex], Number.NaN, event);\n\n if (found) opts.onClick(found.event);\n } else if (event.key === 'Escape') {\n keyboardIndex = -1;\n clear();\n }\n };\n\n return { onClick, onKeyDown, onMouseLeave: clear, onMouseMove };\n}\n\n/** Group for the per-series active markers, reused across renders. */\nexport function ensureMarkerGroup(parent: SVGGElement, current: SVGGElement | null): SVGGElement {\n if (current && parent.contains(current)) return current;\n\n const group = createSvgElement('g', {\n 'aria-hidden': 'true',\n class: 'prism-active-points',\n 'pointer-events': 'none',\n });\n\n parent.appendChild(group);\n\n return group;\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 { createSvgElement } from '../svg/element';\nimport type { Point } from '../svg/path';\nimport type { ChartDimensions, ChartEvent, ChartMargin, Datum, Series, SeriesValue } from '../types';\nimport { type Announcer, describeValues } from './announcer';\nimport type { CrosshairState } from './crosshair';\nimport { getMousePosition } from './events';\nimport { comparisonContent, type TooltipState } from './tooltip';\n\nexport interface SeriesInteractionOptions {\n announcer: Announcer;\n colors: () => string[];\n crosshair?: CrosshairState | null;\n dims: () => ChartDimensions;\n getData: () => Datum[][];\n getPoints: () => Point[][];\n getSeriesList: () => Series[];\n /** Effective chart margin (including any dual-axis widening): hit bounds and tooltip offsets use this, never the shared dimensions. */\n margin: ChartMargin;\n /** Receives one marker per series at the active key. */\n markers: SVGGElement;\n onClick?: ((event: ChartEvent) => void) | undefined;\n onHover?: ((event: ChartEvent | null) => void) | undefined;\n /** Parent of the per-series groups; the nearest series is emphasised and the rest dim. */\n seriesGroup: SVGGElement;\n svg: SVGSVGElement;\n tooltip?: TooltipState | null;\n}\n\ninterface KeyEntry {\n datum: Datum | undefined;\n point: Point | undefined;\n}\n\nconst keyOrder = (key: Datum['key']): number => (key instanceof Date ? key.getTime() : Number(key));\n\nexport function keyLabel(key: Datum['key']): string {\n return key instanceof Date ? key.toLocaleDateString() : String(key);\n}\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\nexport function createSeriesInteraction(opts: SeriesInteractionOptions): ChartEventHandlers {\n let keyboardIndex = -1;\n let active = false;\n\n const renderedHit = (event: Event): { datum: Datum; point: Point; seriesIndex: number } | null => {\n const target = event.target;\n if (\n !(target instanceof SVGElement) ||\n target.tagName.toLowerCase() !== 'circle' ||\n !opts.seriesGroup.contains(target) ||\n !target.parentElement\n )\n return null;\n let group: Element | null = target.parentElement;\n while (group && group.parentNode !== opts.seriesGroup) group = group.parentElement;\n if (!group) return null;\n const seriesIndex = [...opts.seriesGroup.children].indexOf(group);\n const datumIndex = [...target.parentElement.children].indexOf(target);\n const datum = opts.getData()[seriesIndex]?.[datumIndex];\n const point = opts.getPoints()[seriesIndex]?.[datumIndex];\n return datum && point ? { datum, point, seriesIndex } : null;\n };\n\n const entriesAt = (key: Datum['key']): KeyEntry[] => {\n const id = keyId(key);\n const allPoints = opts.getPoints();\n\n return opts.getData().map((data, seriesIndex) => {\n const datumIndex = data.findIndex((datum) => keyId(datum.key) === id);\n\n return datumIndex === -1\n ? { datum: undefined, point: undefined }\n : { datum: data[datumIndex], point: allPoints[seriesIndex]?.[datumIndex] };\n });\n };\n\n const nearestIndex = (entries: KeyEntry[], posY: number): number => {\n let best = -1;\n let minDistance = Infinity;\n\n entries.forEach(({ point }, index) => {\n if (!point) return;\n\n const distance = Number.isFinite(posY) ? Math.abs(point.y - posY) : 0;\n\n if (distance < minDistance) {\n minDistance = distance;\n best = index;\n }\n });\n\n return best;\n };\n\n const highlight = (entries: KeyEntry[], emphasised: number): void => {\n const colors = opts.colors();\n\n opts.markers.replaceChildren();\n entries.forEach(({ point }, index) => {\n if (!point) return;\n\n opts.markers.appendChild(\n createSvgElement('circle', {\n class: 'prism-active-point',\n cx: point.x,\n cy: point.y,\n fill: colors[index],\n r: 4,\n }),\n );\n });\n\n const groups = [...opts.seriesGroup.children];\n\n opts.seriesGroup.classList.toggle('prism-series-focused', emphasised >= 0 && groups.length > 1);\n for (const [index, group] of groups.entries()) group.classList.toggle('prism-series-active', index === emphasised);\n };\n\n const clear = (): void => {\n opts.crosshair?.hide();\n opts.tooltip?.hide();\n opts.announcer.clear();\n highlight([], -1);\n active = false;\n opts.onHover?.(null);\n };\n\n const eventFor = (\n key: Datum['key'],\n posY: number,\n originalEvent: Event,\n ): { entries: KeyEntry[]; event: ChartEvent; index: number } | null => {\n const entries = entriesAt(key);\n const hit = renderedHit(originalEvent);\n const index = hit?.seriesIndex ?? nearestIndex(entries, posY);\n if (hit) entries[index] = { datum: hit.datum, point: hit.point };\n const seriesList = opts.getSeriesList();\n const datum = entries[index]?.datum;\n\n if (index < 0 || !datum) return null;\n\n const values: SeriesValue[] = entries.map((entry, i) => ({ datum: entry.datum, series: seriesList[i] }));\n\n return { entries, event: { datum, originalEvent, series: seriesList[index], values }, index };\n };\n\n const focus = (key: Datum['key'], pos: Point | null, originalEvent: Event): void => {\n const found = eventFor(key, pos?.y ?? Number.NaN, originalEvent);\n\n if (!found) return;\n\n const { entries, event, index } = found;\n const point = entries[index].point!;\n const dims = opts.dims();\n const area = chartArea(dims.width, dims.height, opts.margin);\n const raw = opts.crosshair?.snap === false && pos;\n const label = keyLabel(key);\n const values = event.values ?? [];\n const colors = opts.colors();\n\n opts.crosshair?.show(raw ? pos.x : point.x, raw ? pos.y : point.y, area.width, area.height);\n highlight(entries, index);\n\n const spoken = describeValues(label, values);\n\n if (opts.tooltip) {\n const rows = values.flatMap(({ datum, series }, i) =>\n datum ? [{ color: colors[i], name: series.name, value: String(datum.value) }] : [],\n );\n\n opts.tooltip.show(\n point.x + opts.margin.left,\n point.y + opts.margin.top,\n event.datum,\n event.series,\n comparisonContent(opts.svg.ownerDocument, label, rows, spoken),\n );\n } else {\n opts.announcer.announce(spoken);\n }\n\n active = true;\n opts.onHover?.(event);\n };\n\n const pointerKey = (event: MouseEvent): { key: Datum['key']; pos: Point } | null => {\n const rendered = renderedHit(event);\n if (rendered) return { key: rendered.datum.key, pos: rendered.point };\n const allPoints = opts.getPoints();\n\n if (allPoints.every((points) => points.length === 0)) return null;\n\n const dims = opts.dims();\n const pos = getMousePosition(opts.svg, event, opts.margin.left, opts.margin.top);\n const area = chartArea(dims.width, dims.height, opts.margin);\n\n if (pos.x < 0 || pos.x > area.width || pos.y < 0 || pos.y > area.height) return null;\n\n const key = findNearestKey(opts.getData(), allPoints, pos.x);\n\n return key === null ? null : { key, pos };\n };\n\n const sortedKeys = (): Datum['key'][] =>\n [\n ...new Map(\n opts\n .getData()\n .flat()\n .map((datum) => [keyId(datum.key), datum.key]),\n ).values(),\n ].sort((a, b) => keyOrder(a) - keyOrder(b));\n\n const onMouseMove = (event: MouseEvent): void => {\n const hit = pointerKey(event);\n\n if (hit) focus(hit.key, hit.pos, event);\n else if (active) clear();\n };\n\n const onClick = (event: MouseEvent): void => {\n if (!opts.onClick) return;\n\n const hit = pointerKey(event);\n const found = hit && eventFor(hit.key, hit.pos.y, event);\n\n if (found) opts.onClick(found.event);\n };\n\n const onKeyDown = (event: KeyboardEvent): void => {\n const keys = sortedKeys();\n\n if (keys.length === 0) return;\n\n const last = keys.length - 1;\n const target: Record<string, number> = {\n ArrowLeft: keyboardIndex < 0 ? last : Math.max(0, keyboardIndex - 1),\n ArrowRight: keyboardIndex < 0 ? 0 : Math.min(last, keyboardIndex + 1),\n End: last,\n Home: 0,\n };\n\n if (event.key in target) {\n event.preventDefault();\n keyboardIndex = target[event.key];\n focus(keys[keyboardIndex], null, event);\n } else if ((event.key === 'Enter' || event.key === ' ') && keyboardIndex >= 0 && opts.onClick) {\n event.preventDefault();\n\n const found = eventFor(keys[keyboardIndex], Number.NaN, event);\n\n if (found) opts.onClick(found.event);\n } else if (event.key === 'Escape') {\n keyboardIndex = -1;\n clear();\n }\n };\n\n return { onClick, onKeyDown, onMouseLeave: clear, onMouseMove };\n}\n\n/** Group for the per-series active markers, reused across renders. */\nexport function ensureMarkerGroup(parent: SVGGElement, current: SVGGElement | null): SVGGElement {\n if (current && parent.contains(current)) return current;\n\n const group = createSvgElement('g', {\n 'aria-hidden': 'true',\n class: 'prism-active-points',\n 'pointer-events': 'none',\n });\n\n parent.appendChild(group);\n\n return group;\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- let e=require("@vielzeug/orbit");var t=`position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap`;function comparisonContent(e,n,r,i){let a=e.createElement(`div`),o=e.createElement(`span`),s=e.createElement(`div`),c=e.createElement(`div`);a.className=`prism-tooltip-body`,o.className=`prism-tooltip-sr`,o.style.cssText=t,o.textContent=i,s.setAttribute(`aria-hidden`,`true`),c.className=`prism-tooltip-title`,c.textContent=n,s.appendChild(c);for(let t of r){let n=e.createElement(`div`),r=e.createElement(`span`),i=e.createElement(`span`),a=e.createElement(`span`);n.className=`prism-tooltip-row`,r.className=`prism-tooltip-swatch`,r.style.setProperty(`--prism-swatch`,t.color),i.className=`prism-tooltip-name`,i.textContent=t.name,a.className=`prism-tooltip-value`,a.textContent=t.value,n.append(r,i,a),s.appendChild(n)}return a.append(o,s),a}function createTooltip(t,n){let r=t.style.position,i=getComputedStyle(t).position===`static`;i&&(t.style.position=`relative`);let a=t.ownerDocument.createElement(`div`);a.className=`prism-tooltip`,a.style.position=`absolute`,a.style.pointerEvents=`none`,a.style.top=`0`,a.style.left=`0`,a.setAttribute(`role`,`status`),a.setAttribute(`aria-live`,`polite`),t.appendChild(a);let o=n!==!0&&n?.offset||8,s=n===!0?void 0:n?.render,c=null,l=null,u=!1,cancelClear=()=>{l!==null&&(clearTimeout(l),l=null)},cancelReveal=()=>{c!==null&&(cancelAnimationFrame(c),c=null)},disposeHandle=()=>{cancelReveal(),cancelClear(),a.remove(),i&&t.style.position===`relative`&&(t.style.position=r)};return{dispose:disposeHandle,el:a,hide(){cancelReveal(),cancelClear(),u=!1,a.style.opacity=`0`,l=setTimeout(()=>{l=null,u||(a.textContent=``)},150)},show(n,r,i,l,d){if(!t.isConnected)return;cancelClear();let f=s?s(i,l):d??`${l.name}: ${i.value}`;typeof f==`string`?a.textContent=f:a.replaceChildren(f);let{x:p,y:m}=(0,e.computePosition)({getBoundingClientRect:()=>{let e=t.getBoundingClientRect();return{bottom:e.top+r,height:0,left:e.left+n,right:e.left+n,top:e.top+r,width:0,x:e.left+n,y:e.top+r}}},a,{containingBlock:t,middleware:[(0,e.offset)(o),(0,e.flip)(),(0,e.shift)({padding:8})],placement:`top`});if(a.style.left=`${p}px`,a.style.top=`${m}px`,u){c===null&&(a.style.opacity=`1`);return}u=!0,a.style.opacity=`0`,c=requestAnimationFrame(()=>{c=null,u&&(a.style.opacity=`1`)})},[Symbol.dispose]:disposeHandle}}exports.comparisonContent=comparisonContent,exports.createTooltip=createTooltip;
1
+ let e=require("@vielzeug/orbit");var t=`position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap`;function comparisonContent(e,n,r,i){let a=e.createElement(`div`),o=e.createElement(`span`),s=e.createElement(`div`),c=e.createElement(`div`);a.className=`prism-tooltip-body`,o.className=`prism-tooltip-sr`,o.style.cssText=t,o.textContent=i,s.setAttribute(`aria-hidden`,`true`),c.className=`prism-tooltip-title`,c.textContent=n,s.appendChild(c);for(let t of r){let n=e.createElement(`div`),r=e.createElement(`span`),i=e.createElement(`span`),a=e.createElement(`span`);n.className=`prism-tooltip-row`,r.className=`prism-tooltip-swatch`,r.style.setProperty(`--prism-swatch`,t.color),i.className=`prism-tooltip-name`,i.textContent=t.name,a.className=`prism-tooltip-value`,a.textContent=t.value,n.append(r,i,a),s.appendChild(n)}return a.append(o,s),a}function createTooltip(t,n){let r=t.style.position,i=getComputedStyle(t).position===`static`;i&&(t.style.position=`relative`);let a=t.ownerDocument.createElement(`div`);a.className=`prism-tooltip`,a.style.position=`absolute`,a.style.pointerEvents=`none`,a.style.top=`0`,a.style.left=`0`,a.setAttribute(`role`,`status`),a.setAttribute(`aria-live`,`polite`),t.appendChild(a);let o=n!==!0&&n?.offset||8,s=n===!0?void 0:n?.render,setVisible=e=>{a.classList.toggle(`prism-tooltip--visible`,e),a.style.opacity=e?`1`:`0`},c=null,l=null,u=!1,cancelClear=()=>{l!==null&&(clearTimeout(l),l=null)},cancelReveal=()=>{c!==null&&(cancelAnimationFrame(c),c=null)},disposeHandle=()=>{cancelReveal(),cancelClear(),a.remove(),i&&t.style.position===`relative`&&(t.style.position=r)};return{dispose:disposeHandle,el:a,hide(){cancelReveal(),cancelClear(),u=!1,setVisible(!1),l=setTimeout(()=>{l=null,u||(a.textContent=``)},150)},show(n,r,i,l,d){if(!t.isConnected)return;cancelClear();let f=s?s(i,l):d??`${l.name}: ${i.value}`;typeof f==`string`?a.textContent=f:a.replaceChildren(f);let{x:p,y:m}=(0,e.computePosition)({getBoundingClientRect:()=>{let e=t.getBoundingClientRect();return{bottom:e.top+r,height:0,left:e.left+n,right:e.left+n,top:e.top+r,width:0,x:e.left+n,y:e.top+r}}},a,{containingBlock:t,middleware:[(0,e.offset)(o),(0,e.flip)(),(0,e.shift)({padding:8})],placement:`top`});if(a.style.left=`${p}px`,a.style.top=`${m}px`,u){c===null&&setVisible(!0);return}u=!0,setVisible(!1),c=requestAnimationFrame(()=>{c=null,u&&setVisible(!0)})},[Symbol.dispose]:disposeHandle}}exports.comparisonContent=comparisonContent,exports.createTooltip=createTooltip;
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 type { Datum, Series, TooltipConfig } from '../types';\n\nexport interface TooltipRow {\n color: string;\n name: string;\n value: string;\n}\n\n/** Inline so the spoken text stays hidden even without prism's stylesheet. */\nconst SCREEN_READER_ONLY =\n 'position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap';\n\n/**\n * Title plus one swatch row per series; text only, so data is never parsed as HTML.\n * `spoken` is what the tooltip's live region announces; the visual rows are aria-hidden.\n */\nexport function comparisonContent(\n doc: Document,\n title: string,\n rows: readonly TooltipRow[],\n spoken: string,\n): HTMLElement {\n const body = doc.createElement('div');\n const sr = doc.createElement('span');\n const visual = doc.createElement('div');\n const head = doc.createElement('div');\n\n body.className = 'prism-tooltip-body';\n sr.className = 'prism-tooltip-sr';\n sr.style.cssText = SCREEN_READER_ONLY;\n sr.textContent = spoken;\n visual.setAttribute('aria-hidden', 'true');\n head.className = 'prism-tooltip-title';\n head.textContent = title;\n visual.appendChild(head);\n\n for (const row of rows) {\n const line = doc.createElement('div');\n const swatch = doc.createElement('span');\n const name = doc.createElement('span');\n const value = doc.createElement('span');\n\n line.className = 'prism-tooltip-row';\n swatch.className = 'prism-tooltip-swatch';\n swatch.style.setProperty('--prism-swatch', row.color);\n name.className = 'prism-tooltip-name';\n name.textContent = row.name;\n value.className = 'prism-tooltip-value';\n value.textContent = row.value;\n line.append(swatch, name, value);\n visual.appendChild(line);\n }\n\n body.append(sr, visual);\n\n return body;\n}\n\nexport interface TooltipState {\n dispose(): void;\n el: HTMLDivElement | null;\n hide(): void;\n /** `content` replaces the default `series: value` body when no custom `render` is configured. */\n show(x: number, y: number, datum: Datum, series: Series, content?: Node | string): void;\n [Symbol.dispose](): void;\n}\n\nexport function createTooltip(container: HTMLElement, config?: TooltipConfig | true): TooltipState {\n const previousPosition = container.style.position;\n const changedPosition = getComputedStyle(container).position === 'static';\n\n if (changedPosition) container.style.position = 'relative';\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\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 if (changedPosition && container.style.position === 'relative') container.style.position = previousPosition;\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, content?: Node | string) {\n if (!container.isConnected) return;\n\n cancelClear();\n\n const body = render ? render(datum, series) : (content ?? `${series.name}: ${datum.value}`);\n\n if (typeof body === 'string') el.textContent = body;\n else el.replaceChildren(body);\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":"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"}
1
+ {"version":3,"file":"tooltip.cjs","names":[],"sources":["../../src/interaction/tooltip.ts"],"sourcesContent":["import { computePosition, flip, offset, shift } from '@vielzeug/orbit';\nimport type { Datum, Series, TooltipConfig } from '../types';\n\nexport interface TooltipRow {\n color: string;\n name: string;\n value: string;\n}\n\n/** Inline so the spoken text stays hidden even without prism's stylesheet. */\nconst SCREEN_READER_ONLY =\n 'position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap';\n\n/**\n * Title plus one swatch row per series; text only, so data is never parsed as HTML.\n * `spoken` is what the tooltip's live region announces; the visual rows are aria-hidden.\n */\nexport function comparisonContent(\n doc: Document,\n title: string,\n rows: readonly TooltipRow[],\n spoken: string,\n): HTMLElement {\n const body = doc.createElement('div');\n const sr = doc.createElement('span');\n const visual = doc.createElement('div');\n const head = doc.createElement('div');\n\n body.className = 'prism-tooltip-body';\n sr.className = 'prism-tooltip-sr';\n sr.style.cssText = SCREEN_READER_ONLY;\n sr.textContent = spoken;\n visual.setAttribute('aria-hidden', 'true');\n head.className = 'prism-tooltip-title';\n head.textContent = title;\n visual.appendChild(head);\n\n for (const row of rows) {\n const line = doc.createElement('div');\n const swatch = doc.createElement('span');\n const name = doc.createElement('span');\n const value = doc.createElement('span');\n\n line.className = 'prism-tooltip-row';\n swatch.className = 'prism-tooltip-swatch';\n swatch.style.setProperty('--prism-swatch', row.color);\n name.className = 'prism-tooltip-name';\n name.textContent = row.name;\n value.className = 'prism-tooltip-value';\n value.textContent = row.value;\n line.append(swatch, name, value);\n visual.appendChild(line);\n }\n\n body.append(sr, visual);\n\n return body;\n}\n\nexport interface TooltipState {\n dispose(): void;\n el: HTMLDivElement | null;\n hide(): void;\n /** `content` replaces the default `series: value` body when no custom `render` is configured. */\n show(x: number, y: number, datum: Datum, series: Series, content?: Node | string): void;\n [Symbol.dispose](): void;\n}\n\nexport function createTooltip(container: HTMLElement, config?: TooltipConfig | true): TooltipState {\n const previousPosition = container.style.position;\n const changedPosition = getComputedStyle(container).position === 'static';\n\n if (changedPosition) container.style.position = 'relative';\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\n // Opacity stays inline (the transition is declared in CSS against the token\n // duration); the class carries the rise, which needs to combine with the\n // left/top positioning without fighting it.\n const setVisible = (next: boolean): void => {\n el.classList.toggle('prism-tooltip--visible', next);\n el.style.opacity = next ? '1' : '0';\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 if (changedPosition && container.style.position === 'relative') container.style.position = previousPosition;\n };\n\n return {\n dispose: disposeHandle,\n el,\n hide() {\n cancelReveal();\n cancelClear();\n visible = false;\n setVisible(false);\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, content?: Node | string) {\n if (!container.isConnected) return;\n\n cancelClear();\n\n const body = render ? render(datum, series) : (content ?? `${series.name}: ${datum.value}`);\n\n if (typeof body === 'string') el.textContent = body;\n else el.replaceChildren(body);\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) setVisible(true);\n\n return;\n }\n\n visible = true;\n setVisible(false);\n // One frame at the hidden state first: without it the browser has no\n // starting value to transition from and the tooltip would just appear.\n revealFrame = requestAnimationFrame(() => {\n revealFrame = null;\n\n if (visible) setVisible(true);\n });\n },\n [Symbol.dispose]: disposeHandle,\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../src/interaction/tooltip.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE7D,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,MAAM,EAAE,MAAM,GACb,WAAW,CAmCb;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,IAAI,IAAI,CAAC;IAChB,EAAE,EAAE,cAAc,GAAG,IAAI,CAAC;IAC1B,IAAI,IAAI,IAAI,CAAC;IACb,iGAAiG;IACjG,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IACxF,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,YAAY,CAgHjG"}
1
+ {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../src/interaction/tooltip.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE7D,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,MAAM,EAAE,MAAM,GACb,WAAW,CAmCb;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,IAAI,IAAI,CAAC;IAChB,EAAE,EAAE,cAAc,GAAG,IAAI,CAAC;IAC1B,IAAI,IAAI,IAAI,CAAC;IACb,iGAAiG;IACjG,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IACxF,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,YAAY,CA0HjG"}
@@ -1,2 +1,2 @@
1
- import{computePosition as e,flip as t,offset as n,shift as r}from"@vielzeug/orbit";var i=`position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap`;function comparisonContent(e,t,n,r){let a=e.createElement(`div`),o=e.createElement(`span`),s=e.createElement(`div`),c=e.createElement(`div`);a.className=`prism-tooltip-body`,o.className=`prism-tooltip-sr`,o.style.cssText=i,o.textContent=r,s.setAttribute(`aria-hidden`,`true`),c.className=`prism-tooltip-title`,c.textContent=t,s.appendChild(c);for(let t of n){let n=e.createElement(`div`),r=e.createElement(`span`),i=e.createElement(`span`),a=e.createElement(`span`);n.className=`prism-tooltip-row`,r.className=`prism-tooltip-swatch`,r.style.setProperty(`--prism-swatch`,t.color),i.className=`prism-tooltip-name`,i.textContent=t.name,a.className=`prism-tooltip-value`,a.textContent=t.value,n.append(r,i,a),s.appendChild(n)}return a.append(o,s),a}function createTooltip(i,a){let o=i.style.position,s=getComputedStyle(i).position===`static`;s&&(i.style.position=`relative`);let c=i.ownerDocument.createElement(`div`);c.className=`prism-tooltip`,c.style.position=`absolute`,c.style.pointerEvents=`none`,c.style.top=`0`,c.style.left=`0`,c.setAttribute(`role`,`status`),c.setAttribute(`aria-live`,`polite`),i.appendChild(c);let l=a!==!0&&a?.offset||8,u=a===!0?void 0:a?.render,d=null,f=null,p=!1,cancelClear=()=>{f!==null&&(clearTimeout(f),f=null)},cancelReveal=()=>{d!==null&&(cancelAnimationFrame(d),d=null)},disposeHandle=()=>{cancelReveal(),cancelClear(),c.remove(),s&&i.style.position===`relative`&&(i.style.position=o)};return{dispose:disposeHandle,el:c,hide(){cancelReveal(),cancelClear(),p=!1,c.style.opacity=`0`,f=setTimeout(()=>{f=null,p||(c.textContent=``)},150)},show(a,o,s,f,m){if(!i.isConnected)return;cancelClear();let h=u?u(s,f):m??`${f.name}: ${s.value}`;typeof h==`string`?c.textContent=h:c.replaceChildren(h);let{x:g,y:_}=e({getBoundingClientRect:()=>{let e=i.getBoundingClientRect();return{bottom:e.top+o,height:0,left:e.left+a,right:e.left+a,top:e.top+o,width:0,x:e.left+a,y:e.top+o}}},c,{containingBlock:i,middleware:[n(l),t(),r({padding:8})],placement:`top`});if(c.style.left=`${g}px`,c.style.top=`${_}px`,p){d===null&&(c.style.opacity=`1`);return}p=!0,c.style.opacity=`0`,d=requestAnimationFrame(()=>{d=null,p&&(c.style.opacity=`1`)})},[Symbol.dispose]:disposeHandle}}export{comparisonContent,createTooltip};
1
+ import{computePosition as e,flip as t,offset as n,shift as r}from"@vielzeug/orbit";var i=`position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap`;function comparisonContent(e,t,n,r){let a=e.createElement(`div`),o=e.createElement(`span`),s=e.createElement(`div`),c=e.createElement(`div`);a.className=`prism-tooltip-body`,o.className=`prism-tooltip-sr`,o.style.cssText=i,o.textContent=r,s.setAttribute(`aria-hidden`,`true`),c.className=`prism-tooltip-title`,c.textContent=t,s.appendChild(c);for(let t of n){let n=e.createElement(`div`),r=e.createElement(`span`),i=e.createElement(`span`),a=e.createElement(`span`);n.className=`prism-tooltip-row`,r.className=`prism-tooltip-swatch`,r.style.setProperty(`--prism-swatch`,t.color),i.className=`prism-tooltip-name`,i.textContent=t.name,a.className=`prism-tooltip-value`,a.textContent=t.value,n.append(r,i,a),s.appendChild(n)}return a.append(o,s),a}function createTooltip(i,a){let o=i.style.position,s=getComputedStyle(i).position===`static`;s&&(i.style.position=`relative`);let c=i.ownerDocument.createElement(`div`);c.className=`prism-tooltip`,c.style.position=`absolute`,c.style.pointerEvents=`none`,c.style.top=`0`,c.style.left=`0`,c.setAttribute(`role`,`status`),c.setAttribute(`aria-live`,`polite`),i.appendChild(c);let l=a!==!0&&a?.offset||8,u=a===!0?void 0:a?.render,setVisible=e=>{c.classList.toggle(`prism-tooltip--visible`,e),c.style.opacity=e?`1`:`0`},d=null,f=null,p=!1,cancelClear=()=>{f!==null&&(clearTimeout(f),f=null)},cancelReveal=()=>{d!==null&&(cancelAnimationFrame(d),d=null)},disposeHandle=()=>{cancelReveal(),cancelClear(),c.remove(),s&&i.style.position===`relative`&&(i.style.position=o)};return{dispose:disposeHandle,el:c,hide(){cancelReveal(),cancelClear(),p=!1,setVisible(!1),f=setTimeout(()=>{f=null,p||(c.textContent=``)},150)},show(a,o,s,f,m){if(!i.isConnected)return;cancelClear();let h=u?u(s,f):m??`${f.name}: ${s.value}`;typeof h==`string`?c.textContent=h:c.replaceChildren(h);let{x:g,y:_}=e({getBoundingClientRect:()=>{let e=i.getBoundingClientRect();return{bottom:e.top+o,height:0,left:e.left+a,right:e.left+a,top:e.top+o,width:0,x:e.left+a,y:e.top+o}}},c,{containingBlock:i,middleware:[n(l),t(),r({padding:8})],placement:`top`});if(c.style.left=`${g}px`,c.style.top=`${_}px`,p){d===null&&setVisible(!0);return}p=!0,setVisible(!1),d=requestAnimationFrame(()=>{d=null,p&&setVisible(!0)})},[Symbol.dispose]:disposeHandle}}export{comparisonContent,createTooltip};
2
2
  //# sourceMappingURL=tooltip.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.js","names":[],"sources":["../../src/interaction/tooltip.ts"],"sourcesContent":["import { computePosition, flip, offset, shift } from '@vielzeug/orbit';\nimport type { Datum, Series, TooltipConfig } from '../types';\n\nexport interface TooltipRow {\n color: string;\n name: string;\n value: string;\n}\n\n/** Inline so the spoken text stays hidden even without prism's stylesheet. */\nconst SCREEN_READER_ONLY =\n 'position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap';\n\n/**\n * Title plus one swatch row per series; text only, so data is never parsed as HTML.\n * `spoken` is what the tooltip's live region announces; the visual rows are aria-hidden.\n */\nexport function comparisonContent(\n doc: Document,\n title: string,\n rows: readonly TooltipRow[],\n spoken: string,\n): HTMLElement {\n const body = doc.createElement('div');\n const sr = doc.createElement('span');\n const visual = doc.createElement('div');\n const head = doc.createElement('div');\n\n body.className = 'prism-tooltip-body';\n sr.className = 'prism-tooltip-sr';\n sr.style.cssText = SCREEN_READER_ONLY;\n sr.textContent = spoken;\n visual.setAttribute('aria-hidden', 'true');\n head.className = 'prism-tooltip-title';\n head.textContent = title;\n visual.appendChild(head);\n\n for (const row of rows) {\n const line = doc.createElement('div');\n const swatch = doc.createElement('span');\n const name = doc.createElement('span');\n const value = doc.createElement('span');\n\n line.className = 'prism-tooltip-row';\n swatch.className = 'prism-tooltip-swatch';\n swatch.style.setProperty('--prism-swatch', row.color);\n name.className = 'prism-tooltip-name';\n name.textContent = row.name;\n value.className = 'prism-tooltip-value';\n value.textContent = row.value;\n line.append(swatch, name, value);\n visual.appendChild(line);\n }\n\n body.append(sr, visual);\n\n return body;\n}\n\nexport interface TooltipState {\n dispose(): void;\n el: HTMLDivElement | null;\n hide(): void;\n /** `content` replaces the default `series: value` body when no custom `render` is configured. */\n show(x: number, y: number, datum: Datum, series: Series, content?: Node | string): void;\n [Symbol.dispose](): void;\n}\n\nexport function createTooltip(container: HTMLElement, config?: TooltipConfig | true): TooltipState {\n const previousPosition = container.style.position;\n const changedPosition = getComputedStyle(container).position === 'static';\n\n if (changedPosition) container.style.position = 'relative';\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\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 if (changedPosition && container.style.position === 'relative') container.style.position = previousPosition;\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, content?: Node | string) {\n if (!container.isConnected) return;\n\n cancelClear();\n\n const body = render ? render(datum, series) : (content ?? `${series.name}: ${datum.value}`);\n\n if (typeof body === 'string') el.textContent = body;\n else el.replaceChildren(body);\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":"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"}
1
+ {"version":3,"file":"tooltip.js","names":[],"sources":["../../src/interaction/tooltip.ts"],"sourcesContent":["import { computePosition, flip, offset, shift } from '@vielzeug/orbit';\nimport type { Datum, Series, TooltipConfig } from '../types';\n\nexport interface TooltipRow {\n color: string;\n name: string;\n value: string;\n}\n\n/** Inline so the spoken text stays hidden even without prism's stylesheet. */\nconst SCREEN_READER_ONLY =\n 'position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap';\n\n/**\n * Title plus one swatch row per series; text only, so data is never parsed as HTML.\n * `spoken` is what the tooltip's live region announces; the visual rows are aria-hidden.\n */\nexport function comparisonContent(\n doc: Document,\n title: string,\n rows: readonly TooltipRow[],\n spoken: string,\n): HTMLElement {\n const body = doc.createElement('div');\n const sr = doc.createElement('span');\n const visual = doc.createElement('div');\n const head = doc.createElement('div');\n\n body.className = 'prism-tooltip-body';\n sr.className = 'prism-tooltip-sr';\n sr.style.cssText = SCREEN_READER_ONLY;\n sr.textContent = spoken;\n visual.setAttribute('aria-hidden', 'true');\n head.className = 'prism-tooltip-title';\n head.textContent = title;\n visual.appendChild(head);\n\n for (const row of rows) {\n const line = doc.createElement('div');\n const swatch = doc.createElement('span');\n const name = doc.createElement('span');\n const value = doc.createElement('span');\n\n line.className = 'prism-tooltip-row';\n swatch.className = 'prism-tooltip-swatch';\n swatch.style.setProperty('--prism-swatch', row.color);\n name.className = 'prism-tooltip-name';\n name.textContent = row.name;\n value.className = 'prism-tooltip-value';\n value.textContent = row.value;\n line.append(swatch, name, value);\n visual.appendChild(line);\n }\n\n body.append(sr, visual);\n\n return body;\n}\n\nexport interface TooltipState {\n dispose(): void;\n el: HTMLDivElement | null;\n hide(): void;\n /** `content` replaces the default `series: value` body when no custom `render` is configured. */\n show(x: number, y: number, datum: Datum, series: Series, content?: Node | string): void;\n [Symbol.dispose](): void;\n}\n\nexport function createTooltip(container: HTMLElement, config?: TooltipConfig | true): TooltipState {\n const previousPosition = container.style.position;\n const changedPosition = getComputedStyle(container).position === 'static';\n\n if (changedPosition) container.style.position = 'relative';\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\n // Opacity stays inline (the transition is declared in CSS against the token\n // duration); the class carries the rise, which needs to combine with the\n // left/top positioning without fighting it.\n const setVisible = (next: boolean): void => {\n el.classList.toggle('prism-tooltip--visible', next);\n el.style.opacity = next ? '1' : '0';\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 if (changedPosition && container.style.position === 'relative') container.style.position = previousPosition;\n };\n\n return {\n dispose: disposeHandle,\n el,\n hide() {\n cancelReveal();\n cancelClear();\n visible = false;\n setVisible(false);\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, content?: Node | string) {\n if (!container.isConnected) return;\n\n cancelClear();\n\n const body = render ? render(datum, series) : (content ?? `${series.name}: ${datum.value}`);\n\n if (typeof body === 'string') el.textContent = body;\n else el.replaceChildren(body);\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) setVisible(true);\n\n return;\n }\n\n visible = true;\n setVisible(false);\n // One frame at the hidden state first: without it the browser has no\n // starting value to transition from and the tooltip would just appear.\n revealFrame = requestAnimationFrame(() => {\n revealFrame = null;\n\n if (visible) setVisible(true);\n });\n },\n [Symbol.dispose]: disposeHandle,\n };\n}\n"],"mappings":"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"}