import React, { useEffect, useRef } from 'react'; import * as d3 from 'd3'; import cloud from 'd3-cloud'; import { getSimplifiedTokenizer } from '../src/browser-tokenizers'; export type Language = 'english' | 'chinese'; export interface WordCloudProps { /** The text to generate a word cloud from */ text: string; /** The language of the text ('english' or 'chinese') */ language: Language; /** Width of the word cloud in pixels */ width?: number; /** Height of the word cloud in pixels */ height?: number; /** Font family to use for the words */ fontFamily?: string; /** Maximum number of words to include in the cloud */ maxWords?: number; /** Color scheme to use for the words */ colors?: readonly string[]; /** Padding between words in pixels */ padding?: number; /** Minimum font size for words */ minFontSize?: number; /** Maximum font size for words */ maxFontSize?: number; /** Rotation angles for words (in degrees) */ rotationAngles?: number[]; /** Probability of word rotation */ rotationProbability?: number; /** Optional className for the SVG element */ className?: string; /** Optional style for the SVG element */ style?: React.CSSProperties; } /** * Default options for the word cloud */ const DEFAULT_OPTIONS = { width: 800, height: 600, fontFamily: 'Arial, sans-serif', maxWords: 100, colors: [...d3.schemeCategory10], padding: 5, minFontSize: 10, maxFontSize: 60, rotationAngles: [0, 90], rotationProbability: 0.3 }; /** * React component for generating word clouds from text */ export const WordCloud: React.FC = (props) => { const { text, language, width = DEFAULT_OPTIONS.width, height = DEFAULT_OPTIONS.height, fontFamily = DEFAULT_OPTIONS.fontFamily, maxWords = DEFAULT_OPTIONS.maxWords, colors = DEFAULT_OPTIONS.colors, padding = DEFAULT_OPTIONS.padding, minFontSize = DEFAULT_OPTIONS.minFontSize, maxFontSize = DEFAULT_OPTIONS.maxFontSize, rotationAngles = DEFAULT_OPTIONS.rotationAngles, rotationProbability = DEFAULT_OPTIONS.rotationProbability, className, style } = props; const containerRef = useRef(null); useEffect(() => { if (!containerRef.current || !text) return; // Clear previous content const svg = containerRef.current; while (svg.firstChild) { svg.removeChild(svg.firstChild); } // Get the appropriate tokenizer const tokenizer = getSimplifiedTokenizer(language); // Tokenize the text and get word frequencies const wordCounts = tokenizer.tokenize(text); // Convert to array and sort by frequency const words = Array.from(wordCounts.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, maxWords) .map(([text, size]) => { // Scale the font size based on frequency const counts = Array.from(wordCounts.values()); const minCount = Math.min(...counts); const maxCount = Math.max(...counts); // Linear scaling between min and max font size const scaledSize = minCount === maxCount ? maxFontSize : minFontSize + (size - minCount) / (maxCount - minCount) * (maxFontSize - minFontSize); // Determine if word should be rotated const rotate = Math.random() > rotationProbability ? 0 : rotationAngles[Math.floor(Math.random() * rotationAngles.length)]; return { text, size: scaledSize, rotate }; }); // Generate the layout using d3-cloud const layout = cloud() .size([width, height]) .words(words) .padding(padding) .rotate((d) => (d as any).rotate) .font(fontFamily) .fontSize((d) => (d as any).size) .on('end', (words) => { // Draw the word cloud const colorScale = d3.scaleOrdinal(colors); const g = document.createElementNS('http://www.w3.org/2000/svg', 'g'); g.setAttribute('transform', `translate(${width / 2},${height / 2})`); words.forEach((d, i) => { const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); text.setAttribute('text-anchor', 'middle'); text.setAttribute('transform', `translate(${d.x},${d.y}) rotate(${d.rotate})`); text.setAttribute('font-size', `${d.size}px`); text.setAttribute('font-family', fontFamily); text.setAttribute('fill', colorScale(i.toString())); text.textContent = d.text || ''; g.appendChild(text); }); svg.appendChild(g); }); layout.start(); }, [text, language, width, height, fontFamily, maxWords, colors, padding, minFontSize, maxFontSize, rotationAngles, rotationProbability]); return ( ); }; export default WordCloud;