@vielzeug/prism 26.10.4 → 26.10.6

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 (119) hide show
  1. package/README.md +15 -26
  2. package/dist/animation/reveal.cjs +2 -0
  3. package/dist/animation/reveal.cjs.map +1 -0
  4. package/dist/animation/reveal.d.ts +29 -0
  5. package/dist/animation/reveal.d.ts.map +1 -0
  6. package/dist/animation/reveal.js +2 -0
  7. package/dist/animation/reveal.js.map +1 -0
  8. package/dist/axes/axis.cjs +1 -1
  9. package/dist/axes/axis.cjs.map +1 -1
  10. package/dist/axes/axis.d.ts +10 -3
  11. package/dist/axes/axis.d.ts.map +1 -1
  12. package/dist/axes/axis.js +1 -1
  13. package/dist/axes/axis.js.map +1 -1
  14. package/dist/axes/grid.cjs +1 -1
  15. package/dist/axes/grid.cjs.map +1 -1
  16. package/dist/axes/grid.d.ts +1 -1
  17. package/dist/axes/grid.d.ts.map +1 -1
  18. package/dist/axes/grid.js +1 -1
  19. package/dist/axes/grid.js.map +1 -1
  20. package/dist/charts/area/area-chart.cjs +1 -1
  21. package/dist/charts/area/area-chart.cjs.map +1 -1
  22. package/dist/charts/area/area-chart.d.ts.map +1 -1
  23. package/dist/charts/area/area-chart.js +1 -1
  24. package/dist/charts/area/area-chart.js.map +1 -1
  25. package/dist/charts/bar/bar-chart.cjs +1 -1
  26. package/dist/charts/bar/bar-chart.cjs.map +1 -1
  27. package/dist/charts/bar/bar-chart.d.ts.map +1 -1
  28. package/dist/charts/bar/bar-chart.js +1 -1
  29. package/dist/charts/bar/bar-chart.js.map +1 -1
  30. package/dist/charts/bar/bar-renderer.cjs +1 -1
  31. package/dist/charts/bar/bar-renderer.cjs.map +1 -1
  32. package/dist/charts/bar/bar-renderer.d.ts.map +1 -1
  33. package/dist/charts/bar/bar-renderer.js +1 -1
  34. package/dist/charts/bar/bar-renderer.js.map +1 -1
  35. package/dist/charts/line/line-chart.cjs +1 -1
  36. package/dist/charts/line/line-chart.cjs.map +1 -1
  37. package/dist/charts/line/line-chart.d.ts.map +1 -1
  38. package/dist/charts/line/line-chart.js +1 -1
  39. package/dist/charts/line/line-chart.js.map +1 -1
  40. package/dist/charts/line/line-renderer.cjs +1 -1
  41. package/dist/charts/line/line-renderer.cjs.map +1 -1
  42. package/dist/charts/line/line-renderer.d.ts +5 -2
  43. package/dist/charts/line/line-renderer.d.ts.map +1 -1
  44. package/dist/charts/line/line-renderer.js +1 -1
  45. package/dist/charts/line/line-renderer.js.map +1 -1
  46. package/dist/charts/pie/pie-chart.cjs +1 -1
  47. package/dist/charts/pie/pie-chart.cjs.map +1 -1
  48. package/dist/charts/pie/pie-chart.d.ts.map +1 -1
  49. package/dist/charts/pie/pie-chart.js +1 -1
  50. package/dist/charts/pie/pie-chart.js.map +1 -1
  51. package/dist/charts/scatter/index.d.ts +2 -0
  52. package/dist/charts/scatter/index.d.ts.map +1 -0
  53. package/dist/charts/scatter/scatter-chart.cjs +2 -0
  54. package/dist/charts/scatter/scatter-chart.cjs.map +1 -0
  55. package/dist/charts/scatter/scatter-chart.d.ts +3 -0
  56. package/dist/charts/scatter/scatter-chart.d.ts.map +1 -0
  57. package/dist/charts/scatter/scatter-chart.js +2 -0
  58. package/dist/charts/scatter/scatter-chart.js.map +1 -0
  59. package/dist/charts/scatter/scatter-renderer.cjs +2 -0
  60. package/dist/charts/scatter/scatter-renderer.cjs.map +1 -0
  61. package/dist/charts/scatter/scatter-renderer.d.ts +24 -0
  62. package/dist/charts/scatter/scatter-renderer.d.ts.map +1 -0
  63. package/dist/charts/scatter/scatter-renderer.js +2 -0
  64. package/dist/charts/scatter/scatter-renderer.js.map +1 -0
  65. package/dist/core/chart-base.cjs +1 -1
  66. package/dist/core/chart-base.cjs.map +1 -1
  67. package/dist/core/chart-base.d.ts +1 -0
  68. package/dist/core/chart-base.d.ts.map +1 -1
  69. package/dist/core/chart-base.js +1 -1
  70. package/dist/core/chart-base.js.map +1 -1
  71. package/dist/core/chart-scaffold.cjs +1 -1
  72. package/dist/core/chart-scaffold.cjs.map +1 -1
  73. package/dist/core/chart-scaffold.d.ts +4 -2
  74. package/dist/core/chart-scaffold.d.ts.map +1 -1
  75. package/dist/core/chart-scaffold.js +1 -1
  76. package/dist/core/chart-scaffold.js.map +1 -1
  77. package/dist/index.cjs +1 -1
  78. package/dist/index.d.ts +2 -1
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +1 -1
  81. package/dist/interaction/announcer.cjs +1 -1
  82. package/dist/interaction/announcer.cjs.map +1 -1
  83. package/dist/interaction/announcer.d.ts +1 -1
  84. package/dist/interaction/announcer.d.ts.map +1 -1
  85. package/dist/interaction/announcer.js +1 -1
  86. package/dist/interaction/announcer.js.map +1 -1
  87. package/dist/interaction/legend.cjs +1 -1
  88. package/dist/interaction/legend.cjs.map +1 -1
  89. package/dist/interaction/legend.d.ts +1 -1
  90. package/dist/interaction/legend.d.ts.map +1 -1
  91. package/dist/interaction/legend.js +1 -1
  92. package/dist/interaction/legend.js.map +1 -1
  93. package/dist/interaction/scatter-interaction.cjs +2 -0
  94. package/dist/interaction/scatter-interaction.cjs.map +1 -0
  95. package/dist/interaction/scatter-interaction.d.ts +36 -0
  96. package/dist/interaction/scatter-interaction.d.ts.map +1 -0
  97. package/dist/interaction/scatter-interaction.js +2 -0
  98. package/dist/interaction/scatter-interaction.js.map +1 -0
  99. package/dist/interaction/series-interaction.cjs +1 -1
  100. package/dist/interaction/series-interaction.cjs.map +1 -1
  101. package/dist/interaction/series-interaction.d.ts +3 -1
  102. package/dist/interaction/series-interaction.d.ts.map +1 -1
  103. package/dist/interaction/series-interaction.js +1 -1
  104. package/dist/interaction/series-interaction.js.map +1 -1
  105. package/dist/interaction/tooltip.cjs +1 -1
  106. package/dist/interaction/tooltip.cjs.map +1 -1
  107. package/dist/interaction/tooltip.d.ts.map +1 -1
  108. package/dist/interaction/tooltip.js +1 -1
  109. package/dist/interaction/tooltip.js.map +1 -1
  110. package/dist/prism.cjs +1 -1
  111. package/dist/prism.cjs.map +1 -1
  112. package/dist/prism.iife.js +1 -1
  113. package/dist/prism.iife.js.map +1 -1
  114. package/dist/prism.js +1 -1
  115. package/dist/prism.js.map +1 -1
  116. package/dist/theme/prism.css +6 -0
  117. package/dist/types.d.ts +39 -6
  118. package/dist/types.d.ts.map +1 -1
  119. package/package.json +4 -3
@@ -1 +1 @@
1
- {"version":3,"file":"line-chart.js","names":[],"sources":["../../../src/charts/line/line-chart.ts"],"sourcesContent":["import { positionAxis, renderAxis, resolveTickCount } from '../../axes/axis';\nimport { renderGrid } from '../../axes/grid';\nimport { buildXScale, buildYScale } from '../../core/cartesian-scales';\nimport { clearCartesianDom, createChartScaffold } from '../../core/chart-scaffold';\nimport { chartArea, resolveMargin } from '../../core/layout';\nimport { seriesDomId } from '../../core/series-id';\nimport { createCrosshair } from '../../interaction/crosshair';\nimport { createSeriesInteraction, ensureMarkerGroup } from '../../interaction/series-interaction';\nimport { createSvgElement } from '../../svg/element';\nimport type { Point } from '../../svg/path';\nimport { seriesColor } from '../../theme';\nimport type { ChartHandle, LineChartConfig, LineSeriesConfig, XAxisConfig, YAxisConfig } from '../../types';\nimport { computePoints, renderLine } from './line-renderer';\n\nexport function createLineChart(container: HTMLElement, config: LineChartConfig): ChartHandle<LineSeriesConfig[]> {\n let crosshair: ReturnType<typeof createCrosshair> | null = null;\n let markers: SVGGElement | null = null;\n let rightAxis: SVGGElement | null = null;\n let seriesList = config.series;\n\n return createChartScaffold(\n container,\n config,\n (ctx) => {\n const { groups, legend, tooltip } = ctx;\n const dims = ctx.dimensions;\n const hasRight = seriesList.some((series) => series.yAxis === 'right');\n const hasLeftData = seriesList.some((series) => series.yAxis !== 'right' && series.data.length > 0);\n // The right axis widens the right margin; the effective margin stays local to this\n // render pass (and is handed to the interaction layer) instead of mutating shared dims.\n const margin = {\n ...resolveMargin(config.margin),\n right:\n config.margin?.right ??\n (hasRight ? (config.rightYAxis?.label ? 72 : 50) : resolveMargin(config.margin).right),\n };\n const area = chartArea(dims.width, dims.height, margin);\n const allData = seriesList.map((series) => series.data);\n const allX = allData.flat().map((datum) => datum.key);\n const leftY = seriesList\n .filter((series) => series.yAxis !== 'right')\n .flatMap((series) => series.data.map((datum) => datum.value));\n const rightY = seriesList\n .filter((series) => series.yAxis === 'right')\n .flatMap((series) => series.data.map((datum) => datum.value));\n\n if (allX.length === 0) {\n clearCartesianDom(groups, legend, tooltip, crosshair);\n markers?.replaceChildren();\n rightAxis?.replaceChildren();\n\n return;\n }\n\n if (config.crosshair && !crosshair) {\n crosshair = createCrosshair(ctx.chartArea, config.crosshair);\n }\n\n const xScale = buildXScale(allX, area.width);\n const yScale = buildYScale(leftY, area.height);\n const rightYScale = buildYScale(rightY, area.height);\n\n // Axes render by default (value axis with gridlines); `false` opts out.\n const xAxisConfig: XAxisConfig | false = config.xAxis === false ? false : (config.xAxis ?? {});\n const yAxisConfig: YAxisConfig | false = config.yAxis === false ? false : (config.yAxis ?? { grid: true });\n\n if (yAxisConfig && yAxisConfig.grid && (!hasRight || hasLeftData)) {\n renderGrid(\n groups.grid,\n yScale,\n yAxisConfig.grid,\n area.width,\n 'horizontal',\n resolveTickCount(yAxisConfig, area.height, 'left'),\n );\n } else {\n groups.grid.replaceChildren();\n }\n\n if (xAxisConfig && xAxisConfig.grid) {\n renderGrid(\n groups.grid,\n xScale,\n xAxisConfig.grid,\n area.height,\n 'vertical',\n resolveTickCount(xAxisConfig, area.width, 'bottom'),\n );\n }\n\n if (xAxisConfig) {\n positionAxis(groups.xAxis, xAxisConfig.position ?? 'bottom', area.width, area.height);\n renderAxis(groups.xAxis, xScale, xAxisConfig, area.width, 'bottom');\n } else {\n groups.xAxis.replaceChildren();\n }\n\n if (yAxisConfig && (!hasRight || hasLeftData)) {\n const axis = { ...yAxisConfig, position: hasRight ? ('left' as const) : (yAxisConfig.position ?? 'left') };\n positionAxis(groups.yAxis, axis.position, area.width, area.height);\n renderAxis(groups.yAxis, yScale, axis, area.height, 'left');\n } else {\n groups.yAxis.replaceChildren();\n }\n\n if (hasRight) {\n if (!rightAxis) {\n rightAxis = createSvgElement('g', { class: 'prism-y-axis-right' });\n ctx.chartArea.appendChild(rightAxis);\n }\n const axis = { ...config.rightYAxis, position: 'right' as const };\n positionAxis(rightAxis, 'right', area.width, area.height);\n renderAxis(rightAxis, rightYScale, axis, area.height, 'right');\n } else {\n rightAxis?.replaceChildren();\n }\n\n while (groups.series.children.length > seriesList.length) {\n groups.series.removeChild(groups.series.lastChild!);\n }\n\n const allPoints: Point[][] = [];\n\n for (let i = 0; i < seriesList.length; i++) {\n const series = seriesList[i];\n let group = groups.series.children[i] as SVGGElement | undefined;\n\n if (!group) {\n group = createSvgElement('g', { class: 'prism-line-series' });\n groups.series.appendChild(group);\n }\n\n group.setAttribute('data-series-id', seriesDomId(series, i));\n\n const seriesScale = series.yAxis === 'right' ? rightYScale : yScale;\n const points = computePoints(allData[i], xScale, seriesScale);\n\n allPoints.push(points);\n\n renderLine(group, points, {\n baselineY: seriesScale.map(0),\n color: seriesColor(i, series.color),\n curve: series.curve ?? 'linear',\n disposalSignal: ctx.disposalSignal,\n marks: series.data,\n pointRadius: series.pointRadius ?? 3,\n showPoints: series.showPoints ?? false,\n strokeWidth: series.strokeWidth,\n transition: config.transition,\n });\n }\n\n legend?.update(seriesList.map((s, i) => ({ color: seriesColor(i, s.color), name: s.name })));\n tooltip?.hide();\n markers = ensureMarkerGroup(ctx.chartArea, markers);\n\n return createSeriesInteraction({\n announcer: ctx.announcer,\n colors: () => seriesList.map((s, i) => seriesColor(i, s.color)),\n crosshair,\n dims: () => ctx.dimensions,\n getData: () => allData,\n getPoints: () => allPoints,\n getSeriesList: () => seriesList,\n margin,\n markers,\n onClick: config.onClick,\n onHover: config.onHover,\n seriesGroup: groups.series,\n svg: ctx.svg,\n tooltip,\n });\n },\n (data) => {\n seriesList = data;\n },\n );\n}\n"],"mappings":"kwBAcA,SAAgB,gBAAgB,EAAwB,EAA0D,CAChH,IAAI,EAAuD,KACvD,EAA8B,KAC9B,EAAgC,KAChC,EAAa,EAAO,OAExB,OAAO,EACL,EACA,EACC,GAAQ,CACP,GAAM,CAAE,SAAQ,SAAQ,WAAY,EAC9B,EAAO,EAAI,WACX,EAAW,EAAW,KAAM,GAAW,EAAO,QAAU,OAAO,EAC/D,EAAc,EAAW,KAAM,GAAW,EAAO,QAAU,SAAW,EAAO,KAAK,OAAS,CAAC,EAG5F,EAAS,CACb,GAAG,EAAc,EAAO,MAAM,EAC9B,MACE,EAAO,QAAQ,QACd,EAAY,EAAO,YAAY,MAAQ,GAAK,GAAM,EAAc,EAAO,MAAM,CAAC,CAAC,MACpF,EACM,EAAO,EAAU,EAAK,MAAO,EAAK,OAAQ,CAAM,EAChD,EAAU,EAAW,IAAK,GAAW,EAAO,IAAI,EAChD,EAAO,EAAQ,KAAK,CAAC,CAAC,IAAK,GAAU,EAAM,GAAG,EAC9C,EAAQ,EACX,OAAQ,GAAW,EAAO,QAAU,OAAO,CAAC,CAC5C,QAAS,GAAW,EAAO,KAAK,IAAK,GAAU,EAAM,KAAK,CAAC,EACxD,EAAS,EACZ,OAAQ,GAAW,EAAO,QAAU,OAAO,CAAC,CAC5C,QAAS,GAAW,EAAO,KAAK,IAAK,GAAU,EAAM,KAAK,CAAC,EAE9D,GAAI,EAAK,SAAW,EAAG,CACrB,EAAkB,EAAQ,EAAQ,EAAS,CAAS,EACpD,GAAS,gBAAgB,EACzB,GAAW,gBAAgB,EAE3B,MACF,CAEI,EAAO,WAAa,CAAC,IACvB,EAAY,EAAgB,EAAI,UAAW,EAAO,SAAS,GAG7D,IAAM,EAAS,EAAY,EAAM,EAAK,KAAK,EACrC,EAAS,EAAY,EAAO,EAAK,MAAM,EACvC,EAAc,EAAY,EAAQ,EAAK,MAAM,EAG7C,EAAmC,EAAO,QAAU,GAAQ,GAAS,EAAO,OAAS,CAAC,EACtF,EAAmC,EAAO,QAAU,GAAQ,GAAS,EAAO,OAAS,CAAE,KAAM,EAAK,EAiCxG,GA/BI,GAAe,EAAY,OAAS,CAAC,GAAY,GACnD,EACE,EAAO,KACP,EACA,EAAY,KACZ,EAAK,MACL,aACA,EAAiB,EAAa,EAAK,OAAQ,MAAM,CACnD,EAEA,EAAO,KAAK,gBAAgB,EAG1B,GAAe,EAAY,MAC7B,EACE,EAAO,KACP,EACA,EAAY,KACZ,EAAK,OACL,WACA,EAAiB,EAAa,EAAK,MAAO,QAAQ,CACpD,EAGE,GACF,EAAa,EAAO,MAAO,EAAY,UAAY,SAAU,EAAK,MAAO,EAAK,MAAM,EACpF,EAAW,EAAO,MAAO,EAAQ,EAAa,EAAK,MAAO,QAAQ,GAElE,EAAO,MAAM,gBAAgB,EAG3B,IAAgB,CAAC,GAAY,GAAc,CAC7C,IAAM,EAAO,CAAE,GAAG,EAAa,SAAU,EAAY,OAAoB,EAAY,UAAY,MAAQ,EACzG,EAAa,EAAO,MAAO,EAAK,SAAU,EAAK,MAAO,EAAK,MAAM,EACjE,EAAW,EAAO,MAAO,EAAQ,EAAM,EAAK,OAAQ,MAAM,CAC5D,MACE,EAAO,MAAM,gBAAgB,EAG/B,GAAI,EAAU,CACP,IACH,EAAY,EAAiB,IAAK,CAAE,MAAO,oBAAqB,CAAC,EACjE,EAAI,UAAU,YAAY,CAAS,GAErC,IAAM,EAAO,CAAE,GAAG,EAAO,WAAY,SAAU,OAAiB,EAChE,EAAa,EAAW,QAAS,EAAK,MAAO,EAAK,MAAM,EACxD,EAAW,EAAW,EAAa,EAAM,EAAK,OAAQ,OAAO,CAC/D,MACE,GAAW,gBAAgB,EAG7B,KAAO,EAAO,OAAO,SAAS,OAAS,EAAW,QAChD,EAAO,OAAO,YAAY,EAAO,OAAO,SAAU,EAGpD,IAAM,EAAuB,CAAC,EAE9B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAW,OAAQ,IAAK,CAC1C,IAAM,EAAS,EAAW,GACtB,EAAQ,EAAO,OAAO,SAAS,GAE9B,IACH,EAAQ,EAAiB,IAAK,CAAE,MAAO,mBAAoB,CAAC,EAC5D,EAAO,OAAO,YAAY,CAAK,GAGjC,EAAM,aAAa,iBAAkB,EAAY,EAAQ,CAAC,CAAC,EAE3D,IAAM,EAAc,EAAO,QAAU,QAAU,EAAc,EACvD,EAAS,EAAc,EAAQ,GAAI,EAAQ,CAAW,EAE5D,EAAU,KAAK,CAAM,EAErB,EAAW,EAAO,EAAQ,CACxB,UAAW,EAAY,IAAI,CAAC,EAC5B,MAAO,EAAY,EAAG,EAAO,KAAK,EAClC,MAAO,EAAO,OAAS,SACvB,eAAgB,EAAI,eACpB,MAAO,EAAO,KACd,YAAa,EAAO,aAAe,EACnC,WAAY,EAAO,YAAc,GACjC,YAAa,EAAO,YACpB,WAAY,EAAO,UACrB,CAAC,CACH,CAMA,OAJA,GAAQ,OAAO,EAAW,KAAK,EAAG,KAAO,CAAE,MAAO,EAAY,EAAG,EAAE,KAAK,EAAG,KAAM,EAAE,IAAK,EAAE,CAAC,EAC3F,GAAS,KAAK,EACd,EAAU,EAAkB,EAAI,UAAW,CAAO,EAE3C,EAAwB,CAC7B,UAAW,EAAI,UACf,WAAc,EAAW,KAAK,EAAG,IAAM,EAAY,EAAG,EAAE,KAAK,CAAC,EAC9D,YACA,SAAY,EAAI,WAChB,YAAe,EACf,cAAiB,EACjB,kBAAqB,EACrB,SACA,UACA,QAAS,EAAO,QAChB,QAAS,EAAO,QAChB,YAAa,EAAO,OACpB,IAAK,EAAI,IACT,SACF,CAAC,CACH,EACC,GAAS,CACR,EAAa,CACf,CACF,CACF"}
1
+ {"version":3,"file":"line-chart.js","names":[],"sources":["../../../src/charts/line/line-chart.ts"],"sourcesContent":["import { positionAxis, renderAxis, resolveTickCount, resolveTicks } from '../../axes/axis';\nimport { renderGrid } from '../../axes/grid';\nimport { buildXScale, buildYScale } from '../../core/cartesian-scales';\nimport { clearCartesianDom, createChartScaffold } from '../../core/chart-scaffold';\nimport { chartArea, resolveMargin } from '../../core/layout';\nimport { seriesDomId } from '../../core/series-id';\nimport { createCrosshair } from '../../interaction/crosshair';\nimport { createSeriesInteraction, ensureMarkerGroup } from '../../interaction/series-interaction';\nimport { createSvgElement } from '../../svg/element';\nimport type { Point } from '../../svg/path';\nimport { seriesColor } from '../../theme';\nimport type { ChartHandle, LineChartConfig, LineSeriesConfig, XAxisConfig, YAxisConfig } from '../../types';\nimport { computePoints, renderLine } from './line-renderer';\n\nexport function createLineChart(container: HTMLElement, config: LineChartConfig): ChartHandle<LineSeriesConfig[]> {\n let crosshair: ReturnType<typeof createCrosshair> | null = null;\n let markers: SVGGElement | null = null;\n let rightAxis: SVGGElement | null = null;\n let seriesList = config.series;\n\n return createChartScaffold(\n container,\n config,\n (ctx, reason) => {\n const { groups, legend, tooltip } = ctx;\n legend?.update(seriesList.map((s, i) => ({ color: seriesColor(i, s.color), name: s.name })));\n const dims = ctx.dimensions;\n const hasRight = seriesList.some((series) => series.yAxis === 'right');\n const hasLeftData = seriesList.some((series) => series.yAxis !== 'right' && series.data.length > 0);\n // The right axis widens the right margin; the effective margin stays local to this\n // render pass (and is handed to the interaction layer) instead of mutating shared dims.\n const margin = {\n ...resolveMargin(config.margin),\n right:\n config.margin?.right ??\n (hasRight ? (config.rightYAxis?.label ? 72 : 50) : resolveMargin(config.margin).right),\n };\n const area = chartArea(dims.width, dims.height, margin);\n const allData = seriesList.map((series) => series.data);\n const allX = allData.flat().map((datum) => datum.key);\n const leftY = seriesList\n .filter((series) => series.yAxis !== 'right')\n .flatMap((series) => series.data.map((datum) => datum.value));\n const rightY = seriesList\n .filter((series) => series.yAxis === 'right')\n .flatMap((series) => series.data.map((datum) => datum.value));\n\n if (allX.length === 0) {\n clearCartesianDom(groups, legend, tooltip, crosshair);\n markers?.replaceChildren();\n rightAxis?.replaceChildren();\n\n return;\n }\n\n if (config.crosshair && !crosshair) {\n crosshair = createCrosshair(ctx.chartArea, config.crosshair);\n }\n\n const xScale = buildXScale(allX, area.width);\n const yScale = buildYScale(leftY, area.height);\n const rightYScale = buildYScale(rightY, area.height);\n\n // Axes render by default (value axis with gridlines); `false` opts out. The value\n // axis merges over its default so a partial config (e.g. only `tickFormat`) keeps\n // the default gridlines.\n const xAxisConfig: XAxisConfig | false = config.xAxis === false ? false : (config.xAxis ?? {});\n const yAxisConfig: YAxisConfig | false = config.yAxis === false ? false : { grid: true, ...config.yAxis };\n\n if (yAxisConfig && yAxisConfig.grid && (!hasRight || hasLeftData)) {\n renderGrid(\n groups.grid,\n yScale,\n yAxisConfig.grid,\n area.width,\n 'horizontal',\n resolveTicks(yScale, yAxisConfig, resolveTickCount(yAxisConfig, area.height, 'left')),\n );\n } else {\n groups.grid.replaceChildren();\n }\n\n if (xAxisConfig && xAxisConfig.grid) {\n renderGrid(\n groups.grid,\n xScale,\n xAxisConfig.grid,\n area.height,\n 'vertical',\n resolveTicks(xScale, xAxisConfig, resolveTickCount(xAxisConfig, area.width, 'bottom')),\n );\n }\n\n if (xAxisConfig) {\n positionAxis(groups.xAxis, xAxisConfig.position ?? 'bottom', area.width, area.height);\n renderAxis(groups.xAxis, xScale, xAxisConfig, area.width, 'bottom');\n } else {\n groups.xAxis.replaceChildren();\n }\n\n if (yAxisConfig && (!hasRight || hasLeftData)) {\n const axis = { ...yAxisConfig, position: hasRight ? ('left' as const) : (yAxisConfig.position ?? 'left') };\n positionAxis(groups.yAxis, axis.position, area.width, area.height);\n renderAxis(groups.yAxis, yScale, axis, area.height, 'left');\n } else {\n groups.yAxis.replaceChildren();\n }\n\n if (hasRight) {\n if (!rightAxis) {\n rightAxis = createSvgElement('g', { class: 'prism-y-axis-right' });\n ctx.chartArea.appendChild(rightAxis);\n }\n const axis = { ...config.rightYAxis, position: 'right' as const };\n positionAxis(rightAxis, 'right', area.width, area.height);\n renderAxis(rightAxis, rightYScale, axis, area.height, 'right');\n } else {\n rightAxis?.replaceChildren();\n }\n\n while (groups.series.children.length > seriesList.length) {\n groups.series.removeChild(groups.series.lastChild!);\n }\n\n const allPoints: Point[][] = [];\n\n for (let i = 0; i < seriesList.length; i++) {\n const series = seriesList[i];\n let group = groups.series.children[i] as SVGGElement | undefined;\n\n if (!group) {\n group = createSvgElement('g', { class: 'prism-line-series' });\n groups.series.appendChild(group);\n }\n\n group.setAttribute('data-series-id', seriesDomId(series, i));\n\n const seriesScale = series.yAxis === 'right' ? rightYScale : yScale;\n const points = computePoints(allData[i], xScale, seriesScale);\n\n allPoints.push(points);\n\n renderLine(group, points, {\n bounds: { height: area.height, width: area.width },\n color: seriesColor(i, series.color),\n curve: series.curve ?? 'linear',\n disposalSignal: ctx.disposalSignal,\n marks: series.data,\n pointRadius: series.pointRadius ?? 3,\n showPoints: series.showPoints ?? false,\n strokeWidth: series.strokeWidth,\n transition: reason === 'resize' ? false : config.transition,\n });\n }\n\n tooltip?.hide();\n markers = ensureMarkerGroup(ctx.chartArea, markers);\n\n return createSeriesInteraction({\n announcer: ctx.announcer,\n colors: () => seriesList.map((s, i) => seriesColor(i, s.color)),\n crosshair,\n dims: () => ctx.dimensions,\n getData: () => allData,\n getPoints: () => allPoints,\n getSeriesList: () => seriesList,\n margin,\n markers,\n onClick: config.onClick,\n onHover: config.onHover,\n seriesGroup: groups.series,\n svg: ctx.svg,\n tooltip,\n tooltipFormat: typeof config.tooltip === 'object' ? config.tooltip : undefined,\n });\n },\n (data) => {\n seriesList = data;\n },\n );\n}\n"],"mappings":"oxBAcA,SAAgB,gBAAgB,EAAwB,EAA0D,CAChH,IAAI,EAAuD,KACvD,EAA8B,KAC9B,EAAgC,KAChC,EAAa,EAAO,OAExB,OAAO,EACL,EACA,GACC,EAAK,IAAW,CACf,GAAM,CAAE,SAAQ,SAAQ,WAAY,EACpC,GAAQ,OAAO,EAAW,KAAK,EAAG,KAAO,CAAE,MAAO,EAAY,EAAG,EAAE,KAAK,EAAG,KAAM,EAAE,IAAK,EAAE,CAAC,EAC3F,IAAM,EAAO,EAAI,WACX,EAAW,EAAW,KAAM,GAAW,EAAO,QAAU,OAAO,EAC/D,EAAc,EAAW,KAAM,GAAW,EAAO,QAAU,SAAW,EAAO,KAAK,OAAS,CAAC,EAG5F,EAAS,CACb,GAAG,EAAc,EAAO,MAAM,EAC9B,MACE,EAAO,QAAQ,QACd,EAAY,EAAO,YAAY,MAAQ,GAAK,GAAM,EAAc,EAAO,MAAM,CAAC,CAAC,MACpF,EACM,EAAO,EAAU,EAAK,MAAO,EAAK,OAAQ,CAAM,EAChD,EAAU,EAAW,IAAK,GAAW,EAAO,IAAI,EAChD,EAAO,EAAQ,KAAK,CAAC,CAAC,IAAK,GAAU,EAAM,GAAG,EAC9C,EAAQ,EACX,OAAQ,GAAW,EAAO,QAAU,OAAO,CAAC,CAC5C,QAAS,GAAW,EAAO,KAAK,IAAK,GAAU,EAAM,KAAK,CAAC,EACxD,EAAS,EACZ,OAAQ,GAAW,EAAO,QAAU,OAAO,CAAC,CAC5C,QAAS,GAAW,EAAO,KAAK,IAAK,GAAU,EAAM,KAAK,CAAC,EAE9D,GAAI,EAAK,SAAW,EAAG,CACrB,EAAkB,EAAQ,EAAQ,EAAS,CAAS,EACpD,GAAS,gBAAgB,EACzB,GAAW,gBAAgB,EAE3B,MACF,CAEI,EAAO,WAAa,CAAC,IACvB,EAAY,EAAgB,EAAI,UAAW,EAAO,SAAS,GAG7D,IAAM,EAAS,EAAY,EAAM,EAAK,KAAK,EACrC,EAAS,EAAY,EAAO,EAAK,MAAM,EACvC,EAAc,EAAY,EAAQ,EAAK,MAAM,EAK7C,EAAmC,EAAO,QAAU,GAAQ,GAAS,EAAO,OAAS,CAAC,EACtF,EAAmC,EAAO,QAAU,IAAgB,CAAE,KAAM,GAAM,GAAG,EAAO,KAAM,EAiCxG,GA/BI,GAAe,EAAY,OAAS,CAAC,GAAY,GACnD,EACE,EAAO,KACP,EACA,EAAY,KACZ,EAAK,MACL,aACA,EAAa,EAAQ,EAAa,EAAiB,EAAa,EAAK,OAAQ,MAAM,CAAC,CACtF,EAEA,EAAO,KAAK,gBAAgB,EAG1B,GAAe,EAAY,MAC7B,EACE,EAAO,KACP,EACA,EAAY,KACZ,EAAK,OACL,WACA,EAAa,EAAQ,EAAa,EAAiB,EAAa,EAAK,MAAO,QAAQ,CAAC,CACvF,EAGE,GACF,EAAa,EAAO,MAAO,EAAY,UAAY,SAAU,EAAK,MAAO,EAAK,MAAM,EACpF,EAAW,EAAO,MAAO,EAAQ,EAAa,EAAK,MAAO,QAAQ,GAElE,EAAO,MAAM,gBAAgB,EAG3B,IAAgB,CAAC,GAAY,GAAc,CAC7C,IAAM,EAAO,CAAE,GAAG,EAAa,SAAU,EAAY,OAAoB,EAAY,UAAY,MAAQ,EACzG,EAAa,EAAO,MAAO,EAAK,SAAU,EAAK,MAAO,EAAK,MAAM,EACjE,EAAW,EAAO,MAAO,EAAQ,EAAM,EAAK,OAAQ,MAAM,CAC5D,MACE,EAAO,MAAM,gBAAgB,EAG/B,GAAI,EAAU,CACP,IACH,EAAY,EAAiB,IAAK,CAAE,MAAO,oBAAqB,CAAC,EACjE,EAAI,UAAU,YAAY,CAAS,GAErC,IAAM,EAAO,CAAE,GAAG,EAAO,WAAY,SAAU,OAAiB,EAChE,EAAa,EAAW,QAAS,EAAK,MAAO,EAAK,MAAM,EACxD,EAAW,EAAW,EAAa,EAAM,EAAK,OAAQ,OAAO,CAC/D,MACE,GAAW,gBAAgB,EAG7B,KAAO,EAAO,OAAO,SAAS,OAAS,EAAW,QAChD,EAAO,OAAO,YAAY,EAAO,OAAO,SAAU,EAGpD,IAAM,EAAuB,CAAC,EAE9B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAW,OAAQ,IAAK,CAC1C,IAAM,EAAS,EAAW,GACtB,EAAQ,EAAO,OAAO,SAAS,GAE9B,IACH,EAAQ,EAAiB,IAAK,CAAE,MAAO,mBAAoB,CAAC,EAC5D,EAAO,OAAO,YAAY,CAAK,GAGjC,EAAM,aAAa,iBAAkB,EAAY,EAAQ,CAAC,CAAC,EAE3D,IAAM,EAAc,EAAO,QAAU,QAAU,EAAc,EACvD,EAAS,EAAc,EAAQ,GAAI,EAAQ,CAAW,EAE5D,EAAU,KAAK,CAAM,EAErB,EAAW,EAAO,EAAQ,CACxB,OAAQ,CAAE,OAAQ,EAAK,OAAQ,MAAO,EAAK,KAAM,EACjD,MAAO,EAAY,EAAG,EAAO,KAAK,EAClC,MAAO,EAAO,OAAS,SACvB,eAAgB,EAAI,eACpB,MAAO,EAAO,KACd,YAAa,EAAO,aAAe,EACnC,WAAY,EAAO,YAAc,GACjC,YAAa,EAAO,YACpB,WAAY,IAAW,UAAmB,EAAO,UACnD,CAAC,CACH,CAKA,OAHA,GAAS,KAAK,EACd,EAAU,EAAkB,EAAI,UAAW,CAAO,EAE3C,EAAwB,CAC7B,UAAW,EAAI,UACf,WAAc,EAAW,KAAK,EAAG,IAAM,EAAY,EAAG,EAAE,KAAK,CAAC,EAC9D,YACA,SAAY,EAAI,WAChB,YAAe,EACf,cAAiB,EACjB,kBAAqB,EACrB,SACA,UACA,QAAS,EAAO,QAChB,QAAS,EAAO,QAChB,YAAa,EAAO,OACpB,IAAK,EAAI,IACT,UACA,cAAe,OAAO,EAAO,SAAY,SAAW,EAAO,QAAU,IAAA,EACvE,CAAC,CACH,EACC,GAAS,CACR,EAAa,CACf,CACF,CACF"}
@@ -1,2 +1,2 @@
1
- require("../../_dev.cjs");const e=require("../../svg/element.cjs"),t=require("../../animation/easing.cjs"),n=require("../../animation/motion.cjs"),r=require("../../animation/tween.cjs"),i=require("../../animation/transition.cjs"),a=require("../../core/datum-style.cjs"),o=require("../../svg/path.cjs");var s=new WeakMap,c=new WeakMap;function buildPath(e,t){return t===`monotone`?o.monotonePath(e):t===`step`?o.stepPath(e):o.linePath(e)}function renderLine(o,l,u){let d=a.hasStyledRuns(u.marks),f=u.marks??[],p=n.resolveMotion(u.transition),m=p.duration,h=t.resolveEasing(p.easing),g=null,_=[],v=[];if(d){_=a.computeStyleRuns(f,l.length);let t=[...o.querySelectorAll(`.prism-line-path`)];for(let e of t.slice(_.length))e.remove();v=_.map((n,r)=>{let i=t[r];if(i)return i;let a=e.createSvgElement(`path`,{class:`prism-line-path`,fill:`none`});return o.appendChild(a),a});for(let[t,n]of _.entries())e.setAttributes(v[t],{opacity:n.opacity,stroke:u.color,"stroke-dasharray":n.dash,"stroke-width":u.strokeWidth??2}),v[t].style.strokeWidth=u.strokeWidth===void 0?``:String(u.strokeWidth)}else{let t=[...o.querySelectorAll(`.prism-line-path`)];g=t[0]??null;for(let e of t.slice(1))e.remove();g||(g=e.createSvgElement(`path`,{class:`prism-line-path`,fill:`none`}),o.appendChild(g)),e.setAttributes(g,{opacity:void 0,stroke:u.color,"stroke-dasharray":void 0,"stroke-width":u.strokeWidth??2}),g.style.strokeWidth=u.strokeWidth===void 0?``:String(u.strokeWidth)}let draw=t=>{if(d)for(let[n,r]of _.entries())e.setAttributes(v[n],{d:buildPath(t.slice(r.start,r.end+1),u.curve)});else e.setAttributes(g,{d:buildPath(t,u.curve)})},y=o.querySelector(`.prism-line-dots`);if(u.showPoints?y||(y=e.createSvgElement(`g`,{class:`prism-line-dots`}),o.appendChild(y)):y&&=(y.remove(),null),m===0){if(draw(l),c.set(o,l),y){for(;y.children.length>l.length;)y.removeChild(y.lastChild);for(let t=0;t<l.length;t++){let n=y.children[t];n||(n=e.createSvgElement(`circle`,{class:`prism-line-dot`}),y.appendChild(n)),e.setAttributes(n,{cx:l[t].x,cy:l[t].y,fill:u.color,opacity:f[t]?.opacity,r:u.pointRadius})}}return}s.get(o)?.();let b=!c.has(o),x=b?l.map(e=>({x:e.x,y:u.baselineY})):[];if(y){for(;y.children.length>l.length;)y.removeChild(y.lastChild);for(let t=y.children.length;t<l.length;t++){let n=e.createSvgElement(`circle`,{class:`prism-line-dot`});e.setAttributes(n,{fill:u.color,opacity:f[t]?.opacity,r:u.pointRadius}),y.appendChild(n)}for(let t=0;t<y.children.length;t++)e.setAttributes(y.children[t],{opacity:f[t]?.opacity})}if(!b){let e=c.get(o)??l;for(let[t,n]of l.entries())x.push(e[t]??e.at(-1)??n)}let paintFrame=t=>{let n=l.map((e,n)=>{let i=x[n]??e;return{x:r.tweenNumber(i.x,e.x,t),y:r.tweenNumber(i.y,e.y,t)}});if(c.set(o,n),draw(n),y)for(let t=0;t<l.length;t++){let r=y.children[t];r&&e.setAttributes(r,{cx:n[t].x,cy:n[t].y})}};paintFrame(0),s.set(o,i.startTween({count:1,duration:m,easing:h,onComplete:()=>{s.delete(o),c.set(o,l)},onFrame:e=>paintFrame(e(0)),signal:u.disposalSignal}))}function computePoints(e,t,n){return e.some(e=>e.key==null)||e.some(e=>typeof e.key==`string`),e.map(e=>({x:t.map(e.key),y:n.map(e.value)}))}exports.computePoints=computePoints,exports.renderLine=renderLine;
1
+ require("../../_dev.cjs");const e=require("../../svg/element.cjs"),t=require("../../animation/easing.cjs"),n=require("../../animation/motion.cjs"),r=require("../../animation/tween.cjs"),i=require("../../animation/transition.cjs"),a=require("../../core/datum-style.cjs"),o=require("../../svg/path.cjs"),s=require("../../animation/reveal.cjs");var c=new WeakMap,l=new WeakMap;function buildPath(e,t){return t===`monotone`?o.monotonePath(e):t===`step`?o.stepPath(e):o.linePath(e)}function renderLine(o,u,d){let f=a.hasStyledRuns(d.marks),p=d.marks??[],m=n.resolveMotion(d.transition),h=m.duration,g=t.resolveEasing(m.easing),_=null,v=[],y=[];if(f){v=a.computeStyleRuns(p,u.length);let t=[...o.querySelectorAll(`.prism-line-path`)];for(let e of t.slice(v.length))e.remove();y=v.map((n,r)=>{let i=t[r];if(i)return i;let a=e.createSvgElement(`path`,{class:`prism-line-path`,fill:`none`});return o.appendChild(a),a});for(let[t,n]of v.entries())e.setAttributes(y[t],{opacity:n.opacity,stroke:d.color,"stroke-dasharray":n.dash,"stroke-width":d.strokeWidth??2}),y[t].style.strokeWidth=d.strokeWidth===void 0?``:String(d.strokeWidth)}else{let t=[...o.querySelectorAll(`.prism-line-path`)];_=t[0]??null;for(let e of t.slice(1))e.remove();_||(_=e.createSvgElement(`path`,{class:`prism-line-path`,fill:`none`}),o.appendChild(_)),e.setAttributes(_,{opacity:void 0,stroke:d.color,"stroke-dasharray":void 0,"stroke-width":d.strokeWidth??2}),_.style.strokeWidth=d.strokeWidth===void 0?``:String(d.strokeWidth)}let draw=t=>{if(f)for(let[n,r]of v.entries())e.setAttributes(y[n],{d:buildPath(t.slice(r.start,r.end+1),d.curve)});else e.setAttributes(_,{d:buildPath(t,d.curve)})},b=o.querySelector(`.prism-line-dots`);if(d.showPoints?b||(b=e.createSvgElement(`g`,{class:`prism-line-dots`}),o.appendChild(b)):b&&=(b.remove(),null),h===0){if(c.get(o)?.(),s.cancelReveal(o),draw(u),l.set(o,u),b){for(;b.children.length>u.length;)b.removeChild(b.lastChild);for(let t=0;t<u.length;t++){let n=b.children[t];n||(n=e.createSvgElement(`circle`,{class:`prism-line-dot`}),b.appendChild(n)),e.setAttributes(n,{cx:u[t].x,cy:u[t].y,fill:d.color,opacity:p[t]?.opacity,r:d.pointRadius})}}return}c.get(o)?.(),s.cancelReveal(o);let x=!l.has(o);if(b){for(;b.children.length>u.length;)b.removeChild(b.lastChild);for(let t=b.children.length;t<u.length;t++){let n=e.createSvgElement(`circle`,{class:`prism-line-dot`});e.setAttributes(n,{fill:d.color,opacity:p[t]?.opacity,r:d.pointRadius}),b.appendChild(n)}for(let t=0;t<b.children.length;t++)e.setAttributes(b.children[t],{opacity:p[t]?.opacity})}if(x){if(draw(u),l.set(o,u),b)for(let[t,n]of u.entries()){let r=b.children[t];r&&e.setAttributes(r,{cx:n.x,cy:n.y})}if(u.length>1){let e=_??y[0],t=d.strokeWidth??(Number.parseFloat(getComputedStyle(e).strokeWidth)||2),r=f?v.map((e,t)=>({d:y[t].getAttribute(`d`),end:e.end/(u.length-1),start:e.start/(u.length-1)})):[{d:_.getAttribute(`d`),end:1,start:0}];s.playTraceReveal(o,d.bounds,r,Math.max(t,d.showPoints?d.pointRadius*2:0)+2,n.resolveMotion(d.transition,{defaultDuration:420}),d.disposalSignal)}return}let S=[],C=l.get(o)??u;for(let[e,t]of u.entries())S.push(C[e]??C.at(-1)??t);let paintFrame=t=>{let n=u.map((e,n)=>{let i=S[n]??e;return{x:r.tweenNumber(i.x,e.x,t),y:r.tweenNumber(i.y,e.y,t)}});if(l.set(o,n),draw(n),b)for(let t=0;t<u.length;t++){let r=b.children[t];r&&e.setAttributes(r,{cx:n[t].x,cy:n[t].y})}};paintFrame(0),c.set(o,i.startTween({count:1,duration:h,easing:g,onComplete:()=>{c.delete(o),l.set(o,u)},onFrame:e=>paintFrame(e(0)),signal:d.disposalSignal}))}function computePoints(e,t,n){return e.some(e=>e.key==null)||e.some(e=>typeof e.key==`string`),e.map(e=>({x:t.map(e.key),y:n.map(e.value)}))}exports.computePoints=computePoints,exports.renderLine=renderLine;
2
2
  //# sourceMappingURL=line-renderer.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"line-renderer.cjs","names":[],"sources":["../../../src/charts/line/line-renderer.ts"],"sourcesContent":["import { warn } from '../../_dev';\nimport { resolveEasing } from '../../animation/easing';\nimport { resolveMotion } from '../../animation/motion';\nimport { startTween } from '../../animation/transition';\nimport { tweenNumber } from '../../animation/tween';\nimport { computeStyleRuns, type DatumMark, hasStyledRuns, type StyleRun } from '../../core/datum-style';\nimport { createSvgElement, setAttributes } from '../../svg/element';\nimport type { Point } from '../../svg/path';\nimport { linePath, monotonePath, stepPath } from '../../svg/path';\nimport type { ContinuousDatum, Scale, TransitionOption } from '../../types';\n\nexport interface LineRenderOptions {\n /** Plot-area bottom in area-local coordinates: the mount animation raises the line from here. */\n baselineY: number;\n color: string;\n curve: 'linear' | 'monotone' | 'step';\n /** Aborted when the owning chart is disposed: stops the transition's `requestAnimationFrame` loop from rescheduling. */\n disposalSignal?: AbortSignal;\n /** Per-datum presentation parallel to `points`; styled runs split the line into subpaths. */\n marks?: readonly DatumMark[];\n pointRadius: number;\n showPoints: boolean;\n /** Explicit width beats the `--prism-line-width` theme token. */\n strokeWidth?: number;\n transition?: TransitionOption;\n}\n\nconst activeAnimations = new WeakMap<SVGGElement, () => void>();\n// Geometry currently on screen, so an update interpolates from it even when the\n// series hides its point markers (the dots would otherwise be the only record).\nconst drawnPoints = new WeakMap<SVGGElement, Point[]>();\n\nfunction buildPath(pts: Point[], curve: LineRenderOptions['curve']): string {\n return curve === 'monotone' ? monotonePath(pts) : curve === 'step' ? stepPath(pts) : linePath(pts);\n}\n\nexport function renderLine(parent: SVGGElement, points: Point[], options: LineRenderOptions): void {\n const styled = hasStyledRuns(options.marks);\n const marks = options.marks ?? [];\n const motion = resolveMotion(options.transition);\n const dur = motion.duration;\n const easing = resolveEasing(motion.easing);\n\n let path: SVGPathElement | null = null;\n let runs: readonly StyleRun[] = [];\n let runPaths: SVGPathElement[] = [];\n\n if (styled) {\n runs = computeStyleRuns(marks, points.length);\n\n const existing = [...parent.querySelectorAll<SVGPathElement>('.prism-line-path')];\n\n for (const extra of existing.slice(runs.length)) extra.remove();\n\n runPaths = runs.map((_, r) => {\n const reuse = existing[r];\n\n if (reuse) return reuse;\n\n const created = createSvgElement('path', { class: 'prism-line-path', fill: 'none' });\n\n parent.appendChild(created);\n\n return created;\n });\n\n for (const [r, run] of runs.entries()) {\n setAttributes(runPaths[r], {\n opacity: run.opacity,\n stroke: options.color,\n 'stroke-dasharray': run.dash,\n 'stroke-width': options.strokeWidth ?? 2,\n });\n runPaths[r].style.strokeWidth = options.strokeWidth === undefined ? '' : String(options.strokeWidth);\n }\n } else {\n const existing = [...parent.querySelectorAll<SVGPathElement>('.prism-line-path')];\n\n path = existing[0] ?? null;\n\n for (const stale of existing.slice(1)) stale.remove();\n\n if (!path) {\n path = createSvgElement('path', { class: 'prism-line-path', fill: 'none' });\n parent.appendChild(path);\n }\n\n setAttributes(path, {\n opacity: undefined,\n stroke: options.color,\n 'stroke-dasharray': undefined,\n 'stroke-width': options.strokeWidth ?? 2,\n });\n path.style.strokeWidth = options.strokeWidth === undefined ? '' : String(options.strokeWidth);\n }\n\n const draw = (pts: Point[]): void => {\n if (styled) {\n for (const [r, run] of runs.entries()) {\n setAttributes(runPaths[r], { d: buildPath(pts.slice(run.start, run.end + 1), options.curve) });\n }\n } else {\n setAttributes(path!, { d: buildPath(pts, options.curve) });\n }\n };\n\n let dotsGroup = parent.querySelector<SVGGElement>('.prism-line-dots');\n\n if (options.showPoints) {\n if (!dotsGroup) {\n dotsGroup = createSvgElement('g', { class: 'prism-line-dots' });\n parent.appendChild(dotsGroup);\n }\n } else if (dotsGroup) {\n dotsGroup.remove();\n dotsGroup = null;\n }\n\n if (dur === 0) {\n draw(points);\n drawnPoints.set(parent, points);\n\n if (dotsGroup) {\n while (dotsGroup.children.length > points.length) dotsGroup.removeChild(dotsGroup.lastChild!);\n\n for (let i = 0; i < points.length; i++) {\n let c = dotsGroup.children[i] as SVGCircleElement | undefined;\n\n if (!c) {\n c = createSvgElement('circle', { class: 'prism-line-dot' });\n dotsGroup.appendChild(c);\n }\n\n setAttributes(c, {\n cx: points[i].x,\n cy: points[i].y,\n fill: options.color,\n opacity: marks[i]?.opacity,\n r: options.pointRadius,\n });\n }\n }\n\n return;\n }\n\n activeAnimations.get(parent)?.();\n\n const hasExisting = drawnPoints.has(parent);\n // A first render has no drawn shape to interpolate between: the mount animation\n // raises the whole line from the plot baseline instead.\n const isMount = !hasExisting;\n\n const fromPts: Point[] = isMount ? points.map((p) => ({ x: p.x, y: options.baselineY })) : [];\n\n if (dotsGroup) {\n while (dotsGroup.children.length > points.length) dotsGroup.removeChild(dotsGroup.lastChild!);\n\n for (let i = dotsGroup.children.length; i < points.length; i++) {\n const c = createSvgElement('circle', { class: 'prism-line-dot' });\n\n setAttributes(c, {\n fill: options.color,\n opacity: marks[i]?.opacity,\n r: options.pointRadius,\n });\n dotsGroup.appendChild(c);\n }\n\n for (let i = 0; i < dotsGroup.children.length; i++) {\n setAttributes(dotsGroup.children[i] as SVGCircleElement, { opacity: marks[i]?.opacity });\n }\n }\n\n if (!isMount) {\n const drawn = drawnPoints.get(parent) ?? points;\n\n for (const [i, to] of points.entries()) {\n fromPts.push(drawn[i] ?? drawn.at(-1) ?? to);\n }\n }\n\n const paintFrame = (e: number): void => {\n const interpolated: Point[] = points.map((to, i) => {\n const from = fromPts[i] ?? to;\n\n return { x: tweenNumber(from.x, to.x, e), y: tweenNumber(from.y, to.y, e) };\n });\n\n drawnPoints.set(parent, interpolated);\n draw(interpolated);\n\n if (dotsGroup) {\n for (let i = 0; i < points.length; i++) {\n const c = dotsGroup.children[i] as SVGCircleElement | undefined;\n\n if (c) setAttributes(c, { cx: interpolated[i].x, cy: interpolated[i].y });\n }\n }\n };\n\n // The starting geometry goes on screen immediately: `startTween` only paints on\n // the first frame callback, which would otherwise leave one blank frame.\n paintFrame(0);\n\n activeAnimations.set(\n parent,\n startTween({\n count: 1,\n duration: dur,\n easing,\n onComplete: () => {\n activeAnimations.delete(parent);\n drawnPoints.set(parent, points);\n },\n onFrame: (progress) => paintFrame(progress(0)),\n signal: options.disposalSignal,\n }),\n );\n}\n\nexport function computePoints(data: ContinuousDatum[], xScale: Scale<Date | number>, yScale: Scale<number>): Point[] {\n if (data.some((d) => d.key == null)) {\n warn(\n 'computePoints: datum.key is null or undefined: data must use the Datum shape { key, value }. Did you pass { x, y } instead?',\n );\n } else if (data.some((d) => typeof d.key === 'string')) {\n warn('computePoints: string keys are not supported for line/area charts: use numeric or Date keys.');\n }\n\n return data.map((d) => ({\n x: xScale.map(d.key as Date | number),\n y: yScale.map(d.value),\n }));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"line-renderer.cjs","names":[],"sources":["../../../src/charts/line/line-renderer.ts"],"sourcesContent":["import { warn } from '../../_dev';\nimport { resolveEasing } from '../../animation/easing';\nimport { resolveMotion } from '../../animation/motion';\nimport { cancelReveal, playTraceReveal } from '../../animation/reveal';\nimport { startTween } from '../../animation/transition';\nimport { tweenNumber } from '../../animation/tween';\nimport { computeStyleRuns, type DatumMark, hasStyledRuns, type StyleRun } from '../../core/datum-style';\nimport { createSvgElement, setAttributes } from '../../svg/element';\nimport type { Point } from '../../svg/path';\nimport { linePath, monotonePath, stepPath } from '../../svg/path';\nimport type { ContinuousDatum, Scale, TransitionOption } from '../../types';\n\nexport interface LineRenderOptions {\n /** Plot-area size in area-local coordinates: bounds the entrance mask. */\n bounds: { height: number; width: number };\n color: string;\n curve: 'linear' | 'monotone' | 'step';\n /** Aborted when the owning chart is disposed: stops the transition's `requestAnimationFrame` loop from rescheduling. */\n disposalSignal?: AbortSignal;\n /** Per-datum presentation parallel to `points`; styled runs split the line into subpaths. */\n marks?: readonly DatumMark[];\n pointRadius: number;\n showPoints: boolean;\n /** Explicit width beats the `--prism-line-width` theme token. */\n strokeWidth?: number;\n transition?: TransitionOption;\n}\n\nconst activeAnimations = new WeakMap<SVGGElement, () => void>();\n// Geometry currently on screen, so an update interpolates from it even when the\n// series hides its point markers (the dots would otherwise be the only record).\nconst drawnPoints = new WeakMap<SVGGElement, Point[]>();\n\nfunction buildPath(pts: Point[], curve: LineRenderOptions['curve']): string {\n return curve === 'monotone' ? monotonePath(pts) : curve === 'step' ? stepPath(pts) : linePath(pts);\n}\n\nexport function renderLine(parent: SVGGElement, points: Point[], options: LineRenderOptions): void {\n const styled = hasStyledRuns(options.marks);\n const marks = options.marks ?? [];\n const motion = resolveMotion(options.transition);\n const dur = motion.duration;\n const easing = resolveEasing(motion.easing);\n\n let path: SVGPathElement | null = null;\n let runs: readonly StyleRun[] = [];\n let runPaths: SVGPathElement[] = [];\n\n if (styled) {\n runs = computeStyleRuns(marks, points.length);\n\n const existing = [...parent.querySelectorAll<SVGPathElement>('.prism-line-path')];\n\n for (const extra of existing.slice(runs.length)) extra.remove();\n\n runPaths = runs.map((_, r) => {\n const reuse = existing[r];\n\n if (reuse) return reuse;\n\n const created = createSvgElement('path', { class: 'prism-line-path', fill: 'none' });\n\n parent.appendChild(created);\n\n return created;\n });\n\n for (const [r, run] of runs.entries()) {\n setAttributes(runPaths[r], {\n opacity: run.opacity,\n stroke: options.color,\n 'stroke-dasharray': run.dash,\n 'stroke-width': options.strokeWidth ?? 2,\n });\n runPaths[r].style.strokeWidth = options.strokeWidth === undefined ? '' : String(options.strokeWidth);\n }\n } else {\n const existing = [...parent.querySelectorAll<SVGPathElement>('.prism-line-path')];\n\n path = existing[0] ?? null;\n\n for (const stale of existing.slice(1)) stale.remove();\n\n if (!path) {\n path = createSvgElement('path', { class: 'prism-line-path', fill: 'none' });\n parent.appendChild(path);\n }\n\n setAttributes(path, {\n opacity: undefined,\n stroke: options.color,\n 'stroke-dasharray': undefined,\n 'stroke-width': options.strokeWidth ?? 2,\n });\n path.style.strokeWidth = options.strokeWidth === undefined ? '' : String(options.strokeWidth);\n }\n\n const draw = (pts: Point[]): void => {\n if (styled) {\n for (const [r, run] of runs.entries()) {\n setAttributes(runPaths[r], { d: buildPath(pts.slice(run.start, run.end + 1), options.curve) });\n }\n } else {\n setAttributes(path!, { d: buildPath(pts, options.curve) });\n }\n };\n\n let dotsGroup = parent.querySelector<SVGGElement>('.prism-line-dots');\n\n if (options.showPoints) {\n if (!dotsGroup) {\n dotsGroup = createSvgElement('g', { class: 'prism-line-dots' });\n parent.appendChild(dotsGroup);\n }\n } else if (dotsGroup) {\n dotsGroup.remove();\n dotsGroup = null;\n }\n\n if (dur === 0) {\n activeAnimations.get(parent)?.();\n cancelReveal(parent);\n draw(points);\n drawnPoints.set(parent, points);\n\n if (dotsGroup) {\n while (dotsGroup.children.length > points.length) dotsGroup.removeChild(dotsGroup.lastChild!);\n\n for (let i = 0; i < points.length; i++) {\n let c = dotsGroup.children[i] as SVGCircleElement | undefined;\n\n if (!c) {\n c = createSvgElement('circle', { class: 'prism-line-dot' });\n dotsGroup.appendChild(c);\n }\n\n setAttributes(c, {\n cx: points[i].x,\n cy: points[i].y,\n fill: options.color,\n opacity: marks[i]?.opacity,\n r: options.pointRadius,\n });\n }\n }\n\n return;\n }\n\n activeAnimations.get(parent)?.();\n cancelReveal(parent);\n\n const isMount = !drawnPoints.has(parent);\n\n if (dotsGroup) {\n while (dotsGroup.children.length > points.length) dotsGroup.removeChild(dotsGroup.lastChild!);\n\n for (let i = dotsGroup.children.length; i < points.length; i++) {\n const c = createSvgElement('circle', { class: 'prism-line-dot' });\n\n setAttributes(c, {\n fill: options.color,\n opacity: marks[i]?.opacity,\n r: options.pointRadius,\n });\n dotsGroup.appendChild(c);\n }\n\n for (let i = 0; i < dotsGroup.children.length; i++) {\n setAttributes(dotsGroup.children[i] as SVGCircleElement, { opacity: marks[i]?.opacity });\n }\n }\n\n if (isMount) {\n draw(points);\n drawnPoints.set(parent, points);\n\n if (dotsGroup) {\n for (const [i, point] of points.entries()) {\n const c = dotsGroup.children[i] as SVGCircleElement | undefined;\n\n if (c) setAttributes(c, { cx: point.x, cy: point.y });\n }\n }\n\n if (points.length > 1) {\n const renderedPath = path ?? runPaths[0];\n const strokeWidth = options.strokeWidth ?? (Number.parseFloat(getComputedStyle(renderedPath).strokeWidth) || 2);\n const traces = styled\n ? runs.map((run, i) => ({\n d: runPaths[i].getAttribute('d')!,\n end: run.end / (points.length - 1),\n start: run.start / (points.length - 1),\n }))\n : [{ d: path!.getAttribute('d')!, end: 1, start: 0 }];\n\n // A wide trace also uncovers markers as it reaches them, without a second timeline.\n playTraceReveal(\n parent,\n options.bounds,\n traces,\n Math.max(strokeWidth, options.showPoints ? options.pointRadius * 2 : 0) + 2,\n resolveMotion(options.transition, { defaultDuration: 420 }),\n options.disposalSignal,\n );\n }\n\n return;\n }\n\n const fromPts: Point[] = [];\n const drawn = drawnPoints.get(parent) ?? points;\n\n for (const [i, to] of points.entries()) {\n fromPts.push(drawn[i] ?? drawn.at(-1) ?? to);\n }\n\n const paintFrame = (e: number): void => {\n const interpolated: Point[] = points.map((to, i) => {\n const from = fromPts[i] ?? to;\n\n return { x: tweenNumber(from.x, to.x, e), y: tweenNumber(from.y, to.y, e) };\n });\n\n drawnPoints.set(parent, interpolated);\n draw(interpolated);\n\n if (dotsGroup) {\n for (let i = 0; i < points.length; i++) {\n const c = dotsGroup.children[i] as SVGCircleElement | undefined;\n\n if (c) setAttributes(c, { cx: interpolated[i].x, cy: interpolated[i].y });\n }\n }\n };\n\n // The starting geometry goes on screen immediately: `startTween` only paints on\n // the first frame callback, which would otherwise leave one blank frame.\n paintFrame(0);\n\n activeAnimations.set(\n parent,\n startTween({\n count: 1,\n duration: dur,\n easing,\n onComplete: () => {\n activeAnimations.delete(parent);\n drawnPoints.set(parent, points);\n },\n onFrame: (progress) => paintFrame(progress(0)),\n signal: options.disposalSignal,\n }),\n );\n}\n\nexport function computePoints(data: ContinuousDatum[], xScale: Scale<Date | number>, yScale: Scale<number>): Point[] {\n if (data.some((d) => d.key == null)) {\n warn(\n 'computePoints: datum.key is null or undefined: data must use the Datum shape { key, value }. Did you pass { x, y } instead?',\n );\n } else if (data.some((d) => typeof d.key === 'string')) {\n warn('computePoints: string keys are not supported for line/area charts: use numeric or Date keys.');\n }\n\n return data.map((d) => ({\n x: xScale.map(d.key as Date | number),\n y: yScale.map(d.value),\n }));\n}\n"],"mappings":"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"}
@@ -2,8 +2,11 @@ import { type DatumMark } from '../../core/datum-style';
2
2
  import type { Point } from '../../svg/path';
3
3
  import type { ContinuousDatum, Scale, TransitionOption } from '../../types';
4
4
  export interface LineRenderOptions {
5
- /** Plot-area bottom in area-local coordinates: the mount animation raises the line from here. */
6
- baselineY: number;
5
+ /** Plot-area size in area-local coordinates: bounds the entrance mask. */
6
+ bounds: {
7
+ height: number;
8
+ width: number;
9
+ };
7
10
  color: string;
8
11
  curve: 'linear' | 'monotone' | 'step';
9
12
  /** Aborted when the owning chart is disposed: stops the transition's `requestAnimationFrame` loop from rescheduling. */
@@ -1 +1 @@
1
- {"version":3,"file":"line-renderer.d.ts","sourceRoot":"","sources":["../../../src/charts/line/line-renderer.ts"],"names":[],"mappings":"AAKA,OAAO,EAAoB,KAAK,SAAS,EAAgC,MAAM,wBAAwB,CAAC;AAExG,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE5E,MAAM,WAAW,iBAAiB;IAChC,iGAAiG;IACjG,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC;IACtC,wHAAwH;IACxH,cAAc,CAAC,EAAE,WAAW,CAAC;IAC7B,6FAA6F;IAC7F,KAAK,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAWD,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAuLjG;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,eAAe,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,KAAK,EAAE,CAanH"}
1
+ {"version":3,"file":"line-renderer.d.ts","sourceRoot":"","sources":["../../../src/charts/line/line-renderer.ts"],"names":[],"mappings":"AAMA,OAAO,EAAoB,KAAK,SAAS,EAAgC,MAAM,wBAAwB,CAAC;AAExG,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE5E,MAAM,WAAW,iBAAiB;IAChC,0EAA0E;IAC1E,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC;IACtC,wHAAwH;IACxH,cAAc,CAAC,EAAE,WAAW,CAAC;IAC7B,6FAA6F;IAC7F,KAAK,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAWD,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAyNjG;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,eAAe,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,KAAK,EAAE,CAanH"}
@@ -1,2 +1,2 @@
1
- import"../../_dev.js";import{createSvgElement as e,setAttributes as t}from"../../svg/element.js";import{resolveEasing as n}from"../../animation/easing.js";import{resolveMotion as r}from"../../animation/motion.js";import{tweenNumber as i}from"../../animation/tween.js";import{startTween as a}from"../../animation/transition.js";import{computeStyleRuns as o,hasStyledRuns as s}from"../../core/datum-style.js";import{linePath as c,monotonePath as l,stepPath as u}from"../../svg/path.js";var d=new WeakMap,f=new WeakMap;function buildPath(e,t){return t===`monotone`?l(e):t===`step`?u(e):c(e)}function renderLine(c,l,u){let p=s(u.marks),m=u.marks??[],h=r(u.transition),g=h.duration,_=n(h.easing),v=null,y=[],b=[];if(p){y=o(m,l.length);let n=[...c.querySelectorAll(`.prism-line-path`)];for(let e of n.slice(y.length))e.remove();b=y.map((t,r)=>{let i=n[r];if(i)return i;let a=e(`path`,{class:`prism-line-path`,fill:`none`});return c.appendChild(a),a});for(let[e,n]of y.entries())t(b[e],{opacity:n.opacity,stroke:u.color,"stroke-dasharray":n.dash,"stroke-width":u.strokeWidth??2}),b[e].style.strokeWidth=u.strokeWidth===void 0?``:String(u.strokeWidth)}else{let n=[...c.querySelectorAll(`.prism-line-path`)];v=n[0]??null;for(let e of n.slice(1))e.remove();v||(v=e(`path`,{class:`prism-line-path`,fill:`none`}),c.appendChild(v)),t(v,{opacity:void 0,stroke:u.color,"stroke-dasharray":void 0,"stroke-width":u.strokeWidth??2}),v.style.strokeWidth=u.strokeWidth===void 0?``:String(u.strokeWidth)}let draw=e=>{if(p)for(let[n,r]of y.entries())t(b[n],{d:buildPath(e.slice(r.start,r.end+1),u.curve)});else t(v,{d:buildPath(e,u.curve)})},x=c.querySelector(`.prism-line-dots`);if(u.showPoints?x||(x=e(`g`,{class:`prism-line-dots`}),c.appendChild(x)):x&&=(x.remove(),null),g===0){if(draw(l),f.set(c,l),x){for(;x.children.length>l.length;)x.removeChild(x.lastChild);for(let n=0;n<l.length;n++){let r=x.children[n];r||(r=e(`circle`,{class:`prism-line-dot`}),x.appendChild(r)),t(r,{cx:l[n].x,cy:l[n].y,fill:u.color,opacity:m[n]?.opacity,r:u.pointRadius})}}return}d.get(c)?.();let S=!f.has(c),C=S?l.map(e=>({x:e.x,y:u.baselineY})):[];if(x){for(;x.children.length>l.length;)x.removeChild(x.lastChild);for(let n=x.children.length;n<l.length;n++){let r=e(`circle`,{class:`prism-line-dot`});t(r,{fill:u.color,opacity:m[n]?.opacity,r:u.pointRadius}),x.appendChild(r)}for(let e=0;e<x.children.length;e++)t(x.children[e],{opacity:m[e]?.opacity})}if(!S){let e=f.get(c)??l;for(let[t,n]of l.entries())C.push(e[t]??e.at(-1)??n)}let paintFrame=e=>{let n=l.map((t,n)=>{let r=C[n]??t;return{x:i(r.x,t.x,e),y:i(r.y,t.y,e)}});if(f.set(c,n),draw(n),x)for(let e=0;e<l.length;e++){let r=x.children[e];r&&t(r,{cx:n[e].x,cy:n[e].y})}};paintFrame(0),d.set(c,a({count:1,duration:g,easing:_,onComplete:()=>{d.delete(c),f.set(c,l)},onFrame:e=>paintFrame(e(0)),signal:u.disposalSignal}))}function computePoints(e,t,n){return e.some(e=>e.key==null)||e.some(e=>typeof e.key==`string`),e.map(e=>({x:t.map(e.key),y:n.map(e.value)}))}export{computePoints,renderLine};
1
+ import"../../_dev.js";import{createSvgElement as e,setAttributes as t}from"../../svg/element.js";import{resolveEasing as n}from"../../animation/easing.js";import{resolveMotion as r}from"../../animation/motion.js";import{tweenNumber as i}from"../../animation/tween.js";import{startTween as a}from"../../animation/transition.js";import{computeStyleRuns as o,hasStyledRuns as s}from"../../core/datum-style.js";import{linePath as c,monotonePath as l,stepPath as u}from"../../svg/path.js";import{cancelReveal as d,playTraceReveal as f}from"../../animation/reveal.js";var p=new WeakMap,m=new WeakMap;function buildPath(e,t){return t===`monotone`?l(e):t===`step`?u(e):c(e)}function renderLine(c,l,u){let h=s(u.marks),g=u.marks??[],_=r(u.transition),v=_.duration,y=n(_.easing),b=null,x=[],S=[];if(h){x=o(g,l.length);let n=[...c.querySelectorAll(`.prism-line-path`)];for(let e of n.slice(x.length))e.remove();S=x.map((t,r)=>{let i=n[r];if(i)return i;let a=e(`path`,{class:`prism-line-path`,fill:`none`});return c.appendChild(a),a});for(let[e,n]of x.entries())t(S[e],{opacity:n.opacity,stroke:u.color,"stroke-dasharray":n.dash,"stroke-width":u.strokeWidth??2}),S[e].style.strokeWidth=u.strokeWidth===void 0?``:String(u.strokeWidth)}else{let n=[...c.querySelectorAll(`.prism-line-path`)];b=n[0]??null;for(let e of n.slice(1))e.remove();b||(b=e(`path`,{class:`prism-line-path`,fill:`none`}),c.appendChild(b)),t(b,{opacity:void 0,stroke:u.color,"stroke-dasharray":void 0,"stroke-width":u.strokeWidth??2}),b.style.strokeWidth=u.strokeWidth===void 0?``:String(u.strokeWidth)}let draw=e=>{if(h)for(let[n,r]of x.entries())t(S[n],{d:buildPath(e.slice(r.start,r.end+1),u.curve)});else t(b,{d:buildPath(e,u.curve)})},C=c.querySelector(`.prism-line-dots`);if(u.showPoints?C||(C=e(`g`,{class:`prism-line-dots`}),c.appendChild(C)):C&&=(C.remove(),null),v===0){if(p.get(c)?.(),d(c),draw(l),m.set(c,l),C){for(;C.children.length>l.length;)C.removeChild(C.lastChild);for(let n=0;n<l.length;n++){let r=C.children[n];r||(r=e(`circle`,{class:`prism-line-dot`}),C.appendChild(r)),t(r,{cx:l[n].x,cy:l[n].y,fill:u.color,opacity:g[n]?.opacity,r:u.pointRadius})}}return}p.get(c)?.(),d(c);let w=!m.has(c);if(C){for(;C.children.length>l.length;)C.removeChild(C.lastChild);for(let n=C.children.length;n<l.length;n++){let r=e(`circle`,{class:`prism-line-dot`});t(r,{fill:u.color,opacity:g[n]?.opacity,r:u.pointRadius}),C.appendChild(r)}for(let e=0;e<C.children.length;e++)t(C.children[e],{opacity:g[e]?.opacity})}if(w){if(draw(l),m.set(c,l),C)for(let[e,n]of l.entries()){let r=C.children[e];r&&t(r,{cx:n.x,cy:n.y})}if(l.length>1){let e=b??S[0],t=u.strokeWidth??(Number.parseFloat(getComputedStyle(e).strokeWidth)||2),n=h?x.map((e,t)=>({d:S[t].getAttribute(`d`),end:e.end/(l.length-1),start:e.start/(l.length-1)})):[{d:b.getAttribute(`d`),end:1,start:0}];f(c,u.bounds,n,Math.max(t,u.showPoints?u.pointRadius*2:0)+2,r(u.transition,{defaultDuration:420}),u.disposalSignal)}return}let T=[],E=m.get(c)??l;for(let[e,t]of l.entries())T.push(E[e]??E.at(-1)??t);let paintFrame=e=>{let n=l.map((t,n)=>{let r=T[n]??t;return{x:i(r.x,t.x,e),y:i(r.y,t.y,e)}});if(m.set(c,n),draw(n),C)for(let e=0;e<l.length;e++){let r=C.children[e];r&&t(r,{cx:n[e].x,cy:n[e].y})}};paintFrame(0),p.set(c,a({count:1,duration:v,easing:y,onComplete:()=>{p.delete(c),m.set(c,l)},onFrame:e=>paintFrame(e(0)),signal:u.disposalSignal}))}function computePoints(e,t,n){return e.some(e=>e.key==null)||e.some(e=>typeof e.key==`string`),e.map(e=>({x:t.map(e.key),y:n.map(e.value)}))}export{computePoints,renderLine};
2
2
  //# sourceMappingURL=line-renderer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"line-renderer.js","names":[],"sources":["../../../src/charts/line/line-renderer.ts"],"sourcesContent":["import { warn } from '../../_dev';\nimport { resolveEasing } from '../../animation/easing';\nimport { resolveMotion } from '../../animation/motion';\nimport { startTween } from '../../animation/transition';\nimport { tweenNumber } from '../../animation/tween';\nimport { computeStyleRuns, type DatumMark, hasStyledRuns, type StyleRun } from '../../core/datum-style';\nimport { createSvgElement, setAttributes } from '../../svg/element';\nimport type { Point } from '../../svg/path';\nimport { linePath, monotonePath, stepPath } from '../../svg/path';\nimport type { ContinuousDatum, Scale, TransitionOption } from '../../types';\n\nexport interface LineRenderOptions {\n /** Plot-area bottom in area-local coordinates: the mount animation raises the line from here. */\n baselineY: number;\n color: string;\n curve: 'linear' | 'monotone' | 'step';\n /** Aborted when the owning chart is disposed: stops the transition's `requestAnimationFrame` loop from rescheduling. */\n disposalSignal?: AbortSignal;\n /** Per-datum presentation parallel to `points`; styled runs split the line into subpaths. */\n marks?: readonly DatumMark[];\n pointRadius: number;\n showPoints: boolean;\n /** Explicit width beats the `--prism-line-width` theme token. */\n strokeWidth?: number;\n transition?: TransitionOption;\n}\n\nconst activeAnimations = new WeakMap<SVGGElement, () => void>();\n// Geometry currently on screen, so an update interpolates from it even when the\n// series hides its point markers (the dots would otherwise be the only record).\nconst drawnPoints = new WeakMap<SVGGElement, Point[]>();\n\nfunction buildPath(pts: Point[], curve: LineRenderOptions['curve']): string {\n return curve === 'monotone' ? monotonePath(pts) : curve === 'step' ? stepPath(pts) : linePath(pts);\n}\n\nexport function renderLine(parent: SVGGElement, points: Point[], options: LineRenderOptions): void {\n const styled = hasStyledRuns(options.marks);\n const marks = options.marks ?? [];\n const motion = resolveMotion(options.transition);\n const dur = motion.duration;\n const easing = resolveEasing(motion.easing);\n\n let path: SVGPathElement | null = null;\n let runs: readonly StyleRun[] = [];\n let runPaths: SVGPathElement[] = [];\n\n if (styled) {\n runs = computeStyleRuns(marks, points.length);\n\n const existing = [...parent.querySelectorAll<SVGPathElement>('.prism-line-path')];\n\n for (const extra of existing.slice(runs.length)) extra.remove();\n\n runPaths = runs.map((_, r) => {\n const reuse = existing[r];\n\n if (reuse) return reuse;\n\n const created = createSvgElement('path', { class: 'prism-line-path', fill: 'none' });\n\n parent.appendChild(created);\n\n return created;\n });\n\n for (const [r, run] of runs.entries()) {\n setAttributes(runPaths[r], {\n opacity: run.opacity,\n stroke: options.color,\n 'stroke-dasharray': run.dash,\n 'stroke-width': options.strokeWidth ?? 2,\n });\n runPaths[r].style.strokeWidth = options.strokeWidth === undefined ? '' : String(options.strokeWidth);\n }\n } else {\n const existing = [...parent.querySelectorAll<SVGPathElement>('.prism-line-path')];\n\n path = existing[0] ?? null;\n\n for (const stale of existing.slice(1)) stale.remove();\n\n if (!path) {\n path = createSvgElement('path', { class: 'prism-line-path', fill: 'none' });\n parent.appendChild(path);\n }\n\n setAttributes(path, {\n opacity: undefined,\n stroke: options.color,\n 'stroke-dasharray': undefined,\n 'stroke-width': options.strokeWidth ?? 2,\n });\n path.style.strokeWidth = options.strokeWidth === undefined ? '' : String(options.strokeWidth);\n }\n\n const draw = (pts: Point[]): void => {\n if (styled) {\n for (const [r, run] of runs.entries()) {\n setAttributes(runPaths[r], { d: buildPath(pts.slice(run.start, run.end + 1), options.curve) });\n }\n } else {\n setAttributes(path!, { d: buildPath(pts, options.curve) });\n }\n };\n\n let dotsGroup = parent.querySelector<SVGGElement>('.prism-line-dots');\n\n if (options.showPoints) {\n if (!dotsGroup) {\n dotsGroup = createSvgElement('g', { class: 'prism-line-dots' });\n parent.appendChild(dotsGroup);\n }\n } else if (dotsGroup) {\n dotsGroup.remove();\n dotsGroup = null;\n }\n\n if (dur === 0) {\n draw(points);\n drawnPoints.set(parent, points);\n\n if (dotsGroup) {\n while (dotsGroup.children.length > points.length) dotsGroup.removeChild(dotsGroup.lastChild!);\n\n for (let i = 0; i < points.length; i++) {\n let c = dotsGroup.children[i] as SVGCircleElement | undefined;\n\n if (!c) {\n c = createSvgElement('circle', { class: 'prism-line-dot' });\n dotsGroup.appendChild(c);\n }\n\n setAttributes(c, {\n cx: points[i].x,\n cy: points[i].y,\n fill: options.color,\n opacity: marks[i]?.opacity,\n r: options.pointRadius,\n });\n }\n }\n\n return;\n }\n\n activeAnimations.get(parent)?.();\n\n const hasExisting = drawnPoints.has(parent);\n // A first render has no drawn shape to interpolate between: the mount animation\n // raises the whole line from the plot baseline instead.\n const isMount = !hasExisting;\n\n const fromPts: Point[] = isMount ? points.map((p) => ({ x: p.x, y: options.baselineY })) : [];\n\n if (dotsGroup) {\n while (dotsGroup.children.length > points.length) dotsGroup.removeChild(dotsGroup.lastChild!);\n\n for (let i = dotsGroup.children.length; i < points.length; i++) {\n const c = createSvgElement('circle', { class: 'prism-line-dot' });\n\n setAttributes(c, {\n fill: options.color,\n opacity: marks[i]?.opacity,\n r: options.pointRadius,\n });\n dotsGroup.appendChild(c);\n }\n\n for (let i = 0; i < dotsGroup.children.length; i++) {\n setAttributes(dotsGroup.children[i] as SVGCircleElement, { opacity: marks[i]?.opacity });\n }\n }\n\n if (!isMount) {\n const drawn = drawnPoints.get(parent) ?? points;\n\n for (const [i, to] of points.entries()) {\n fromPts.push(drawn[i] ?? drawn.at(-1) ?? to);\n }\n }\n\n const paintFrame = (e: number): void => {\n const interpolated: Point[] = points.map((to, i) => {\n const from = fromPts[i] ?? to;\n\n return { x: tweenNumber(from.x, to.x, e), y: tweenNumber(from.y, to.y, e) };\n });\n\n drawnPoints.set(parent, interpolated);\n draw(interpolated);\n\n if (dotsGroup) {\n for (let i = 0; i < points.length; i++) {\n const c = dotsGroup.children[i] as SVGCircleElement | undefined;\n\n if (c) setAttributes(c, { cx: interpolated[i].x, cy: interpolated[i].y });\n }\n }\n };\n\n // The starting geometry goes on screen immediately: `startTween` only paints on\n // the first frame callback, which would otherwise leave one blank frame.\n paintFrame(0);\n\n activeAnimations.set(\n parent,\n startTween({\n count: 1,\n duration: dur,\n easing,\n onComplete: () => {\n activeAnimations.delete(parent);\n drawnPoints.set(parent, points);\n },\n onFrame: (progress) => paintFrame(progress(0)),\n signal: options.disposalSignal,\n }),\n );\n}\n\nexport function computePoints(data: ContinuousDatum[], xScale: Scale<Date | number>, yScale: Scale<number>): Point[] {\n if (data.some((d) => d.key == null)) {\n warn(\n 'computePoints: datum.key is null or undefined: data must use the Datum shape { key, value }. Did you pass { x, y } instead?',\n );\n } else if (data.some((d) => typeof d.key === 'string')) {\n warn('computePoints: string keys are not supported for line/area charts: use numeric or Date keys.');\n }\n\n return data.map((d) => ({\n x: xScale.map(d.key as Date | number),\n y: yScale.map(d.value),\n }));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"line-renderer.js","names":[],"sources":["../../../src/charts/line/line-renderer.ts"],"sourcesContent":["import { warn } from '../../_dev';\nimport { resolveEasing } from '../../animation/easing';\nimport { resolveMotion } from '../../animation/motion';\nimport { cancelReveal, playTraceReveal } from '../../animation/reveal';\nimport { startTween } from '../../animation/transition';\nimport { tweenNumber } from '../../animation/tween';\nimport { computeStyleRuns, type DatumMark, hasStyledRuns, type StyleRun } from '../../core/datum-style';\nimport { createSvgElement, setAttributes } from '../../svg/element';\nimport type { Point } from '../../svg/path';\nimport { linePath, monotonePath, stepPath } from '../../svg/path';\nimport type { ContinuousDatum, Scale, TransitionOption } from '../../types';\n\nexport interface LineRenderOptions {\n /** Plot-area size in area-local coordinates: bounds the entrance mask. */\n bounds: { height: number; width: number };\n color: string;\n curve: 'linear' | 'monotone' | 'step';\n /** Aborted when the owning chart is disposed: stops the transition's `requestAnimationFrame` loop from rescheduling. */\n disposalSignal?: AbortSignal;\n /** Per-datum presentation parallel to `points`; styled runs split the line into subpaths. */\n marks?: readonly DatumMark[];\n pointRadius: number;\n showPoints: boolean;\n /** Explicit width beats the `--prism-line-width` theme token. */\n strokeWidth?: number;\n transition?: TransitionOption;\n}\n\nconst activeAnimations = new WeakMap<SVGGElement, () => void>();\n// Geometry currently on screen, so an update interpolates from it even when the\n// series hides its point markers (the dots would otherwise be the only record).\nconst drawnPoints = new WeakMap<SVGGElement, Point[]>();\n\nfunction buildPath(pts: Point[], curve: LineRenderOptions['curve']): string {\n return curve === 'monotone' ? monotonePath(pts) : curve === 'step' ? stepPath(pts) : linePath(pts);\n}\n\nexport function renderLine(parent: SVGGElement, points: Point[], options: LineRenderOptions): void {\n const styled = hasStyledRuns(options.marks);\n const marks = options.marks ?? [];\n const motion = resolveMotion(options.transition);\n const dur = motion.duration;\n const easing = resolveEasing(motion.easing);\n\n let path: SVGPathElement | null = null;\n let runs: readonly StyleRun[] = [];\n let runPaths: SVGPathElement[] = [];\n\n if (styled) {\n runs = computeStyleRuns(marks, points.length);\n\n const existing = [...parent.querySelectorAll<SVGPathElement>('.prism-line-path')];\n\n for (const extra of existing.slice(runs.length)) extra.remove();\n\n runPaths = runs.map((_, r) => {\n const reuse = existing[r];\n\n if (reuse) return reuse;\n\n const created = createSvgElement('path', { class: 'prism-line-path', fill: 'none' });\n\n parent.appendChild(created);\n\n return created;\n });\n\n for (const [r, run] of runs.entries()) {\n setAttributes(runPaths[r], {\n opacity: run.opacity,\n stroke: options.color,\n 'stroke-dasharray': run.dash,\n 'stroke-width': options.strokeWidth ?? 2,\n });\n runPaths[r].style.strokeWidth = options.strokeWidth === undefined ? '' : String(options.strokeWidth);\n }\n } else {\n const existing = [...parent.querySelectorAll<SVGPathElement>('.prism-line-path')];\n\n path = existing[0] ?? null;\n\n for (const stale of existing.slice(1)) stale.remove();\n\n if (!path) {\n path = createSvgElement('path', { class: 'prism-line-path', fill: 'none' });\n parent.appendChild(path);\n }\n\n setAttributes(path, {\n opacity: undefined,\n stroke: options.color,\n 'stroke-dasharray': undefined,\n 'stroke-width': options.strokeWidth ?? 2,\n });\n path.style.strokeWidth = options.strokeWidth === undefined ? '' : String(options.strokeWidth);\n }\n\n const draw = (pts: Point[]): void => {\n if (styled) {\n for (const [r, run] of runs.entries()) {\n setAttributes(runPaths[r], { d: buildPath(pts.slice(run.start, run.end + 1), options.curve) });\n }\n } else {\n setAttributes(path!, { d: buildPath(pts, options.curve) });\n }\n };\n\n let dotsGroup = parent.querySelector<SVGGElement>('.prism-line-dots');\n\n if (options.showPoints) {\n if (!dotsGroup) {\n dotsGroup = createSvgElement('g', { class: 'prism-line-dots' });\n parent.appendChild(dotsGroup);\n }\n } else if (dotsGroup) {\n dotsGroup.remove();\n dotsGroup = null;\n }\n\n if (dur === 0) {\n activeAnimations.get(parent)?.();\n cancelReveal(parent);\n draw(points);\n drawnPoints.set(parent, points);\n\n if (dotsGroup) {\n while (dotsGroup.children.length > points.length) dotsGroup.removeChild(dotsGroup.lastChild!);\n\n for (let i = 0; i < points.length; i++) {\n let c = dotsGroup.children[i] as SVGCircleElement | undefined;\n\n if (!c) {\n c = createSvgElement('circle', { class: 'prism-line-dot' });\n dotsGroup.appendChild(c);\n }\n\n setAttributes(c, {\n cx: points[i].x,\n cy: points[i].y,\n fill: options.color,\n opacity: marks[i]?.opacity,\n r: options.pointRadius,\n });\n }\n }\n\n return;\n }\n\n activeAnimations.get(parent)?.();\n cancelReveal(parent);\n\n const isMount = !drawnPoints.has(parent);\n\n if (dotsGroup) {\n while (dotsGroup.children.length > points.length) dotsGroup.removeChild(dotsGroup.lastChild!);\n\n for (let i = dotsGroup.children.length; i < points.length; i++) {\n const c = createSvgElement('circle', { class: 'prism-line-dot' });\n\n setAttributes(c, {\n fill: options.color,\n opacity: marks[i]?.opacity,\n r: options.pointRadius,\n });\n dotsGroup.appendChild(c);\n }\n\n for (let i = 0; i < dotsGroup.children.length; i++) {\n setAttributes(dotsGroup.children[i] as SVGCircleElement, { opacity: marks[i]?.opacity });\n }\n }\n\n if (isMount) {\n draw(points);\n drawnPoints.set(parent, points);\n\n if (dotsGroup) {\n for (const [i, point] of points.entries()) {\n const c = dotsGroup.children[i] as SVGCircleElement | undefined;\n\n if (c) setAttributes(c, { cx: point.x, cy: point.y });\n }\n }\n\n if (points.length > 1) {\n const renderedPath = path ?? runPaths[0];\n const strokeWidth = options.strokeWidth ?? (Number.parseFloat(getComputedStyle(renderedPath).strokeWidth) || 2);\n const traces = styled\n ? runs.map((run, i) => ({\n d: runPaths[i].getAttribute('d')!,\n end: run.end / (points.length - 1),\n start: run.start / (points.length - 1),\n }))\n : [{ d: path!.getAttribute('d')!, end: 1, start: 0 }];\n\n // A wide trace also uncovers markers as it reaches them, without a second timeline.\n playTraceReveal(\n parent,\n options.bounds,\n traces,\n Math.max(strokeWidth, options.showPoints ? options.pointRadius * 2 : 0) + 2,\n resolveMotion(options.transition, { defaultDuration: 420 }),\n options.disposalSignal,\n );\n }\n\n return;\n }\n\n const fromPts: Point[] = [];\n const drawn = drawnPoints.get(parent) ?? points;\n\n for (const [i, to] of points.entries()) {\n fromPts.push(drawn[i] ?? drawn.at(-1) ?? to);\n }\n\n const paintFrame = (e: number): void => {\n const interpolated: Point[] = points.map((to, i) => {\n const from = fromPts[i] ?? to;\n\n return { x: tweenNumber(from.x, to.x, e), y: tweenNumber(from.y, to.y, e) };\n });\n\n drawnPoints.set(parent, interpolated);\n draw(interpolated);\n\n if (dotsGroup) {\n for (let i = 0; i < points.length; i++) {\n const c = dotsGroup.children[i] as SVGCircleElement | undefined;\n\n if (c) setAttributes(c, { cx: interpolated[i].x, cy: interpolated[i].y });\n }\n }\n };\n\n // The starting geometry goes on screen immediately: `startTween` only paints on\n // the first frame callback, which would otherwise leave one blank frame.\n paintFrame(0);\n\n activeAnimations.set(\n parent,\n startTween({\n count: 1,\n duration: dur,\n easing,\n onComplete: () => {\n activeAnimations.delete(parent);\n drawnPoints.set(parent, points);\n },\n onFrame: (progress) => paintFrame(progress(0)),\n signal: options.disposalSignal,\n }),\n );\n}\n\nexport function computePoints(data: ContinuousDatum[], xScale: Scale<Date | number>, yScale: Scale<number>): Point[] {\n if (data.some((d) => d.key == null)) {\n warn(\n 'computePoints: datum.key is null or undefined: data must use the Datum shape { key, value }. Did you pass { x, y } instead?',\n );\n } else if (data.some((d) => typeof d.key === 'string')) {\n warn('computePoints: string keys are not supported for line/area charts: use numeric or Date keys.');\n }\n\n return data.map((d) => ({\n x: xScale.map(d.key as Date | number),\n y: yScale.map(d.value),\n }));\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../../svg/element.cjs"),t=require("../../svg/text.cjs"),n=require("../../core/chart-scaffold.cjs"),r=require("../../theme/index.cjs"),i=require("../../animation/easing.cjs"),a=require("../../animation/motion.cjs"),o=require("../../animation/tween.cjs"),s=require("../../animation/transition.cjs"),c=require("./pie-renderer.cjs");var l=2*Math.PI,u=12;function labelFits(e,n){let r=e.outerRadius-e.innerRadius,i=(e.outerRadius+e.innerRadius)/2,a=(e.endAngle-e.startAngle-2*e.padAngle)*i;return r>=u*1.4&&a>=t.estimateTextWidth(n,u)+8}var d=-Math.PI/2,f=Math.PI/2;function semiAngles(e){return e===`semi`?{end:f,start:d}:{end:l,start:0}}function createPieChart(t,l){let u=l.variant??`pie`,d=l.padPixels??(u===`pie`?0:8),f=l.cornerRadius??(u===`pie`?0:8),p=l.data,m=e.createSvgElement(`circle`,{class:`prism-pie-bg`,"pointer-events":`none`}),h=e.createSvgElement(`g`,{class:`prism-pie-slices`,"shape-rendering":`geometricPrecision`}),g=e.createSvgElement(`g`,{class:`prism-pie-labels`,"pointer-events":`none`}),_=[],v=[],y=null;function renderLabels(t){g.replaceChildren();for(let n=0;n<_.length;n++){let r=_[n],i=t[n];if(!i?.label||!labelFits(r,i.label))continue;let{x:a,y:o}=c.arcCentroid(r),s=e.createSvgElement(`text`,{class:`prism-pie-label`});e.setAttributes(s,{"dominant-baseline":`middle`,fill:`var(--prism-pie-label-color, #fff)`,"font-family":`var(--prism-font-family, system-ui)`,"font-size":`var(--prism-pie-label-size, 12px)`,"text-anchor":`middle`,x:a,y:o}),s.textContent=i.label,g.appendChild(s)}}return n.createRadialScaffold(t,{a11y:l.a11y,legend:l.legend,tooltip:l.tooltip},n=>{let{legend:b,svg:x,tooltip:S}=n;x.contains(m)||(x.appendChild(m),x.appendChild(h),x.appendChild(g));let{height:C,width:w}=n.dimensions,T=u===`semi`,E=w/2,D=T?C*.85:C/2,O=T?Math.min(E,D)-8:Math.min(w,C)/2-8,k=u===`pie`?0:Math.round(O*.55),A=l.innerRadius===void 0?k:l.innerRadius,j=Math.max(A+1,O),M=p,{end:N,start:P}=semiAngles(u);for(_=c.computeArcs(M,E,D,j,A,P,N,d,f,e=>r.seriesColor(e),!1),e.setAttributes(m,{cx:E,cy:D,r:A>0?A:0}),m.setAttribute(`style`,`fill:var(--prism-bg,#fff)`);h.children.length>_.length;)h.removeChild(h.lastChild);g.replaceChildren();let F=a.resolveMotion(l.transition),I=F.duration,L=i.resolveEasing(F.easing);for(let t=0;t<_.length;t++){let n=_[t],r=h.children[t];r||(r=e.createSvgElement(`path`,{class:`prism-pie-slice`}),h.appendChild(r)),r.setAttribute(`fill`,n.color),r.setAttribute(`stroke`,`none`),r.style.cursor=l.onClick||l.onHover?`pointer`:``}y?.();let R=v.length===0,z=v;y=s.startTween({count:1,duration:I,easing:L,onComplete:()=>renderLabels(M),onFrame:t=>{let n=t(0);if(R){let t=o.tweenNumber(P,N,n);v=_.map((n,r)=>{let i=h.children[r];if(!i)return n;if(t<=n.startAngle)return e.setAttributes(i,{d:``}),{...n,endAngle:n.startAngle};let a=Math.min(n.endAngle,t);return e.setAttributes(i,{d:c.arcPath({...n,endAngle:a})}),{...n,endAngle:a}});return}v=_.map((t,r)=>{let i=h.children[r],a=z.at(-1),s=z[r]??{...t,endAngle:a?.endAngle??P,startAngle:a?.endAngle??P},l={...t,endAngle:o.tweenNumber(s.endAngle,t.endAngle,n),innerRadius:o.tweenNumber(s.innerRadius,t.innerRadius,n),outerRadius:o.tweenNumber(s.outerRadius,t.outerRadius,n),padAngle:o.tweenNumber(s.padAngle,t.padAngle,n),startAngle:o.tweenNumber(s.startAngle,t.startAngle,n)};return i&&e.setAttributes(i,{d:l.endAngle-l.startAngle<=1e-6?``:c.arcPath(l)}),l})},signal:n.disposalSignal}),b?.update(_.map(e=>({color:e.color,name:e.slice.label??``}))),S?.hide();let B=_.reduce((e,t)=>e+Math.max(0,t.slice.value),0),V=-1,describe=e=>{let t=B>0?Math.round(Math.max(0,e.slice.value)/B*1e3)/10:0;return`${e.slice.label??`Slice ${e.index+1}`}: ${e.slice.value} (${t}%)`},setActive=e=>{V=e,h.classList.toggle(`prism-pie-focused`,e>=0);for(let[t,n]of[...h.children].entries())n.classList.toggle(`prism-pie-slice--active`,t===e)},activate=(e,r)=>{let i=_[e];if(!i)return;setActive(e),l.onHover?.({index:e,originalEvent:r,slice:i.slice});let a=describe(i);if(!S){n.announcer.announce(a);return}let{x:o,y:s}=c.arcCentroid(i),u=x.getBoundingClientRect(),d=t.getBoundingClientRect();S.show(o+(u.left-d.left),s+(u.top-d.top),{key:e,value:i.slice.value},{color:i.color,data:[],name:i.slice.label??``},a)},deactivate=()=>{setActive(-1),S?.hide(),n.announcer.clear(),l.onHover?.(null)},hitAt=e=>{let t=x.getBoundingClientRect();return hitTestArc(_,e.clientX-t.left,e.clientY-t.top,u)},onMouseMove=e=>{let t=hitAt(e);t>=0?t!==V&&activate(t,e):V>=0&&deactivate()},onClick=e=>{if(!l.onClick)return;let t=hitAt(e);t>=0&&l.onClick({index:t,originalEvent:e,slice:_[t].slice})},onKeyDown=e=>{let t=_.length;if(t===0)return;let n={ArrowDown:1,ArrowLeft:-1,ArrowRight:1,ArrowUp:-1};e.key in n?(e.preventDefault(),activate(V<0?n[e.key]>0?0:t-1:(V+n[e.key]+t)%t,e)):e.key===`Home`||e.key===`End`?(e.preventDefault(),activate(e.key===`Home`?0:t-1,e)):(e.key===`Enter`||e.key===` `)&&V>=0&&l.onClick?(e.preventDefault(),l.onClick({index:V,originalEvent:e,slice:_[V].slice})):e.key===`Escape`&&deactivate()};return{onClick,onKeyDown,onMouseLeave:deactivate,onMouseMove}},e=>{p=e})}function hitTestArc(e,t,n,r){for(let i=0;i<e.length;i++){let a=e[i],o=t-a.centerX,s=n-a.centerY,c=Math.sqrt(o*o+s*s);if(c<a.outerRadius&&c>=a.innerRadius){let e=Math.atan2(o,-s);if(r===`semi`){if(e>=a.startAngle-1e-9&&e<=a.endAngle+1e-9)return i}else{let t=e<0?e+l:e;if(t>=a.startAngle-1e-9&&t<=a.endAngle+1e-9)return i}}}return-1}exports.createPieChart=createPieChart;
1
+ const e=require("../../svg/element.cjs"),t=require("../../svg/text.cjs"),n=require("../../core/chart-scaffold.cjs"),r=require("../../theme/index.cjs"),i=require("../../animation/easing.cjs"),a=require("../../animation/motion.cjs"),o=require("../../animation/tween.cjs"),s=require("../../animation/transition.cjs"),c=require("./pie-renderer.cjs");var l=2*Math.PI,u=12;function labelFits(e,n){let r=e.outerRadius-e.innerRadius,i=(e.outerRadius+e.innerRadius)/2,a=(e.endAngle-e.startAngle-2*e.padAngle)*i;return r>=u*1.4&&a>=t.estimateTextWidth(n,u)+8}var d=-Math.PI/2,f=Math.PI/2;function semiAngles(e){return e===`semi`?{end:f,start:d}:{end:l,start:0}}function createPieChart(t,l){let u=l.variant??`pie`,d=l.padPixels??(u===`pie`?0:8),f=l.cornerRadius??(u===`pie`?0:8),p=l.data,m=e.createSvgElement(`circle`,{class:`prism-pie-bg`,"pointer-events":`none`}),h=e.createSvgElement(`g`,{class:`prism-pie-slices`,"shape-rendering":`geometricPrecision`}),g=e.createSvgElement(`g`,{class:`prism-pie-labels`,"pointer-events":`none`}),_=[],v=[],y=null;function renderLabels(t){g.replaceChildren();for(let n=0;n<_.length;n++){let r=_[n],i=t[n];if(!i?.label||!labelFits(r,i.label))continue;let{x:a,y:o}=c.arcCentroid(r),s=e.createSvgElement(`text`,{class:`prism-pie-label`});e.setAttributes(s,{"dominant-baseline":`middle`,fill:`var(--prism-pie-label-color, #fff)`,"font-family":`var(--prism-font-family, system-ui)`,"font-size":`var(--prism-pie-label-size, 12px)`,"text-anchor":`middle`,x:a,y:o}),s.textContent=i.label,g.appendChild(s)}}return n.createRadialScaffold(t,{a11y:l.a11y,legend:l.legend,tooltip:l.tooltip},n=>{let{legend:b,svg:x,tooltip:S}=n,C=p.some(e=>e.value>0);b?.update(C?p.map((e,t)=>({color:e.color??r.seriesColor(t),name:e.label??``})):[]),x.contains(m)||(x.appendChild(m),x.appendChild(h),x.appendChild(g));let{height:w,width:T}=n.dimensions,E=u===`semi`,D=T/2,O=E?w*.85:w/2,k=E?Math.min(D,O)-8:Math.min(T,w)/2-8,A=u===`pie`?0:Math.round(k*.55),j=l.innerRadius===void 0?A:l.innerRadius,M=Math.max(j+1,k),N=p,{end:P,start:F}=semiAngles(u);for(_=c.computeArcs(N,D,O,M,j,F,P,d,f,e=>r.seriesColor(e),!1),e.setAttributes(m,{cx:D,cy:O,r:j>0?j:0}),m.setAttribute(`style`,`fill:var(--prism-bg,#fff)`);h.children.length>_.length;)h.removeChild(h.lastChild);g.replaceChildren();let I=a.resolveMotion(l.transition),L=I.duration,R=i.resolveEasing(I.easing);for(let t=0;t<_.length;t++){let n=_[t],r=h.children[t];r||(r=e.createSvgElement(`path`,{class:`prism-pie-slice`}),h.appendChild(r)),r.setAttribute(`fill`,n.color),r.setAttribute(`stroke`,`none`),r.style.cursor=l.onClick||l.onHover?`pointer`:``}y?.();let z=v.length===0,B=v;y=s.startTween({count:1,duration:L,easing:R,onComplete:()=>renderLabels(N),onFrame:t=>{let n=t(0);if(z){let t=o.tweenNumber(F,P,n);v=_.map((n,r)=>{let i=h.children[r];if(!i)return n;if(t<=n.startAngle)return e.setAttributes(i,{d:``}),{...n,endAngle:n.startAngle};let a=Math.min(n.endAngle,t);return e.setAttributes(i,{d:c.arcPath({...n,endAngle:a})}),{...n,endAngle:a}});return}v=_.map((t,r)=>{let i=h.children[r],a=B.at(-1),s=B[r]??{...t,endAngle:a?.endAngle??F,startAngle:a?.endAngle??F},l={...t,endAngle:o.tweenNumber(s.endAngle,t.endAngle,n),innerRadius:o.tweenNumber(s.innerRadius,t.innerRadius,n),outerRadius:o.tweenNumber(s.outerRadius,t.outerRadius,n),padAngle:o.tweenNumber(s.padAngle,t.padAngle,n),startAngle:o.tweenNumber(s.startAngle,t.startAngle,n)};return i&&e.setAttributes(i,{d:l.endAngle-l.startAngle<=1e-6?``:c.arcPath(l)}),l})},signal:n.disposalSignal}),S?.hide();let V=_.reduce((e,t)=>e+Math.max(0,t.slice.value),0),H=-1,describe=e=>{let t=V>0?Math.round(Math.max(0,e.slice.value)/V*1e3)/10:0;return`${e.slice.label??`Slice ${e.index+1}`}: ${e.slice.value} (${t}%)`},setActive=e=>{H=e,h.classList.toggle(`prism-pie-focused`,e>=0);for(let[t,n]of[...h.children].entries())n.classList.toggle(`prism-pie-slice--active`,t===e)},activate=(e,r)=>{let i=_[e];if(!i)return;setActive(e),l.onHover?.({index:e,originalEvent:r,slice:i.slice});let a=describe(i);if(!S){n.announcer.announce(a);return}let{x:o,y:s}=c.arcCentroid(i),u=x.getBoundingClientRect(),d=t.getBoundingClientRect();S.show(o+(u.left-d.left),s+(u.top-d.top),{key:e,value:i.slice.value},{color:i.color,data:[],name:i.slice.label??``},a)},deactivate=()=>{setActive(-1),S?.hide(),n.announcer.clear(),l.onHover?.(null)},hitAt=e=>{let t=x.getBoundingClientRect();return hitTestArc(_,e.clientX-t.left,e.clientY-t.top,u)},onMouseMove=e=>{let t=hitAt(e);t>=0?t!==H&&activate(t,e):H>=0&&deactivate()},onClick=e=>{if(!l.onClick)return;let t=hitAt(e);t>=0&&l.onClick({index:t,originalEvent:e,slice:_[t].slice})},onKeyDown=e=>{let t=_.length;if(t===0)return;let n={ArrowDown:1,ArrowLeft:-1,ArrowRight:1,ArrowUp:-1};e.key in n?(e.preventDefault(),activate(H<0?n[e.key]>0?0:t-1:(H+n[e.key]+t)%t,e)):e.key===`Home`||e.key===`End`?(e.preventDefault(),activate(e.key===`Home`?0:t-1,e)):(e.key===`Enter`||e.key===` `)&&H>=0&&l.onClick?(e.preventDefault(),l.onClick({index:H,originalEvent:e,slice:_[H].slice})):e.key===`Escape`&&deactivate()};return{onClick,onKeyDown,onMouseLeave:deactivate,onMouseMove}},e=>{p=e})}function hitTestArc(e,t,n,r){for(let i=0;i<e.length;i++){let a=e[i],o=t-a.centerX,s=n-a.centerY,c=Math.sqrt(o*o+s*s);if(c<a.outerRadius&&c>=a.innerRadius){let e=Math.atan2(o,-s);if(r===`semi`){if(e>=a.startAngle-1e-9&&e<=a.endAngle+1e-9)return i}else{let t=e<0?e+l:e;if(t>=a.startAngle-1e-9&&t<=a.endAngle+1e-9)return i}}}return-1}exports.createPieChart=createPieChart;
2
2
  //# sourceMappingURL=pie-chart.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pie-chart.cjs","names":[],"sources":["../../../src/charts/pie/pie-chart.ts"],"sourcesContent":["import { resolveEasing } from '../../animation/easing';\nimport { resolveMotion } from '../../animation/motion';\nimport { startTween } from '../../animation/transition';\nimport { tweenNumber } from '../../animation/tween';\nimport type { ChartEventHandlers } from '../../core/chart-scaffold';\nimport { createRadialScaffold } from '../../core/chart-scaffold';\nimport { createSvgElement, setAttributes } from '../../svg/element';\nimport { estimateTextWidth } from '../../svg/text';\nimport { seriesColor } from '../../theme';\nimport type { ChartHandle, PieChartConfig, PieSliceConfig } from '../../types';\nimport { type Arc, arcCentroid, arcPath, computeArcs } from './pie-renderer';\n\nconst TWO_PI = 2 * Math.PI;\n/** Default `--prism-pie-label-size` in px, used to decide whether a label fits its slice. */\nconst LABEL_FONT_SIZE = 12;\n\n/** A label fits when its slice is wide enough along the centroid arc and thick enough radially. */\nfunction labelFits(arc: Arc, label: string): boolean {\n const thickness = arc.outerRadius - arc.innerRadius;\n const midRadius = (arc.outerRadius + arc.innerRadius) / 2;\n const arcLength = (arc.endAngle - arc.startAngle - 2 * arc.padAngle) * midRadius;\n\n return thickness >= LABEL_FONT_SIZE * 1.4 && arcLength >= estimateTextWidth(label, LABEL_FONT_SIZE) + 8;\n}\n\nconst SEMI_START = -Math.PI / 2; // -90° = 9-o'clock (left)\nconst SEMI_END = Math.PI / 2; // +90° = 3-o'clock (right) → true 180° half-circle\n\nfunction semiAngles(variant: PieChartConfig['variant']): { end: number; start: number } {\n return variant === 'semi' ? { end: SEMI_END, start: SEMI_START } : { end: TWO_PI, start: 0 };\n}\n\nexport function createPieChart(container: HTMLElement, config: PieChartConfig): ChartHandle<PieSliceConfig[]> {\n const variant = config.variant ?? 'pie';\n const padPixels = config.padPixels ?? (variant === 'pie' ? 0 : 8);\n const cornerRadius = config.cornerRadius ?? (variant === 'pie' ? 0 : 8);\n let data = config.data;\n\n // Pie SVG elements live directly on the SVG (not inside chartArea groups).\n // We create them once and reuse across renders.\n const bgCircle = createSvgElement('circle', { class: 'prism-pie-bg', 'pointer-events': 'none' });\n const pieGroup = createSvgElement('g', {\n class: 'prism-pie-slices',\n 'shape-rendering': 'geometricPrecision',\n });\n const labelGroup = createSvgElement('g', { class: 'prism-pie-labels', 'pointer-events': 'none' });\n\n // Current arcs shared between renderFn and event handlers via closure.\n let currentArcs: Arc[] = [];\n // The geometry actually on screen right now (mid-tween included): the update\n // morph interpolates from this, so interrupting a tween never jumps.\n let drawnArcs: Arc[] = [];\n let activeTween: (() => void) | null = null;\n\n function renderLabels(slices: PieSliceConfig[]): void {\n labelGroup.replaceChildren();\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n const slice = slices[i];\n\n if (!slice?.label || !labelFits(arc, slice.label)) continue;\n\n const { x, y } = arcCentroid(arc);\n const text = createSvgElement('text', { class: 'prism-pie-label' });\n\n setAttributes(text, {\n 'dominant-baseline': 'middle',\n fill: 'var(--prism-pie-label-color, #fff)',\n 'font-family': 'var(--prism-font-family, system-ui)',\n 'font-size': 'var(--prism-pie-label-size, 12px)',\n 'text-anchor': 'middle',\n x,\n y,\n });\n\n text.textContent = slice.label;\n labelGroup.appendChild(text);\n }\n }\n\n return createRadialScaffold(\n container,\n {\n a11y: config.a11y,\n legend: config.legend,\n tooltip: config.tooltip,\n },\n (ctx): ChartEventHandlers => {\n const { legend, svg, tooltip } = ctx;\n\n // Append pie groups to SVG on first render (idempotent).\n if (!svg.contains(bgCircle)) {\n svg.appendChild(bgCircle);\n svg.appendChild(pieGroup);\n svg.appendChild(labelGroup);\n }\n\n const { height: h, width: w } = ctx.dimensions;\n const isSemi = variant === 'semi';\n const cx = w / 2;\n const cy = isSemi ? h * 0.85 : h / 2;\n const padding = 8;\n const outer = isSemi ? Math.min(cx, cy) - padding : Math.min(w, h) / 2 - padding;\n const defaultInner = variant === 'pie' ? 0 : Math.round(outer * 0.55);\n const inner = config.innerRadius !== undefined ? config.innerRadius : defaultInner;\n const outerR = Math.max(inner + 1, outer);\n\n const slices = data;\n const { end, start } = semiAngles(variant);\n\n currentArcs = computeArcs(\n slices,\n cx,\n cy,\n outerR,\n inner,\n start,\n end,\n padPixels,\n cornerRadius,\n (i) => seriesColor(i),\n false,\n );\n\n setAttributes(bgCircle, { cx, cy, r: inner > 0 ? inner : 0 });\n bgCircle.setAttribute('style', 'fill:var(--prism-bg,#fff)');\n\n while (pieGroup.children.length > currentArcs.length) pieGroup.removeChild(pieGroup.lastChild!);\n\n labelGroup.replaceChildren();\n\n const motion = resolveMotion(config.transition);\n const dur = motion.duration;\n const easing = resolveEasing(motion.easing);\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n let path = pieGroup.children[i] as SVGPathElement | undefined;\n\n if (!path) {\n path = createSvgElement('path', { class: 'prism-pie-slice' });\n pieGroup.appendChild(path);\n }\n\n path.setAttribute('fill', arc.color);\n path.setAttribute('stroke', 'none');\n path.style.cursor = config.onClick || config.onHover ? 'pointer' : '';\n }\n\n activeTween?.();\n\n // Mount sweeps the ring in once; an update morphs every slice from its\n // drawn angles to the new ones, which reads as the data changing rather\n // than the chart redrawing.\n const isMount = drawnArcs.length === 0;\n const fromArcs = drawnArcs;\n\n activeTween = startTween({\n count: 1,\n duration: dur,\n easing,\n onComplete: () => renderLabels(slices),\n onFrame: (progress) => {\n const e = progress(0);\n\n if (isMount) {\n const revealAngle = tweenNumber(start, end, e);\n\n drawnArcs = currentArcs.map((a, j) => {\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n\n if (!el) return a;\n\n if (revealAngle <= a.startAngle) {\n setAttributes(el, { d: '' });\n\n return { ...a, endAngle: a.startAngle };\n }\n\n const visibleEnd = Math.min(a.endAngle, revealAngle);\n\n setAttributes(el, { d: arcPath({ ...a, endAngle: visibleEnd }) });\n\n return { ...a, endAngle: visibleEnd };\n });\n\n return;\n }\n\n drawnArcs = currentArcs.map((to, j) => {\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n const lastDrawn = fromArcs.at(-1);\n // A slice that was not drawn before grows out of the end of the last drawn one.\n const from: Arc = fromArcs[j] ?? {\n ...to,\n endAngle: lastDrawn?.endAngle ?? start,\n startAngle: lastDrawn?.endAngle ?? start,\n };\n const morphed: Arc = {\n ...to,\n endAngle: tweenNumber(from.endAngle, to.endAngle, e),\n innerRadius: tweenNumber(from.innerRadius, to.innerRadius, e),\n outerRadius: tweenNumber(from.outerRadius, to.outerRadius, e),\n padAngle: tweenNumber(from.padAngle, to.padAngle, e),\n startAngle: tweenNumber(from.startAngle, to.startAngle, e),\n };\n\n if (!el) return morphed;\n\n setAttributes(el, {\n d: morphed.endAngle - morphed.startAngle <= 1e-6 ? '' : arcPath(morphed),\n });\n\n return morphed;\n });\n },\n signal: ctx.disposalSignal,\n });\n\n legend?.update(currentArcs.map((arc) => ({ color: arc.color, name: arc.slice.label ?? '' })));\n tooltip?.hide();\n\n const total = currentArcs.reduce((sum, arc) => sum + Math.max(0, arc.slice.value), 0);\n let activeIndex = -1;\n\n const describe = (arc: Arc): string => {\n const percent = total > 0 ? Math.round((Math.max(0, arc.slice.value) / total) * 1000) / 10 : 0;\n\n return `${arc.slice.label ?? `Slice ${arc.index + 1}`}: ${arc.slice.value} (${percent}%)`;\n };\n\n const setActive = (index: number): void => {\n activeIndex = index;\n pieGroup.classList.toggle('prism-pie-focused', index >= 0);\n for (const [i, el] of [...pieGroup.children].entries())\n el.classList.toggle('prism-pie-slice--active', i === index);\n };\n\n const activate = (index: number, originalEvent: Event): void => {\n const arc = currentArcs[index];\n\n if (!arc) return;\n\n setActive(index);\n config.onHover?.({ index, originalEvent, slice: arc.slice });\n\n const text = describe(arc);\n\n if (!tooltip) {\n ctx.announcer.announce(text);\n\n return;\n }\n\n const { x, y } = arcCentroid(arc);\n const svgRect = svg.getBoundingClientRect();\n const contR = container.getBoundingClientRect();\n\n tooltip.show(\n x + (svgRect.left - contR.left),\n y + (svgRect.top - contR.top),\n { key: index, value: arc.slice.value },\n { color: arc.color, data: [], name: arc.slice.label ?? '' },\n text,\n );\n };\n\n const deactivate = (): void => {\n setActive(-1);\n tooltip?.hide();\n ctx.announcer.clear();\n config.onHover?.(null);\n };\n\n const hitAt = (e: MouseEvent): number => {\n const svgRect = svg.getBoundingClientRect();\n\n return hitTestArc(currentArcs, e.clientX - svgRect.left, e.clientY - svgRect.top, variant);\n };\n\n const onMouseMove = (e: MouseEvent): void => {\n const hit = hitAt(e);\n\n if (hit >= 0) {\n if (hit !== activeIndex) activate(hit, e);\n } else if (activeIndex >= 0) {\n deactivate();\n }\n };\n\n const onClick = (e: MouseEvent): void => {\n if (!config.onClick) return;\n\n const hit = hitAt(e);\n\n if (hit >= 0) config.onClick({ index: hit, originalEvent: e, slice: currentArcs[hit].slice });\n };\n\n const onKeyDown = (e: KeyboardEvent): void => {\n const count = currentArcs.length;\n\n if (count === 0) return;\n\n const step: Record<string, number> = { ArrowDown: 1, ArrowLeft: -1, ArrowRight: 1, ArrowUp: -1 };\n\n if (e.key in step) {\n e.preventDefault();\n activate(\n activeIndex < 0 ? (step[e.key] > 0 ? 0 : count - 1) : (activeIndex + step[e.key] + count) % count,\n e,\n );\n } else if (e.key === 'Home' || e.key === 'End') {\n e.preventDefault();\n activate(e.key === 'Home' ? 0 : count - 1, e);\n } else if ((e.key === 'Enter' || e.key === ' ') && activeIndex >= 0 && config.onClick) {\n e.preventDefault();\n config.onClick({ index: activeIndex, originalEvent: e, slice: currentArcs[activeIndex].slice });\n } else if (e.key === 'Escape') {\n deactivate();\n }\n };\n\n return { onClick, onKeyDown, onMouseLeave: deactivate, onMouseMove };\n },\n (next) => {\n data = next;\n },\n );\n}\n\nfunction hitTestArc(arcs: Arc[], mx: number, my: number, variant: PieChartConfig['variant']): number {\n for (let i = 0; i < arcs.length; i++) {\n const arc = arcs[i];\n const dx = mx - arc.centerX;\n const dy = my - arc.centerY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < arc.outerRadius && dist >= arc.innerRadius) {\n const rawAngle = Math.atan2(dx, -dy);\n\n if (variant === 'semi') {\n if (rawAngle >= arc.startAngle - 1e-9 && rawAngle <= arc.endAngle + 1e-9) return i;\n } else {\n const angle = rawAngle < 0 ? rawAngle + TWO_PI : rawAngle;\n\n if (angle >= arc.startAngle - 1e-9 && angle <= arc.endAngle + 1e-9) return i;\n }\n }\n }\n\n return -1;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pie-chart.cjs","names":[],"sources":["../../../src/charts/pie/pie-chart.ts"],"sourcesContent":["import { resolveEasing } from '../../animation/easing';\nimport { resolveMotion } from '../../animation/motion';\nimport { startTween } from '../../animation/transition';\nimport { tweenNumber } from '../../animation/tween';\nimport type { ChartEventHandlers } from '../../core/chart-scaffold';\nimport { createRadialScaffold } from '../../core/chart-scaffold';\nimport { createSvgElement, setAttributes } from '../../svg/element';\nimport { estimateTextWidth } from '../../svg/text';\nimport { seriesColor } from '../../theme';\nimport type { ChartHandle, PieChartConfig, PieSliceConfig } from '../../types';\nimport { type Arc, arcCentroid, arcPath, computeArcs } from './pie-renderer';\n\nconst TWO_PI = 2 * Math.PI;\n/** Default `--prism-pie-label-size` in px, used to decide whether a label fits its slice. */\nconst LABEL_FONT_SIZE = 12;\n\n/** A label fits when its slice is wide enough along the centroid arc and thick enough radially. */\nfunction labelFits(arc: Arc, label: string): boolean {\n const thickness = arc.outerRadius - arc.innerRadius;\n const midRadius = (arc.outerRadius + arc.innerRadius) / 2;\n const arcLength = (arc.endAngle - arc.startAngle - 2 * arc.padAngle) * midRadius;\n\n return thickness >= LABEL_FONT_SIZE * 1.4 && arcLength >= estimateTextWidth(label, LABEL_FONT_SIZE) + 8;\n}\n\nconst SEMI_START = -Math.PI / 2; // -90° = 9-o'clock (left)\nconst SEMI_END = Math.PI / 2; // +90° = 3-o'clock (right) → true 180° half-circle\n\nfunction semiAngles(variant: PieChartConfig['variant']): { end: number; start: number } {\n return variant === 'semi' ? { end: SEMI_END, start: SEMI_START } : { end: TWO_PI, start: 0 };\n}\n\nexport function createPieChart(container: HTMLElement, config: PieChartConfig): ChartHandle<PieSliceConfig[]> {\n const variant = config.variant ?? 'pie';\n const padPixels = config.padPixels ?? (variant === 'pie' ? 0 : 8);\n const cornerRadius = config.cornerRadius ?? (variant === 'pie' ? 0 : 8);\n let data = config.data;\n\n // Pie SVG elements live directly on the SVG (not inside chartArea groups).\n // We create them once and reuse across renders.\n const bgCircle = createSvgElement('circle', { class: 'prism-pie-bg', 'pointer-events': 'none' });\n const pieGroup = createSvgElement('g', {\n class: 'prism-pie-slices',\n 'shape-rendering': 'geometricPrecision',\n });\n const labelGroup = createSvgElement('g', { class: 'prism-pie-labels', 'pointer-events': 'none' });\n\n // Current arcs shared between renderFn and event handlers via closure.\n let currentArcs: Arc[] = [];\n // The geometry actually on screen right now (mid-tween included): the update\n // morph interpolates from this, so interrupting a tween never jumps.\n let drawnArcs: Arc[] = [];\n let activeTween: (() => void) | null = null;\n\n function renderLabels(slices: PieSliceConfig[]): void {\n labelGroup.replaceChildren();\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n const slice = slices[i];\n\n if (!slice?.label || !labelFits(arc, slice.label)) continue;\n\n const { x, y } = arcCentroid(arc);\n const text = createSvgElement('text', { class: 'prism-pie-label' });\n\n setAttributes(text, {\n 'dominant-baseline': 'middle',\n fill: 'var(--prism-pie-label-color, #fff)',\n 'font-family': 'var(--prism-font-family, system-ui)',\n 'font-size': 'var(--prism-pie-label-size, 12px)',\n 'text-anchor': 'middle',\n x,\n y,\n });\n\n text.textContent = slice.label;\n labelGroup.appendChild(text);\n }\n }\n\n return createRadialScaffold(\n container,\n {\n a11y: config.a11y,\n legend: config.legend,\n tooltip: config.tooltip,\n },\n (ctx): ChartEventHandlers => {\n const { legend, svg, tooltip } = ctx;\n const hasSlices = data.some((slice) => slice.value > 0);\n legend?.update(\n hasSlices ? data.map((slice, i) => ({ color: slice.color ?? seriesColor(i), name: slice.label ?? '' })) : [],\n );\n\n // Append pie groups to SVG on first render (idempotent).\n if (!svg.contains(bgCircle)) {\n svg.appendChild(bgCircle);\n svg.appendChild(pieGroup);\n svg.appendChild(labelGroup);\n }\n\n const { height: h, width: w } = ctx.dimensions;\n const isSemi = variant === 'semi';\n const cx = w / 2;\n const cy = isSemi ? h * 0.85 : h / 2;\n const padding = 8;\n const outer = isSemi ? Math.min(cx, cy) - padding : Math.min(w, h) / 2 - padding;\n const defaultInner = variant === 'pie' ? 0 : Math.round(outer * 0.55);\n const inner = config.innerRadius !== undefined ? config.innerRadius : defaultInner;\n const outerR = Math.max(inner + 1, outer);\n\n const slices = data;\n const { end, start } = semiAngles(variant);\n\n currentArcs = computeArcs(\n slices,\n cx,\n cy,\n outerR,\n inner,\n start,\n end,\n padPixels,\n cornerRadius,\n (i) => seriesColor(i),\n false,\n );\n\n setAttributes(bgCircle, { cx, cy, r: inner > 0 ? inner : 0 });\n bgCircle.setAttribute('style', 'fill:var(--prism-bg,#fff)');\n\n while (pieGroup.children.length > currentArcs.length) pieGroup.removeChild(pieGroup.lastChild!);\n\n labelGroup.replaceChildren();\n\n const motion = resolveMotion(config.transition);\n const dur = motion.duration;\n const easing = resolveEasing(motion.easing);\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n let path = pieGroup.children[i] as SVGPathElement | undefined;\n\n if (!path) {\n path = createSvgElement('path', { class: 'prism-pie-slice' });\n pieGroup.appendChild(path);\n }\n\n path.setAttribute('fill', arc.color);\n path.setAttribute('stroke', 'none');\n path.style.cursor = config.onClick || config.onHover ? 'pointer' : '';\n }\n\n activeTween?.();\n\n // Mount sweeps the ring in once; an update morphs every slice from its\n // drawn angles to the new ones, which reads as the data changing rather\n // than the chart redrawing.\n const isMount = drawnArcs.length === 0;\n const fromArcs = drawnArcs;\n\n activeTween = startTween({\n count: 1,\n duration: dur,\n easing,\n onComplete: () => renderLabels(slices),\n onFrame: (progress) => {\n const e = progress(0);\n\n if (isMount) {\n const revealAngle = tweenNumber(start, end, e);\n\n drawnArcs = currentArcs.map((a, j) => {\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n\n if (!el) return a;\n\n if (revealAngle <= a.startAngle) {\n setAttributes(el, { d: '' });\n\n return { ...a, endAngle: a.startAngle };\n }\n\n const visibleEnd = Math.min(a.endAngle, revealAngle);\n\n setAttributes(el, { d: arcPath({ ...a, endAngle: visibleEnd }) });\n\n return { ...a, endAngle: visibleEnd };\n });\n\n return;\n }\n\n drawnArcs = currentArcs.map((to, j) => {\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n const lastDrawn = fromArcs.at(-1);\n // A slice that was not drawn before grows out of the end of the last drawn one.\n const from: Arc = fromArcs[j] ?? {\n ...to,\n endAngle: lastDrawn?.endAngle ?? start,\n startAngle: lastDrawn?.endAngle ?? start,\n };\n const morphed: Arc = {\n ...to,\n endAngle: tweenNumber(from.endAngle, to.endAngle, e),\n innerRadius: tweenNumber(from.innerRadius, to.innerRadius, e),\n outerRadius: tweenNumber(from.outerRadius, to.outerRadius, e),\n padAngle: tweenNumber(from.padAngle, to.padAngle, e),\n startAngle: tweenNumber(from.startAngle, to.startAngle, e),\n };\n\n if (!el) return morphed;\n\n setAttributes(el, {\n d: morphed.endAngle - morphed.startAngle <= 1e-6 ? '' : arcPath(morphed),\n });\n\n return morphed;\n });\n },\n signal: ctx.disposalSignal,\n });\n\n tooltip?.hide();\n\n const total = currentArcs.reduce((sum, arc) => sum + Math.max(0, arc.slice.value), 0);\n let activeIndex = -1;\n\n const describe = (arc: Arc): string => {\n const percent = total > 0 ? Math.round((Math.max(0, arc.slice.value) / total) * 1000) / 10 : 0;\n\n return `${arc.slice.label ?? `Slice ${arc.index + 1}`}: ${arc.slice.value} (${percent}%)`;\n };\n\n const setActive = (index: number): void => {\n activeIndex = index;\n pieGroup.classList.toggle('prism-pie-focused', index >= 0);\n for (const [i, el] of [...pieGroup.children].entries())\n el.classList.toggle('prism-pie-slice--active', i === index);\n };\n\n const activate = (index: number, originalEvent: Event): void => {\n const arc = currentArcs[index];\n\n if (!arc) return;\n\n setActive(index);\n config.onHover?.({ index, originalEvent, slice: arc.slice });\n\n const text = describe(arc);\n\n if (!tooltip) {\n ctx.announcer.announce(text);\n\n return;\n }\n\n const { x, y } = arcCentroid(arc);\n const svgRect = svg.getBoundingClientRect();\n const contR = container.getBoundingClientRect();\n\n tooltip.show(\n x + (svgRect.left - contR.left),\n y + (svgRect.top - contR.top),\n { key: index, value: arc.slice.value },\n { color: arc.color, data: [], name: arc.slice.label ?? '' },\n text,\n );\n };\n\n const deactivate = (): void => {\n setActive(-1);\n tooltip?.hide();\n ctx.announcer.clear();\n config.onHover?.(null);\n };\n\n const hitAt = (e: MouseEvent): number => {\n const svgRect = svg.getBoundingClientRect();\n\n return hitTestArc(currentArcs, e.clientX - svgRect.left, e.clientY - svgRect.top, variant);\n };\n\n const onMouseMove = (e: MouseEvent): void => {\n const hit = hitAt(e);\n\n if (hit >= 0) {\n if (hit !== activeIndex) activate(hit, e);\n } else if (activeIndex >= 0) {\n deactivate();\n }\n };\n\n const onClick = (e: MouseEvent): void => {\n if (!config.onClick) return;\n\n const hit = hitAt(e);\n\n if (hit >= 0) config.onClick({ index: hit, originalEvent: e, slice: currentArcs[hit].slice });\n };\n\n const onKeyDown = (e: KeyboardEvent): void => {\n const count = currentArcs.length;\n\n if (count === 0) return;\n\n const step: Record<string, number> = { ArrowDown: 1, ArrowLeft: -1, ArrowRight: 1, ArrowUp: -1 };\n\n if (e.key in step) {\n e.preventDefault();\n activate(\n activeIndex < 0 ? (step[e.key] > 0 ? 0 : count - 1) : (activeIndex + step[e.key] + count) % count,\n e,\n );\n } else if (e.key === 'Home' || e.key === 'End') {\n e.preventDefault();\n activate(e.key === 'Home' ? 0 : count - 1, e);\n } else if ((e.key === 'Enter' || e.key === ' ') && activeIndex >= 0 && config.onClick) {\n e.preventDefault();\n config.onClick({ index: activeIndex, originalEvent: e, slice: currentArcs[activeIndex].slice });\n } else if (e.key === 'Escape') {\n deactivate();\n }\n };\n\n return { onClick, onKeyDown, onMouseLeave: deactivate, onMouseMove };\n },\n (next) => {\n data = next;\n },\n );\n}\n\nfunction hitTestArc(arcs: Arc[], mx: number, my: number, variant: PieChartConfig['variant']): number {\n for (let i = 0; i < arcs.length; i++) {\n const arc = arcs[i];\n const dx = mx - arc.centerX;\n const dy = my - arc.centerY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < arc.outerRadius && dist >= arc.innerRadius) {\n const rawAngle = Math.atan2(dx, -dy);\n\n if (variant === 'semi') {\n if (rawAngle >= arc.startAngle - 1e-9 && rawAngle <= arc.endAngle + 1e-9) return i;\n } else {\n const angle = rawAngle < 0 ? rawAngle + TWO_PI : rawAngle;\n\n if (angle >= arc.startAngle - 1e-9 && angle <= arc.endAngle + 1e-9) return i;\n }\n }\n }\n\n return -1;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"pie-chart.d.ts","sourceRoot":"","sources":["../../../src/charts/pie/pie-chart.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAuB/E,wBAAgB,cAAc,CAAC,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,cAAc,GAAG,WAAW,CAAC,cAAc,EAAE,CAAC,CAyS5G"}
1
+ {"version":3,"file":"pie-chart.d.ts","sourceRoot":"","sources":["../../../src/charts/pie/pie-chart.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAuB/E,wBAAgB,cAAc,CAAC,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,cAAc,GAAG,WAAW,CAAC,cAAc,EAAE,CAAC,CA4S5G"}
@@ -1,2 +1,2 @@
1
- import{createSvgElement as e,setAttributes as t}from"../../svg/element.js";import{estimateTextWidth as n}from"../../svg/text.js";import{createRadialScaffold as r}from"../../core/chart-scaffold.js";import{seriesColor as i}from"../../theme/index.js";import{resolveEasing as a}from"../../animation/easing.js";import{resolveMotion as o}from"../../animation/motion.js";import{tweenNumber as s}from"../../animation/tween.js";import{startTween as c}from"../../animation/transition.js";import{arcCentroid as l,arcPath as u,computeArcs as d}from"./pie-renderer.js";var f=2*Math.PI,p=12;function labelFits(e,t){let r=e.outerRadius-e.innerRadius,i=(e.outerRadius+e.innerRadius)/2,a=(e.endAngle-e.startAngle-2*e.padAngle)*i;return r>=p*1.4&&a>=n(t,p)+8}var m=-Math.PI/2,h=Math.PI/2;function semiAngles(e){return e===`semi`?{end:h,start:m}:{end:f,start:0}}function createPieChart(n,f){let p=f.variant??`pie`,m=f.padPixels??(p===`pie`?0:8),h=f.cornerRadius??(p===`pie`?0:8),g=f.data,_=e(`circle`,{class:`prism-pie-bg`,"pointer-events":`none`}),v=e(`g`,{class:`prism-pie-slices`,"shape-rendering":`geometricPrecision`}),y=e(`g`,{class:`prism-pie-labels`,"pointer-events":`none`}),b=[],x=[],S=null;function renderLabels(n){y.replaceChildren();for(let r=0;r<b.length;r++){let i=b[r],a=n[r];if(!a?.label||!labelFits(i,a.label))continue;let{x:o,y:s}=l(i),c=e(`text`,{class:`prism-pie-label`});t(c,{"dominant-baseline":`middle`,fill:`var(--prism-pie-label-color, #fff)`,"font-family":`var(--prism-font-family, system-ui)`,"font-size":`var(--prism-pie-label-size, 12px)`,"text-anchor":`middle`,x:o,y:s}),c.textContent=a.label,y.appendChild(c)}}return r(n,{a11y:f.a11y,legend:f.legend,tooltip:f.tooltip},r=>{let{legend:C,svg:w,tooltip:T}=r;w.contains(_)||(w.appendChild(_),w.appendChild(v),w.appendChild(y));let{height:E,width:D}=r.dimensions,O=p===`semi`,k=D/2,A=O?E*.85:E/2,j=O?Math.min(k,A)-8:Math.min(D,E)/2-8,M=p===`pie`?0:Math.round(j*.55),N=f.innerRadius===void 0?M:f.innerRadius,P=Math.max(N+1,j),F=g,{end:I,start:L}=semiAngles(p);for(b=d(F,k,A,P,N,L,I,m,h,e=>i(e),!1),t(_,{cx:k,cy:A,r:N>0?N:0}),_.setAttribute(`style`,`fill:var(--prism-bg,#fff)`);v.children.length>b.length;)v.removeChild(v.lastChild);y.replaceChildren();let R=o(f.transition),z=R.duration,B=a(R.easing);for(let t=0;t<b.length;t++){let n=b[t],r=v.children[t];r||(r=e(`path`,{class:`prism-pie-slice`}),v.appendChild(r)),r.setAttribute(`fill`,n.color),r.setAttribute(`stroke`,`none`),r.style.cursor=f.onClick||f.onHover?`pointer`:``}S?.();let V=x.length===0,H=x;S=c({count:1,duration:z,easing:B,onComplete:()=>renderLabels(F),onFrame:e=>{let n=e(0);if(V){let e=s(L,I,n);x=b.map((n,r)=>{let i=v.children[r];if(!i)return n;if(e<=n.startAngle)return t(i,{d:``}),{...n,endAngle:n.startAngle};let a=Math.min(n.endAngle,e);return t(i,{d:u({...n,endAngle:a})}),{...n,endAngle:a}});return}x=b.map((e,r)=>{let i=v.children[r],a=H.at(-1),o=H[r]??{...e,endAngle:a?.endAngle??L,startAngle:a?.endAngle??L},c={...e,endAngle:s(o.endAngle,e.endAngle,n),innerRadius:s(o.innerRadius,e.innerRadius,n),outerRadius:s(o.outerRadius,e.outerRadius,n),padAngle:s(o.padAngle,e.padAngle,n),startAngle:s(o.startAngle,e.startAngle,n)};return i&&t(i,{d:c.endAngle-c.startAngle<=1e-6?``:u(c)}),c})},signal:r.disposalSignal}),C?.update(b.map(e=>({color:e.color,name:e.slice.label??``}))),T?.hide();let U=b.reduce((e,t)=>e+Math.max(0,t.slice.value),0),W=-1,describe=e=>{let t=U>0?Math.round(Math.max(0,e.slice.value)/U*1e3)/10:0;return`${e.slice.label??`Slice ${e.index+1}`}: ${e.slice.value} (${t}%)`},setActive=e=>{W=e,v.classList.toggle(`prism-pie-focused`,e>=0);for(let[t,n]of[...v.children].entries())n.classList.toggle(`prism-pie-slice--active`,t===e)},activate=(e,t)=>{let i=b[e];if(!i)return;setActive(e),f.onHover?.({index:e,originalEvent:t,slice:i.slice});let a=describe(i);if(!T){r.announcer.announce(a);return}let{x:o,y:s}=l(i),c=w.getBoundingClientRect(),u=n.getBoundingClientRect();T.show(o+(c.left-u.left),s+(c.top-u.top),{key:e,value:i.slice.value},{color:i.color,data:[],name:i.slice.label??``},a)},deactivate=()=>{setActive(-1),T?.hide(),r.announcer.clear(),f.onHover?.(null)},hitAt=e=>{let t=w.getBoundingClientRect();return hitTestArc(b,e.clientX-t.left,e.clientY-t.top,p)},onMouseMove=e=>{let t=hitAt(e);t>=0?t!==W&&activate(t,e):W>=0&&deactivate()},onClick=e=>{if(!f.onClick)return;let t=hitAt(e);t>=0&&f.onClick({index:t,originalEvent:e,slice:b[t].slice})},onKeyDown=e=>{let t=b.length;if(t===0)return;let n={ArrowDown:1,ArrowLeft:-1,ArrowRight:1,ArrowUp:-1};e.key in n?(e.preventDefault(),activate(W<0?n[e.key]>0?0:t-1:(W+n[e.key]+t)%t,e)):e.key===`Home`||e.key===`End`?(e.preventDefault(),activate(e.key===`Home`?0:t-1,e)):(e.key===`Enter`||e.key===` `)&&W>=0&&f.onClick?(e.preventDefault(),f.onClick({index:W,originalEvent:e,slice:b[W].slice})):e.key===`Escape`&&deactivate()};return{onClick,onKeyDown,onMouseLeave:deactivate,onMouseMove}},e=>{g=e})}function hitTestArc(e,t,n,r){for(let i=0;i<e.length;i++){let a=e[i],o=t-a.centerX,s=n-a.centerY,c=Math.sqrt(o*o+s*s);if(c<a.outerRadius&&c>=a.innerRadius){let e=Math.atan2(o,-s);if(r===`semi`){if(e>=a.startAngle-1e-9&&e<=a.endAngle+1e-9)return i}else{let t=e<0?e+f:e;if(t>=a.startAngle-1e-9&&t<=a.endAngle+1e-9)return i}}}return-1}export{createPieChart};
1
+ import{createSvgElement as e,setAttributes as t}from"../../svg/element.js";import{estimateTextWidth as n}from"../../svg/text.js";import{createRadialScaffold as r}from"../../core/chart-scaffold.js";import{seriesColor as i}from"../../theme/index.js";import{resolveEasing as a}from"../../animation/easing.js";import{resolveMotion as o}from"../../animation/motion.js";import{tweenNumber as s}from"../../animation/tween.js";import{startTween as c}from"../../animation/transition.js";import{arcCentroid as l,arcPath as u,computeArcs as d}from"./pie-renderer.js";var f=2*Math.PI,p=12;function labelFits(e,t){let r=e.outerRadius-e.innerRadius,i=(e.outerRadius+e.innerRadius)/2,a=(e.endAngle-e.startAngle-2*e.padAngle)*i;return r>=p*1.4&&a>=n(t,p)+8}var m=-Math.PI/2,h=Math.PI/2;function semiAngles(e){return e===`semi`?{end:h,start:m}:{end:f,start:0}}function createPieChart(n,f){let p=f.variant??`pie`,m=f.padPixels??(p===`pie`?0:8),h=f.cornerRadius??(p===`pie`?0:8),g=f.data,_=e(`circle`,{class:`prism-pie-bg`,"pointer-events":`none`}),v=e(`g`,{class:`prism-pie-slices`,"shape-rendering":`geometricPrecision`}),y=e(`g`,{class:`prism-pie-labels`,"pointer-events":`none`}),b=[],x=[],S=null;function renderLabels(n){y.replaceChildren();for(let r=0;r<b.length;r++){let i=b[r],a=n[r];if(!a?.label||!labelFits(i,a.label))continue;let{x:o,y:s}=l(i),c=e(`text`,{class:`prism-pie-label`});t(c,{"dominant-baseline":`middle`,fill:`var(--prism-pie-label-color, #fff)`,"font-family":`var(--prism-font-family, system-ui)`,"font-size":`var(--prism-pie-label-size, 12px)`,"text-anchor":`middle`,x:o,y:s}),c.textContent=a.label,y.appendChild(c)}}return r(n,{a11y:f.a11y,legend:f.legend,tooltip:f.tooltip},r=>{let{legend:C,svg:w,tooltip:T}=r,E=g.some(e=>e.value>0);C?.update(E?g.map((e,t)=>({color:e.color??i(t),name:e.label??``})):[]),w.contains(_)||(w.appendChild(_),w.appendChild(v),w.appendChild(y));let{height:D,width:O}=r.dimensions,k=p===`semi`,A=O/2,j=k?D*.85:D/2,M=k?Math.min(A,j)-8:Math.min(O,D)/2-8,N=p===`pie`?0:Math.round(M*.55),P=f.innerRadius===void 0?N:f.innerRadius,F=Math.max(P+1,M),I=g,{end:L,start:R}=semiAngles(p);for(b=d(I,A,j,F,P,R,L,m,h,e=>i(e),!1),t(_,{cx:A,cy:j,r:P>0?P:0}),_.setAttribute(`style`,`fill:var(--prism-bg,#fff)`);v.children.length>b.length;)v.removeChild(v.lastChild);y.replaceChildren();let z=o(f.transition),B=z.duration,V=a(z.easing);for(let t=0;t<b.length;t++){let n=b[t],r=v.children[t];r||(r=e(`path`,{class:`prism-pie-slice`}),v.appendChild(r)),r.setAttribute(`fill`,n.color),r.setAttribute(`stroke`,`none`),r.style.cursor=f.onClick||f.onHover?`pointer`:``}S?.();let H=x.length===0,U=x;S=c({count:1,duration:B,easing:V,onComplete:()=>renderLabels(I),onFrame:e=>{let n=e(0);if(H){let e=s(R,L,n);x=b.map((n,r)=>{let i=v.children[r];if(!i)return n;if(e<=n.startAngle)return t(i,{d:``}),{...n,endAngle:n.startAngle};let a=Math.min(n.endAngle,e);return t(i,{d:u({...n,endAngle:a})}),{...n,endAngle:a}});return}x=b.map((e,r)=>{let i=v.children[r],a=U.at(-1),o=U[r]??{...e,endAngle:a?.endAngle??R,startAngle:a?.endAngle??R},c={...e,endAngle:s(o.endAngle,e.endAngle,n),innerRadius:s(o.innerRadius,e.innerRadius,n),outerRadius:s(o.outerRadius,e.outerRadius,n),padAngle:s(o.padAngle,e.padAngle,n),startAngle:s(o.startAngle,e.startAngle,n)};return i&&t(i,{d:c.endAngle-c.startAngle<=1e-6?``:u(c)}),c})},signal:r.disposalSignal}),T?.hide();let W=b.reduce((e,t)=>e+Math.max(0,t.slice.value),0),G=-1,describe=e=>{let t=W>0?Math.round(Math.max(0,e.slice.value)/W*1e3)/10:0;return`${e.slice.label??`Slice ${e.index+1}`}: ${e.slice.value} (${t}%)`},setActive=e=>{G=e,v.classList.toggle(`prism-pie-focused`,e>=0);for(let[t,n]of[...v.children].entries())n.classList.toggle(`prism-pie-slice--active`,t===e)},activate=(e,t)=>{let i=b[e];if(!i)return;setActive(e),f.onHover?.({index:e,originalEvent:t,slice:i.slice});let a=describe(i);if(!T){r.announcer.announce(a);return}let{x:o,y:s}=l(i),c=w.getBoundingClientRect(),u=n.getBoundingClientRect();T.show(o+(c.left-u.left),s+(c.top-u.top),{key:e,value:i.slice.value},{color:i.color,data:[],name:i.slice.label??``},a)},deactivate=()=>{setActive(-1),T?.hide(),r.announcer.clear(),f.onHover?.(null)},hitAt=e=>{let t=w.getBoundingClientRect();return hitTestArc(b,e.clientX-t.left,e.clientY-t.top,p)},onMouseMove=e=>{let t=hitAt(e);t>=0?t!==G&&activate(t,e):G>=0&&deactivate()},onClick=e=>{if(!f.onClick)return;let t=hitAt(e);t>=0&&f.onClick({index:t,originalEvent:e,slice:b[t].slice})},onKeyDown=e=>{let t=b.length;if(t===0)return;let n={ArrowDown:1,ArrowLeft:-1,ArrowRight:1,ArrowUp:-1};e.key in n?(e.preventDefault(),activate(G<0?n[e.key]>0?0:t-1:(G+n[e.key]+t)%t,e)):e.key===`Home`||e.key===`End`?(e.preventDefault(),activate(e.key===`Home`?0:t-1,e)):(e.key===`Enter`||e.key===` `)&&G>=0&&f.onClick?(e.preventDefault(),f.onClick({index:G,originalEvent:e,slice:b[G].slice})):e.key===`Escape`&&deactivate()};return{onClick,onKeyDown,onMouseLeave:deactivate,onMouseMove}},e=>{g=e})}function hitTestArc(e,t,n,r){for(let i=0;i<e.length;i++){let a=e[i],o=t-a.centerX,s=n-a.centerY,c=Math.sqrt(o*o+s*s);if(c<a.outerRadius&&c>=a.innerRadius){let e=Math.atan2(o,-s);if(r===`semi`){if(e>=a.startAngle-1e-9&&e<=a.endAngle+1e-9)return i}else{let t=e<0?e+f:e;if(t>=a.startAngle-1e-9&&t<=a.endAngle+1e-9)return i}}}return-1}export{createPieChart};
2
2
  //# sourceMappingURL=pie-chart.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pie-chart.js","names":[],"sources":["../../../src/charts/pie/pie-chart.ts"],"sourcesContent":["import { resolveEasing } from '../../animation/easing';\nimport { resolveMotion } from '../../animation/motion';\nimport { startTween } from '../../animation/transition';\nimport { tweenNumber } from '../../animation/tween';\nimport type { ChartEventHandlers } from '../../core/chart-scaffold';\nimport { createRadialScaffold } from '../../core/chart-scaffold';\nimport { createSvgElement, setAttributes } from '../../svg/element';\nimport { estimateTextWidth } from '../../svg/text';\nimport { seriesColor } from '../../theme';\nimport type { ChartHandle, PieChartConfig, PieSliceConfig } from '../../types';\nimport { type Arc, arcCentroid, arcPath, computeArcs } from './pie-renderer';\n\nconst TWO_PI = 2 * Math.PI;\n/** Default `--prism-pie-label-size` in px, used to decide whether a label fits its slice. */\nconst LABEL_FONT_SIZE = 12;\n\n/** A label fits when its slice is wide enough along the centroid arc and thick enough radially. */\nfunction labelFits(arc: Arc, label: string): boolean {\n const thickness = arc.outerRadius - arc.innerRadius;\n const midRadius = (arc.outerRadius + arc.innerRadius) / 2;\n const arcLength = (arc.endAngle - arc.startAngle - 2 * arc.padAngle) * midRadius;\n\n return thickness >= LABEL_FONT_SIZE * 1.4 && arcLength >= estimateTextWidth(label, LABEL_FONT_SIZE) + 8;\n}\n\nconst SEMI_START = -Math.PI / 2; // -90° = 9-o'clock (left)\nconst SEMI_END = Math.PI / 2; // +90° = 3-o'clock (right) → true 180° half-circle\n\nfunction semiAngles(variant: PieChartConfig['variant']): { end: number; start: number } {\n return variant === 'semi' ? { end: SEMI_END, start: SEMI_START } : { end: TWO_PI, start: 0 };\n}\n\nexport function createPieChart(container: HTMLElement, config: PieChartConfig): ChartHandle<PieSliceConfig[]> {\n const variant = config.variant ?? 'pie';\n const padPixels = config.padPixels ?? (variant === 'pie' ? 0 : 8);\n const cornerRadius = config.cornerRadius ?? (variant === 'pie' ? 0 : 8);\n let data = config.data;\n\n // Pie SVG elements live directly on the SVG (not inside chartArea groups).\n // We create them once and reuse across renders.\n const bgCircle = createSvgElement('circle', { class: 'prism-pie-bg', 'pointer-events': 'none' });\n const pieGroup = createSvgElement('g', {\n class: 'prism-pie-slices',\n 'shape-rendering': 'geometricPrecision',\n });\n const labelGroup = createSvgElement('g', { class: 'prism-pie-labels', 'pointer-events': 'none' });\n\n // Current arcs shared between renderFn and event handlers via closure.\n let currentArcs: Arc[] = [];\n // The geometry actually on screen right now (mid-tween included): the update\n // morph interpolates from this, so interrupting a tween never jumps.\n let drawnArcs: Arc[] = [];\n let activeTween: (() => void) | null = null;\n\n function renderLabels(slices: PieSliceConfig[]): void {\n labelGroup.replaceChildren();\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n const slice = slices[i];\n\n if (!slice?.label || !labelFits(arc, slice.label)) continue;\n\n const { x, y } = arcCentroid(arc);\n const text = createSvgElement('text', { class: 'prism-pie-label' });\n\n setAttributes(text, {\n 'dominant-baseline': 'middle',\n fill: 'var(--prism-pie-label-color, #fff)',\n 'font-family': 'var(--prism-font-family, system-ui)',\n 'font-size': 'var(--prism-pie-label-size, 12px)',\n 'text-anchor': 'middle',\n x,\n y,\n });\n\n text.textContent = slice.label;\n labelGroup.appendChild(text);\n }\n }\n\n return createRadialScaffold(\n container,\n {\n a11y: config.a11y,\n legend: config.legend,\n tooltip: config.tooltip,\n },\n (ctx): ChartEventHandlers => {\n const { legend, svg, tooltip } = ctx;\n\n // Append pie groups to SVG on first render (idempotent).\n if (!svg.contains(bgCircle)) {\n svg.appendChild(bgCircle);\n svg.appendChild(pieGroup);\n svg.appendChild(labelGroup);\n }\n\n const { height: h, width: w } = ctx.dimensions;\n const isSemi = variant === 'semi';\n const cx = w / 2;\n const cy = isSemi ? h * 0.85 : h / 2;\n const padding = 8;\n const outer = isSemi ? Math.min(cx, cy) - padding : Math.min(w, h) / 2 - padding;\n const defaultInner = variant === 'pie' ? 0 : Math.round(outer * 0.55);\n const inner = config.innerRadius !== undefined ? config.innerRadius : defaultInner;\n const outerR = Math.max(inner + 1, outer);\n\n const slices = data;\n const { end, start } = semiAngles(variant);\n\n currentArcs = computeArcs(\n slices,\n cx,\n cy,\n outerR,\n inner,\n start,\n end,\n padPixels,\n cornerRadius,\n (i) => seriesColor(i),\n false,\n );\n\n setAttributes(bgCircle, { cx, cy, r: inner > 0 ? inner : 0 });\n bgCircle.setAttribute('style', 'fill:var(--prism-bg,#fff)');\n\n while (pieGroup.children.length > currentArcs.length) pieGroup.removeChild(pieGroup.lastChild!);\n\n labelGroup.replaceChildren();\n\n const motion = resolveMotion(config.transition);\n const dur = motion.duration;\n const easing = resolveEasing(motion.easing);\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n let path = pieGroup.children[i] as SVGPathElement | undefined;\n\n if (!path) {\n path = createSvgElement('path', { class: 'prism-pie-slice' });\n pieGroup.appendChild(path);\n }\n\n path.setAttribute('fill', arc.color);\n path.setAttribute('stroke', 'none');\n path.style.cursor = config.onClick || config.onHover ? 'pointer' : '';\n }\n\n activeTween?.();\n\n // Mount sweeps the ring in once; an update morphs every slice from its\n // drawn angles to the new ones, which reads as the data changing rather\n // than the chart redrawing.\n const isMount = drawnArcs.length === 0;\n const fromArcs = drawnArcs;\n\n activeTween = startTween({\n count: 1,\n duration: dur,\n easing,\n onComplete: () => renderLabels(slices),\n onFrame: (progress) => {\n const e = progress(0);\n\n if (isMount) {\n const revealAngle = tweenNumber(start, end, e);\n\n drawnArcs = currentArcs.map((a, j) => {\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n\n if (!el) return a;\n\n if (revealAngle <= a.startAngle) {\n setAttributes(el, { d: '' });\n\n return { ...a, endAngle: a.startAngle };\n }\n\n const visibleEnd = Math.min(a.endAngle, revealAngle);\n\n setAttributes(el, { d: arcPath({ ...a, endAngle: visibleEnd }) });\n\n return { ...a, endAngle: visibleEnd };\n });\n\n return;\n }\n\n drawnArcs = currentArcs.map((to, j) => {\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n const lastDrawn = fromArcs.at(-1);\n // A slice that was not drawn before grows out of the end of the last drawn one.\n const from: Arc = fromArcs[j] ?? {\n ...to,\n endAngle: lastDrawn?.endAngle ?? start,\n startAngle: lastDrawn?.endAngle ?? start,\n };\n const morphed: Arc = {\n ...to,\n endAngle: tweenNumber(from.endAngle, to.endAngle, e),\n innerRadius: tweenNumber(from.innerRadius, to.innerRadius, e),\n outerRadius: tweenNumber(from.outerRadius, to.outerRadius, e),\n padAngle: tweenNumber(from.padAngle, to.padAngle, e),\n startAngle: tweenNumber(from.startAngle, to.startAngle, e),\n };\n\n if (!el) return morphed;\n\n setAttributes(el, {\n d: morphed.endAngle - morphed.startAngle <= 1e-6 ? '' : arcPath(morphed),\n });\n\n return morphed;\n });\n },\n signal: ctx.disposalSignal,\n });\n\n legend?.update(currentArcs.map((arc) => ({ color: arc.color, name: arc.slice.label ?? '' })));\n tooltip?.hide();\n\n const total = currentArcs.reduce((sum, arc) => sum + Math.max(0, arc.slice.value), 0);\n let activeIndex = -1;\n\n const describe = (arc: Arc): string => {\n const percent = total > 0 ? Math.round((Math.max(0, arc.slice.value) / total) * 1000) / 10 : 0;\n\n return `${arc.slice.label ?? `Slice ${arc.index + 1}`}: ${arc.slice.value} (${percent}%)`;\n };\n\n const setActive = (index: number): void => {\n activeIndex = index;\n pieGroup.classList.toggle('prism-pie-focused', index >= 0);\n for (const [i, el] of [...pieGroup.children].entries())\n el.classList.toggle('prism-pie-slice--active', i === index);\n };\n\n const activate = (index: number, originalEvent: Event): void => {\n const arc = currentArcs[index];\n\n if (!arc) return;\n\n setActive(index);\n config.onHover?.({ index, originalEvent, slice: arc.slice });\n\n const text = describe(arc);\n\n if (!tooltip) {\n ctx.announcer.announce(text);\n\n return;\n }\n\n const { x, y } = arcCentroid(arc);\n const svgRect = svg.getBoundingClientRect();\n const contR = container.getBoundingClientRect();\n\n tooltip.show(\n x + (svgRect.left - contR.left),\n y + (svgRect.top - contR.top),\n { key: index, value: arc.slice.value },\n { color: arc.color, data: [], name: arc.slice.label ?? '' },\n text,\n );\n };\n\n const deactivate = (): void => {\n setActive(-1);\n tooltip?.hide();\n ctx.announcer.clear();\n config.onHover?.(null);\n };\n\n const hitAt = (e: MouseEvent): number => {\n const svgRect = svg.getBoundingClientRect();\n\n return hitTestArc(currentArcs, e.clientX - svgRect.left, e.clientY - svgRect.top, variant);\n };\n\n const onMouseMove = (e: MouseEvent): void => {\n const hit = hitAt(e);\n\n if (hit >= 0) {\n if (hit !== activeIndex) activate(hit, e);\n } else if (activeIndex >= 0) {\n deactivate();\n }\n };\n\n const onClick = (e: MouseEvent): void => {\n if (!config.onClick) return;\n\n const hit = hitAt(e);\n\n if (hit >= 0) config.onClick({ index: hit, originalEvent: e, slice: currentArcs[hit].slice });\n };\n\n const onKeyDown = (e: KeyboardEvent): void => {\n const count = currentArcs.length;\n\n if (count === 0) return;\n\n const step: Record<string, number> = { ArrowDown: 1, ArrowLeft: -1, ArrowRight: 1, ArrowUp: -1 };\n\n if (e.key in step) {\n e.preventDefault();\n activate(\n activeIndex < 0 ? (step[e.key] > 0 ? 0 : count - 1) : (activeIndex + step[e.key] + count) % count,\n e,\n );\n } else if (e.key === 'Home' || e.key === 'End') {\n e.preventDefault();\n activate(e.key === 'Home' ? 0 : count - 1, e);\n } else if ((e.key === 'Enter' || e.key === ' ') && activeIndex >= 0 && config.onClick) {\n e.preventDefault();\n config.onClick({ index: activeIndex, originalEvent: e, slice: currentArcs[activeIndex].slice });\n } else if (e.key === 'Escape') {\n deactivate();\n }\n };\n\n return { onClick, onKeyDown, onMouseLeave: deactivate, onMouseMove };\n },\n (next) => {\n data = next;\n },\n );\n}\n\nfunction hitTestArc(arcs: Arc[], mx: number, my: number, variant: PieChartConfig['variant']): number {\n for (let i = 0; i < arcs.length; i++) {\n const arc = arcs[i];\n const dx = mx - arc.centerX;\n const dy = my - arc.centerY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < arc.outerRadius && dist >= arc.innerRadius) {\n const rawAngle = Math.atan2(dx, -dy);\n\n if (variant === 'semi') {\n if (rawAngle >= arc.startAngle - 1e-9 && rawAngle <= arc.endAngle + 1e-9) return i;\n } else {\n const angle = rawAngle < 0 ? rawAngle + TWO_PI : rawAngle;\n\n if (angle >= arc.startAngle - 1e-9 && angle <= arc.endAngle + 1e-9) return i;\n }\n }\n }\n\n return -1;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pie-chart.js","names":[],"sources":["../../../src/charts/pie/pie-chart.ts"],"sourcesContent":["import { resolveEasing } from '../../animation/easing';\nimport { resolveMotion } from '../../animation/motion';\nimport { startTween } from '../../animation/transition';\nimport { tweenNumber } from '../../animation/tween';\nimport type { ChartEventHandlers } from '../../core/chart-scaffold';\nimport { createRadialScaffold } from '../../core/chart-scaffold';\nimport { createSvgElement, setAttributes } from '../../svg/element';\nimport { estimateTextWidth } from '../../svg/text';\nimport { seriesColor } from '../../theme';\nimport type { ChartHandle, PieChartConfig, PieSliceConfig } from '../../types';\nimport { type Arc, arcCentroid, arcPath, computeArcs } from './pie-renderer';\n\nconst TWO_PI = 2 * Math.PI;\n/** Default `--prism-pie-label-size` in px, used to decide whether a label fits its slice. */\nconst LABEL_FONT_SIZE = 12;\n\n/** A label fits when its slice is wide enough along the centroid arc and thick enough radially. */\nfunction labelFits(arc: Arc, label: string): boolean {\n const thickness = arc.outerRadius - arc.innerRadius;\n const midRadius = (arc.outerRadius + arc.innerRadius) / 2;\n const arcLength = (arc.endAngle - arc.startAngle - 2 * arc.padAngle) * midRadius;\n\n return thickness >= LABEL_FONT_SIZE * 1.4 && arcLength >= estimateTextWidth(label, LABEL_FONT_SIZE) + 8;\n}\n\nconst SEMI_START = -Math.PI / 2; // -90° = 9-o'clock (left)\nconst SEMI_END = Math.PI / 2; // +90° = 3-o'clock (right) → true 180° half-circle\n\nfunction semiAngles(variant: PieChartConfig['variant']): { end: number; start: number } {\n return variant === 'semi' ? { end: SEMI_END, start: SEMI_START } : { end: TWO_PI, start: 0 };\n}\n\nexport function createPieChart(container: HTMLElement, config: PieChartConfig): ChartHandle<PieSliceConfig[]> {\n const variant = config.variant ?? 'pie';\n const padPixels = config.padPixels ?? (variant === 'pie' ? 0 : 8);\n const cornerRadius = config.cornerRadius ?? (variant === 'pie' ? 0 : 8);\n let data = config.data;\n\n // Pie SVG elements live directly on the SVG (not inside chartArea groups).\n // We create them once and reuse across renders.\n const bgCircle = createSvgElement('circle', { class: 'prism-pie-bg', 'pointer-events': 'none' });\n const pieGroup = createSvgElement('g', {\n class: 'prism-pie-slices',\n 'shape-rendering': 'geometricPrecision',\n });\n const labelGroup = createSvgElement('g', { class: 'prism-pie-labels', 'pointer-events': 'none' });\n\n // Current arcs shared between renderFn and event handlers via closure.\n let currentArcs: Arc[] = [];\n // The geometry actually on screen right now (mid-tween included): the update\n // morph interpolates from this, so interrupting a tween never jumps.\n let drawnArcs: Arc[] = [];\n let activeTween: (() => void) | null = null;\n\n function renderLabels(slices: PieSliceConfig[]): void {\n labelGroup.replaceChildren();\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n const slice = slices[i];\n\n if (!slice?.label || !labelFits(arc, slice.label)) continue;\n\n const { x, y } = arcCentroid(arc);\n const text = createSvgElement('text', { class: 'prism-pie-label' });\n\n setAttributes(text, {\n 'dominant-baseline': 'middle',\n fill: 'var(--prism-pie-label-color, #fff)',\n 'font-family': 'var(--prism-font-family, system-ui)',\n 'font-size': 'var(--prism-pie-label-size, 12px)',\n 'text-anchor': 'middle',\n x,\n y,\n });\n\n text.textContent = slice.label;\n labelGroup.appendChild(text);\n }\n }\n\n return createRadialScaffold(\n container,\n {\n a11y: config.a11y,\n legend: config.legend,\n tooltip: config.tooltip,\n },\n (ctx): ChartEventHandlers => {\n const { legend, svg, tooltip } = ctx;\n const hasSlices = data.some((slice) => slice.value > 0);\n legend?.update(\n hasSlices ? data.map((slice, i) => ({ color: slice.color ?? seriesColor(i), name: slice.label ?? '' })) : [],\n );\n\n // Append pie groups to SVG on first render (idempotent).\n if (!svg.contains(bgCircle)) {\n svg.appendChild(bgCircle);\n svg.appendChild(pieGroup);\n svg.appendChild(labelGroup);\n }\n\n const { height: h, width: w } = ctx.dimensions;\n const isSemi = variant === 'semi';\n const cx = w / 2;\n const cy = isSemi ? h * 0.85 : h / 2;\n const padding = 8;\n const outer = isSemi ? Math.min(cx, cy) - padding : Math.min(w, h) / 2 - padding;\n const defaultInner = variant === 'pie' ? 0 : Math.round(outer * 0.55);\n const inner = config.innerRadius !== undefined ? config.innerRadius : defaultInner;\n const outerR = Math.max(inner + 1, outer);\n\n const slices = data;\n const { end, start } = semiAngles(variant);\n\n currentArcs = computeArcs(\n slices,\n cx,\n cy,\n outerR,\n inner,\n start,\n end,\n padPixels,\n cornerRadius,\n (i) => seriesColor(i),\n false,\n );\n\n setAttributes(bgCircle, { cx, cy, r: inner > 0 ? inner : 0 });\n bgCircle.setAttribute('style', 'fill:var(--prism-bg,#fff)');\n\n while (pieGroup.children.length > currentArcs.length) pieGroup.removeChild(pieGroup.lastChild!);\n\n labelGroup.replaceChildren();\n\n const motion = resolveMotion(config.transition);\n const dur = motion.duration;\n const easing = resolveEasing(motion.easing);\n\n for (let i = 0; i < currentArcs.length; i++) {\n const arc = currentArcs[i];\n let path = pieGroup.children[i] as SVGPathElement | undefined;\n\n if (!path) {\n path = createSvgElement('path', { class: 'prism-pie-slice' });\n pieGroup.appendChild(path);\n }\n\n path.setAttribute('fill', arc.color);\n path.setAttribute('stroke', 'none');\n path.style.cursor = config.onClick || config.onHover ? 'pointer' : '';\n }\n\n activeTween?.();\n\n // Mount sweeps the ring in once; an update morphs every slice from its\n // drawn angles to the new ones, which reads as the data changing rather\n // than the chart redrawing.\n const isMount = drawnArcs.length === 0;\n const fromArcs = drawnArcs;\n\n activeTween = startTween({\n count: 1,\n duration: dur,\n easing,\n onComplete: () => renderLabels(slices),\n onFrame: (progress) => {\n const e = progress(0);\n\n if (isMount) {\n const revealAngle = tweenNumber(start, end, e);\n\n drawnArcs = currentArcs.map((a, j) => {\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n\n if (!el) return a;\n\n if (revealAngle <= a.startAngle) {\n setAttributes(el, { d: '' });\n\n return { ...a, endAngle: a.startAngle };\n }\n\n const visibleEnd = Math.min(a.endAngle, revealAngle);\n\n setAttributes(el, { d: arcPath({ ...a, endAngle: visibleEnd }) });\n\n return { ...a, endAngle: visibleEnd };\n });\n\n return;\n }\n\n drawnArcs = currentArcs.map((to, j) => {\n const el = pieGroup.children[j] as SVGPathElement | undefined;\n const lastDrawn = fromArcs.at(-1);\n // A slice that was not drawn before grows out of the end of the last drawn one.\n const from: Arc = fromArcs[j] ?? {\n ...to,\n endAngle: lastDrawn?.endAngle ?? start,\n startAngle: lastDrawn?.endAngle ?? start,\n };\n const morphed: Arc = {\n ...to,\n endAngle: tweenNumber(from.endAngle, to.endAngle, e),\n innerRadius: tweenNumber(from.innerRadius, to.innerRadius, e),\n outerRadius: tweenNumber(from.outerRadius, to.outerRadius, e),\n padAngle: tweenNumber(from.padAngle, to.padAngle, e),\n startAngle: tweenNumber(from.startAngle, to.startAngle, e),\n };\n\n if (!el) return morphed;\n\n setAttributes(el, {\n d: morphed.endAngle - morphed.startAngle <= 1e-6 ? '' : arcPath(morphed),\n });\n\n return morphed;\n });\n },\n signal: ctx.disposalSignal,\n });\n\n tooltip?.hide();\n\n const total = currentArcs.reduce((sum, arc) => sum + Math.max(0, arc.slice.value), 0);\n let activeIndex = -1;\n\n const describe = (arc: Arc): string => {\n const percent = total > 0 ? Math.round((Math.max(0, arc.slice.value) / total) * 1000) / 10 : 0;\n\n return `${arc.slice.label ?? `Slice ${arc.index + 1}`}: ${arc.slice.value} (${percent}%)`;\n };\n\n const setActive = (index: number): void => {\n activeIndex = index;\n pieGroup.classList.toggle('prism-pie-focused', index >= 0);\n for (const [i, el] of [...pieGroup.children].entries())\n el.classList.toggle('prism-pie-slice--active', i === index);\n };\n\n const activate = (index: number, originalEvent: Event): void => {\n const arc = currentArcs[index];\n\n if (!arc) return;\n\n setActive(index);\n config.onHover?.({ index, originalEvent, slice: arc.slice });\n\n const text = describe(arc);\n\n if (!tooltip) {\n ctx.announcer.announce(text);\n\n return;\n }\n\n const { x, y } = arcCentroid(arc);\n const svgRect = svg.getBoundingClientRect();\n const contR = container.getBoundingClientRect();\n\n tooltip.show(\n x + (svgRect.left - contR.left),\n y + (svgRect.top - contR.top),\n { key: index, value: arc.slice.value },\n { color: arc.color, data: [], name: arc.slice.label ?? '' },\n text,\n );\n };\n\n const deactivate = (): void => {\n setActive(-1);\n tooltip?.hide();\n ctx.announcer.clear();\n config.onHover?.(null);\n };\n\n const hitAt = (e: MouseEvent): number => {\n const svgRect = svg.getBoundingClientRect();\n\n return hitTestArc(currentArcs, e.clientX - svgRect.left, e.clientY - svgRect.top, variant);\n };\n\n const onMouseMove = (e: MouseEvent): void => {\n const hit = hitAt(e);\n\n if (hit >= 0) {\n if (hit !== activeIndex) activate(hit, e);\n } else if (activeIndex >= 0) {\n deactivate();\n }\n };\n\n const onClick = (e: MouseEvent): void => {\n if (!config.onClick) return;\n\n const hit = hitAt(e);\n\n if (hit >= 0) config.onClick({ index: hit, originalEvent: e, slice: currentArcs[hit].slice });\n };\n\n const onKeyDown = (e: KeyboardEvent): void => {\n const count = currentArcs.length;\n\n if (count === 0) return;\n\n const step: Record<string, number> = { ArrowDown: 1, ArrowLeft: -1, ArrowRight: 1, ArrowUp: -1 };\n\n if (e.key in step) {\n e.preventDefault();\n activate(\n activeIndex < 0 ? (step[e.key] > 0 ? 0 : count - 1) : (activeIndex + step[e.key] + count) % count,\n e,\n );\n } else if (e.key === 'Home' || e.key === 'End') {\n e.preventDefault();\n activate(e.key === 'Home' ? 0 : count - 1, e);\n } else if ((e.key === 'Enter' || e.key === ' ') && activeIndex >= 0 && config.onClick) {\n e.preventDefault();\n config.onClick({ index: activeIndex, originalEvent: e, slice: currentArcs[activeIndex].slice });\n } else if (e.key === 'Escape') {\n deactivate();\n }\n };\n\n return { onClick, onKeyDown, onMouseLeave: deactivate, onMouseMove };\n },\n (next) => {\n data = next;\n },\n );\n}\n\nfunction hitTestArc(arcs: Arc[], mx: number, my: number, variant: PieChartConfig['variant']): number {\n for (let i = 0; i < arcs.length; i++) {\n const arc = arcs[i];\n const dx = mx - arc.centerX;\n const dy = my - arc.centerY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < arc.outerRadius && dist >= arc.innerRadius) {\n const rawAngle = Math.atan2(dx, -dy);\n\n if (variant === 'semi') {\n if (rawAngle >= arc.startAngle - 1e-9 && rawAngle <= arc.endAngle + 1e-9) return i;\n } else {\n const angle = rawAngle < 0 ? rawAngle + TWO_PI : rawAngle;\n\n if (angle >= arc.startAngle - 1e-9 && angle <= arc.endAngle + 1e-9) return i;\n }\n }\n }\n\n return -1;\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { createScatterChart } from './scatter-chart';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/charts/scatter/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../svg/element.cjs"),t=require("../../axes/axis.cjs"),n=require("../../axes/grid.cjs"),r=require("../../core/cartesian-scales.cjs"),i=require("../../core/layout.cjs"),a=require("../../core/chart-scaffold.cjs"),o=require("../../core/series-id.cjs"),s=require("../../interaction/crosshair.cjs"),c=require("../../interaction/series-interaction.cjs"),l=require("../../theme/index.cjs"),u=require("../../interaction/scatter-interaction.cjs"),d=require("./scatter-renderer.cjs");function createScatterChart(f,p){let m=null,h=null,g=p.series;return a.createChartScaffold(f,p,(f,_)=>{let{groups:v,legend:y,tooltip:b}=f;y?.update(g.map((e,t)=>({color:l.seriesColor(t,e.color),name:e.name})));let x=f.dimensions,S=i.chartArea(x.width,x.height,x.margin),C=g.map(e=>e.data),w=C.flat().map(e=>e.key),T=C.flat().map(e=>e.value);if(w.length===0){a.clearCartesianDom(v,y,b,m),h?.replaceChildren();return}p.crosshair&&!m&&(m=s.createCrosshair(f.chartArea,p.crosshair));let E=r.buildXScale(w,S.width),D=r.buildYScale(T,S.height,!1),O=p.xAxis===!1?!1:p.xAxis??{},k=p.yAxis!==!1&&{grid:!0,...p.yAxis};for(k&&k.grid?n.renderGrid(v.grid,D,k.grid,S.width,`horizontal`,t.resolveTicks(D,k,t.resolveTickCount(k,S.height,`left`))):v.grid.replaceChildren(),O&&O.grid&&n.renderGrid(v.grid,E,O.grid,S.height,`vertical`,t.resolveTicks(E,O,t.resolveTickCount(O,S.width,`bottom`))),O?(t.positionAxis(v.xAxis,O.position??`bottom`,S.width,S.height),t.renderAxis(v.xAxis,E,O,S.width,`bottom`)):v.xAxis.replaceChildren(),k?(t.positionAxis(v.yAxis,k.position??`left`,S.width,S.height),t.renderAxis(v.yAxis,D,k,S.height,`left`)):v.yAxis.replaceChildren();v.series.children.length>g.length;)v.series.removeChild(v.series.lastChild);let A=[];for(let[t,n]of g.entries()){let r=v.series.children[t];r||(r=e.createSvgElement(`g`,{class:`prism-scatter-series`}),v.series.appendChild(r)),r.setAttribute(`data-series-id`,o.seriesDomId(n,t));let i=d.computeScatterPoints(n.data,E,D);A.push(i),d.renderScatterPoints(r,i,{color:l.seriesColor(t,n.color),disposalSignal:f.disposalSignal,marks:n.data,pointRadius:n.pointRadius??3,transition:_!==`resize`&&p.transition})}return b?.hide(),h=c.ensureMarkerGroup(f.chartArea,h),u.createScatterInteraction({announcer:f.announcer,colors:()=>g.map((e,t)=>l.seriesColor(t,e.color)),crosshair:m,dims:()=>f.dimensions,getData:()=>C,getPoints:()=>A,getSeriesList:()=>g,margin:x.margin,markers:h,onClick:p.onClick,onHover:p.onHover,seriesGroup:v.series,svg:f.svg,tooltip:b,tooltipFormat:typeof p.tooltip==`object`?p.tooltip:void 0})},e=>{g=e})}exports.createScatterChart=createScatterChart;
2
+ //# sourceMappingURL=scatter-chart.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scatter-chart.cjs","names":[],"sources":["../../../src/charts/scatter/scatter-chart.ts"],"sourcesContent":["import { positionAxis, renderAxis, resolveTickCount, resolveTicks } from '../../axes/axis';\nimport { renderGrid } from '../../axes/grid';\nimport { buildXScale, buildYScale } from '../../core/cartesian-scales';\nimport { clearCartesianDom, createChartScaffold } from '../../core/chart-scaffold';\nimport { chartArea } from '../../core/layout';\nimport { seriesDomId } from '../../core/series-id';\nimport { createCrosshair } from '../../interaction/crosshair';\nimport { createScatterInteraction } from '../../interaction/scatter-interaction';\nimport { ensureMarkerGroup } from '../../interaction/series-interaction';\nimport { createSvgElement } from '../../svg/element';\nimport type { Point } from '../../svg/path';\nimport { seriesColor } from '../../theme';\nimport type { ChartHandle, ScatterChartConfig, ScatterSeriesConfig, XAxisConfig, YAxisConfig } from '../../types';\nimport { computeScatterPoints, renderScatterPoints } from './scatter-renderer';\n\nexport function createScatterChart(\n container: HTMLElement,\n config: ScatterChartConfig,\n): ChartHandle<ScatterSeriesConfig[]> {\n let crosshair: ReturnType<typeof createCrosshair> | null = null;\n let markers: SVGGElement | null = null;\n let seriesList = config.series;\n\n return createChartScaffold(\n container,\n config,\n (ctx, reason) => {\n const { groups, legend, tooltip } = ctx;\n legend?.update(seriesList.map((s, i) => ({ color: seriesColor(i, s.color), name: s.name })));\n const dims = ctx.dimensions;\n const area = chartArea(dims.width, dims.height, dims.margin);\n const allData = seriesList.map((series) => series.data);\n const allX = allData.flat().map((datum) => datum.key);\n const allY = allData.flat().map((datum) => datum.value);\n\n if (allX.length === 0) {\n clearCartesianDom(groups, legend, tooltip, crosshair);\n markers?.replaceChildren();\n\n return;\n }\n\n if (config.crosshair && !crosshair) {\n crosshair = createCrosshair(ctx.chartArea, config.crosshair);\n }\n\n const xScale = buildXScale(allX, area.width);\n // Independent points have no baseline to anchor, so the y domain fits the data.\n const yScale = buildYScale(allY, area.height, false);\n\n // Axes render by default (value axis with gridlines); `false` opts out. The value\n // axis merges over its default so a partial config keeps the default gridlines.\n const xAxisConfig: XAxisConfig | false = config.xAxis === false ? false : (config.xAxis ?? {});\n const yAxisConfig: YAxisConfig | false = config.yAxis === false ? false : { grid: true, ...config.yAxis };\n\n if (yAxisConfig && yAxisConfig.grid) {\n renderGrid(\n groups.grid,\n yScale,\n yAxisConfig.grid,\n area.width,\n 'horizontal',\n resolveTicks(yScale, yAxisConfig, resolveTickCount(yAxisConfig, area.height, 'left')),\n );\n } else {\n groups.grid.replaceChildren();\n }\n\n if (xAxisConfig && xAxisConfig.grid) {\n renderGrid(\n groups.grid,\n xScale,\n xAxisConfig.grid,\n area.height,\n 'vertical',\n resolveTicks(xScale, xAxisConfig, resolveTickCount(xAxisConfig, area.width, 'bottom')),\n );\n }\n\n if (xAxisConfig) {\n positionAxis(groups.xAxis, xAxisConfig.position ?? 'bottom', area.width, area.height);\n renderAxis(groups.xAxis, xScale, xAxisConfig, area.width, 'bottom');\n } else {\n groups.xAxis.replaceChildren();\n }\n\n if (yAxisConfig) {\n positionAxis(groups.yAxis, yAxisConfig.position ?? 'left', area.width, area.height);\n renderAxis(groups.yAxis, yScale, yAxisConfig, area.height, 'left');\n } else {\n groups.yAxis.replaceChildren();\n }\n\n while (groups.series.children.length > seriesList.length) {\n groups.series.removeChild(groups.series.lastChild!);\n }\n\n const allPoints: Point[][] = [];\n\n for (const [i, series] of seriesList.entries()) {\n let group = groups.series.children[i] as SVGGElement | undefined;\n\n if (!group) {\n group = createSvgElement('g', { class: 'prism-scatter-series' });\n groups.series.appendChild(group);\n }\n\n group.setAttribute('data-series-id', seriesDomId(series, i));\n\n const points = computeScatterPoints(series.data, xScale, yScale);\n\n allPoints.push(points);\n\n renderScatterPoints(group, points, {\n color: seriesColor(i, series.color),\n disposalSignal: ctx.disposalSignal,\n marks: series.data,\n pointRadius: series.pointRadius ?? 3,\n transition: reason === 'resize' ? false : config.transition,\n });\n }\n\n tooltip?.hide();\n markers = ensureMarkerGroup(ctx.chartArea, markers);\n\n return createScatterInteraction({\n announcer: ctx.announcer,\n colors: () => seriesList.map((s, i) => seriesColor(i, s.color)),\n crosshair,\n dims: () => ctx.dimensions,\n getData: () => allData,\n getPoints: () => allPoints,\n getSeriesList: () => seriesList,\n margin: dims.margin,\n markers,\n onClick: config.onClick,\n onHover: config.onHover,\n seriesGroup: groups.series,\n svg: ctx.svg,\n tooltip,\n tooltipFormat: typeof config.tooltip === 'object' ? config.tooltip : undefined,\n });\n },\n (data) => {\n seriesList = data;\n },\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ChartHandle, ScatterChartConfig, ScatterSeriesConfig } from '../../types';
2
+ export declare function createScatterChart(container: HTMLElement, config: ScatterChartConfig): ChartHandle<ScatterSeriesConfig[]>;
3
+ //# sourceMappingURL=scatter-chart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scatter-chart.d.ts","sourceRoot":"","sources":["../../../src/charts/scatter/scatter-chart.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,mBAAmB,EAA4B,MAAM,aAAa,CAAC;AAGlH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,WAAW,EACtB,MAAM,EAAE,kBAAkB,GACzB,WAAW,CAAC,mBAAmB,EAAE,CAAC,CAiIpC"}
@@ -0,0 +1,2 @@
1
+ import{createSvgElement as e}from"../../svg/element.js";import{positionAxis as t,renderAxis as n,resolveTickCount as r,resolveTicks as i}from"../../axes/axis.js";import{renderGrid as a}from"../../axes/grid.js";import{buildXScale as o,buildYScale as s}from"../../core/cartesian-scales.js";import{chartArea as c}from"../../core/layout.js";import{clearCartesianDom as l,createChartScaffold as u}from"../../core/chart-scaffold.js";import{seriesDomId as d}from"../../core/series-id.js";import{createCrosshair as f}from"../../interaction/crosshair.js";import{ensureMarkerGroup as p}from"../../interaction/series-interaction.js";import{seriesColor as m}from"../../theme/index.js";import{createScatterInteraction as h}from"../../interaction/scatter-interaction.js";import{computeScatterPoints as g,renderScatterPoints as _}from"./scatter-renderer.js";function createScatterChart(v,y){let b=null,x=null,S=y.series;return u(v,y,(u,v)=>{let{groups:C,legend:w,tooltip:T}=u;w?.update(S.map((e,t)=>({color:m(t,e.color),name:e.name})));let E=u.dimensions,D=c(E.width,E.height,E.margin),O=S.map(e=>e.data),k=O.flat().map(e=>e.key),A=O.flat().map(e=>e.value);if(k.length===0){l(C,w,T,b),x?.replaceChildren();return}y.crosshair&&!b&&(b=f(u.chartArea,y.crosshair));let j=o(k,D.width),M=s(A,D.height,!1),N=y.xAxis===!1?!1:y.xAxis??{},P=y.yAxis!==!1&&{grid:!0,...y.yAxis};for(P&&P.grid?a(C.grid,M,P.grid,D.width,`horizontal`,i(M,P,r(P,D.height,`left`))):C.grid.replaceChildren(),N&&N.grid&&a(C.grid,j,N.grid,D.height,`vertical`,i(j,N,r(N,D.width,`bottom`))),N?(t(C.xAxis,N.position??`bottom`,D.width,D.height),n(C.xAxis,j,N,D.width,`bottom`)):C.xAxis.replaceChildren(),P?(t(C.yAxis,P.position??`left`,D.width,D.height),n(C.yAxis,M,P,D.height,`left`)):C.yAxis.replaceChildren();C.series.children.length>S.length;)C.series.removeChild(C.series.lastChild);let F=[];for(let[t,n]of S.entries()){let r=C.series.children[t];r||(r=e(`g`,{class:`prism-scatter-series`}),C.series.appendChild(r)),r.setAttribute(`data-series-id`,d(n,t));let i=g(n.data,j,M);F.push(i),_(r,i,{color:m(t,n.color),disposalSignal:u.disposalSignal,marks:n.data,pointRadius:n.pointRadius??3,transition:v!==`resize`&&y.transition})}return T?.hide(),x=p(u.chartArea,x),h({announcer:u.announcer,colors:()=>S.map((e,t)=>m(t,e.color)),crosshair:b,dims:()=>u.dimensions,getData:()=>O,getPoints:()=>F,getSeriesList:()=>S,margin:E.margin,markers:x,onClick:y.onClick,onHover:y.onHover,seriesGroup:C.series,svg:u.svg,tooltip:T,tooltipFormat:typeof y.tooltip==`object`?y.tooltip:void 0})},e=>{S=e})}export{createScatterChart};
2
+ //# sourceMappingURL=scatter-chart.js.map