npm package: @rdxui/react-venn-cloud
Description: A React component for rendering text inside a Venn diagram layout
Latest version: 0.1.1

# @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

```bash
# npm
npm install @rdxui/react-venn-cloud

# yarn
yarn add @rdxui/react-venn-cloud

# pnpm
pnpm add @rdxui/react-venn-cloud
```

**Peer dependencies:** `react >= 18.0.0` and `react-dom >= 18.0.0`

## Quick Start

```tsx
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:

```ts
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

```ts
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:

```tsx
{ id: '1', label: 'React', value: 80, color: '#61dafb' }
```

The color utilities are also exported for use elsewhere:

```ts
import {
  VENN_COLOR_PALETTE,   // The 20-color array
  getColorByLabel,      // (label: string) => string
  hashString,           // (str: string) => number
} from '@rdxui/react-venn-cloud';
```

## Click Handlers

```tsx
<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

```tsx
<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:

```tsx
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

```bash
# Install dependencies
pnpm install

# Run demo (Vite)
pnpm dev

# Run demo (Rsbuild)
pnpm dev:rsbuild

# Build library
pnpm build

# Type check
pnpm typecheck
```

## License

MIT