@rudra-studio/rudra-charts 1.0.1

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 (42) hide show
  1. package/README.md +7 -0
  2. package/components/AnimatedLineChart/index.d.ts +29 -0
  3. package/components/AnimatedLineChart/index.js +109 -0
  4. package/components/BubbleChart/index.d.ts +24 -0
  5. package/components/BubbleChart/index.js +126 -0
  6. package/components/CandleStickChart/index.d.ts +24 -0
  7. package/components/CandleStickChart/index.js +116 -0
  8. package/components/DonutChart/index.d.ts +19 -0
  9. package/components/DonutChart/index.js +77 -0
  10. package/components/FunnelChart/index.d.ts +22 -0
  11. package/components/FunnelChart/index.js +105 -0
  12. package/components/GaugeChart/index.d.ts +25 -0
  13. package/components/GaugeChart/index.js +85 -0
  14. package/components/HeatMap/index.d.ts +24 -0
  15. package/components/HeatMap/index.js +146 -0
  16. package/components/Histogram/index.d.ts +20 -0
  17. package/components/Histogram/index.js +158 -0
  18. package/components/HorizontalBarChart/index.d.ts +22 -0
  19. package/components/HorizontalBarChart/index.js +82 -0
  20. package/components/HundredPercentBarChart/index.d.ts +21 -0
  21. package/components/HundredPercentBarChart/index.js +96 -0
  22. package/components/MultiLineChart/index.d.ts +21 -0
  23. package/components/MultiLineChart/index.js +128 -0
  24. package/components/PipelineChart/index.d.ts +26 -0
  25. package/components/PipelineChart/index.js +174 -0
  26. package/components/ScatterPlot/index.d.ts +23 -0
  27. package/components/ScatterPlot/index.js +118 -0
  28. package/components/SpiderChart/index.d.ts +27 -0
  29. package/components/SpiderChart/index.js +169 -0
  30. package/components/StackedArea/index.d.ts +22 -0
  31. package/components/StackedArea/index.js +101 -0
  32. package/components/StackedBarChart/index.d.ts +21 -0
  33. package/components/StackedBarChart/index.js +96 -0
  34. package/components/TreeMap/index.d.ts +19 -0
  35. package/components/TreeMap/index.js +137 -0
  36. package/components/VerticalBarChart/index.d.ts +21 -0
  37. package/components/VerticalBarChart/index.js +97 -0
  38. package/components/WaterfallChart/index.d.ts +26 -0
  39. package/components/WaterfallChart/index.js +147 -0
  40. package/index.d.ts +38 -0
  41. package/index.js +23 -0
  42. package/package.json +46 -0
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # rudra-charts
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Running unit tests
6
+
7
+ Run `nx test rudra-charts` to execute the unit tests via [Vitest](https://vitest.dev/).
@@ -0,0 +1,29 @@
1
+ import { default as React } from 'react';
2
+ export interface LineDataPoint {
3
+ x: string;
4
+ y: number;
5
+ }
6
+ export interface LineSeries {
7
+ id: string;
8
+ color?: string;
9
+ data: LineDataPoint[];
10
+ }
11
+ export interface AnimatedLineChartProps {
12
+ /** The dataset to render. Array of series for multi-line support. */
13
+ series?: LineSeries[];
14
+ /** Line curve style */
15
+ curveType?: 'linear' | 'smooth' | 'step';
16
+ /** Stroke width of the lines */
17
+ strokeWidth?: number;
18
+ /** Show dots on data points? */
19
+ showDots?: boolean;
20
+ /** Default palette if individual series colors are not provided */
21
+ themeColors?: string[];
22
+ marginTop?: number;
23
+ marginBottom?: number;
24
+ marginLeft?: number;
25
+ marginRight?: number;
26
+ className?: string;
27
+ }
28
+ export declare const AnimatedLineChart: React.FC<AnimatedLineChartProps>;
29
+ export default AnimatedLineChart;
@@ -0,0 +1,109 @@
1
+ import { useEffect as e, useRef as t, useState as n } from "react";
2
+ import * as r from "d3";
3
+ import { jsx as i } from "react/jsx-runtime";
4
+ //#region src/components/AnimatedLineChart/index.tsx
5
+ var a = ({ series: a = [{
6
+ id: "Product A",
7
+ data: [
8
+ {
9
+ x: "Q1",
10
+ y: 30
11
+ },
12
+ {
13
+ x: "Q2",
14
+ y: 80
15
+ },
16
+ {
17
+ x: "Q3",
18
+ y: 45
19
+ },
20
+ {
21
+ x: "Q4",
22
+ y: 100
23
+ }
24
+ ]
25
+ }, {
26
+ id: "Product B",
27
+ data: [
28
+ {
29
+ x: "Q1",
30
+ y: 10
31
+ },
32
+ {
33
+ x: "Q2",
34
+ y: 40
35
+ },
36
+ {
37
+ x: "Q3",
38
+ y: 70
39
+ },
40
+ {
41
+ x: "Q4",
42
+ y: 50
43
+ }
44
+ ]
45
+ }], curveType: o = "smooth", strokeWidth: s = 3, showDots: c = !0, themeColors: l = [
46
+ "#3b82f6",
47
+ "#10b981",
48
+ "#f59e0b",
49
+ "#ef4444",
50
+ "#8b5cf6"
51
+ ], marginTop: u = 20, marginBottom: d = 30, marginLeft: f = 40, marginRight: p = 20, className: m = "" }) => {
52
+ let h = t(null), g = t(null), [_, v] = n({
53
+ width: 0,
54
+ height: 300
55
+ });
56
+ return e(() => {
57
+ let e = h.current;
58
+ if (!e) return;
59
+ let t = new ResizeObserver((e) => {
60
+ e[0] && v({
61
+ width: e[0].contentRect.width,
62
+ height: e[0].contentRect.height || 300
63
+ });
64
+ });
65
+ return t.observe(e), () => t.unobserve(e);
66
+ }, []), e(() => {
67
+ if (!g.current || _.width === 0 || a.length === 0) return;
68
+ let { width: e, height: t } = _, n = r.select(g.current), i = Array.from(new Set(a.flatMap((e) => e.data.map((e) => e.x)))), m = r.max(a.flatMap((e) => e.data.map((e) => e.y))) || 100, h = r.scalePoint().domain(i).range([f, e - p]).padding(.1), v = r.scaleLinear().domain([0, m]).nice().range([t - d, u]), y = r.scaleOrdinal().range(l), b = r.line().x((e) => h(e.x)).y((e) => v(e.y));
69
+ o === "smooth" ? b.curve(r.curveMonotoneX) : o === "step" ? b.curve(r.curveStepAfter) : b.curve(r.curveLinear), n.select(".x-axis").empty() && (n.append("g").attr("class", "axis x-axis"), n.append("g").attr("class", "axis y-axis")), n.select(".x-axis").attr("transform", `translate(0,${t - d})`).transition().duration(500).call(r.axisBottom(h)).attr("font-size", "12px").attr("font-family", "ui-sans-serif, system-ui").attr("color", "#64748b"), n.select(".y-axis").attr("transform", `translate(${f},0)`).transition().duration(500).call(r.axisLeft(v).ticks(5)).attr("font-size", "12px").attr("font-family", "ui-sans-serif, system-ui").attr("color", "#64748b"), n.select(".y-axis").select(".domain").remove(), n.selectAll(".y-axis .tick line").attr("x2", e - f - p).attr("stroke-opacity", .1);
70
+ let x = n.select(".lines-group");
71
+ x.empty() && (x = n.append("g").attr("class", "lines-group"));
72
+ let S = x.selectAll(".line-path").data(a, (e) => e.id);
73
+ S.exit().transition().duration(400).style("opacity", 0).remove(), S.enter().append("path").attr("class", "line-path").attr("fill", "none").attr("stroke", (e, t) => e.color || y(e.id)).attr("stroke-width", s).attr("stroke-linecap", "round").attr("stroke-linejoin", "round").attr("d", (e) => b(e.data)).each(function() {
74
+ let e = this.getTotalLength();
75
+ r.select(this).attr("stroke-dasharray", `${e} ${e}`).attr("stroke-dashoffset", e).transition().duration(1500).ease(r.easeCubicOut).attr("stroke-dashoffset", 0);
76
+ }), S.transition().duration(750).ease(r.easeCubicOut).attr("d", (e) => b(e.data)).attr("stroke", (e, t) => e.color || y(e.id)).attr("stroke-width", s);
77
+ let C = n.select(".dots-group");
78
+ if (C.empty() && (C = n.append("g").attr("class", "dots-group")), c) {
79
+ let e = a.flatMap((e) => e.data.map((t) => ({
80
+ ...t,
81
+ seriesId: e.id,
82
+ color: e.color || y(e.id)
83
+ }))), t = C.selectAll(".dot").data(e, (e) => `${e.seriesId}-${e.x}`);
84
+ t.exit().transition().duration(300).attr("r", 0).remove(), t.enter().append("circle").attr("class", "dot").attr("cx", (e) => h(e.x)).attr("cy", (e) => v(e.y)).attr("r", 0).attr("fill", "white").attr("stroke", (e) => e.color).attr("stroke-width", 2).transition().duration(750).delay(500).attr("r", s + 1), t.transition().duration(750).attr("cx", (e) => h(e.x)).attr("cy", (e) => v(e.y)).attr("stroke", (e) => e.color).attr("stroke-width", 2);
85
+ } else C.selectAll(".dot").remove();
86
+ }, [
87
+ a,
88
+ _,
89
+ o,
90
+ s,
91
+ c,
92
+ l,
93
+ u,
94
+ d,
95
+ f,
96
+ p
97
+ ]), /* @__PURE__ */ i("div", {
98
+ ref: h,
99
+ className: `w-full min-h-[300px] h-full relative ${m}`,
100
+ children: /* @__PURE__ */ i("svg", {
101
+ ref: g,
102
+ width: "100%",
103
+ height: "100%",
104
+ className: "block absolute inset-0 overflow-visible"
105
+ })
106
+ });
107
+ };
108
+ //#endregion
109
+ export { a as default };
@@ -0,0 +1,24 @@
1
+ import { default as React } from 'react';
2
+ export interface BubbleDataPoint {
3
+ id: string | number;
4
+ x: number;
5
+ y: number;
6
+ z: number;
7
+ category: string;
8
+ label?: string;
9
+ }
10
+ export interface AnimatedBubbleChartProps {
11
+ /** The dataset containing x, y, and z values. */
12
+ data?: BubbleDataPoint[];
13
+ /** Default palette for the categories */
14
+ themeColors?: string[];
15
+ /** The maximum radius (in pixels) for the largest bubble in the dataset */
16
+ maxRadius?: number;
17
+ marginTop?: number;
18
+ marginBottom?: number;
19
+ marginLeft?: number;
20
+ marginRight?: number;
21
+ className?: string;
22
+ }
23
+ export declare const AnimatedBubbleChart: React.FC<AnimatedBubbleChartProps>;
24
+ export default AnimatedBubbleChart;
@@ -0,0 +1,126 @@
1
+ import { useEffect as e, useRef as t, useState as n } from "react";
2
+ import * as r from "d3";
3
+ import { jsx as i } from "react/jsx-runtime";
4
+ //#region src/components/BubbleChart/index.tsx
5
+ var a = ({ data: a = [
6
+ {
7
+ id: 1,
8
+ x: 1200,
9
+ y: 80,
10
+ z: 15e3,
11
+ category: "Group A",
12
+ label: "Campaign 1"
13
+ },
14
+ {
15
+ id: 2,
16
+ x: 2500,
17
+ y: 110,
18
+ z: 42e3,
19
+ category: "Group A",
20
+ label: "Campaign 2"
21
+ },
22
+ {
23
+ id: 3,
24
+ x: 3400,
25
+ y: 150,
26
+ z: 31e3,
27
+ category: "Group B",
28
+ label: "Campaign 3"
29
+ },
30
+ {
31
+ id: 4,
32
+ x: 4800,
33
+ y: 210,
34
+ z: 86e3,
35
+ category: "Group B",
36
+ label: "Campaign 4"
37
+ },
38
+ {
39
+ id: 5,
40
+ x: 5900,
41
+ y: 180,
42
+ z: 54e3,
43
+ category: "Group C",
44
+ label: "Campaign 5"
45
+ },
46
+ {
47
+ id: 6,
48
+ x: 7200,
49
+ y: 250,
50
+ z: 98e3,
51
+ category: "Group C",
52
+ label: "Campaign 6"
53
+ },
54
+ {
55
+ id: 7,
56
+ x: 8100,
57
+ y: 310,
58
+ z: 22e3,
59
+ category: "Group A",
60
+ label: "Campaign 7"
61
+ },
62
+ {
63
+ id: 8,
64
+ x: 9500,
65
+ y: 290,
66
+ z: 115e3,
67
+ category: "Group B",
68
+ label: "Campaign 8"
69
+ }
70
+ ], themeColors: o = [
71
+ "#3b82f6",
72
+ "#10b981",
73
+ "#f59e0b",
74
+ "#ef4444",
75
+ "#8b5cf6"
76
+ ], maxRadius: s = 35, marginTop: c = 20, marginBottom: l = 40, marginLeft: u = 50, marginRight: d = 40, className: f = "" }) => {
77
+ let p = t(null), m = t(null), [h, g] = n({
78
+ width: 0,
79
+ height: 400
80
+ });
81
+ return e(() => {
82
+ let e = p.current;
83
+ if (!e) return;
84
+ let t = new ResizeObserver((e) => {
85
+ e[0] && g({
86
+ width: e[0].contentRect.width,
87
+ height: e[0].contentRect.height || 400
88
+ });
89
+ });
90
+ return t.observe(e), () => t.unobserve(e);
91
+ }, []), e(() => {
92
+ if (!m.current || h.width === 0 || a.length === 0) return;
93
+ let { width: e, height: t } = h, n = r.select(m.current), i = r.max(a, (e) => e.x) || 100, f = r.max(a, (e) => e.y) || 100, p = r.max(a, (e) => e.z) || 100, g = r.scaleLinear().domain([0, i * 1.1]).nice().range([u, e - d]), _ = r.scaleLinear().domain([0, f * 1.1]).nice().range([t - l, c]), v = r.scaleSqrt().domain([0, p]).range([2, s]), y = Array.from(new Set(a.map((e) => e.category))), b = r.scaleOrdinal().domain(y).range(o);
94
+ n.select(".x-axis").empty() && (n.append("g").attr("class", "axis x-axis"), n.append("g").attr("class", "axis y-axis")), n.select(".x-axis").attr("transform", `translate(0,${t - l})`).transition().duration(500).call(r.axisBottom(g).ticks(8).tickSize(-(t - c - l))).attr("font-size", "12px").attr("font-family", "ui-sans-serif, system-ui").attr("color", "#64748b").call((e) => e.select(".domain").remove()).call((e) => e.selectAll(".tick line").attr("stroke-opacity", .1)), n.select(".y-axis").attr("transform", `translate(${u},0)`).transition().duration(500).call(r.axisLeft(_).ticks(6).tickSize(-(e - u - d))).attr("font-size", "12px").attr("font-family", "ui-sans-serif, system-ui").attr("color", "#64748b").call((e) => e.select(".domain").remove()).call((e) => e.selectAll(".tick line").attr("stroke-opacity", .1));
95
+ let x = n.select(".bubbles-group");
96
+ x.empty() && (x = n.append("g").attr("class", "bubbles-group"));
97
+ let S = x.selectAll(".bubble").data(a, (e) => e.id);
98
+ S.exit().transition().duration(300).attr("r", 0).remove();
99
+ let C = S.enter().append("circle").attr("class", "bubble").attr("cx", (e) => g(e.x)).attr("cy", (e) => _(e.y)).attr("r", 0).attr("fill", (e) => b(e.category)).attr("opacity", .7).attr("stroke", "#ffffff").attr("stroke-width", 1.5).style("cursor", "pointer");
100
+ C.transition().duration(800).delay((e, t) => t * 50).ease(r.easeElasticOut.amplitude(1).period(.5)).attr("r", (e) => v(e.z)), S.transition().duration(750).ease(r.easeCubicOut).attr("cx", (e) => g(e.x)).attr("cy", (e) => _(e.y)).attr("r", (e) => v(e.z)).attr("fill", (e) => b(e.category)), C.merge(S).on("mouseenter", function() {
101
+ x.selectAll(".bubble").transition().duration(200).attr("opacity", .2), r.select(this).raise().transition().duration(200).attr("opacity", .9).attr("stroke-width", 3);
102
+ }).on("mouseleave", function() {
103
+ x.selectAll(".bubble").transition().duration(200).attr("opacity", .7).attr("stroke-width", 1.5);
104
+ });
105
+ }, [
106
+ a,
107
+ h,
108
+ s,
109
+ o,
110
+ c,
111
+ l,
112
+ u,
113
+ d
114
+ ]), /* @__PURE__ */ i("div", {
115
+ ref: p,
116
+ className: `w-full min-h-[400px] h-full relative ${f}`,
117
+ children: /* @__PURE__ */ i("svg", {
118
+ ref: m,
119
+ width: "100%",
120
+ height: "100%",
121
+ className: "block absolute inset-0 overflow-visible"
122
+ })
123
+ });
124
+ };
125
+ //#endregion
126
+ export { a as default };
@@ -0,0 +1,24 @@
1
+ import { default as React } from 'react';
2
+ export interface CandlestickDataPoint {
3
+ id: string | number;
4
+ date: string;
5
+ open: number;
6
+ high: number;
7
+ low: number;
8
+ close: number;
9
+ }
10
+ export interface AnimatedCandlestickChartProps {
11
+ /** The OHLC dataset. */
12
+ data?: CandlestickDataPoint[];
13
+ /** Color for days where Close > Open (default: Emerald Green) */
14
+ colorBullish?: string;
15
+ /** Color for days where Open > Close (default: Red) */
16
+ colorBearish?: string;
17
+ marginTop?: number;
18
+ marginBottom?: number;
19
+ marginLeft?: number;
20
+ marginRight?: number;
21
+ className?: string;
22
+ }
23
+ export declare const AnimatedCandlestickChart: React.FC<AnimatedCandlestickChartProps>;
24
+ export default AnimatedCandlestickChart;
@@ -0,0 +1,116 @@
1
+ import { useEffect as e, useRef as t, useState as n } from "react";
2
+ import * as r from "d3";
3
+ import { jsx as i } from "react/jsx-runtime";
4
+ //#region src/components/CandleStickChart/index.tsx
5
+ var a = ({ data: a = [
6
+ {
7
+ id: 1,
8
+ date: "Oct 1",
9
+ open: 120,
10
+ high: 125,
11
+ low: 115,
12
+ close: 122
13
+ },
14
+ {
15
+ id: 2,
16
+ date: "Oct 2",
17
+ open: 122,
18
+ high: 130,
19
+ low: 120,
20
+ close: 128
21
+ },
22
+ {
23
+ id: 3,
24
+ date: "Oct 3",
25
+ open: 128,
26
+ high: 132,
27
+ low: 125,
28
+ close: 126
29
+ },
30
+ {
31
+ id: 4,
32
+ date: "Oct 4",
33
+ open: 126,
34
+ high: 127,
35
+ low: 118,
36
+ close: 119
37
+ },
38
+ {
39
+ id: 5,
40
+ date: "Oct 5",
41
+ open: 119,
42
+ high: 135,
43
+ low: 115,
44
+ close: 133
45
+ },
46
+ {
47
+ id: 6,
48
+ date: "Oct 6",
49
+ open: 133,
50
+ high: 140,
51
+ low: 130,
52
+ close: 138
53
+ },
54
+ {
55
+ id: 7,
56
+ date: "Oct 7",
57
+ open: 138,
58
+ high: 142,
59
+ low: 135,
60
+ close: 136
61
+ }
62
+ ], colorBullish: o = "#10b981", colorBearish: s = "#ef4444", marginTop: c = 20, marginBottom: l = 40, marginLeft: u = 50, marginRight: d = 20, className: f = "" }) => {
63
+ let p = t(null), m = t(null), [h, g] = n({
64
+ width: 0,
65
+ height: 350
66
+ });
67
+ return e(() => {
68
+ let e = p.current;
69
+ if (!e) return;
70
+ let t = new ResizeObserver((e) => {
71
+ e[0] && g({
72
+ width: e[0].contentRect.width,
73
+ height: e[0].contentRect.height || 350
74
+ });
75
+ });
76
+ return t.observe(e), () => t.unobserve(e);
77
+ }, []), e(() => {
78
+ if (!m.current || h.width === 0 || a.length === 0) return;
79
+ let { width: e, height: t } = h, n = r.select(m.current), i = r.scaleBand().domain(a.map((e) => e.date)).range([u, e - d]).padding(.25), f = r.min(a, (e) => e.low) || 0, p = r.max(a, (e) => e.high) || 100, g = (p - f) * .1, _ = r.scaleLinear().domain([Math.max(0, f - g), p + g]).nice().range([t - l, c]), v = (e) => e.close >= e.open;
80
+ n.select(".x-axis").empty() && (n.append("g").attr("class", "axis x-axis"), n.append("g").attr("class", "axis y-axis")), n.select(".x-axis").attr("transform", `translate(0,${t - l})`).transition().duration(500).call(r.axisBottom(i).tickSizeOuter(0)).attr("font-size", "12px").attr("font-family", "ui-sans-serif, system-ui").attr("color", "#64748b").call((e) => e.select(".domain").remove()), n.select(".y-axis").attr("transform", `translate(${u},0)`).transition().duration(500).call(r.axisLeft(_).ticks(6).tickFormat((e) => `$${e}`).tickSize(-(e - u - d))).attr("font-size", "12px").attr("font-family", "ui-sans-serif, system-ui").attr("color", "#64748b").call((e) => e.select(".domain").remove()).call((e) => e.selectAll(".tick line").attr("stroke-opacity", .1).attr("stroke-dasharray", "4,4"));
81
+ let y = n.select(".wicks-group");
82
+ y.empty() && (y = n.append("g").attr("class", "wicks-group"));
83
+ let b = y.selectAll(".wick").data(a, (e) => e.id);
84
+ b.exit().remove(), b.enter().append("line").attr("class", "wick").attr("x1", (e) => (i(e.date) || 0) + i.bandwidth() / 2).attr("x2", (e) => (i(e.date) || 0) + i.bandwidth() / 2).attr("y1", (e) => _(e.open)).attr("y2", (e) => _(e.open)).attr("stroke", (e) => v(e) ? o : s).attr("stroke-width", 2).transition().duration(600).ease(r.easeCubicOut).attr("y1", (e) => _(e.high)).attr("y2", (e) => _(e.low)), b.transition().duration(750).attr("x1", (e) => (i(e.date) || 0) + i.bandwidth() / 2).attr("x2", (e) => (i(e.date) || 0) + i.bandwidth() / 2).attr("y1", (e) => _(e.high)).attr("y2", (e) => _(e.low)).attr("stroke", (e) => v(e) ? o : s);
85
+ let x = n.select(".bodies-group");
86
+ x.empty() && (x = n.append("g").attr("class", "bodies-group"));
87
+ let S = x.selectAll(".body").data(a, (e) => e.id);
88
+ S.exit().remove();
89
+ let C = S.enter().append("rect").attr("class", "body").attr("x", (e) => i(e.date) || 0).attr("width", i.bandwidth()).attr("y", (e) => _(e.open)).attr("height", 0).attr("fill", (e) => v(e) ? o : s).attr("rx", 2).style("cursor", "pointer");
90
+ C.transition().duration(600).delay(200).ease(r.easeCubicOut).attr("y", (e) => _(Math.max(e.open, e.close))).attr("height", (e) => Math.max(2, Math.abs(_(e.open) - _(e.close)))), S.transition().duration(750).attr("x", (e) => i(e.date) || 0).attr("width", i.bandwidth()).attr("y", (e) => _(Math.max(e.open, e.close))).attr("height", (e) => Math.max(2, Math.abs(_(e.open) - _(e.close)))).attr("fill", (e) => v(e) ? o : s), C.merge(S).on("mouseenter", function(e, t) {
91
+ x.selectAll(".body").transition().duration(200).attr("opacity", .4), y.selectAll(".wick").transition().duration(200).attr("opacity", .4), r.select(this).transition().duration(200).attr("opacity", 1).attr("filter", "brightness(1.1)"), y.selectAll(".wick").filter((e) => e.id === t.id).transition().duration(200).attr("opacity", 1).attr("stroke-width", 3);
92
+ }).on("mouseleave", function() {
93
+ x.selectAll(".body").transition().duration(200).attr("opacity", 1).attr("filter", "none"), y.selectAll(".wick").transition().duration(200).attr("opacity", 1).attr("stroke-width", 2);
94
+ });
95
+ }, [
96
+ a,
97
+ h,
98
+ o,
99
+ s,
100
+ c,
101
+ l,
102
+ u,
103
+ d
104
+ ]), /* @__PURE__ */ i("div", {
105
+ ref: p,
106
+ className: `w-full min-h-[350px] h-full relative ${f}`,
107
+ children: /* @__PURE__ */ i("svg", {
108
+ ref: m,
109
+ width: "100%",
110
+ height: "100%",
111
+ className: "block absolute inset-0 overflow-visible"
112
+ })
113
+ });
114
+ };
115
+ //#endregion
116
+ export { a as default };
@@ -0,0 +1,19 @@
1
+ import { default as React } from 'react';
2
+ export interface PieDataPoint {
3
+ label: string;
4
+ value: number;
5
+ color?: string;
6
+ }
7
+ export interface AnimatedDonutChartProps {
8
+ /** The dataset */
9
+ data?: PieDataPoint[];
10
+ /** The thickness of the donut ring */
11
+ thickness?: number;
12
+ /** Default palette if individual colors aren't provided */
13
+ themeColors?: string[];
14
+ /** Text to show in the center by default (e.g., "Total Sales") */
15
+ centerLabel?: string;
16
+ className?: string;
17
+ }
18
+ export declare const AnimatedDonutChart: React.FC<AnimatedDonutChartProps>;
19
+ export default AnimatedDonutChart;
@@ -0,0 +1,77 @@
1
+ import { useEffect as e, useRef as t, useState as n } from "react";
2
+ import * as r from "d3";
3
+ import { jsx as i } from "react/jsx-runtime";
4
+ //#region src/components/DonutChart/index.tsx
5
+ var a = ({ data: a = [
6
+ {
7
+ label: "Mobile",
8
+ value: 45
9
+ },
10
+ {
11
+ label: "Desktop",
12
+ value: 35
13
+ },
14
+ {
15
+ label: "Tablet",
16
+ value: 20
17
+ }
18
+ ], thickness: o = 60, themeColors: s = [
19
+ "#3b82f6",
20
+ "#10b981",
21
+ "#f59e0b",
22
+ "#ef4444",
23
+ "#8b5cf6"
24
+ ], centerLabel: c = "Total", className: l = "" }) => {
25
+ let u = t(null), d = t(null), [f, p] = n({
26
+ width: 0,
27
+ height: 300
28
+ });
29
+ return e(() => {
30
+ let e = u.current;
31
+ if (!e) return;
32
+ let t = new ResizeObserver((e) => {
33
+ e[0] && p({
34
+ width: e[0].contentRect.width,
35
+ height: e[0].contentRect.height || 300
36
+ });
37
+ });
38
+ return t.observe(e), () => t.unobserve(e);
39
+ }, []), e(() => {
40
+ if (!d.current || f.width === 0 || a.length === 0) return;
41
+ let { width: e, height: t } = f, n = Math.min(e, t) / 2 - 20, i = n - o, l = r.select(d.current);
42
+ l.selectAll("*").remove();
43
+ let u = l.append("g").attr("transform", `translate(${e / 2},${t / 2})`), p = r.scaleOrdinal().range(s), m = r.pie().value((e) => e.value).sort(null), h = r.arc().innerRadius(i).outerRadius(n).cornerRadius(4).padAngle(.02), g = r.arc().innerRadius(i).outerRadius(n + 10).cornerRadius(4).padAngle(.02), _ = u.selectAll(".arc").data(m(a)).enter().append("g").attr("class", "arc").append("path").attr("fill", (e, t) => e.data.color || p(t.toString())).attr("stroke", "#ffffff").attr("stroke-width", "2px").style("cursor", "pointer");
44
+ _.transition().duration(1e3).ease(r.easeCubicOut).attrTween("d", function(e) {
45
+ let t = r.interpolate({
46
+ startAngle: 0,
47
+ endAngle: 0
48
+ }, e);
49
+ return function(e) {
50
+ return h(t(e));
51
+ };
52
+ });
53
+ let v = r.sum(a, (e) => e.value), y = u.append("g").attr("text-anchor", "middle"), b = y.append("text").text(v).attr("y", 0).attr("font-size", "24px").attr("font-weight", "bold").attr("fill", "#0f172a").attr("font-family", "ui-sans-serif, system-ui"), x = y.append("text").text(c).attr("y", 20).attr("font-size", "12px").attr("fill", "#64748b").attr("font-family", "ui-sans-serif, system-ui").attr("text-transform", "uppercase").attr("letter-spacing", "1px");
54
+ _.on("mouseenter", function(e, t) {
55
+ r.select(this).transition().duration(200).attr("d", g), b.text(t.data.value), x.text(t.data.label);
56
+ }).on("mouseleave", function(e, t) {
57
+ r.select(this).transition().duration(200).attr("d", h), b.text(v), x.text(c);
58
+ });
59
+ }, [
60
+ a,
61
+ f,
62
+ o,
63
+ s,
64
+ c
65
+ ]), /* @__PURE__ */ i("div", {
66
+ ref: u,
67
+ className: `w-full min-h-[300px] h-full relative ${l}`,
68
+ children: /* @__PURE__ */ i("svg", {
69
+ ref: d,
70
+ width: "100%",
71
+ height: "100%",
72
+ className: "block absolute inset-0 overflow-visible"
73
+ })
74
+ });
75
+ };
76
+ //#endregion
77
+ export { a as default };
@@ -0,0 +1,22 @@
1
+ import { default as React } from 'react';
2
+ export interface FunnelDataPoint {
3
+ stage: string;
4
+ value: number;
5
+ }
6
+ export interface AnimatedFunnelChartProps {
7
+ /** The pipeline dataset. Should ideally be in descending order. */
8
+ data?: FunnelDataPoint[];
9
+ /** Default palette for the funnel stages */
10
+ themeColors?: string[];
11
+ /** Show the exact number inside the funnel */
12
+ showValues?: boolean;
13
+ /** Show the drop-off percentage compared to the PREVIOUS stage */
14
+ showConversionRate?: boolean;
15
+ marginTop?: number;
16
+ marginBottom?: number;
17
+ marginLeft?: number;
18
+ marginRight?: number;
19
+ className?: string;
20
+ }
21
+ export declare const AnimatedFunnelChart: React.FC<AnimatedFunnelChartProps>;
22
+ export default AnimatedFunnelChart;
@@ -0,0 +1,105 @@
1
+ import { useEffect as e, useRef as t, useState as n } from "react";
2
+ import * as r from "d3";
3
+ import { jsx as i } from "react/jsx-runtime";
4
+ //#region src/components/FunnelChart/index.tsx
5
+ var a = ({ data: a = [
6
+ {
7
+ stage: "Website Visitors",
8
+ value: 12500
9
+ },
10
+ {
11
+ stage: "Signed Up",
12
+ value: 6e3
13
+ },
14
+ {
15
+ stage: "Onboarded",
16
+ value: 3500
17
+ },
18
+ {
19
+ stage: "Subscribed",
20
+ value: 1200
21
+ },
22
+ {
23
+ stage: "Retained (1yr)",
24
+ value: 850
25
+ }
26
+ ], themeColors: o = [
27
+ "#3b82f6",
28
+ "#0ea5e9",
29
+ "#06b6d4",
30
+ "#14b8a6",
31
+ "#10b981"
32
+ ], showValues: s = !0, showConversionRate: c = !0, marginTop: l = 20, marginBottom: u = 20, marginLeft: d = 130, marginRight: f = 80, className: p = "" }) => {
33
+ let m = t(null), h = t(null), [g, _] = n({
34
+ width: 0,
35
+ height: 350
36
+ });
37
+ return e(() => {
38
+ let e = m.current;
39
+ if (!e) return;
40
+ let t = new ResizeObserver((e) => {
41
+ e[0] && _({
42
+ width: e[0].contentRect.width,
43
+ height: e[0].contentRect.height || 350
44
+ });
45
+ });
46
+ return t.observe(e), () => t.unobserve(e);
47
+ }, []), e(() => {
48
+ if (!h.current || g.width === 0 || a.length === 0) return;
49
+ let { width: e, height: t } = g, n = r.select(h.current);
50
+ n.selectAll("*").remove();
51
+ let i = e - d - f, p = d + i / 2, m = r.max(a, (e) => e.value) || 100, _ = r.scaleBand().domain(a.map((e) => e.stage)).range([l, t - u]).padding(.15), v = r.scaleLinear().domain([0, m]).range([0, i]), y = r.scaleOrdinal().range(o), b = a.map((e, t) => {
52
+ let n = v(e.value), r = t < a.length - 1 ? v(a[t + 1].value) : n * .8;
53
+ return {
54
+ ...e,
55
+ topWidth: n,
56
+ bottomWidth: r,
57
+ yTop: _(e.stage),
58
+ yBottom: _(e.stage) + _.bandwidth(),
59
+ index: t,
60
+ conversion: t === 0 ? null : (e.value / a[t - 1].value * 100).toFixed(1)
61
+ };
62
+ }), x = n.append("g").attr("class", "funnel-group").selectAll(".funnel-layer").data(b, (e) => e.stage).enter().append("polygon").attr("class", "funnel-layer").attr("points", (e) => `
63
+ ${p},${e.yTop}
64
+ ${p},${e.yTop}
65
+ ${p},${e.yBottom}
66
+ ${p},${e.yBottom}
67
+ `).attr("fill", (e, t) => y(t.toString())).style("cursor", "pointer");
68
+ x.transition().duration(800).ease(r.easeCubicOut).delay((e, t) => t * 100).attr("points", (e) => `
69
+ ${p - e.topWidth / 2},${e.yTop}
70
+ ${p + e.topWidth / 2},${e.yTop}
71
+ ${p + e.bottomWidth / 2},${e.yBottom}
72
+ ${p - e.bottomWidth / 2},${e.yBottom}
73
+ `), x.on("mouseenter", function() {
74
+ r.select(this).transition().duration(200).attr("opacity", .8);
75
+ }).on("mouseleave", function() {
76
+ r.select(this).transition().duration(200).attr("opacity", 1);
77
+ });
78
+ let S = n.append("g").attr("class", "labels-group");
79
+ if (S.selectAll(".stage-label").data(b).enter().append("text").attr("class", "stage-label").attr("x", d - 15).attr("y", (e) => e.yTop + _.bandwidth() / 2).attr("dy", "0.35em").attr("text-anchor", "end").attr("font-size", "12px").attr("font-weight", "600").attr("font-family", "ui-sans-serif, system-ui").attr("fill", "#475569").text((e) => e.stage).style("opacity", 0).transition().duration(500).delay((e, t) => 400 + t * 100).style("opacity", 1), s && S.selectAll(".value-label").data(b).enter().append("text").attr("class", "value-label").attr("x", p).attr("y", (e) => e.yTop + _.bandwidth() / 2).attr("dy", "0.35em").attr("text-anchor", "middle").attr("font-size", "14px").attr("font-weight", "bold").attr("font-family", "ui-sans-serif, system-ui").attr("fill", "#ffffff").text((e) => e.value.toLocaleString()).style("opacity", 0).transition().duration(500).delay((e, t) => 600 + t * 100).style("opacity", 1), c) {
80
+ let e = S.selectAll(".conversion-group").data(b.filter((e) => e.index > 0)).enter().append("g").attr("class", "conversion-group").attr("transform", (e) => `translate(${p + e.topWidth / 2 + 15}, ${e.yTop + _.bandwidth() / 2})`).style("opacity", 0);
81
+ e.append("rect").attr("y", -12).attr("height", 24).attr("width", 54).attr("rx", 12).attr("fill", "#f1f5f9"), e.append("text").attr("x", 27).attr("y", 0).attr("dy", "0.35em").attr("text-anchor", "middle").attr("font-size", "11px").attr("font-weight", "bold").attr("font-family", "ui-sans-serif, system-ui").attr("fill", "#64748b").text((e) => `${e.conversion}%`), e.transition().duration(500).delay((e, t) => 800 + t * 100).style("opacity", 1);
82
+ }
83
+ }, [
84
+ a,
85
+ g,
86
+ o,
87
+ s,
88
+ c,
89
+ l,
90
+ u,
91
+ d,
92
+ f
93
+ ]), /* @__PURE__ */ i("div", {
94
+ ref: m,
95
+ className: `w-full min-h-[350px] h-full relative ${p}`,
96
+ children: /* @__PURE__ */ i("svg", {
97
+ ref: h,
98
+ width: "100%",
99
+ height: "100%",
100
+ className: "block absolute inset-0 overflow-visible"
101
+ })
102
+ });
103
+ };
104
+ //#endregion
105
+ export { a as default };