cs-2022-class-profile/components/Wordcloud.tsx

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 &nbsp;
<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 &nbsp;
<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>
);
}