layerchart 2.0.0-next.61 → 2.0.0-next.63

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 (182) hide show
  1. package/dist/canvas.d.ts +6 -2
  2. package/dist/canvas.js +6 -2
  3. package/dist/components/Arc/Arc.base.svelte +49 -11
  4. package/dist/components/Arc/Arc.shared.svelte.d.ts +2 -0
  5. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-full-circle--360-degree-range--1.png +0 -0
  6. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-full-circle--360-degree-range--2.png +0 -0
  7. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-innerRadius-of-0--pie-slice--1.png +0 -0
  8. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-innerRadius-of-0--pie-slice--2.png +0 -0
  9. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-negative-domain-values-1.png +0 -0
  10. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-negative-domain-values-2.png +0 -0
  11. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-partial-arc--e-g---180-degrees--1.png +0 -0
  12. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-partial-arc--e-g---180-degrees--2.png +0 -0
  13. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-at-max-domain-1.png +0 -0
  14. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-at-max-domain-2.png +0 -0
  15. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-below-domain-min-1.png +0 -0
  16. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-below-domain-min-2.png +0 -0
  17. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-exceeding-domain-max-1.png +0 -0
  18. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-exceeding-domain-max-2.png +0 -0
  19. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-of-0-1.png +0 -0
  20. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-edge-cases-should-handle-value-of-0-2.png +0 -0
  21. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-enter-events-1.png +0 -0
  22. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-enter-events-2.png +0 -0
  23. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-move-events-1.png +0 -0
  24. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-pointer-move-events-2.png +0 -0
  25. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-touch-move-events-1.png +0 -0
  26. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-events-should-handle-touch-move-events-2.png +0 -0
  27. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-custom-class-1.png +0 -0
  28. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-custom-class-2.png +0 -0
  29. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fill-color-1.png +0 -0
  30. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fill-color-2.png +0 -0
  31. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fillOpacity-1.png +0 -0
  32. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-fillOpacity-2.png +0 -0
  33. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-offset-to-arc-position-1.png +0 -0
  34. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-offset-to-arc-position-2.png +0 -0
  35. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-opacity-1.png +0 -0
  36. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-opacity-2.png +0 -0
  37. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-stroke-color-1.png +0 -0
  38. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-stroke-color-2.png +0 -0
  39. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-strokeWidth-1.png +0 -0
  40. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-strokeWidth-2.png +0 -0
  41. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-zero-offset-by-default-1.png +0 -0
  42. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-apply-zero-offset-by-default-2.png +0 -0
  43. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-hide-on-pointer-leave-1.png +0 -0
  44. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-hide-on-pointer-leave-2.png +0 -0
  45. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-show-on-pointer-enter-with-data-1.png +0 -0
  46. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-call-tooltip-show-on-pointer-enter-with-data-2.png +0 -0
  47. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-handle-custom-start-angle-in-range-1.png +0 -0
  48. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-handle-custom-start-angle-in-range-2.png +0 -0
  49. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-have-stroke--none--by-default-1.png +0 -0
  50. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-have-stroke--none--by-default-2.png +0 -0
  51. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-an-arc-path-with-value-1.png +0 -0
  52. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-an-arc-path-with-value-2.png +0 -0
  53. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-when-track-prop-is-provided-1.png +0 -0
  54. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-when-track-prop-is-provided-2.png +0 -0
  55. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-custom-class-1.png +0 -0
  56. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-custom-class-2.png +0 -0
  57. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackCornerRadius-1.png +0 -0
  58. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackCornerRadius-2.png +0 -0
  59. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackEndAngle-1.png +0 -0
  60. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackEndAngle-2.png +0 -0
  61. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-1.png +0 -0
  62. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-2.png +0 -0
  63. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-and-trackOuterRadius-1.png +0 -0
  64. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackInnerRadius-and-trackOuterRadius-2.png +0 -0
  65. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackOuterRadius-1.png +0 -0
  66. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackOuterRadius-2.png +0 -0
  67. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackPadAngle-1.png +0 -0
  68. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackPadAngle-2.png +0 -0
  69. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-1.png +0 -0
  70. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-2.png +0 -0
  71. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-and-trackEndAngle-1.png +0 -0
  72. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-track-with-trackStartAngle-and-trackEndAngle-2.png +0 -0
  73. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-both-startAngle-and-endAngle-1.png +0 -0
  74. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-both-startAngle-and-endAngle-2.png +0 -0
  75. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-cornerRadius-1.png +0 -0
  76. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-cornerRadius-2.png +0 -0
  77. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-1.png +0 -0
  78. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-2.png +0 -0
  79. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-and-range-1.png +0 -0
  80. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-domain-and-range-2.png +0 -0
  81. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-range-1.png +0 -0
  82. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-custom-range-2.png +0 -0
  83. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-endAngle-in-radians-1.png +0 -0
  84. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-endAngle-in-radians-2.png +0 -0
  85. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-1.png +0 -0
  86. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-2.png +0 -0
  87. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-and-outerRadius-1.png +0 -0
  88. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-innerRadius-and-outerRadius-2.png +0 -0
  89. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-outerRadius-1.png +0 -0
  90. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-outerRadius-2.png +0 -0
  91. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-padAngle-1.png +0 -0
  92. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-padAngle-2.png +0 -0
  93. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-startAngle-in-radians-1.png +0 -0
  94. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-props-should-render-with-startAngle-in-radians-2.png +0 -0
  95. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-Arc-element-1.png +0 -0
  96. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-Arc-element-2.png +0 -0
  97. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-track-1.png +0 -0
  98. package/dist/components/Arc/__screenshots__/Arc.svelte.test.ts/Arc-should-render-track-2.png +0 -0
  99. package/dist/components/ArcLabel/ArcLabel.shared.svelte.d.ts +1 -0
  100. package/dist/components/{ArcLabel.svelte.test.js → ArcLabel/ArcLabel.svelte.test.js} +3 -3
  101. package/dist/components/ArcLabel/__screenshots__/ArcLabel.svelte.test.ts/ArcLabel-renders-a-text-element-with-the-supplied-value-at-the-centroid-1.png +0 -0
  102. package/dist/components/ArcLabel/__screenshots__/ArcLabel.svelte.test.ts/ArcLabel-renders-a-text-element-with-the-supplied-value-at-the-centroid-2.png +0 -0
  103. package/dist/components/Blur/Blur.canvas.svelte +25 -0
  104. package/dist/components/Blur/Blur.canvas.svelte.d.ts +4 -0
  105. package/dist/components/Blur/Blur.html.svelte +11 -0
  106. package/dist/components/Blur/Blur.html.svelte.d.ts +4 -0
  107. package/dist/components/{Blur.svelte.d.ts → Blur/Blur.shared.svelte.d.ts} +3 -5
  108. package/dist/components/Blur/Blur.svelte +23 -0
  109. package/dist/components/Blur/Blur.svelte.d.ts +4 -0
  110. package/dist/components/Blur/Blur.svg.svelte +24 -0
  111. package/dist/components/Blur/Blur.svg.svelte.d.ts +4 -0
  112. package/dist/components/Chart/Chart.base.svelte +13 -7
  113. package/dist/components/Chart/ChartCore.svelte.test.d.ts +1 -0
  114. package/dist/components/{ChartCore.svelte.test.js → Chart/ChartCore.svelte.test.js} +1 -1
  115. package/dist/components/Circle/Circle.shared.svelte.js +24 -5
  116. package/dist/components/Circle/Circle.svelte.test.js +70 -0
  117. package/dist/components/Dodge/Dodge.shared.svelte.d.ts +132 -0
  118. package/dist/components/Dodge/Dodge.shared.svelte.js +240 -0
  119. package/dist/components/Dodge/Dodge.svelte +88 -0
  120. package/dist/components/Dodge/Dodge.svelte.d.ts +27 -0
  121. package/dist/components/Dodge/Dodge.test.d.ts +1 -0
  122. package/dist/components/Dodge/Dodge.test.js +128 -0
  123. package/dist/components/Image/Image.html.svelte +0 -8
  124. package/dist/components/Image/Image.svg.svelte +1 -9
  125. package/dist/components/Link/Link.base.svelte +15 -9
  126. package/dist/components/Path/Path.canvas.svelte +5 -2
  127. package/dist/components/Path/Path.shared.svelte.d.ts +17 -4
  128. package/dist/components/Path/Path.shared.svelte.js +26 -8
  129. package/dist/components/Path/Path.svg.svelte +75 -60
  130. package/dist/components/Pattern/Pattern.canvas.svelte +4 -1
  131. package/dist/components/Pattern/Pattern.shared.svelte.d.ts +31 -2
  132. package/dist/components/Pattern/Pattern.shared.svelte.js +20 -1
  133. package/dist/components/Pattern/Pattern.svg.svelte +17 -1
  134. package/dist/components/Raster/Raster.base.svelte +2 -8
  135. package/dist/components/Rect/Rect.canvas.svelte +2 -4
  136. package/dist/components/Rect/Rect.canvas.svelte.d.ts +1 -1
  137. package/dist/components/Rect/Rect.html.svelte +3 -9
  138. package/dist/components/Rect/Rect.html.svelte.d.ts +1 -1
  139. package/dist/components/Rect/Rect.shared.svelte.d.ts +5 -2
  140. package/dist/components/Rect/Rect.shared.svelte.js +26 -13
  141. package/dist/components/Rect/Rect.svelte.test.js +45 -0
  142. package/dist/components/Rect/Rect.svg.svelte +36 -21
  143. package/dist/components/Rect/Rect.svg.svelte.d.ts +1 -1
  144. package/dist/components/RectClipPath/RectClipPath.base.svelte +25 -1
  145. package/dist/components/RectClipPath/RectClipPath.shared.svelte.d.ts +8 -0
  146. package/dist/components/Spline/Spline.base.svelte +3 -2
  147. package/dist/components/Text/Text.canvas.svelte +9 -0
  148. package/dist/components/Text/Text.html.svelte +6 -0
  149. package/dist/components/Text/Text.shared.svelte.d.ts +25 -2
  150. package/dist/components/Text/Text.shared.svelte.js +36 -5
  151. package/dist/components/Text/Text.svelte.test.js +40 -0
  152. package/dist/components/Text/Text.svg.svelte +7 -1
  153. package/dist/components/Trail/Trail.base.svelte +10 -7
  154. package/dist/components/Waffle/Waffle.shared.svelte.d.ts +182 -0
  155. package/dist/components/Waffle/Waffle.shared.svelte.js +300 -0
  156. package/dist/components/Waffle/Waffle.svelte +148 -0
  157. package/dist/components/Waffle/Waffle.svelte.d.ts +5 -0
  158. package/dist/components/charts/__screenshots__/ArcChart.svelte.test.ts/ArcChart-uses-the-chart-value-accessor-for-explicit-per-series-tooltip-values-1.png +0 -0
  159. package/dist/components/charts/__screenshots__/ArcChart.svelte.test.ts/ArcChart-uses-the-chart-value-accessor-for-explicit-per-series-tooltip-values-2.png +0 -0
  160. package/dist/components/charts/__screenshots__/BarChart.svelte.test.ts/BarChart-legend-series-toggle-adjusts-group-scale-should-adjust-grouped-bar-widths-when-series-are-toggled-via-legend-1.png +0 -0
  161. package/dist/components/charts/__screenshots__/PieChart.svelte.test.ts/PieChart-uses-hovered-slice-identity-for-implicit-tooltip-series-1.png +0 -0
  162. package/dist/components/charts/__screenshots__/PieChart.svelte.test.ts/PieChart-uses-hovered-slice-identity-for-implicit-tooltip-series-2.png +0 -0
  163. package/dist/components/index.d.ts +6 -2
  164. package/dist/components/index.js +6 -2
  165. package/dist/html.d.ts +6 -2
  166. package/dist/html.js +6 -2
  167. package/dist/states/chart.svelte.d.ts +4 -2
  168. package/dist/states/chart.svelte.js +53 -22
  169. package/dist/states/chart.svelte.test.js +54 -1
  170. package/dist/states/series.svelte.js +9 -13
  171. package/dist/states/series.svelte.test.js +5 -1
  172. package/dist/svg.d.ts +6 -2
  173. package/dist/svg.js +6 -2
  174. package/dist/utils/canvas.js +54 -13
  175. package/dist/utils/canvas.svelte.test.js +44 -0
  176. package/dist/utils/download.d.ts +5 -3
  177. package/dist/utils/download.js +36 -16
  178. package/dist/utils/stack.js +10 -2
  179. package/package.json +1 -1
  180. package/dist/components/Blur.svelte +0 -49
  181. /package/dist/components/{ArcLabel.svelte.test.d.ts → ArcLabel/ArcLabel.svelte.test.d.ts} +0 -0
  182. /package/dist/components/{ChartCore.svelte.test.d.ts → Blur/Blur.shared.svelte.js} +0 -0
@@ -0,0 +1,5 @@
1
+ export type { WaffleProps, WafflePropsWithoutHTML } from './Waffle.shared.svelte.js';
2
+ import { type WaffleProps } from './Waffle.shared.svelte.js';
3
+ declare const Waffle: import("svelte").Component<WaffleProps, {}, "">;
4
+ type Waffle = ReturnType<typeof Waffle>;
5
+ export default Waffle;
@@ -18,8 +18,8 @@ export { default as Bar } from './Bar/Bar.svelte';
18
18
  export * from './Bar/Bar.svelte';
19
19
  export { default as Bars } from './Bars/Bars.svelte';
20
20
  export * from './Bars/Bars.svelte';
21
- export { default as Blur } from './Blur.svelte';
22
- export * from './Blur.svelte';
21
+ export { default as Blur } from './Blur/Blur.svelte';
22
+ export * from './Blur/Blur.svelte';
23
23
  export { default as BoxPlot } from './BoxPlot/BoxPlot.svelte';
24
24
  export * from './BoxPlot/BoxPlot.svelte';
25
25
  export { default as Bounds } from './Bounds.svelte';
@@ -49,6 +49,8 @@ export { default as Contour } from './Contour/Contour.svelte';
49
49
  export * from './Contour/Contour.svelte';
50
50
  export { default as Density } from './Density/Density.svelte';
51
51
  export * from './Density/Density.svelte';
52
+ export { default as Dodge } from './Dodge/Dodge.svelte';
53
+ export * from './Dodge/Dodge.svelte';
52
54
  export { default as Ellipse } from './Ellipse/Ellipse.svelte';
53
55
  export * from './Ellipse/Ellipse.svelte';
54
56
  export { default as Frame } from './Frame/Frame.svelte';
@@ -125,5 +127,7 @@ export { default as Violin } from './Violin/Violin.svelte';
125
127
  export * from './Violin/Violin.svelte';
126
128
  export { default as Voronoi } from './Voronoi/Voronoi.svelte';
127
129
  export * from './Voronoi/Voronoi.svelte';
130
+ export { default as Waffle } from './Waffle/Waffle.svelte';
131
+ export * from './Waffle/Waffle.svelte';
128
132
  export { default as WebGL } from './layers/WebGL.svelte';
129
133
  export * from './layers/WebGL.svelte';
@@ -18,8 +18,8 @@ export { default as Bar } from './Bar/Bar.svelte';
18
18
  export * from './Bar/Bar.svelte';
19
19
  export { default as Bars } from './Bars/Bars.svelte';
20
20
  export * from './Bars/Bars.svelte';
21
- export { default as Blur } from './Blur.svelte';
22
- export * from './Blur.svelte';
21
+ export { default as Blur } from './Blur/Blur.svelte';
22
+ export * from './Blur/Blur.svelte';
23
23
  export { default as BoxPlot } from './BoxPlot/BoxPlot.svelte';
24
24
  export * from './BoxPlot/BoxPlot.svelte';
25
25
  export { default as Bounds } from './Bounds.svelte';
@@ -49,6 +49,8 @@ export { default as Contour } from './Contour/Contour.svelte';
49
49
  export * from './Contour/Contour.svelte';
50
50
  export { default as Density } from './Density/Density.svelte';
51
51
  export * from './Density/Density.svelte';
52
+ export { default as Dodge } from './Dodge/Dodge.svelte';
53
+ export * from './Dodge/Dodge.svelte';
52
54
  export { default as Ellipse } from './Ellipse/Ellipse.svelte';
53
55
  export * from './Ellipse/Ellipse.svelte';
54
56
  export { default as Frame } from './Frame/Frame.svelte';
@@ -125,5 +127,7 @@ export { default as Violin } from './Violin/Violin.svelte';
125
127
  export * from './Violin/Violin.svelte';
126
128
  export { default as Voronoi } from './Voronoi/Voronoi.svelte';
127
129
  export * from './Voronoi/Voronoi.svelte';
130
+ export { default as Waffle } from './Waffle/Waffle.svelte';
131
+ export * from './Waffle/Waffle.svelte';
128
132
  export { default as WebGL } from './layers/WebGL.svelte';
129
133
  export * from './layers/WebGL.svelte';
package/dist/html.d.ts CHANGED
@@ -72,8 +72,8 @@ export { default as Calendar } from './components/Calendar/Calendar.html.svelte'
72
72
  export type { CalendarProps, CalendarPropsWithoutHTML, CalendarCell, } from './components/Calendar/Calendar.shared.svelte.js';
73
73
  export { default as Month } from './components/Month/Month.html.svelte';
74
74
  export type { MonthProps, MonthPropsWithoutHTML, MonthCell, } from './components/Month/Month.shared.svelte.js';
75
- export { default as Blur } from './components/Blur.svelte';
76
- export * from './components/Blur.svelte';
75
+ export { default as Blur } from './components/Blur/Blur.svelte';
76
+ export * from './components/Blur/Blur.svelte';
77
77
  export { default as Bounds } from './components/Bounds.svelte';
78
78
  export * from './components/Bounds.svelte';
79
79
  export { default as BrushContext } from './components/BrushContext.svelte';
@@ -114,3 +114,7 @@ export { default as Sankey } from './components/graph/Sankey.svelte';
114
114
  export * from './components/graph/Sankey.svelte';
115
115
  export { default as ForceSimulation } from './components/force/ForceSimulation.svelte';
116
116
  export * from './components/force/ForceSimulation.svelte';
117
+ export { default as Dodge } from './components/Dodge/Dodge.svelte';
118
+ export * from './components/Dodge/Dodge.svelte';
119
+ export { default as Waffle } from './components/Waffle/Waffle.svelte';
120
+ export * from './components/Waffle/Waffle.svelte';
package/dist/html.js CHANGED
@@ -47,8 +47,8 @@ export { default as Month } from './components/Month/Month.html.svelte';
47
47
  // helpers, context providers, or composite chart wrappers). Re-exported here
48
48
  // so the per-layer sub-path has a complete API.
49
49
  // Helpers / context providers
50
- export { default as Blur } from './components/Blur.svelte';
51
- export * from './components/Blur.svelte';
50
+ export { default as Blur } from './components/Blur/Blur.svelte';
51
+ export * from './components/Blur/Blur.svelte';
52
52
  export { default as Bounds } from './components/Bounds.svelte';
53
53
  export * from './components/Bounds.svelte';
54
54
  export { default as BrushContext } from './components/BrushContext.svelte';
@@ -91,3 +91,7 @@ export { default as Sankey } from './components/graph/Sankey.svelte';
91
91
  export * from './components/graph/Sankey.svelte';
92
92
  export { default as ForceSimulation } from './components/force/ForceSimulation.svelte';
93
93
  export * from './components/force/ForceSimulation.svelte';
94
+ export { default as Dodge } from './components/Dodge/Dodge.svelte';
95
+ export * from './components/Dodge/Dodge.svelte';
96
+ export { default as Waffle } from './components/Waffle/Waffle.svelte';
97
+ export * from './components/Waffle/Waffle.svelte';
@@ -50,7 +50,6 @@ export interface RegisterComponentOptions<T extends Element = Element> {
50
50
  }
51
51
  export declare class ChartState<TData = any, XScale extends AnyScale = AnyScale, YScale extends AnyScale = AnyScale> {
52
52
  #private;
53
- private _propsGetter;
54
53
  props: ChartPropsWithoutHTML<TData, XScale, YScale>;
55
54
  geoState: GeoState;
56
55
  transformState: TransformState;
@@ -90,7 +89,10 @@ export declare class ChartState<TData = any, XScale extends AnyScale = AnyScale,
90
89
  private _xDomainIsDate;
91
90
  private _yDomainIsDate;
92
91
  meta: Record<string, any>;
93
- constructor(propsGetter: () => ChartPropsWithoutHTML<TData, XScale, YScale>);
92
+ constructor(props: ChartPropsWithoutHTML<TData, XScale, YScale>, overrides?: {
93
+ brushXDomain?: () => BrushDomainType | undefined;
94
+ brushYDomain?: () => BrushDomainType | undefined;
95
+ });
94
96
  containerWidth: number;
95
97
  containerHeight: number;
96
98
  data: import("d3-sankey").SankeyGraph<any, any> | readonly TData[] | import("d3-hierarchy").HierarchyNode<TData>;
@@ -17,11 +17,22 @@ const defaultPadding = { top: 0, right: 0, bottom: 0, left: 0 };
17
17
  const EMPTY_SERIES = [];
18
18
  /** Svelte context key for tracking the nearest parent ComponentNode. */
19
19
  const _ParentNodeContext = new Context('ComponentTreeParent');
20
+ /** Mark info is "empty" when none of the fields the chart uses for series /
21
+ * domain inference are populated. Pixel-mode primitives produce empty info
22
+ * since they have no string/function accessors and no own data. */
23
+ function isEmptyMarkInfo(info) {
24
+ return !info.x && !info.y && !info.data && !info.color && !info.seriesKey && !info.label;
25
+ }
20
26
  export class ChartState {
21
- // Props getter function - set in constructor
22
- _propsGetter;
23
- // Props - accessed via getter function for fine-grained reactivity
24
- props = $derived(this._propsGetter());
27
+ // The `$props()` proxy from the host component. Reads on `this.props.X` go
28
+ // straight through to the underlying reactive prop — no spread / no derived
29
+ // wrapper needed.
30
+ props;
31
+ // Brush-domain overrides. The host component owns the brush state as local
32
+ // `$state` and supplies these getters so brush selections take precedence
33
+ // over `props.xDomain` / `props.yDomain` when reading the effective domain.
34
+ #brushXDomain;
35
+ #brushYDomain;
25
36
  // State / contexts
26
37
  geoState;
27
38
  transformState = $state(null);
@@ -132,14 +143,26 @@ export class ChartState {
132
143
  };
133
144
  });
134
145
  if (markInfo && !insideCompositeMark) {
135
- $effect(() => {
136
- const info = markInfo();
137
- // Skip registration for empty mark info (e.g. pixel-mode marks)
138
- // to avoid unnecessary array push/splice and version bumps
139
- if (!info.x && !info.y && !info.data && !info.color && !info.seriesKey && !info.label)
140
- return;
141
- return untrack(() => this.registerMark(info));
142
- });
146
+ // Probe once at construction: if mark info is initially empty
147
+ // (pixel-mode primitives where cx/cy/r are numbers), skip the
148
+ // tracking $effect entirely. This is the common case for
149
+ // mark-heavy scenes (force simulations, scatter plots with
150
+ // pixel coordinates) and avoids one effect frame per primitive.
151
+ //
152
+ // Trade-off: a primitive that starts in pixel mode and later
153
+ // flips to data mode (e.g. cx changes from number to string at
154
+ // runtime) won't register a mark. This is uncommon — modes are
155
+ // typically static — but if needed, use explicit `series` on the
156
+ // chart instead of relying on implicit mark-derived series.
157
+ const initial = untrack(markInfo);
158
+ if (!isEmptyMarkInfo(initial)) {
159
+ $effect(() => {
160
+ const info = markInfo();
161
+ if (isEmptyMarkInfo(info))
162
+ return;
163
+ return untrack(() => this.registerMark(info));
164
+ });
165
+ }
143
166
  }
144
167
  return node;
145
168
  }
@@ -159,8 +182,10 @@ export class ChartState {
159
182
  _yDomainIsDate = false;
160
183
  // Meta data - reactive to props.meta changes
161
184
  meta = $derived(this.props.meta ?? {});
162
- constructor(propsGetter) {
163
- this._propsGetter = propsGetter;
185
+ constructor(props, overrides) {
186
+ this.props = props;
187
+ this.#brushXDomain = overrides?.brushXDomain ?? (() => undefined);
188
+ this.#brushYDomain = overrides?.brushYDomain ?? (() => undefined);
164
189
  // Create GeoState instance — pass a dimensions getter so projection
165
190
  // is available during SSR (where $effect doesn't run)
166
191
  this.geoState = new GeoState(() => this.props.geo ?? {}, () => ({ width: this.width, height: this.height }));
@@ -272,7 +297,7 @@ export class ChartState {
272
297
  }
273
298
  });
274
299
  // Set up domain motion if motion prop is configured
275
- const motionProp = propsGetter().motion;
300
+ const motionProp = props.motion;
276
301
  if (motionProp) {
277
302
  const resolved = parseMotionProp(motionProp);
278
303
  this._xDomainMotion = createControlledMotion([], resolved);
@@ -373,7 +398,7 @@ export class ChartState {
373
398
  if (this.props.bandPadding != null && this.valueAxis === 'y') {
374
399
  return scaleBand().padding(this.props.bandPadding);
375
400
  }
376
- return autoScale(this.props.xDomain, this.flatData, this.x);
401
+ return autoScale(this.#brushXDomain() ?? this.props.xDomain, this.flatData, this.x);
377
402
  });
378
403
  _yScaleProp = $derived.by(() => {
379
404
  if (this.props.yScale)
@@ -385,7 +410,7 @@ export class ChartState {
385
410
  if (this.props.bandPadding != null && this.valueAxis === 'x') {
386
411
  return scaleBand().padding(this.props.bandPadding);
387
412
  }
388
- return autoScale(this.props.yDomain, this.flatData, this.y);
413
+ return autoScale(this.#brushYDomain() ?? this.props.yDomain, this.flatData, this.y);
389
414
  });
390
415
  _zScaleProp = $derived.by(() => {
391
416
  return this.props.zScale ?? autoScale(this.props.zDomain, this.flatData, this.props.z);
@@ -579,7 +604,9 @@ export class ChartState {
579
604
  return undefined;
580
605
  }
581
606
  resolveDomain(axis) {
582
- const domain = axis === 'x' ? this.props.xDomain : this.props.yDomain;
607
+ const domain = axis === 'x'
608
+ ? (this.#brushXDomain() ?? this.props.xDomain)
609
+ : (this.#brushYDomain() ?? this.props.yDomain);
583
610
  const interval = axis === 'x' ? this.props.xInterval : this.props.yInterval;
584
611
  const explicitBaseline = axis === 'x' ? this.props.xBaseline : this.props.yBaseline;
585
612
  // Use explicit baseline if provided (null means "no baseline"), otherwise auto-derive
@@ -629,11 +656,15 @@ export class ChartState {
629
656
  extraMarkValues.push(...info.data.flatMap(accessor(markAccessor)));
630
657
  }
631
658
  }
632
- const allValues = [...seriesValues, ...extraMarkValues];
633
- if (baseline != null) {
634
- return [min([baseline, ...allValues]), max([baseline, ...allValues])];
659
+ const allValues = [...seriesValues, ...extraMarkValues].filter((v) => v != null);
660
+ if (allValues.length > 0) {
661
+ if (baseline != null) {
662
+ return [min([baseline, ...allValues]), max([baseline, ...allValues])];
663
+ }
664
+ return extent(allValues);
635
665
  }
636
- return extent(allValues);
666
+ // Series are metadata-only (e.g. categorical legend with no per-series
667
+ // values on the axis) — fall through to other resolution paths.
637
668
  }
638
669
  }
639
670
  // Interval-based domain: extend to the next interval offset
@@ -9,7 +9,7 @@ function createChartState(props) {
9
9
  let cleanup;
10
10
  let state;
11
11
  cleanup = $effect.root(() => {
12
- state = new ChartState(() => props);
12
+ state = new ChartState(props);
13
13
  });
14
14
  // Access derived values after reactive graph is set up
15
15
  flushSync();
@@ -893,6 +893,59 @@ describe('ChartState implicit series domain update on visibility toggle', () =>
893
893
  }
894
894
  });
895
895
  });
896
+ describe('ChartState metadata-only series', () => {
897
+ it('should not produce [undefined, undefined] domain when items lack series-key properties', () => {
898
+ const data = [
899
+ { date: '2024-01', category: 'svelte' },
900
+ { date: '2024-02', category: 'sveltekit' },
901
+ { date: '2024-03', category: 'ecosystem' },
902
+ ];
903
+ const { state, cleanup } = createChartState({
904
+ data,
905
+ x: 'date',
906
+ valueAxis: 'y',
907
+ series: [
908
+ { key: 'svelte', color: 'red' },
909
+ { key: 'sveltekit', color: 'orange' },
910
+ { key: 'ecosystem', color: 'blue' },
911
+ ],
912
+ });
913
+ try {
914
+ expect(state._yDomain).toBeUndefined();
915
+ }
916
+ finally {
917
+ cleanup();
918
+ }
919
+ });
920
+ it('should remain stable across visibility toggles instead of throwing', () => {
921
+ const data = [
922
+ { date: '2024-01', category: 'svelte' },
923
+ { date: '2024-02', category: 'sveltekit' },
924
+ ];
925
+ const { state, cleanup } = createChartState({
926
+ data,
927
+ x: 'date',
928
+ valueAxis: 'y',
929
+ series: [
930
+ { key: 'svelte', color: 'red' },
931
+ { key: 'sveltekit', color: 'orange' },
932
+ ],
933
+ motion: { type: 'spring' },
934
+ });
935
+ try {
936
+ expect(state._yDomain).toBeUndefined();
937
+ expect(() => {
938
+ state.seriesState.selectedKeys.toggle('svelte');
939
+ flushSync();
940
+ }).not.toThrow();
941
+ expect(state._yDomain).toBeUndefined();
942
+ expect(state.seriesState.visibleSeries).toHaveLength(1);
943
+ }
944
+ finally {
945
+ cleanup();
946
+ }
947
+ });
948
+ });
896
949
  describe('ChartState degenerate domain', () => {
897
950
  it('should expand degenerate y domain [0, 0] to [0, 1]', () => {
898
951
  const data = [
@@ -9,19 +9,6 @@ export class SeriesState {
9
9
  #series = $derived(this._getSeries());
10
10
  #stackConfig = $derived(this._getStackConfig());
11
11
  selectedKeys;
12
- /**
13
- * Reactively syncs selectedKeys when series `selected` props change.
14
- * When any series explicitly sets `selected: false`, the remaining series
15
- * (with `selected` undefined or true) are pre-selected.
16
- */
17
- #_syncSelectedFromProps = $effect.root(() => {
18
- $effect(() => {
19
- const keys = SeriesState.#selectedKeysFromSeries(this.#series);
20
- if (keys) {
21
- this.selectedKeys.current = keys;
22
- }
23
- });
24
- });
25
12
  /**
26
13
  * The current highlight series key for the chart.
27
14
  */
@@ -32,6 +19,15 @@ export class SeriesState {
32
19
  // Compute initial selectedKeys synchronously from series `selected` props
33
20
  const initialKeys = SeriesState.#selectedKeysFromSeries(getSeries());
34
21
  this.selectedKeys = new SelectionState({ initial: initialKeys ?? undefined });
22
+ // Reactively sync selectedKeys when series `selected` props change.
23
+ // When any series explicitly sets `selected: false`, the remaining series
24
+ // (with `selected` undefined or true) are pre-selected.
25
+ $effect(() => {
26
+ const keys = SeriesState.#selectedKeysFromSeries(this.#series);
27
+ if (keys) {
28
+ this.selectedKeys.current = keys;
29
+ }
30
+ });
35
31
  }
36
32
  /**
37
33
  * Extract selected keys from series definitions.
@@ -11,7 +11,11 @@ const series = [
11
11
  { key: 'oranges', color: 'orange' },
12
12
  ];
13
13
  function createSeriesState(seriesData = series, stackConfig = null) {
14
- return new SeriesState(() => seriesData, () => stackConfig);
14
+ let state;
15
+ $effect.root(() => {
16
+ state = new SeriesState(() => seriesData, () => stackConfig);
17
+ });
18
+ return state;
15
19
  }
16
20
  describe('SeriesState', () => {
17
21
  describe('constructor and basic properties', () => {
package/dist/svg.d.ts CHANGED
@@ -126,8 +126,8 @@ export { default as Graticule } from './components/geo/Graticule/Graticule.svg.s
126
126
  export type { GraticuleProps, GraticulePropsWithoutHTML, } from './components/geo/Graticule/Graticule.shared.svelte.js';
127
127
  export { default as Ribbon } from './components/graph/Ribbon/Ribbon.svg.svelte';
128
128
  export type { RibbonProps, RibbonPropsWithoutHTML, } from './components/graph/Ribbon/Ribbon.shared.svelte.js';
129
- export { default as Blur } from './components/Blur.svelte';
130
- export * from './components/Blur.svelte';
129
+ export { default as Blur } from './components/Blur/Blur.svelte';
130
+ export * from './components/Blur/Blur.svelte';
131
131
  export { default as Bounds } from './components/Bounds.svelte';
132
132
  export * from './components/Bounds.svelte';
133
133
  export { default as BrushContext } from './components/BrushContext.svelte';
@@ -168,6 +168,10 @@ export { default as Sankey } from './components/graph/Sankey.svelte';
168
168
  export * from './components/graph/Sankey.svelte';
169
169
  export { default as ForceSimulation } from './components/force/ForceSimulation.svelte';
170
170
  export * from './components/force/ForceSimulation.svelte';
171
+ export { default as Dodge } from './components/Dodge/Dodge.svelte';
172
+ export * from './components/Dodge/Dodge.svelte';
173
+ export { default as Waffle } from './components/Waffle/Waffle.svelte';
174
+ export * from './components/Waffle/Waffle.svelte';
171
175
  export { default as GeoLegend } from './components/geo/GeoLegend/GeoLegend.svelte';
172
176
  export { default as GeoProjection } from './components/geo/GeoProjection/GeoProjection.svelte';
173
177
  export { default as GeoRaster } from './components/geo/GeoRaster/GeoRaster.svelte';
package/dist/svg.js CHANGED
@@ -77,8 +77,8 @@ export { default as Ribbon } from './components/graph/Ribbon/Ribbon.svg.svelte';
77
77
  // helpers, context providers, or composite chart wrappers). Re-exported here
78
78
  // so the per-layer sub-path has a complete API.
79
79
  // Helpers / context providers
80
- export { default as Blur } from './components/Blur.svelte';
81
- export * from './components/Blur.svelte';
80
+ export { default as Blur } from './components/Blur/Blur.svelte';
81
+ export * from './components/Blur/Blur.svelte';
82
82
  export { default as Bounds } from './components/Bounds.svelte';
83
83
  export * from './components/Bounds.svelte';
84
84
  export { default as BrushContext } from './components/BrushContext.svelte';
@@ -121,6 +121,10 @@ export { default as Sankey } from './components/graph/Sankey.svelte';
121
121
  export * from './components/graph/Sankey.svelte';
122
122
  export { default as ForceSimulation } from './components/force/ForceSimulation.svelte';
123
123
  export * from './components/force/ForceSimulation.svelte';
124
+ export { default as Dodge } from './components/Dodge/Dodge.svelte';
125
+ export * from './components/Dodge/Dodge.svelte';
126
+ export { default as Waffle } from './components/Waffle/Waffle.svelte';
127
+ export * from './components/Waffle/Waffle.svelte';
124
128
  // Geo helpers (no per-layer rendering)
125
129
  export { default as GeoLegend } from './components/geo/GeoLegend/GeoLegend.svelte';
126
130
  export { default as GeoProjection } from './components/geo/GeoProjection/GeoProjection.svelte';
@@ -13,6 +13,16 @@ function isTransparentFill(fill) {
13
13
  // Match rgba(..., 0) - alpha channel is 0 (fully transparent)
14
14
  return /rgba\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*0\s*\)/.test(fill);
15
15
  }
16
+ /**
17
+ * Returns true if a style value cannot be assigned directly to a canvas
18
+ * context and must first be resolved through the hidden `<svg>` helper —
19
+ * specifically `var(...)` references and the `currentColor` keyword.
20
+ */
21
+ function needsCSSResolution(value) {
22
+ if (typeof value !== 'string')
23
+ return false;
24
+ return value.includes('var(') || value.toLowerCase() === 'currentcolor';
25
+ }
16
26
  const CANVAS_STYLES_ELEMENT_ID = '__layerchart_canvas_styles_id';
17
27
  /**
18
28
  * Parse an inline CSS style string into a StyleOptions object.
@@ -122,9 +132,8 @@ function render(ctx, render, styleOptions = {}, { applyText, } = {}) {
122
132
  // TODO: Consider memoizing? How about reactiving to CSS variable changes (light/dark mode toggle)
123
133
  let resolvedStyles;
124
134
  if (typeof document === 'undefined' ||
125
- (styleOptions.classes == null &&
126
- !Object.values(mergedStyles).some((v) => typeof v === 'string' && v.includes('var(')))) {
127
- // Skip resolving styles if running on server (no DOM), or no classes are provided and no styles are using CSS variables
135
+ (styleOptions.classes == null && !Object.values(mergedStyles).some(needsCSSResolution))) {
136
+ // Skip resolving styles if running on server (no DOM), or no classes are provided and no styles need CSS resolution (`var(...)` / `currentColor`)
128
137
  resolvedStyles = mergedStyles;
129
138
  // On server, provide sensible defaults for styles that would normally come from CSS
130
139
  if (typeof document === 'undefined') {
@@ -136,12 +145,12 @@ function render(ctx, render, styleOptions = {}, { applyText, } = {}) {
136
145
  else {
137
146
  // Remove constant non-css variable properties (ex. `strokeWidth: 0.5`, `fill: #123456`) as not needed and improves memoization cache hit
138
147
  const { constantStyles, variableStyles } = Object.entries(mergedStyles).reduce((acc, [key, value]) => {
139
- if (typeof value === 'number' || (typeof value === 'string' && !value.includes('var('))) {
140
- acc.constantStyles[key] = value;
141
- }
142
- else if (typeof value === 'string' && value.includes('var(')) {
148
+ if (needsCSSResolution(value)) {
143
149
  acc.variableStyles[key] = value;
144
150
  }
151
+ else if (typeof value === 'number' || typeof value === 'string') {
152
+ acc.constantStyles[key] = value;
153
+ }
145
154
  return acc;
146
155
  }, { constantStyles: {}, variableStyles: {} });
147
156
  const computedStyles = getComputedStyles(ctx.canvas, {
@@ -200,7 +209,7 @@ function render(ctx, render, styleOptions = {}, { applyText, } = {}) {
200
209
  styleOptions.styles?.fill instanceof CanvasGradient) ||
201
210
  (typeof CanvasPattern !== 'undefined' &&
202
211
  styleOptions.styles?.fill instanceof CanvasPattern) ||
203
- !styleOptions.styles?.fill?.includes('var'))
212
+ !needsCSSResolution(styleOptions.styles?.fill))
204
213
  ? styleOptions.styles.fill
205
214
  : resolvedStyles?.fill;
206
215
  if (fill && !isTransparentFill(fill)) {
@@ -217,7 +226,7 @@ function render(ctx, render, styleOptions = {}, { applyText, } = {}) {
217
226
  const stroke = styleOptions.styles?.stroke &&
218
227
  ((typeof CanvasGradient !== 'undefined' &&
219
228
  styleOptions.styles?.stroke instanceof CanvasGradient) ||
220
- !styleOptions.styles?.stroke?.includes('var'))
229
+ !needsCSSResolution(styleOptions.styles?.stroke))
221
230
  ? styleOptions.styles?.stroke
222
231
  : resolvedStyles?.stroke;
223
232
  if (stroke && !['none'].includes(stroke)) {
@@ -371,10 +380,18 @@ export function _createPattern(ctx, width, height, shapes, background) {
371
380
  const patternCtx = patternCanvas.getContext('2d');
372
381
  // Add pattern canvas to DOM to allow computed styles to be read (`getComputedStyles()`)
373
382
  ctx.canvas.after(patternCanvas);
374
- // TODO: Fix blurry pattern
375
- // const newScale = scaleCanvas(patternCtx, width, height);
376
- patternCanvas.width = width;
377
- patternCanvas.height = height;
383
+ // Render the pattern at the device pixel ratio so the bitmap tile is
384
+ // sharp on high-DPI screens. Chrome samples patterns in the canvas's
385
+ // user (post-transform) coordinate space, so 1 source pixel = 1 user px
386
+ // by default — without DPR-scaling the bitmap, a 12 logical-px tile is
387
+ // sampled from 12 source pixels, leaving each device pixel of fill to
388
+ // be interpolated from a 1/dpr-th of a source pixel (blurry). Drawing
389
+ // at DPR resolution and scaling the pattern back down to user space at
390
+ // fill time keeps tiles sharp.
391
+ const dpr = (typeof window !== 'undefined' ? window.devicePixelRatio : 1) || 1;
392
+ patternCanvas.width = Math.max(1, Math.round(width * dpr));
393
+ patternCanvas.height = Math.max(1, Math.round(height * dpr));
394
+ patternCtx.scale(dpr, dpr);
378
395
  if (background) {
379
396
  patternCtx.fillStyle = background;
380
397
  patternCtx.fillRect(0, 0, width, height);
@@ -389,9 +406,23 @@ export function _createPattern(ctx, width, height, shapes, background) {
389
406
  styles: { stroke: shape.stroke, strokeWidth: shape.strokeWidth, opacity: shape.opacity },
390
407
  });
391
408
  }
409
+ else if (shape.type === 'rect') {
410
+ const rx = typeof shape.rx === 'string' ? toRectCornerPx(shape.rx, shape.width) : shape.rx;
411
+ const ry = typeof shape.ry === 'string' ? toRectCornerPx(shape.ry, shape.height) : (shape.ry ?? rx);
412
+ renderRect(patternCtx, { x: shape.x, y: shape.y, width: shape.width, height: shape.height, rx, ry }, { styles: { fill: shape.fill, opacity: shape.opacity } });
413
+ }
392
414
  patternCtx.restore();
393
415
  }
394
416
  const pattern = ctx.createPattern(patternCanvas, 'repeat');
417
+ // Scale-only matrix; no translate so the pattern anchors to the path's
418
+ // local origin at fill time (matches SVG `patternUnits="userSpaceOnUse"`).
419
+ // Use the *actual* bitmap pixel dimensions for the scale so rounding
420
+ // `width * dpr` to an integer doesn't accumulate drift across tiles.
421
+ if (pattern) {
422
+ const sx = width / patternCanvas.width;
423
+ const sy = height / patternCanvas.height;
424
+ pattern.setTransform(new DOMMatrix([sx, 0, 0, sy, 0, 0]));
425
+ }
395
426
  // Cleanup
396
427
  ctx.canvas.parentElement?.removeChild(patternCanvas);
397
428
  return pattern;
@@ -400,3 +431,13 @@ export function _createPattern(ctx, width, height, shapes, background) {
400
431
  export const createPattern = memoize(_createPattern, {
401
432
  cacheKey: (args) => JSON.stringify(args.slice(1)), // Ignore `ctx` argument
402
433
  });
434
+ function toRectCornerPx(value, max) {
435
+ if (value.endsWith('%')) {
436
+ const pct = parseFloat(value);
437
+ if (!Number.isFinite(pct))
438
+ return 0;
439
+ return (max / 2) * (pct / 100);
440
+ }
441
+ const n = parseFloat(value);
442
+ return Number.isFinite(n) ? n : 0;
443
+ }
@@ -403,6 +403,33 @@ describe('renderPathData', () => {
403
403
  expect(strokeSpy).toHaveBeenCalled();
404
404
  expect(ctx.strokeStyle).toBe('#008000');
405
405
  });
406
+ it('resolves currentColor stroke through the SVG helper', () => {
407
+ const parent = canvas.parentElement;
408
+ const previousColor = parent.style.color;
409
+ parent.style.color = 'rgb(255, 165, 0)';
410
+ renderPathData(ctx, 'M0,0 L100,0', {
411
+ styles: {
412
+ fill: 'none',
413
+ stroke: 'currentColor',
414
+ strokeOpacity: '1',
415
+ opacity: '1',
416
+ strokeWidth: '2',
417
+ },
418
+ });
419
+ // Canvas normalizes rgb(255, 165, 0) → '#ffa500'
420
+ expect(ctx.strokeStyle).toBe('#ffa500');
421
+ parent.style.color = previousColor;
422
+ });
423
+ it('resolves currentColor fill through the SVG helper', () => {
424
+ const parent = canvas.parentElement;
425
+ const previousColor = parent.style.color;
426
+ parent.style.color = 'rgb(128, 0, 128)';
427
+ renderPathData(ctx, 'M0,0 L100,0 L100,100 Z', {
428
+ styles: { fill: 'currentColor', fillOpacity: '1', opacity: '1', stroke: 'none' },
429
+ });
430
+ expect(ctx.fillStyle).toBe('#800080');
431
+ parent.style.color = previousColor;
432
+ });
406
433
  });
407
434
  // ---------------------------------------------------------------------------
408
435
  // renderText
@@ -704,6 +731,23 @@ describe('_getComputedStyles', () => {
704
731
  // 'red' resolves to 'rgb(255, 0, 0)' in the browser
705
732
  expect(result.fill).toMatch(/rgb\(255,\s*0,\s*0\)/);
706
733
  });
734
+ it('resolves currentColor for fill via inherited color', () => {
735
+ // Set color on the canvas's parent so the helper SVG (sibling of canvas) inherits it
736
+ const parent = canvas.parentElement;
737
+ const previousColor = parent.style.color;
738
+ parent.style.color = 'rgb(0, 128, 0)';
739
+ const result = _getComputedStyles(canvas, { styles: { fill: 'currentColor' } });
740
+ expect(result.fill).toMatch(/rgb\(0,\s*128,\s*0\)/);
741
+ parent.style.color = previousColor;
742
+ });
743
+ it('resolves currentColor for stroke via inherited color', () => {
744
+ const parent = canvas.parentElement;
745
+ const previousColor = parent.style.color;
746
+ parent.style.color = 'rgb(0, 0, 255)';
747
+ const result = _getComputedStyles(canvas, { styles: { stroke: 'currentColor' } });
748
+ expect(result.stroke).toMatch(/rgb\(0,\s*0,\s*255\)/);
749
+ parent.style.color = previousColor;
750
+ });
707
751
  it('returns empty object when DOM throws (graceful error handling)', () => {
708
752
  // Simulate error by breaking canvas.after
709
753
  const originalAfter = canvas.after.bind(canvas);
@@ -17,9 +17,11 @@ export type ChartImageOptions = {
17
17
  */
18
18
  quality?: number;
19
19
  /**
20
- * Device pixel ratio to use when rasterising SVG layers.
21
- * Higher values produce crisper images on retina displays.
22
- * Defaults to `window.devicePixelRatio` (usually 1 or 2).
20
+ * Device pixel ratio to use when rasterising the image. Defaults to `1`
21
+ * so the output matches the chart's CSS dimensions (looks the same as
22
+ * what's on the page when viewed 1:1). Set to `window.devicePixelRatio`
23
+ * (or higher) to produce crisper images on retina displays at the cost
24
+ * of larger files.
23
25
  */
24
26
  pixelRatio?: number;
25
27
  };