@rokkit/chart 1.3.7 → 1.3.9

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 (162) hide show
  1. package/dist/AnimatedPlot.svelte +26 -8
  2. package/dist/AnimatedPlot.svelte.d.ts +5 -0
  3. package/dist/Chart.svelte +51 -65
  4. package/dist/FacetPlot.svelte +1 -1
  5. package/dist/Plot/Axis.svelte +19 -10
  6. package/dist/Plot/Root.svelte +67 -46
  7. package/dist/Plot/Root.svelte.d.ts +29 -2
  8. package/dist/Plot/Timeline.svelte +39 -13
  9. package/dist/Plot/index.d.ts +5 -5
  10. package/dist/Plot/index.js +5 -5
  11. package/dist/Plot.svelte +95 -178
  12. package/dist/Plot.svelte.d.ts +4 -0
  13. package/dist/PlotState.svelte.d.ts +29 -1
  14. package/dist/PlotState.svelte.js +163 -19
  15. package/dist/PlotSurface.svelte +162 -0
  16. package/dist/PlotSurface.svelte.d.ts +25 -0
  17. package/dist/charts/AreaChart.svelte +12 -2
  18. package/dist/charts/AreaChart.svelte.d.ts +6 -0
  19. package/dist/charts/BarChart.svelte +40 -4
  20. package/dist/charts/BarChart.svelte.d.ts +15 -1
  21. package/dist/charts/BoxPlot.svelte +7 -3
  22. package/dist/charts/BoxPlot.svelte.d.ts +2 -0
  23. package/dist/charts/BubbleChart.svelte +37 -4
  24. package/dist/charts/BubbleChart.svelte.d.ts +12 -1
  25. package/dist/charts/LineChart.svelte +4 -2
  26. package/dist/charts/LineChart.svelte.d.ts +1 -0
  27. package/dist/charts/PieChart.svelte +5 -3
  28. package/dist/charts/PieChart.svelte.d.ts +1 -0
  29. package/dist/charts/ScatterPlot.svelte +35 -4
  30. package/dist/charts/ScatterPlot.svelte.d.ts +11 -1
  31. package/dist/charts/ViolinPlot.svelte +7 -3
  32. package/dist/charts/ViolinPlot.svelte.d.ts +2 -0
  33. package/dist/examples/BarChartExample.svelte +1 -1
  34. package/dist/geoms/Arc.svelte +21 -54
  35. package/dist/geoms/Arc.svelte.d.ts +4 -4
  36. package/dist/geoms/Area.svelte +45 -44
  37. package/dist/geoms/Area.svelte.d.ts +8 -1
  38. package/dist/geoms/Bar.svelte +33 -45
  39. package/dist/geoms/Bar.svelte.d.ts +9 -0
  40. package/dist/geoms/Box.svelte +103 -61
  41. package/dist/geoms/Box.svelte.d.ts +12 -3
  42. package/dist/geoms/Candlestick.svelte +26 -75
  43. package/dist/geoms/Candlestick.svelte.d.ts +2 -0
  44. package/dist/geoms/Heatmap.svelte +21 -66
  45. package/dist/geoms/Heatmap.svelte.d.ts +2 -0
  46. package/dist/geoms/Hexbin.svelte +20 -100
  47. package/dist/geoms/Hexbin.svelte.d.ts +2 -0
  48. package/dist/geoms/Jitter.svelte +67 -0
  49. package/dist/geoms/Jitter.svelte.d.ts +17 -0
  50. package/dist/geoms/Line.svelte +27 -31
  51. package/dist/geoms/Line.svelte.d.ts +5 -0
  52. package/dist/geoms/Point.svelte +31 -60
  53. package/dist/geoms/Point.svelte.d.ts +5 -1
  54. package/dist/geoms/Ribbon.svelte +30 -124
  55. package/dist/geoms/Ribbon.svelte.d.ts +2 -0
  56. package/dist/geoms/Rule.svelte +117 -0
  57. package/dist/geoms/Rule.svelte.d.ts +19 -0
  58. package/dist/geoms/Violin.svelte +36 -31
  59. package/dist/geoms/Violin.svelte.d.ts +10 -3
  60. package/dist/geoms/Waterfall.svelte +25 -79
  61. package/dist/geoms/Waterfall.svelte.d.ts +2 -0
  62. package/dist/geoms/lib/GeomState.svelte.d.ts +51 -0
  63. package/dist/geoms/lib/GeomState.svelte.js +79 -0
  64. package/dist/geoms/lib/aesthetics.d.ts +36 -0
  65. package/dist/geoms/lib/aesthetics.js +45 -0
  66. package/dist/geoms/lib/areas.d.ts +6 -2
  67. package/dist/geoms/lib/areas.js +20 -12
  68. package/dist/geoms/lib/bars.d.ts +18 -3
  69. package/dist/geoms/lib/bars.js +80 -101
  70. package/dist/geoms/lib/marks/arc.d.ts +24 -0
  71. package/dist/geoms/lib/marks/arc.js +28 -0
  72. package/dist/geoms/lib/marks/area.d.ts +23 -0
  73. package/dist/geoms/lib/marks/area.js +38 -0
  74. package/dist/geoms/lib/marks/bar.d.ts +16 -0
  75. package/dist/geoms/lib/marks/bar.js +43 -0
  76. package/dist/geoms/lib/marks/box.d.ts +14 -0
  77. package/dist/geoms/lib/marks/box.js +30 -0
  78. package/dist/geoms/lib/marks/candlestick.d.ts +27 -0
  79. package/dist/geoms/lib/marks/candlestick.js +52 -0
  80. package/dist/geoms/lib/marks/heatmap.d.ts +24 -0
  81. package/dist/geoms/lib/marks/heatmap.js +41 -0
  82. package/dist/geoms/lib/marks/hexbin.d.ts +29 -0
  83. package/dist/geoms/lib/marks/hexbin.js +62 -0
  84. package/dist/geoms/lib/marks/jitter.d.ts +21 -0
  85. package/dist/geoms/lib/marks/jitter.js +36 -0
  86. package/dist/geoms/lib/marks/line.d.ts +26 -0
  87. package/dist/geoms/lib/marks/line.js +25 -0
  88. package/dist/geoms/lib/marks/point.d.ts +25 -0
  89. package/dist/geoms/lib/marks/point.js +58 -0
  90. package/dist/geoms/lib/marks/ribbon.d.ts +31 -0
  91. package/dist/geoms/lib/marks/ribbon.js +77 -0
  92. package/dist/geoms/lib/marks/violin.d.ts +13 -0
  93. package/dist/geoms/lib/marks/violin.js +31 -0
  94. package/dist/geoms/lib/marks/waterfall.d.ts +25 -0
  95. package/dist/geoms/lib/marks/waterfall.js +58 -0
  96. package/dist/index.d.ts +25 -12
  97. package/dist/index.js +26 -13
  98. package/dist/lib/brewing/marks/arcs.js +7 -1
  99. package/dist/lib/brewing/marks/boxes.d.ts +11 -2
  100. package/dist/lib/brewing/marks/boxes.js +32 -11
  101. package/dist/lib/brewing/marks/lines.d.ts +4 -1
  102. package/dist/lib/brewing/marks/lines.js +18 -15
  103. package/dist/lib/brewing/marks/swarm.d.ts +32 -0
  104. package/dist/lib/brewing/marks/swarm.js +125 -0
  105. package/dist/lib/brewing/marks/violins.d.ts +4 -20
  106. package/dist/lib/brewing/marks/violins.js +41 -25
  107. package/dist/lib/brewing/stats.d.ts +3 -1
  108. package/dist/lib/brewing/stats.js +28 -11
  109. package/dist/lib/plot/scales.js +12 -7
  110. package/dist/lib/plot/types.d.ts +9 -1
  111. package/dist/lib/plot/types.js +3 -1
  112. package/dist/lib/preset.d.ts +11 -1
  113. package/dist/lib/preset.js +16 -4
  114. package/package.json +1 -1
  115. package/dist/Plot/Arc.svelte +0 -30
  116. package/dist/Plot/Arc.svelte.d.ts +0 -18
  117. package/dist/Plot/Area.svelte +0 -54
  118. package/dist/Plot/Area.svelte.d.ts +0 -14
  119. package/dist/Plot/Bar.svelte +0 -69
  120. package/dist/Plot/Bar.svelte.d.ts +0 -11
  121. package/dist/Plot/Line.svelte +0 -54
  122. package/dist/Plot/Line.svelte.d.ts +0 -13
  123. package/dist/Plot/Point.svelte +0 -58
  124. package/dist/Plot/Point.svelte.d.ts +0 -13
  125. package/dist/lib/brewer.d.ts +0 -9
  126. package/dist/lib/brewer.js +0 -25
  127. package/dist/lib/brewing/BoxBrewer.svelte.d.ts +0 -8
  128. package/dist/lib/brewing/BoxBrewer.svelte.js +0 -14
  129. package/dist/lib/brewing/CartesianBrewer.svelte.d.ts +0 -8
  130. package/dist/lib/brewing/CartesianBrewer.svelte.js +0 -21
  131. package/dist/lib/brewing/PieBrewer.svelte.d.ts +0 -8
  132. package/dist/lib/brewing/PieBrewer.svelte.js +0 -14
  133. package/dist/lib/brewing/QuartileBrewer.svelte.d.ts +0 -9
  134. package/dist/lib/brewing/QuartileBrewer.svelte.js +0 -51
  135. package/dist/lib/brewing/ViolinBrewer.svelte.d.ts +0 -8
  136. package/dist/lib/brewing/ViolinBrewer.svelte.js +0 -14
  137. package/dist/lib/brewing/axes.svelte.d.ts +0 -66
  138. package/dist/lib/brewing/axes.svelte.js +0 -270
  139. package/dist/lib/brewing/bars.svelte.d.ts +0 -56
  140. package/dist/lib/brewing/bars.svelte.js +0 -201
  141. package/dist/lib/brewing/brewer.svelte.d.ts +0 -124
  142. package/dist/lib/brewing/brewer.svelte.js +0 -277
  143. package/dist/lib/brewing/dimensions.svelte.d.ts +0 -35
  144. package/dist/lib/brewing/dimensions.svelte.js +0 -56
  145. package/dist/lib/brewing/index.svelte.d.ts +0 -118
  146. package/dist/lib/brewing/index.svelte.js +0 -205
  147. package/dist/lib/brewing/legends.svelte.d.ts +0 -48
  148. package/dist/lib/brewing/legends.svelte.js +0 -137
  149. package/dist/lib/brewing/marks/areas.d.ts +0 -31
  150. package/dist/lib/brewing/marks/areas.js +0 -72
  151. package/dist/lib/brewing/marks/bars.d.ts +0 -1
  152. package/dist/lib/brewing/marks/bars.js +0 -49
  153. package/dist/lib/brewing/scales.svelte.d.ts +0 -24
  154. package/dist/lib/brewing/scales.svelte.js +0 -82
  155. package/dist/lib/context.d.ts +0 -13
  156. package/dist/lib/context.js +0 -131
  157. package/dist/lib/plot/chartProps.d.ts +0 -177
  158. package/dist/lib/plot/chartProps.js +0 -76
  159. package/dist/lib/scales.svelte.d.ts +0 -35
  160. package/dist/lib/scales.svelte.js +0 -151
  161. package/dist/lib/ticks.d.ts +0 -36
  162. package/dist/lib/ticks.js +0 -46
@@ -0,0 +1,62 @@
1
+ import { resolveAlpha } from '../aesthetics.js'
2
+
3
+ /** SVG path for a pointy-top hexagon of radius r, centered at the origin. */
4
+ export function hexPath(r) {
5
+ const angles = [0, 1, 2, 3, 4, 5].map((i) => ((i * 60 - 30) * Math.PI) / 180)
6
+ return `${angles.map((a, i) => `${i === 0 ? 'M' : 'L'}${r * Math.cos(a)},${r * Math.sin(a)}`).join('')}Z`
7
+ }
8
+
9
+ /** Group points into hexagonal bins and count per bin. */
10
+ function hexBin(rows, xField, yField, xScale, yScale, r) {
11
+ const dx = r * 2 * Math.sin(Math.PI / 3)
12
+ const dy = r * 1.5
13
+ const bins = new Map()
14
+ for (const d of rows) {
15
+ const px = xScale(d[xField ?? ''])
16
+ const py = yScale(d[yField ?? ''])
17
+ if (px === null || px === undefined || py === null || py === undefined) continue
18
+ const col = Math.round(px / dx)
19
+ const row = Math.round(py / dy)
20
+ const cx = col * dx + (row % 2 ? dx / 2 : 0)
21
+ const cy = row * dy
22
+ const key = `${col},${row}`
23
+ let bin = bins.get(key)
24
+ if (!bin) {
25
+ bin = { cx, cy, count: 0, points: [] }
26
+ bins.set(key, bin)
27
+ }
28
+ bin.count++
29
+ bin.points.push(d)
30
+ }
31
+ return [...bins.values()]
32
+ }
33
+
34
+ /**
35
+ * Build renderable hex bins. Fill is the continuous color scale (by count) or a density-ramp
36
+ * rgba fallback. `alpha` is a flat multiplier on top (default 1 → unchanged).
37
+ * @param {{ data: any[], plot: any, channels: any, options?: any, alpha?: number, type?: string }} ctx
38
+ */
39
+ export function buildHexbinMarks({ data, plot, channels, options = {}, alpha, type = 'hexbin' }) {
40
+ const { xScale, yScale } = plot
41
+ if (!data?.length || !xScale || !yScale) return []
42
+
43
+ const radius = options.radius ?? 20
44
+ const bins = hexBin(data, channels.x, channels.y, xScale, yScale, radius)
45
+ const maxCount = Math.max(1, ...bins.map((b) => b.count))
46
+ const colorScale = plot.continuousColorScale?.scale
47
+ const a = resolveAlpha(alpha, type, plot.chartPreset)
48
+
49
+ return bins.map((bin, i) => ({
50
+ key: `hex-${i}`,
51
+ cx: bin.cx,
52
+ cy: bin.cy,
53
+ count: bin.count,
54
+ fill: colorScale
55
+ ? colorScale(bin.count)
56
+ : `rgba(66, 133, 244, ${0.1 + 0.9 * (bin.count / maxCount)})`,
57
+ alpha: a,
58
+ value: bin.count,
59
+ display: `${bin.count}`,
60
+ data: { count: bin.count, x: bin.cx, y: bin.cy }
61
+ }))
62
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Build renderable jitter/swarm points. Fill + outline come from `fill` (defaulting to
3
+ * `x`); an explicit `color` channel overrides the outline stroke. Adds fixed `alpha`.
4
+ * Horizontal orientation swaps each point's screen coords via place().
5
+ * @param {{ data: any[], plot: any, channels: any, options?: any, alpha?: number, type?: string }} ctx
6
+ */
7
+ export function buildJitterMarks({ data, plot, channels, options, alpha, type }: {
8
+ data: any[];
9
+ plot: any;
10
+ channels: any;
11
+ options?: any;
12
+ alpha?: number;
13
+ type?: string;
14
+ }): {
15
+ cx: any;
16
+ cy: any;
17
+ stroke: any;
18
+ alpha: number;
19
+ fill: string;
20
+ data: Record<string, unknown>;
21
+ }[];
@@ -0,0 +1,36 @@
1
+ import { buildSwarm } from '../../../lib/brewing/marks/swarm.js'
2
+ import { resolveAlpha } from '../aesthetics.js'
3
+
4
+ /**
5
+ * Build renderable jitter/swarm points. Fill + outline come from `fill` (defaulting to
6
+ * `x`); an explicit `color` channel overrides the outline stroke. Adds fixed `alpha`.
7
+ * Horizontal orientation swaps each point's screen coords via place().
8
+ * @param {{ data: any[], plot: any, channels: any, options?: any, alpha?: number, type?: string }} ctx
9
+ */
10
+ export function buildJitterMarks({ data, plot, channels, options = {}, alpha, type = 'jitter' }) {
11
+ const { xScale, yScale, colors } = plot
12
+ if (!data?.length || !xScale || !yScale) return []
13
+
14
+ const fillField = channels.fill ?? channels.x
15
+ const raw = buildSwarm(
16
+ data,
17
+ { x: channels.x, y: channels.y, fill: fillField },
18
+ xScale,
19
+ yScale,
20
+ colors,
21
+ { method: options.method, r: options.r, side: options.side }
22
+ )
23
+ const a = resolveAlpha(alpha, type, plot.chartPreset)
24
+ const colorField = channels.color
25
+ const flip = plot.isFlipped
26
+ return raw.map((p) => {
27
+ const s = flip ? plot.place(p.cx, p.cy) : { x: p.cx, y: p.cy }
28
+ return {
29
+ ...p,
30
+ cx: s.x,
31
+ cy: s.y,
32
+ stroke: colorField ? (colors.get(p.data[colorField])?.stroke ?? p.stroke) : p.stroke,
33
+ alpha: a
34
+ }
35
+ })
36
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Build renderable line segments. A line has no interior, so its single aesthetic is
3
+ * the stroke — driven by `color`, with `fill` accepted as an alias (ggplot geom_line
4
+ * uses `colour`). Each segment gets the geom's fixed `alpha`.
5
+ * @param {{ data: any[], plot: any, channels: any, options?: any, alpha?: number, type?: string }} ctx
6
+ */
7
+ export function buildLineMarks({ data, plot, channels, options, alpha, type }: {
8
+ data: any[];
9
+ plot: any;
10
+ channels: any;
11
+ options?: any;
12
+ alpha?: number;
13
+ type?: string;
14
+ }): {
15
+ alpha: number;
16
+ d: string | null;
17
+ fill: string;
18
+ stroke: string;
19
+ points: {
20
+ x: number;
21
+ y: number;
22
+ data: Record<string, unknown>;
23
+ i: number;
24
+ }[];
25
+ key?: unknown;
26
+ }[];
@@ -0,0 +1,25 @@
1
+ import { buildLines } from '../../../lib/brewing/marks/lines.js'
2
+ import { resolveAlpha } from '../aesthetics.js'
3
+
4
+ /**
5
+ * Build renderable line segments. A line has no interior, so its single aesthetic is
6
+ * the stroke — driven by `color`, with `fill` accepted as an alias (ggplot geom_line
7
+ * uses `colour`). Each segment gets the geom's fixed `alpha`.
8
+ * @param {{ data: any[], plot: any, channels: any, options?: any, alpha?: number, type?: string }} ctx
9
+ */
10
+ export function buildLineMarks({ data, plot, channels, options = {}, alpha, type = 'line' }) {
11
+ const { xScale, yScale, colors } = plot
12
+ if (!data?.length || !xScale || !yScale) return []
13
+ const lineColor = channels.color ?? channels.fill
14
+ const segs = buildLines(
15
+ data,
16
+ { x: channels.x, y: channels.y, color: lineColor },
17
+ xScale,
18
+ yScale,
19
+ colors,
20
+ options.curve,
21
+ plot.place.bind(plot)
22
+ )
23
+ const a = resolveAlpha(alpha, type, plot.chartPreset)
24
+ return segs.map((seg) => ({ ...seg, alpha: a }))
25
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Build renderable point marks: geometry (from buildPoints) + shared aesthetics
3
+ * (fill/color split + alpha) + value/display. The geom template just renders these.
4
+ * @param {{ data: any[], plot: any, channels: any, options?: any, alpha?: number, type?: string }} ctx
5
+ */
6
+ export function buildPointMarks({ data, plot, channels, options, alpha, type }: {
7
+ data: any[];
8
+ plot: any;
9
+ channels: any;
10
+ options?: any;
11
+ alpha?: number;
12
+ type?: string;
13
+ }): {
14
+ cx: any;
15
+ cy: any;
16
+ fill: string;
17
+ stroke: string;
18
+ alpha: number;
19
+ value: unknown;
20
+ display: string;
21
+ key: string;
22
+ data: Record<string, unknown>;
23
+ r: number;
24
+ symbolPath: string | null;
25
+ }[];
@@ -0,0 +1,58 @@
1
+ import { scaleSqrt } from 'd3-scale'
2
+ import { buildPoints } from '../../../lib/brewing/marks/points.js'
3
+ import { resolveFillStroke, resolveAlpha } from '../aesthetics.js'
4
+
5
+ /**
6
+ * Size scale for a bubble/size channel — sqrt so area (not radius) is proportional.
7
+ * @returns {import('d3-scale').ScalePower<number, number> | null}
8
+ */
9
+ function buildSizeScale(data, sizeField, options) {
10
+ if (!sizeField || !data?.length) return null
11
+ const vals = data.map((d) => Number(d[sizeField])).filter((v) => !isNaN(v))
12
+ if (!vals.length) return null
13
+ return scaleSqrt()
14
+ .domain([Math.min(...vals), Math.max(...vals)])
15
+ .range([options?.minRadius ?? 3, options?.maxRadius ?? 20])
16
+ }
17
+
18
+ /**
19
+ * Build renderable point marks: geometry (from buildPoints) + shared aesthetics
20
+ * (fill/color split + alpha) + value/display. The geom template just renders these.
21
+ * @param {{ data: any[], plot: any, channels: any, options?: any, alpha?: number, type?: string }} ctx
22
+ */
23
+ export function buildPointMarks({ data, plot, channels, options = {}, alpha, type = 'point' }) {
24
+ const { xScale, yScale, colors } = plot
25
+ if (!data?.length || !xScale || !yScale) return []
26
+
27
+ const sizeScale = buildSizeScale(data, channels.size, options)
28
+ const raw = buildPoints(
29
+ data,
30
+ channels,
31
+ xScale,
32
+ yScale,
33
+ colors,
34
+ sizeScale,
35
+ plot.symbols,
36
+ options.radius ?? 4,
37
+ options.jitter ?? null
38
+ )
39
+ const a = resolveAlpha(alpha, type, plot.chartPreset)
40
+ const yf = channels.y
41
+
42
+ return raw.map((p, i) => {
43
+ const { fill, stroke } = resolveFillStroke(p.data, channels, colors)
44
+ // Horizontal orientation: swap each point's screen coords via place().
45
+ const s = plot.isFlipped ? plot.place(p.cx, p.cy) : { x: p.cx, y: p.cy }
46
+ return {
47
+ ...p,
48
+ cx: s.x,
49
+ cy: s.y,
50
+ fill,
51
+ stroke,
52
+ alpha: a,
53
+ value: yf ? p.data[yf] : undefined,
54
+ display: yf ? String(p.data[yf] ?? '') : '',
55
+ key: `${i}::${p.data[channels.x ?? '']}::${p.data[yf ?? '']}`
56
+ }
57
+ })
58
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Build a Sankey-style ribbon layout: links (flows) + source/target node boxes. Link fill
3
+ * comes from the source (then target) palette entry; `alpha` is fixed (default 0.5). Returns
4
+ * an object rather than a flat array, so the geom template renders links + node labels.
5
+ * @param {{ data: any[], plot: any, options?: any, alpha?: number, type?: string }} ctx
6
+ */
7
+ export function buildRibbonMarks({ data, plot, options, alpha, type }: {
8
+ data: any[];
9
+ plot: any;
10
+ options?: any;
11
+ alpha?: number;
12
+ type?: string;
13
+ }): {
14
+ links: {
15
+ key: string;
16
+ d: string;
17
+ fill: any;
18
+ alpha: number;
19
+ data: any;
20
+ }[];
21
+ sourceNodes: {
22
+ name: any;
23
+ y: number;
24
+ height: number;
25
+ }[];
26
+ targetNodes: {
27
+ name: any;
28
+ y: number;
29
+ height: number;
30
+ }[];
31
+ };
@@ -0,0 +1,77 @@
1
+ import { resolveAlpha } from '../aesthetics.js'
2
+
3
+ /**
4
+ * Build a Sankey-style ribbon layout: links (flows) + source/target node boxes. Link fill
5
+ * comes from the source (then target) palette entry; `alpha` is fixed (default 0.5). Returns
6
+ * an object rather than a flat array, so the geom template renders links + node labels.
7
+ * @param {{ data: any[], plot: any, options?: any, alpha?: number, type?: string }} ctx
8
+ */
9
+ export function buildRibbonMarks({ data, plot, options = {}, alpha, type = 'ribbon' }) {
10
+ const empty = { links: [], sourceNodes: [], targetNodes: [] }
11
+ if (!data?.length) return empty
12
+
13
+ const { colors, innerHeight, innerWidth } = plot
14
+ const sourceField = options.source ?? 'source'
15
+ const targetField = options.target ?? 'target'
16
+ const valueField = options.value ?? 'value'
17
+ const a = resolveAlpha(alpha, type, plot.chartPreset)
18
+
19
+ const flows = data.map((d) => ({
20
+ source: String(d[sourceField]),
21
+ target: String(d[targetField]),
22
+ value: Number(d[valueField]) || 0,
23
+ data: d
24
+ }))
25
+
26
+ const sourceMap = new Map()
27
+ const targetMap = new Map()
28
+ for (const f of flows) {
29
+ sourceMap.set(f.source, (sourceMap.get(f.source) ?? 0) + f.value)
30
+ targetMap.set(f.target, (targetMap.get(f.target) ?? 0) + f.value)
31
+ }
32
+
33
+ const totalSource = Math.max(1, [...sourceMap.values()].reduce((s, v) => s + v, 0))
34
+ const totalTarget = Math.max(1, [...targetMap.values()].reduce((s, v) => s + v, 0))
35
+ const padding = 4
36
+ const availHeight = innerHeight - padding * (Math.max(sourceMap.size, targetMap.size) - 1)
37
+
38
+ let sourceY = 0
39
+ const sourceNodes = []
40
+ for (const [name, value] of sourceMap) {
41
+ const h = (value / totalSource) * availHeight
42
+ sourceNodes.push({ name, y: sourceY, height: h })
43
+ sourceY += h + padding
44
+ }
45
+
46
+ let targetY = 0
47
+ const targetNodes = []
48
+ for (const [name, value] of targetMap) {
49
+ const h = (value / totalTarget) * availHeight
50
+ targetNodes.push({ name, y: targetY, height: h })
51
+ targetY += h + padding
52
+ }
53
+
54
+ const sourceOffsets = new Map(sourceNodes.map((n) => [n.name, n.y]))
55
+ const targetOffsets = new Map(targetNodes.map((n) => [n.name, n.y]))
56
+ const x0 = 0
57
+ const x1 = innerWidth
58
+
59
+ const links = flows.map((f, i) => {
60
+ const sy0 = sourceOffsets.get(f.source) ?? 0
61
+ const ty0 = targetOffsets.get(f.target) ?? 0
62
+ const sourceTotal = sourceMap.get(f.source) ?? 1
63
+ const targetTotal = targetMap.get(f.target) ?? 1
64
+ const sh = (f.value / sourceTotal) * (sourceNodes.find((n) => n.name === f.source)?.height ?? 0)
65
+ const th = (f.value / targetTotal) * (targetNodes.find((n) => n.name === f.target)?.height ?? 0)
66
+
67
+ const path = `M${x0},${sy0} C${innerWidth * 0.4},${sy0} ${innerWidth * 0.6},${ty0} ${x1},${ty0} L${x1},${ty0 + th} C${innerWidth * 0.6},${ty0 + th} ${innerWidth * 0.4},${sy0 + sh} ${x0},${sy0 + sh} Z`
68
+
69
+ sourceOffsets.set(f.source, sy0 + sh)
70
+ targetOffsets.set(f.target, ty0 + th)
71
+
72
+ const fill = colors?.get(f.source)?.fill ?? colors?.get(f.target)?.fill ?? '#888'
73
+ return { key: `ribbon-${i}`, d: path, fill, alpha: a, data: f.data }
74
+ })
75
+
76
+ return { links, sourceNodes, targetNodes }
77
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Build renderable violin silhouettes. Interior + outline come from `fill` (defaulting to
3
+ * `x`); an explicit `color` channel overrides the outline stroke. Adds fixed `alpha`.
4
+ * @param {{ data: any[], plot: any, channels: any, options?: any, alpha?: number, type?: string }} ctx
5
+ */
6
+ export function buildViolinMarks({ data, plot, channels, options, alpha, type }: {
7
+ data: any[];
8
+ plot: any;
9
+ channels: any;
10
+ options?: any;
11
+ alpha?: number;
12
+ type?: string;
13
+ }): any;
@@ -0,0 +1,31 @@
1
+ import { buildViolins } from '../../../lib/brewing/marks/violins.js'
2
+ import { resolveAlpha } from '../aesthetics.js'
3
+
4
+ /**
5
+ * Build renderable violin silhouettes. Interior + outline come from `fill` (defaulting to
6
+ * `x`); an explicit `color` channel overrides the outline stroke. Adds fixed `alpha`.
7
+ * @param {{ data: any[], plot: any, channels: any, options?: any, alpha?: number, type?: string }} ctx
8
+ */
9
+ export function buildViolinMarks({ data, plot, channels, options = {}, alpha, type = 'violin' }) {
10
+ const { xScale, yScale, colors } = plot
11
+ if (!data?.length || !xScale || !yScale) return []
12
+
13
+ const fillField = channels.fill ?? channels.x
14
+ const violins = buildViolins(
15
+ data,
16
+ { x: channels.x, fill: fillField, pattern: channels.pattern },
17
+ xScale,
18
+ yScale,
19
+ colors,
20
+ plot.place.bind(plot),
21
+ options.side,
22
+ plot.patterns
23
+ )
24
+ const a = resolveAlpha(alpha, type, plot.chartPreset)
25
+ const colorField = channels.color
26
+ return violins.map((v) => ({
27
+ ...v,
28
+ stroke: colorField ? (colors.get(v.data[colorField])?.stroke ?? v.stroke) : v.stroke,
29
+ alpha: a
30
+ }))
31
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Build renderable waterfall bars. Bar color is semantic (positive/negative/total from
3
+ * options), not the shared palette. Adds fixed `alpha` and carries connector anchors.
4
+ * @param {{ data: any[], plot: any, channels: any, options?: any, alpha?: number, type?: string }} ctx
5
+ */
6
+ export function buildWaterfallMarks({ data, plot, channels, options, alpha, type }: {
7
+ data: any[];
8
+ plot: any;
9
+ channels: any;
10
+ options?: any;
11
+ alpha?: number;
12
+ type?: string;
13
+ }): {
14
+ key: string;
15
+ x: number;
16
+ y: number;
17
+ width: number;
18
+ height: number;
19
+ fill: any;
20
+ alpha: number;
21
+ bandStart: any;
22
+ bandEnd: any;
23
+ cumY: any;
24
+ data: any;
25
+ }[];
@@ -0,0 +1,58 @@
1
+ import { resolveAlpha } from '../aesthetics.js'
2
+
3
+ /**
4
+ * Build renderable waterfall bars. Bar color is semantic (positive/negative/total from
5
+ * options), not the shared palette. Adds fixed `alpha` and carries connector anchors.
6
+ * @param {{ data: any[], plot: any, channels: any, options?: any, alpha?: number, type?: string }} ctx
7
+ */
8
+ export function buildWaterfallMarks({ data, plot, channels, options = {}, alpha, type = 'waterfall' }) {
9
+ const { xScale, yScale } = plot
10
+ if (!data?.length || !xScale || !yScale) return []
11
+
12
+ const positiveColor = options.positiveColor ?? '#22c55e'
13
+ const negativeColor = options.negativeColor ?? '#ef4444'
14
+ const totalColor = options.totalColor ?? '#3b82f6'
15
+ const totalField = options.totalField ?? undefined
16
+ const a = resolveAlpha(alpha, type, plot.chartPreset)
17
+
18
+ const bw = typeof xScale.bandwidth === 'function' ? xScale.bandwidth() : 10
19
+ let cumulative = 0
20
+
21
+ return data.map((d, i) => {
22
+ const xVal = d[channels.x ?? '']
23
+ const yVal = Number(d[channels.y ?? ''])
24
+ const isTotal = totalField ? Boolean(d[totalField]) : false
25
+ const xPos = xScale(xVal) ?? 0
26
+ let barTop
27
+ let barBottom
28
+ let fill
29
+
30
+ if (isTotal) {
31
+ barTop = yScale(Math.max(0, cumulative)) ?? 0
32
+ barBottom = yScale(0) ?? 0
33
+ fill = totalColor
34
+ } else {
35
+ const start = cumulative
36
+ cumulative += yVal
37
+ barTop = yScale(Math.max(start, cumulative)) ?? 0
38
+ barBottom = yScale(Math.min(start, cumulative)) ?? 0
39
+ fill = yVal >= 0 ? positiveColor : negativeColor
40
+ }
41
+
42
+ const c1 = plot.place(xPos, barTop)
43
+ const c2 = plot.place(xPos + bw, barBottom)
44
+ return {
45
+ key: `${xVal}-${i}`,
46
+ x: Math.min(c1.x, c2.x),
47
+ y: Math.min(c1.y, c2.y),
48
+ width: Math.abs(c2.x - c1.x),
49
+ height: Math.max(1, Math.abs(c2.y - c1.y)),
50
+ fill,
51
+ alpha: a,
52
+ bandStart: xPos,
53
+ bandEnd: xPos + bw,
54
+ cumY: yScale(cumulative) ?? 0,
55
+ data: d
56
+ }
57
+ })
58
+ }
package/dist/index.d.ts CHANGED
@@ -10,6 +10,15 @@ export namespace Plot {
10
10
  export { Arc };
11
11
  export { Highlight };
12
12
  export { Trend };
13
+ export { Box };
14
+ export { Violin };
15
+ export { Jitter };
16
+ export { Candlestick };
17
+ export { Heatmap };
18
+ export { Hexbin };
19
+ export { Ribbon };
20
+ export { Waterfall };
21
+ export { Rule };
13
22
  }
14
23
  export namespace PlotLayers { }
15
24
  export { default as PlotChart } from "./Plot.svelte";
@@ -32,6 +41,8 @@ export { default as GeomHexbin } from "./geoms/Hexbin.svelte";
32
41
  export { default as GeomRibbon } from "./geoms/Ribbon.svelte";
33
42
  export { default as GeomHighlight } from "./geoms/Highlight.svelte";
34
43
  export { default as GeomTrend } from "./geoms/Trend.svelte";
44
+ export { default as GeomJitter } from "./geoms/Jitter.svelte";
45
+ export { default as GeomRule } from "./geoms/Rule.svelte";
35
46
  export { default as Chart } from "./Chart.svelte";
36
47
  export { default as Sparkline } from "./Sparkline.svelte";
37
48
  export { default as BarChart } from "./charts/BarChart.svelte";
@@ -43,13 +54,6 @@ export { default as BoxPlot } from "./charts/BoxPlot.svelte";
43
54
  export { default as ViolinPlot } from "./charts/ViolinPlot.svelte";
44
55
  export { default as BubbleChart } from "./charts/BubbleChart.svelte";
45
56
  export { PlotState } from "./PlotState.svelte.js";
46
- export { ChartBrewer } from "./lib/brewing/index.svelte.js";
47
- export * from "./lib/brewing/index.svelte.js";
48
- export { CartesianBrewer } from "./lib/brewing/CartesianBrewer.svelte.js";
49
- export { PieBrewer } from "./lib/brewing/PieBrewer.svelte.js";
50
- export { QuartileBrewer } from "./lib/brewing/QuartileBrewer.svelte.js";
51
- export { BoxBrewer } from "./lib/brewing/BoxBrewer.svelte.js";
52
- export { ViolinBrewer } from "./lib/brewing/ViolinBrewer.svelte.js";
53
57
  export { createCrossFilter } from "./crossfilter/createCrossFilter.svelte.js";
54
58
  export { default as CrossFilter } from "./crossfilter/CrossFilter.svelte";
55
59
  export { default as FilterBar } from "./crossfilter/FilterBar.svelte";
@@ -57,14 +61,23 @@ export { default as FilterSlider } from "./crossfilter/FilterSlider.svelte";
57
61
  export { default as FilterHistogram } from "./crossfilter/FilterHistogram.svelte";
58
62
  import Root from './Plot/Root.svelte';
59
63
  import Axis from './Plot/Axis.svelte';
60
- import Bar from './Plot/Bar.svelte';
64
+ import Bar from './geoms/Bar.svelte';
61
65
  import Grid from './Plot/Grid.svelte';
62
66
  import Legend from './Plot/Legend.svelte';
63
- import Line from './Plot/Line.svelte';
64
- import Area from './Plot/Area.svelte';
65
- import Point from './Plot/Point.svelte';
66
- import Arc from './Plot/Arc.svelte';
67
+ import Line from './geoms/Line.svelte';
68
+ import Area from './geoms/Area.svelte';
69
+ import Point from './geoms/Point.svelte';
70
+ import Arc from './geoms/Arc.svelte';
67
71
  import Highlight from './geoms/Highlight.svelte';
68
72
  import Trend from './geoms/Trend.svelte';
73
+ import Box from './geoms/Box.svelte';
74
+ import Violin from './geoms/Violin.svelte';
75
+ import Jitter from './geoms/Jitter.svelte';
76
+ import Candlestick from './geoms/Candlestick.svelte';
77
+ import Heatmap from './geoms/Heatmap.svelte';
78
+ import Hexbin from './geoms/Hexbin.svelte';
79
+ import Ribbon from './geoms/Ribbon.svelte';
80
+ import Waterfall from './geoms/Waterfall.svelte';
81
+ import Rule from './geoms/Rule.svelte';
69
82
  export { createChartPreset, defaultPreset } from "./lib/preset.js";
70
83
  export { buildSequentialScale, buildDivergingScale } from "./lib/brewing/colors.js";
package/dist/index.js CHANGED
@@ -1,14 +1,23 @@
1
1
  import Root from './Plot/Root.svelte'
2
2
  import Axis from './Plot/Axis.svelte'
3
- import Bar from './Plot/Bar.svelte'
3
+ import Bar from './geoms/Bar.svelte'
4
4
  import Grid from './Plot/Grid.svelte'
5
5
  import Legend from './Plot/Legend.svelte'
6
- import Line from './Plot/Line.svelte'
7
- import Area from './Plot/Area.svelte'
8
- import Point from './Plot/Point.svelte'
9
- import Arc from './Plot/Arc.svelte'
6
+ import Line from './geoms/Line.svelte'
7
+ import Area from './geoms/Area.svelte'
8
+ import Point from './geoms/Point.svelte'
9
+ import Arc from './geoms/Arc.svelte'
10
10
  import Highlight from './geoms/Highlight.svelte'
11
11
  import Trend from './geoms/Trend.svelte'
12
+ import Box from './geoms/Box.svelte'
13
+ import Violin from './geoms/Violin.svelte'
14
+ import Jitter from './geoms/Jitter.svelte'
15
+ import Candlestick from './geoms/Candlestick.svelte'
16
+ import Heatmap from './geoms/Heatmap.svelte'
17
+ import Hexbin from './geoms/Hexbin.svelte'
18
+ import Ribbon from './geoms/Ribbon.svelte'
19
+ import Waterfall from './geoms/Waterfall.svelte'
20
+ import Rule from './geoms/Rule.svelte'
12
21
 
13
22
  // Composable Plot primitives — use as <Plot.Root>, <Plot.Axis>, <Plot.Bar>, etc.
14
23
  export const Plot = {
@@ -22,7 +31,16 @@ export const Plot = {
22
31
  Point,
23
32
  Arc,
24
33
  Highlight,
25
- Trend
34
+ Trend,
35
+ Box,
36
+ Violin,
37
+ Jitter,
38
+ Candlestick,
39
+ Heatmap,
40
+ Hexbin,
41
+ Ribbon,
42
+ Waterfall,
43
+ Rule
26
44
  }
27
45
 
28
46
  /** @deprecated Use Plot instead */
@@ -54,6 +72,8 @@ export { default as GeomHexbin } from './geoms/Hexbin.svelte'
54
72
  export { default as GeomRibbon } from './geoms/Ribbon.svelte'
55
73
  export { default as GeomHighlight } from './geoms/Highlight.svelte'
56
74
  export { default as GeomTrend } from './geoms/Trend.svelte'
75
+ export { default as GeomJitter } from './geoms/Jitter.svelte'
76
+ export { default as GeomRule } from './geoms/Rule.svelte'
57
77
 
58
78
  // Export standalone components
59
79
  export { default as Chart } from './Chart.svelte'
@@ -71,14 +91,7 @@ export { default as BubbleChart } from './charts/BubbleChart.svelte'
71
91
  export { PlotState } from './PlotState.svelte.js'
72
92
 
73
93
  // Export utilities
74
- export { ChartBrewer } from './lib/brewing/index.svelte.js'
75
- export * from './lib/brewing/index.svelte.js'
76
94
  export { buildSequentialScale, buildDivergingScale } from './lib/brewing/colors.js'
77
- export { CartesianBrewer } from './lib/brewing/CartesianBrewer.svelte.js'
78
- export { PieBrewer } from './lib/brewing/PieBrewer.svelte.js'
79
- export { QuartileBrewer } from './lib/brewing/QuartileBrewer.svelte.js'
80
- export { BoxBrewer } from './lib/brewing/BoxBrewer.svelte.js'
81
- export { ViolinBrewer } from './lib/brewing/ViolinBrewer.svelte.js'
82
95
 
83
96
  // CrossFilter system
84
97
  export { createCrossFilter } from './crossfilter/createCrossFilter.svelte.js'
@@ -15,7 +15,13 @@ import { toPatternId } from '../../brewing/patterns.js'
15
15
  export function buildArcs(data, channels, colors, width, height, opts = {}, patterns) {
16
16
  const { color: lf, y: yf } = channels
17
17
  const radius = Math.min(width, height) / 2
18
- const innerRadius = opts.innerRadius ?? 0
18
+ // innerRadius: a value <= 1 is a fraction of the radius (responsive donut);
19
+ // a value > 1 is absolute pixels. Clamp below the outer radius so a bad value
20
+ // (e.g. a fraction/pixel mixup) can never invert the arc into a giant ring
21
+ // rendered outside the circle.
22
+ const rawInner = opts.innerRadius ?? 0
23
+ const innerPx = rawInner <= 1 ? rawInner * radius : rawInner
24
+ const innerRadius = Math.max(0, Math.min(innerPx, radius * 0.98))
19
25
  const pieGen = pie().value((d) => Number(d[yf]))
20
26
  const arcGen = arc().innerRadius(innerRadius).outerRadius(radius)
21
27
  const slices = pieGen(data)