@rokkit/chart 1.3.8 → 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 (130) hide show
  1. package/dist/Chart.svelte +50 -64
  2. package/dist/Plot/Root.svelte +4 -1
  3. package/dist/Plot/Root.svelte.d.ts +2 -0
  4. package/dist/Plot/index.d.ts +1 -1
  5. package/dist/Plot/index.js +1 -1
  6. package/dist/PlotState.svelte.d.ts +14 -2
  7. package/dist/PlotState.svelte.js +46 -4
  8. package/dist/charts/AreaChart.svelte +8 -1
  9. package/dist/charts/AreaChart.svelte.d.ts +5 -0
  10. package/dist/charts/BarChart.svelte +8 -1
  11. package/dist/charts/BarChart.svelte.d.ts +5 -0
  12. package/dist/charts/BoxPlot.svelte +3 -1
  13. package/dist/charts/BoxPlot.svelte.d.ts +1 -0
  14. package/dist/charts/BubbleChart.svelte +3 -1
  15. package/dist/charts/BubbleChart.svelte.d.ts +1 -0
  16. package/dist/charts/LineChart.svelte +3 -1
  17. package/dist/charts/LineChart.svelte.d.ts +1 -0
  18. package/dist/charts/PieChart.svelte +4 -2
  19. package/dist/charts/PieChart.svelte.d.ts +1 -0
  20. package/dist/charts/ScatterPlot.svelte +3 -1
  21. package/dist/charts/ScatterPlot.svelte.d.ts +1 -0
  22. package/dist/charts/ViolinPlot.svelte +3 -1
  23. package/dist/charts/ViolinPlot.svelte.d.ts +1 -0
  24. package/dist/geoms/Arc.svelte +21 -55
  25. package/dist/geoms/Arc.svelte.d.ts +4 -4
  26. package/dist/geoms/Area.svelte +45 -44
  27. package/dist/geoms/Area.svelte.d.ts +8 -1
  28. package/dist/geoms/Bar.svelte +33 -56
  29. package/dist/geoms/Bar.svelte.d.ts +9 -0
  30. package/dist/geoms/Box.svelte +118 -131
  31. package/dist/geoms/Box.svelte.d.ts +5 -3
  32. package/dist/geoms/Candlestick.svelte +22 -77
  33. package/dist/geoms/Candlestick.svelte.d.ts +2 -0
  34. package/dist/geoms/Heatmap.svelte +21 -66
  35. package/dist/geoms/Heatmap.svelte.d.ts +2 -0
  36. package/dist/geoms/Hexbin.svelte +20 -100
  37. package/dist/geoms/Hexbin.svelte.d.ts +2 -0
  38. package/dist/geoms/Jitter.svelte +21 -36
  39. package/dist/geoms/Jitter.svelte.d.ts +5 -3
  40. package/dist/geoms/Line.svelte +27 -31
  41. package/dist/geoms/Line.svelte.d.ts +5 -0
  42. package/dist/geoms/Point.svelte +31 -66
  43. package/dist/geoms/Point.svelte.d.ts +5 -1
  44. package/dist/geoms/Ribbon.svelte +30 -124
  45. package/dist/geoms/Ribbon.svelte.d.ts +2 -0
  46. package/dist/geoms/Rule.svelte +5 -1
  47. package/dist/geoms/Rule.svelte.d.ts +3 -0
  48. package/dist/geoms/Violin.svelte +21 -40
  49. package/dist/geoms/Violin.svelte.d.ts +5 -3
  50. package/dist/geoms/Waterfall.svelte +19 -80
  51. package/dist/geoms/Waterfall.svelte.d.ts +2 -0
  52. package/dist/geoms/lib/GeomState.svelte.d.ts +51 -0
  53. package/dist/geoms/lib/GeomState.svelte.js +79 -0
  54. package/dist/geoms/lib/aesthetics.d.ts +36 -0
  55. package/dist/geoms/lib/aesthetics.js +45 -0
  56. package/dist/geoms/lib/areas.d.ts +1 -1
  57. package/dist/geoms/lib/areas.js +4 -2
  58. package/dist/geoms/lib/bars.d.ts +2 -2
  59. package/dist/geoms/lib/bars.js +13 -8
  60. package/dist/geoms/lib/marks/arc.d.ts +24 -0
  61. package/dist/geoms/lib/marks/arc.js +28 -0
  62. package/dist/geoms/lib/marks/area.d.ts +23 -0
  63. package/dist/geoms/lib/marks/area.js +38 -0
  64. package/dist/geoms/lib/marks/bar.d.ts +16 -0
  65. package/dist/geoms/lib/marks/bar.js +43 -0
  66. package/dist/geoms/lib/marks/box.d.ts +14 -0
  67. package/dist/geoms/lib/marks/box.js +30 -0
  68. package/dist/geoms/lib/marks/candlestick.d.ts +27 -0
  69. package/dist/geoms/lib/marks/candlestick.js +52 -0
  70. package/dist/geoms/lib/marks/heatmap.d.ts +24 -0
  71. package/dist/geoms/lib/marks/heatmap.js +41 -0
  72. package/dist/geoms/lib/marks/hexbin.d.ts +29 -0
  73. package/dist/geoms/lib/marks/hexbin.js +62 -0
  74. package/dist/geoms/lib/marks/jitter.d.ts +21 -0
  75. package/dist/geoms/lib/marks/jitter.js +36 -0
  76. package/dist/geoms/lib/marks/line.d.ts +26 -0
  77. package/dist/geoms/lib/marks/line.js +25 -0
  78. package/dist/geoms/lib/marks/point.d.ts +25 -0
  79. package/dist/geoms/lib/marks/point.js +58 -0
  80. package/dist/geoms/lib/marks/ribbon.d.ts +31 -0
  81. package/dist/geoms/lib/marks/ribbon.js +77 -0
  82. package/dist/geoms/lib/marks/violin.d.ts +13 -0
  83. package/dist/geoms/lib/marks/violin.js +31 -0
  84. package/dist/geoms/lib/marks/waterfall.d.ts +25 -0
  85. package/dist/geoms/lib/marks/waterfall.js +58 -0
  86. package/dist/index.d.ts +1 -8
  87. package/dist/index.js +1 -8
  88. package/dist/lib/preset.d.ts +11 -1
  89. package/dist/lib/preset.js +16 -4
  90. package/package.json +1 -1
  91. package/dist/Plot/Arc.svelte +0 -30
  92. package/dist/Plot/Arc.svelte.d.ts +0 -18
  93. package/dist/lib/brewer.d.ts +0 -9
  94. package/dist/lib/brewer.js +0 -25
  95. package/dist/lib/brewing/BoxBrewer.svelte.d.ts +0 -8
  96. package/dist/lib/brewing/BoxBrewer.svelte.js +0 -14
  97. package/dist/lib/brewing/CartesianBrewer.svelte.d.ts +0 -8
  98. package/dist/lib/brewing/CartesianBrewer.svelte.js +0 -21
  99. package/dist/lib/brewing/PieBrewer.svelte.d.ts +0 -8
  100. package/dist/lib/brewing/PieBrewer.svelte.js +0 -14
  101. package/dist/lib/brewing/QuartileBrewer.svelte.d.ts +0 -9
  102. package/dist/lib/brewing/QuartileBrewer.svelte.js +0 -51
  103. package/dist/lib/brewing/ViolinBrewer.svelte.d.ts +0 -8
  104. package/dist/lib/brewing/ViolinBrewer.svelte.js +0 -14
  105. package/dist/lib/brewing/axes.svelte.d.ts +0 -66
  106. package/dist/lib/brewing/axes.svelte.js +0 -270
  107. package/dist/lib/brewing/bars.svelte.d.ts +0 -56
  108. package/dist/lib/brewing/bars.svelte.js +0 -201
  109. package/dist/lib/brewing/brewer.svelte.d.ts +0 -124
  110. package/dist/lib/brewing/brewer.svelte.js +0 -277
  111. package/dist/lib/brewing/dimensions.svelte.d.ts +0 -35
  112. package/dist/lib/brewing/dimensions.svelte.js +0 -56
  113. package/dist/lib/brewing/index.svelte.d.ts +0 -118
  114. package/dist/lib/brewing/index.svelte.js +0 -205
  115. package/dist/lib/brewing/legends.svelte.d.ts +0 -48
  116. package/dist/lib/brewing/legends.svelte.js +0 -137
  117. package/dist/lib/brewing/marks/areas.d.ts +0 -31
  118. package/dist/lib/brewing/marks/areas.js +0 -72
  119. package/dist/lib/brewing/marks/bars.d.ts +0 -1
  120. package/dist/lib/brewing/marks/bars.js +0 -49
  121. package/dist/lib/brewing/scales.svelte.d.ts +0 -24
  122. package/dist/lib/brewing/scales.svelte.js +0 -82
  123. package/dist/lib/context.d.ts +0 -13
  124. package/dist/lib/context.js +0 -131
  125. package/dist/lib/plot/chartProps.d.ts +0 -177
  126. package/dist/lib/plot/chartProps.js +0 -76
  127. package/dist/lib/scales.svelte.d.ts +0 -35
  128. package/dist/lib/scales.svelte.js +0 -151
  129. package/dist/lib/ticks.d.ts +0 -36
  130. package/dist/lib/ticks.js +0 -46
@@ -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
@@ -54,13 +54,6 @@ export { default as BoxPlot } from "./charts/BoxPlot.svelte";
54
54
  export { default as ViolinPlot } from "./charts/ViolinPlot.svelte";
55
55
  export { default as BubbleChart } from "./charts/BubbleChart.svelte";
56
56
  export { PlotState } from "./PlotState.svelte.js";
57
- export { ChartBrewer } from "./lib/brewing/index.svelte.js";
58
- export * from "./lib/brewing/index.svelte.js";
59
- export { CartesianBrewer } from "./lib/brewing/CartesianBrewer.svelte.js";
60
- export { PieBrewer } from "./lib/brewing/PieBrewer.svelte.js";
61
- export { QuartileBrewer } from "./lib/brewing/QuartileBrewer.svelte.js";
62
- export { BoxBrewer } from "./lib/brewing/BoxBrewer.svelte.js";
63
- export { ViolinBrewer } from "./lib/brewing/ViolinBrewer.svelte.js";
64
57
  export { createCrossFilter } from "./crossfilter/createCrossFilter.svelte.js";
65
58
  export { default as CrossFilter } from "./crossfilter/CrossFilter.svelte";
66
59
  export { default as FilterBar } from "./crossfilter/FilterBar.svelte";
@@ -74,7 +67,7 @@ import Legend from './Plot/Legend.svelte';
74
67
  import Line from './geoms/Line.svelte';
75
68
  import Area from './geoms/Area.svelte';
76
69
  import Point from './geoms/Point.svelte';
77
- import Arc from './Plot/Arc.svelte';
70
+ import Arc from './geoms/Arc.svelte';
78
71
  import Highlight from './geoms/Highlight.svelte';
79
72
  import Trend from './geoms/Trend.svelte';
80
73
  import Box from './geoms/Box.svelte';
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import Legend from './Plot/Legend.svelte'
6
6
  import Line from './geoms/Line.svelte'
7
7
  import Area from './geoms/Area.svelte'
8
8
  import Point from './geoms/Point.svelte'
9
- import Arc from './Plot/Arc.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
12
  import Box from './geoms/Box.svelte'
@@ -91,14 +91,7 @@ export { default as BubbleChart } from './charts/BubbleChart.svelte'
91
91
  export { PlotState } from './PlotState.svelte.js'
92
92
 
93
93
  // Export utilities
94
- export { ChartBrewer } from './lib/brewing/index.svelte.js'
95
- export * from './lib/brewing/index.svelte.js'
96
94
  export { buildSequentialScale, buildDivergingScale } from './lib/brewing/colors.js'
97
- export { CartesianBrewer } from './lib/brewing/CartesianBrewer.svelte.js'
98
- export { PieBrewer } from './lib/brewing/PieBrewer.svelte.js'
99
- export { QuartileBrewer } from './lib/brewing/QuartileBrewer.svelte.js'
100
- export { BoxBrewer } from './lib/brewing/BoxBrewer.svelte.js'
101
- export { ViolinBrewer } from './lib/brewing/ViolinBrewer.svelte.js'
102
95
 
103
96
  // CrossFilter system
104
97
  export { createCrossFilter } from './crossfilter/createCrossFilter.svelte.js'
@@ -21,9 +21,19 @@ export namespace defaultPreset {
21
21
  }
22
22
  namespace opacity {
23
23
  let area: number;
24
+ let bar: number;
25
+ let line: number;
26
+ let point: number;
27
+ let arc: number;
24
28
  let box: number;
25
29
  let violin: number;
26
- let point: number;
30
+ let jitter: number;
31
+ let heatmap: number;
32
+ let candlestick: number;
33
+ let hexbin: number;
34
+ let ribbon: number;
35
+ let waterfall: number;
36
+ let rule: number;
27
37
  }
28
38
  let patterns: string[];
29
39
  let symbols: string[];
@@ -7,11 +7,23 @@ export const defaultPreset = {
7
7
  light: { fill: '300', stroke: '700' },
8
8
  dark: { fill: '500', stroke: '200' }
9
9
  },
10
+ // Per-geom default mark opacity. `alpha` (a fixed 0–1 prop on each geom / Plot.Root)
11
+ // overrides these; solid marks default to 1 so `alpha` is the single opacity knob.
10
12
  opacity: {
11
- area: 0.6,
12
- box: 0.5,
13
- violin: 0.5,
14
- point: 0.8
13
+ area: 0.6,
14
+ bar: 1,
15
+ line: 1,
16
+ point: 0.8,
17
+ arc: 1,
18
+ box: 0.5,
19
+ violin: 0.5,
20
+ jitter: 0.8,
21
+ heatmap: 1,
22
+ candlestick: 1,
23
+ hexbin: 1,
24
+ ribbon: 0.5,
25
+ waterfall: 1,
26
+ rule: 1
15
27
  },
16
28
  patterns: ['diagonal', 'dots', 'triangles', 'hatch', 'lattice', 'swell',
17
29
  'checkerboard', 'waves', 'petals', 'brick', 'diamonds', 'tile',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rokkit/chart",
3
- "version": "1.3.8",
3
+ "version": "1.3.9",
4
4
  "type": "module",
5
5
  "description": "Data-driven chart components",
6
6
  "repository": {
@@ -1,30 +0,0 @@
1
- <script lang="ts">
2
- import { getContext } from 'svelte'
3
- import type { ChartBrewer } from '../lib/brewing/brewer.svelte.js'
4
-
5
- const brewer = getContext<ChartBrewer>('chart-brewer')
6
- </script>
7
-
8
- {#if brewer && brewer.arcs && brewer.arcs.length > 0}
9
- <g
10
- class="chart-arcs"
11
- data-plot-type="arc"
12
- transform="translate({brewer.width / 2}, {brewer.height / 2})"
13
- >
14
- {#each brewer.arcs as arc (arc.key)}
15
- <path
16
- d={arc.d}
17
- fill={arc.fill}
18
- stroke={arc.stroke}
19
- stroke-width="1"
20
- data-plot-element="arc"
21
- />
22
- {/each}
23
- </g>
24
- {/if}
25
-
26
- <style>
27
- .chart-arcs {
28
- pointer-events: none;
29
- }
30
- </style>
@@ -1,18 +0,0 @@
1
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
- $$bindings?: Bindings;
4
- } & Exports;
5
- (internal: unknown, props: {
6
- $$events?: Events;
7
- $$slots?: Slots;
8
- }): Exports & {
9
- $set?: any;
10
- $on?: any;
11
- };
12
- z_$$bindings?: Bindings;
13
- }
14
- declare const Arc: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
- [evt: string]: CustomEvent<any>;
16
- }, {}, {}, string>;
17
- type Arc = InstanceType<typeof Arc>;
18
- export default Arc;
@@ -1,9 +0,0 @@
1
- /**
2
- * Get fill patterns for a set of values
3
- *
4
- * @param {Array} values - Array of values
5
- * @param {Object} swatch - Object with keys for color, gray, and pattern
6
- * @param {Boolean} gray - Boolean to determine if gray or color
7
- * @returns {Object} - Object with keys for pattern and color
8
- */
9
- export function getFillPatterns(values: any[], swatch: Object, gray?: boolean): Object;
@@ -1,25 +0,0 @@
1
- /**
2
- * Get fill patterns for a set of values
3
- *
4
- * @param {Array} values - Array of values
5
- * @param {Object} swatch - Object with keys for color, gray, and pattern
6
- * @param {Boolean} gray - Boolean to determine if gray or color
7
- * @returns {Object} - Object with keys for pattern and color
8
- */
9
- export function getFillPatterns(values, swatch, gray = false) {
10
- const colors = gray ? swatch.keys.gray : swatch.keys.color
11
- const max_colors = colors.length
12
- const max_patterns = swatch.keys.pattern.length
13
-
14
- const mix = values
15
- .map((value, index) => ({
16
- [value]: {
17
- pattern: swatch.keys.pattern[index % max_patterns],
18
- color: colors[index % max_colors]
19
- }
20
- }))
21
- .reduce((acc, current) => ({ ...acc, ...current }), {})
22
- return mix
23
- }
24
-
25
- // export function getStrokePatterns() {}
@@ -1,8 +0,0 @@
1
- /**
2
- * Brewer for box plots.
3
- * fill = box interior color, color = whisker/outline stroke.
4
- */
5
- export class BoxBrewer extends QuartileBrewer {
6
- boxes: any[];
7
- }
8
- import { QuartileBrewer } from './QuartileBrewer.svelte.js';
@@ -1,14 +0,0 @@
1
- import { QuartileBrewer } from './QuartileBrewer.svelte.js'
2
- import { buildBoxes } from './marks/boxes.js'
3
-
4
- /**
5
- * Brewer for box plots.
6
- * fill = box interior color, color = whisker/outline stroke.
7
- */
8
- export class BoxBrewer extends QuartileBrewer {
9
- boxes = $derived(
10
- this.xScale && this.yScale
11
- ? buildBoxes(this.processedData, this.channels, this.xScale, this.yScale, this.colorMap)
12
- : []
13
- )
14
- }
@@ -1,8 +0,0 @@
1
- /**
2
- * Brewer for cartesian charts (Bar, Line, Area).
3
- * Groups by x (and fill/color if set) and applies the given stat.
4
- */
5
- export class CartesianBrewer extends ChartBrewer {
6
- transform(data: any, channels: any, stat: any): any;
7
- }
8
- import { ChartBrewer } from './brewer.svelte.js';
@@ -1,21 +0,0 @@
1
- import { SvelteSet } from 'svelte/reactivity'
2
- import { ChartBrewer } from './brewer.svelte.js'
3
- import { applyAggregate } from './stats.js'
4
-
5
- /**
6
- * Brewer for cartesian charts (Bar, Line, Area).
7
- * Groups by x (and fill/color if set) and applies the given stat.
8
- */
9
- export class CartesianBrewer extends ChartBrewer {
10
- transform(data, channels, stat) {
11
- if (stat === 'identity' || !channels.x || !channels.y) return data
12
- // Group by all mapped aesthetic dimensions so they survive aggregation.
13
- // e.g. x=region, fill=region, pattern=quarter → by=['region','quarter']
14
- const by = [
15
- ...new SvelteSet(
16
- [channels.x, channels.fill, channels.color, channels.pattern].filter(Boolean)
17
- )
18
- ]
19
- return applyAggregate(data, { by, value: channels.y, stat })
20
- }
21
- }