waffle-charts-cli 0.1.2 → 0.1.4

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.
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # WaffleCharts CLI 🧇
2
2
 
3
- The official CLI for [WaffleCharts](https://mbuchthal.github.io/waffle-charts).
3
+ The official CLI for [WaffleCharts](https://surprisewaffles-io.github.io/waffle-charts).
4
4
 
5
5
  > **Beautiful, Headless, Copy-Pasteable Charts for React.**
6
6
  > Built with [Visx](https://airbnb.io/visx) and [Tailwind CSS](https://tailwindcss.com).
7
7
 
8
8
  [![NPM Version](https://img.shields.io/npm/v/waffle-charts-cli)](https://www.npmjs.com/package/waffle-charts-cli)
9
- [![License](https://img.shields.io/npm/l/waffle-charts-cli)](https://github.com/mbuchthal/waffle-charts/blob/main/LICENSE)
9
+ [![License](https://img.shields.io/npm/l/waffle-charts-cli)](https://github.com/surprisewaffles-io/waffle-charts/blob/main/LICENSE)
10
10
 
11
11
  ## Why WaffleCharts?
12
12
 
@@ -56,4 +56,4 @@ npx waffle-charts-cli add bar-chart line-chart
56
56
 
57
57
  ## License
58
58
 
59
- MIT © [WaffleCharts](https://github.com/mbuchthal/waffle-charts)
59
+ MIT © [WaffleCharts](https://github.com/surprisewaffles-io/waffle-charts)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waffle-charts-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "CLI to add WaffleCharts components to your project",
5
5
  "main": "bin/index.js",
6
6
  "bin": {
@@ -16,9 +16,9 @@
16
16
  "visx"
17
17
  ],
18
18
  "repository": {
19
- "type": "git",
20
- "url": "git+https://github.com/mbuchthal/waffle-charts.git"
19
+ "url": "git+https://github.com/surprisewaffles-io/waffle-charts.git"
21
20
  },
21
+ "homepage": "https://surprisewaffles-io.github.io/waffle-charts",
22
22
  "author": "WaffleCharts Team",
23
23
  "license": "MIT",
24
24
  "files": [
package/src/registry.js CHANGED
@@ -53,5 +53,10 @@ export const registry = {
53
53
  file: "CompositeChart.tsx",
54
54
  label: "Composite Chart",
55
55
  dependencies: ["@visx/shape", "@visx/scale", "@visx/axis", "@visx/grid", "@visx/group", "@visx/responsive", "@visx/tooltip", "@visx/curve", "@visx/event", "clsx", "tailwind-merge"],
56
+ },
57
+ "chord-chart": {
58
+ file: "ChordChart.tsx",
59
+ label: "Chord Diagram",
60
+ dependencies: ["@visx/chord", "@visx/scale", "@visx/tooltip", "@visx/shape", "@visx/responsive", "@visx/group", "clsx", "tailwind-merge"],
56
61
  }
57
62
  };
@@ -0,0 +1,153 @@
1
+ import { Chord, Ribbon } from '@visx/chord';
2
+ import { scaleOrdinal } from '@visx/scale';
3
+ import { useTooltip, useTooltipInPortal, defaultStyles } from '@visx/tooltip';
4
+ import { Arc } from '@visx/shape';
5
+ import { ParentSize } from '@visx/responsive';
6
+ import { cn } from '../../lib/utils';
7
+ import React, { useMemo, useState, useRef } from 'react';
8
+ import { Group } from '@visx/group';
9
+
10
+ // Types
11
+ export type ChordChartProps = {
12
+ data: number[][];
13
+ keys: string[];
14
+ width?: number;
15
+ height?: number;
16
+ className?: string;
17
+ colorScheme?: string[];
18
+ };
19
+
20
+ type ChordChartContentProps = ChordChartProps & {
21
+ width: number;
22
+ height: number;
23
+ };
24
+
25
+ function ChordChartContent({
26
+ data,
27
+ keys,
28
+ width,
29
+ height,
30
+ className,
31
+ colorScheme = ['#a855f7', '#ec4899', '#3b82f6', '#14b8a6', '#f59e0b', '#ef4444'],
32
+ }: ChordChartContentProps) {
33
+ const outerRadius = Math.min(width, height) * 0.5 - 40;
34
+ const innerRadius = outerRadius - 20;
35
+
36
+ const [activeArc, setActiveArc] = useState<number | null>(null);
37
+
38
+ const colorScale = useMemo(
39
+ () =>
40
+ scaleOrdinal({
41
+ domain: keys,
42
+ range: colorScheme,
43
+ }),
44
+ [keys, colorScheme]
45
+ );
46
+
47
+ // Tooltip
48
+ const {
49
+ tooltipOpen,
50
+ tooltipLeft,
51
+ tooltipTop,
52
+ tooltipData,
53
+ hideTooltip,
54
+ showTooltip,
55
+ } = useTooltip<{ label: string; value: number }>();
56
+
57
+ const { containerRef, TooltipInPortal } = useTooltipInPortal({
58
+ scroll: true,
59
+ });
60
+
61
+ // Use a local ref to access the SVG element for bounding rect
62
+ const svgRef = useRef<SVGSVGElement>(null);
63
+
64
+ const setRefs = (node: SVGSVGElement | null) => {
65
+ containerRef(node);
66
+ svgRef.current = node;
67
+ };
68
+
69
+ if (width < 50) return null;
70
+
71
+ return (
72
+ <div className={cn("relative font-sans", className)}>
73
+ <svg ref={setRefs} width={width} height={height} className="overflow-visible">
74
+ <Group top={height / 2} left={width / 2}>
75
+ <Chord matrix={data} padAngle={0.05} sortSubgroups={(a, b) => b - a}>
76
+ {({ chords }) => (
77
+ <g>
78
+ {/* Ribbons */}
79
+ {chords.groups.map((group, i) => (
80
+ <Arc
81
+ key={`arc-${i}`}
82
+ data={group}
83
+ innerRadius={innerRadius}
84
+ outerRadius={outerRadius}
85
+ fill={colorScale(keys[i])}
86
+ className="transition-opacity duration-200 cursor-pointer"
87
+ opacity={activeArc === null || activeArc === i ? 1 : 0.3}
88
+ onMouseEnter={() => setActiveArc(i)}
89
+ onMouseLeave={() => setActiveArc(null)}
90
+ />
91
+ ))}
92
+
93
+ {chords.map((chord, i) => (
94
+ <Ribbon
95
+ key={`ribbon-${i}`}
96
+ chord={chord}
97
+ radius={innerRadius}
98
+ fill={colorScale(keys[chord.source.index])}
99
+ fillOpacity={0.75}
100
+ className="transition-all duration-200 hover:fill-opacity-100 mix-blend-multiply dark:mix-blend-screen"
101
+ opacity={
102
+ activeArc === null ||
103
+ activeArc === chord.source.index ||
104
+ activeArc === chord.target.index ? 0.75 : 0.1
105
+ }
106
+ onMouseEnter={(event) => {
107
+ const rect = svgRef.current?.getBoundingClientRect();
108
+ if (!rect) return;
109
+
110
+ showTooltip({
111
+ tooltipData: {
112
+ label: `${keys[chord.source.index]} ↔ ${keys[chord.target.index]}`,
113
+ value: chord.source.value
114
+ },
115
+ tooltipLeft: event.clientX - rect.left,
116
+ tooltipTop: event.clientY - rect.top,
117
+ });
118
+ }}
119
+ onMouseLeave={() => hideTooltip()}
120
+ />
121
+ ))}
122
+ </g>
123
+ )}
124
+ </Chord>
125
+ </Group>
126
+ </svg>
127
+
128
+ {/* Tooltip */}
129
+ {tooltipOpen && tooltipData && (
130
+ <TooltipInPortal
131
+ top={tooltipTop}
132
+ left={tooltipLeft}
133
+ style={{ ...defaultStyles, padding: 0, borderRadius: 0, boxShadow: 'none', background: 'transparent' }}
134
+ >
135
+ <div className="rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95">
136
+ <span className="font-semibold block">{tooltipData.label}</span>
137
+ <span className="text-xs text-muted-foreground">Flow: {tooltipData.value}</span>
138
+ </div>
139
+ </TooltipInPortal>
140
+ )}
141
+ </div>
142
+ );
143
+ }
144
+
145
+ export const ChordChart = (props: ChordChartProps) => {
146
+ return (
147
+ <div className="w-full h-[400px]">
148
+ <ParentSize>
149
+ {({ width, height }) => <ChordChartContent {...props} width={width} height={height} />}
150
+ </ParentSize>
151
+ </div>
152
+ );
153
+ }