141 lines
3.3 KiB
TypeScript
141 lines
3.3 KiB
TypeScript
import { scaleLog } from "@visx/scale";
|
|
import { Text } from "@visx/text";
|
|
import { Wordcloud as VisxWordcloud } from "@visx/Wordcloud";
|
|
import { mockCategoricalData } from "data/mocks";
|
|
import React, { useState } from "react";
|
|
import { Color } from "utils/Color";
|
|
|
|
interface ExampleProps {
|
|
width: number;
|
|
height: number;
|
|
showControls?: boolean;
|
|
}
|
|
|
|
export interface WordData {
|
|
text: string;
|
|
value: number;
|
|
}
|
|
|
|
const colors = [
|
|
Color.primaryAccent,
|
|
Color.primaryAccentLight,
|
|
// Color.primaryAccentLighter,
|
|
];
|
|
|
|
function getWords(): WordData[] {
|
|
return mockCategoricalData.map((word) => ({
|
|
text: word.key,
|
|
value: word.value,
|
|
}));
|
|
}
|
|
|
|
function getRotationDegree() {
|
|
const rand = Math.random();
|
|
const degree = rand > 0.5 ? 60 : -60;
|
|
return rand * degree;
|
|
}
|
|
|
|
const words = [...getWords(), ...getWords()];
|
|
|
|
const fontScale = scaleLog({
|
|
domain: [
|
|
Math.min(...words.map((w) => w.value)),
|
|
Math.max(...words.map((w) => w.value)),
|
|
],
|
|
range: [10, 100],
|
|
});
|
|
const fontSizeSetter = (datum: WordData) => fontScale(datum.value);
|
|
|
|
const fixedValueGenerator = () => 0.5;
|
|
|
|
type SpiralType = "archimedean" | "rectangular";
|
|
|
|
export default function Wordcloud({
|
|
width,
|
|
height,
|
|
showControls,
|
|
}: ExampleProps) {
|
|
const [spiralType, setSpiralType] = useState<SpiralType>("rectangular");
|
|
const [withRotation, setWithRotation] = useState(false);
|
|
|
|
return (
|
|
<div className="wordcloud">
|
|
<VisxWordcloud
|
|
words={words}
|
|
width={width}
|
|
height={height}
|
|
fontSize={fontSizeSetter}
|
|
font="Inconsolata"
|
|
padding={30}
|
|
spiral={spiralType}
|
|
rotate={0}
|
|
random={fixedValueGenerator}
|
|
>
|
|
{(cloudWords) =>
|
|
cloudWords.map((w, i) => (
|
|
<Text
|
|
key={w.text}
|
|
fill={colors[i % colors.length]}
|
|
textAnchor={"middle"}
|
|
transform={`translate(${w.x ?? 0}, ${w.y ?? 0})`}
|
|
fontSize={w.size}
|
|
fontFamily={w.font}
|
|
fontWeight={300}
|
|
>
|
|
{w.text}
|
|
</Text>
|
|
))
|
|
}
|
|
</VisxWordcloud>
|
|
{showControls && (
|
|
<div>
|
|
<label>
|
|
Spiral type
|
|
<select
|
|
onChange={(e) => setSpiralType(e.target.value as SpiralType)}
|
|
value={spiralType}
|
|
>
|
|
<option key={"archimedean"} value={"archimedean"}>
|
|
archimedean
|
|
</option>
|
|
<option key={"rectangular"} value={"rectangular"}>
|
|
rectangular
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label>
|
|
With rotation
|
|
<input
|
|
type="checkbox"
|
|
checked={withRotation}
|
|
onChange={() => setWithRotation(!withRotation)}
|
|
/>
|
|
</label>
|
|
<br />
|
|
</div>
|
|
)}
|
|
<style jsx>{`
|
|
.wordcloud {
|
|
display: flex;
|
|
flex-direction: column;
|
|
user-select: none;
|
|
}
|
|
.wordcloud svg {
|
|
margin: 1rem 0;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.wordcloud label {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
font-size: 14px;
|
|
margin-right: 8px;
|
|
}
|
|
.wordcloud textarea {
|
|
min-height: 100px;
|
|
}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
}
|