@rdxui/react-venn-cloud
v0.1.1
Published
A React component for rendering text inside a Venn diagram layout
Maintainers
Readme
@rdxui/react-venn-cloud
A lightweight React component that renders text inside a three-region Venn diagram (left, center/overlap, right). Words are sized by value and automatically laid out within SVG ellipses using @chenglou/pretext.
Installation
# npm
npm install @rdxui/react-venn-cloud
# yarn
yarn add @rdxui/react-venn-cloud
# pnpm
pnpm add @rdxui/react-venn-cloudPeer dependencies: react >= 18.0.0 and react-dom >= 18.0.0
Quick Start
import { VennTextCloud } from '@rdxui/react-venn-cloud';
function App() {
return (
<VennTextCloud
width={800}
height={500}
dataByRegion={{
left: [
{ id: '1', label: 'Machine Learning', value: 90 },
{ id: '2', label: 'Deep Learning', value: 70 },
],
center: [
{ id: '3', label: 'Python', value: 100 },
{ id: '4', label: 'Statistics', value: 60 },
],
right: [
{ id: '5', label: 'Web Development', value: 85 },
{ id: '6', label: 'React', value: 75 },
],
}}
/>
);
}Features
- Three-region Venn layout (left, overlap, right) with semi-transparent fills
- Automatic word sizing based on value
- Hash-based color assignment per label (deterministic, no explicit color needed)
- Optional per-word custom color override
- Width-aware text wrapping with ellipsis on overflow
- Built-in hover tooltip
- Per-region click handlers with typed payload support
- Fits within any given width/height container
- Zero CSS dependencies — all inline styles
- Tree-shakeable ESM + CJS builds
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| width | number | required | Width of the SVG container in pixels |
| height | number | required | Height of the SVG container in pixels |
| dataByRegion | VennTextCloudDataByRegion | required | Data grouped by region (left, center, right) |
| fontSizeRange | [number, number] | [12, 20] | Min and max font size range |
| maxDisplayTextLength | number | undefined | Truncate label text beyond this character count |
| maxLinesPerDatum | number | 2 | Max lines per word before ellipsis |
| fontFamily | string | 'Impact, ..., sans-serif' | Font family for rendered text |
| tooltipOffset | number | 12 | Pixel offset for the hover tooltip |
| onDatumClickByRegion | VennTextCloudClickHandlersByRegion | undefined | Click handlers per region |
| renderTooltipContent | VennTextCloudTooltipFormatter | undefined | Custom tooltip renderer |
Data Format
Each datum in a region has the following shape:
interface VennTextCloudDatum<TPayload = unknown> {
id: string; // Unique identifier
label: string; // Display text (used for layout and color hashing)
fullLabel?: string; // Full text shown in tooltip (falls back to label)
value: number; // Determines font size (higher = larger)
color?: string; // Optional color override (defaults to hash-based color)
payload?: TPayload; // Custom metadata passed to click handlers
}Regions
type VennTextCloudRegion = 'left' | 'center' | 'right';
type VennTextCloudDataByRegion<TPayload = unknown> = Partial<
Record<VennTextCloudRegion, VennTextCloudDatum<TPayload>[]>
>;Color System
When no color is specified on a datum, a color is automatically assigned by hashing the label against a built-in 20-color palette. The same label always produces the same color.
You can override this per datum:
{ id: '1', label: 'React', value: 80, color: '#61dafb' }The color utilities are also exported for use elsewhere:
import {
VENN_COLOR_PALETTE, // The 20-color array
getColorByLabel, // (label: string) => string
hashString, // (str: string) => number
} from '@rdxui/react-venn-cloud';Click Handlers
<VennTextCloud
width={800}
height={500}
dataByRegion={data}
onDatumClickByRegion={{
left: (details) => console.log(details.datum.label),
center: (details) => console.log(details.datum.label),
right: (details) => console.log(details.datum.label),
}}
/>The details object contains:
| Field | Type | Description |
|---|---|---|
| region | VennTextCloudRegion | Which region was clicked |
| datum | VennTextCloudDatum | The clicked datum |
| clientX / clientY | number | Mouse position (viewport) |
| localX / localY | number | Mouse position (relative to SVG) |
| anchorRef | React.MutableRefObject | Positioned anchor element for popovers |
Custom Tooltip
<VennTextCloud
width={800}
height={500}
dataByRegion={data}
renderTooltipContent={(datum) => (
<div>
<strong>{datum.label}</strong>
<span> — {datum.value} mentions</span>
</div>
)}
/>Typed Payloads
Use generics to type your custom payload data:
interface MyPayload {
url: string;
category: string;
}
const data: VennTextCloudDataByRegion<MyPayload> = {
left: [
{
id: '1',
label: 'React',
value: 90,
payload: { url: '/react', category: 'frontend' },
},
],
};
<VennTextCloud<MyPayload>
width={800}
height={500}
dataByRegion={data}
onDatumClickByRegion={{
left: (details) => {
// details.datum.payload is typed as MyPayload
window.open(details.datum.payload?.url);
},
}}
/>Development
# Install dependencies
pnpm install
# Run demo (Vite)
pnpm dev
# Run demo (Rsbuild)
pnpm dev:rsbuild
# Build library
pnpm build
# Type check
pnpm typecheckLicense
MIT
