From 6735c52914d3f25ace962f2a40d951ec6772d8b8 Mon Sep 17 00:00:00 2001 From: Amy Date: Wed, 27 Jul 2022 19:46:08 -0400 Subject: [PATCH 1/3] Bar Graph Component (#16) Adds a `` component and a `` component. Closes #1. Possible changes for the future: - Refactor to make the horizontal and vertical bar graphs into one component - Add (optional) graph title Co-authored-by: Amy Wang Reviewed-on: https://git.csclub.uwaterloo.ca/www/cs-2022-class-profile/pulls/16 Reviewed-by: Shahan Neda --- .vscode/settings.json | 1 + components/BarGraph.module.css | 36 ++++ components/BarGraph.tsx | 353 +++++++++++++++++++++++++++++++++ data/mocks.ts | 16 +- package-lock.json | 3 + package.json | 3 + pages/playground.module.css | 7 + pages/playground.tsx | 44 +++- 8 files changed, 453 insertions(+), 10 deletions(-) create mode 100644 components/BarGraph.module.css create mode 100644 components/BarGraph.tsx create mode 100644 pages/playground.module.css diff --git a/.vscode/settings.json b/.vscode/settings.json index 12f41f3..99e58e0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "typescript.tsdk": "node_modules/typescript/lib", "eslint.format.enable": true, "eslint.codeActionsOnSave.mode": "all", + "css.format.spaceAroundSelectorSeparator": true, "[css]": { "editor.suggest.insertMode": "replace", "gitlens.codeLens.scopes": ["document"], diff --git a/components/BarGraph.module.css b/components/BarGraph.module.css new file mode 100644 index 0000000..025976a --- /dev/null +++ b/components/BarGraph.module.css @@ -0,0 +1,36 @@ +.barBackground { + fill: var(--card-background); +} + +.bar { + fill: var(--primary-accent-light); +} + +.barText { + visibility: hidden; + + font-family: "Inconsolata", "monospace"; + font-weight: 800; + fill: var(--label); +} + +.barGroup:hover .bar { + fill: var(--primary-accent); + filter: drop-shadow(0 0 calc(4rem / 16) var(--primary-accent)); +} + +.barGroup:hover .barText { + visibility: visible; +} + +.tickLabel { + font-family: "Inconsolata", "monospace"; + font-weight: 800; + fill: var(--label); +} + +.axisLabel { + font-family: "Inconsolata", "monospace"; + font-weight: 800; + fill: var(--label); +} diff --git a/components/BarGraph.tsx b/components/BarGraph.tsx new file mode 100644 index 0000000..222770d --- /dev/null +++ b/components/BarGraph.tsx @@ -0,0 +1,353 @@ +import { AxisBottom, AxisLeft } from "@visx/axis"; +import { bottomTickLabelProps } from "@visx/axis/lib/axis/AxisBottom"; +import { leftTickLabelProps } from "@visx/axis/lib/axis/AxisLeft"; +import { GridColumns, GridRows } from "@visx/grid"; +import { Group } from "@visx/group"; +import { scaleBand, scaleLinear } from "@visx/scale"; +import { Bar } from "@visx/shape"; +import { Text } from "@visx/text"; +import React from "react"; +import { Color } from "utils/Color"; + +import styles from "./BarGraph.module.css"; + +interface BarGraphProps { + data: BarGraphData[]; + /** Width of the entire graph, in pixels. */ + width: number; + /** Height of the entire graph, in pixels. */ + height: number; + /** Distance between the edge of the graph and the area where the bars are drawn, in pixels. */ + margin: { + top: number; + bottom: number; + left: number; + right: number; + }; + className?: string; + /** Font size of the category tick labels, in pixels. Default is 16px. */ + categoryTickLabelSize?: number; + /** Font size of the value tick labels, in pixels. Default is 16px. */ + valueTickLabelSize?: number; + /** Font size of the value that appears when hovering over a bar, in pixels. */ + hoverLabelSize?: number; + /** Label text for the category axis. */ + categoryAxisLabel?: string; + /** Font size of the label for the cateogry axis, in pixels. */ + categoryAxisLabelSize?: number; + /** Controls the distance between the category axis label and the category axis. */ + categoryAxisLabelOffset?: number; + /** Label text for the value axis. */ + valueAxisLabel?: string; + /** Font size of the label for the value axis, in pixels. */ + valueAxisLabelSize?: number; + /** Controls the distance between the value axis label and the value axis. */ + valueAxisLabelOffset?: number; +} + +interface BarGraphData { + category: string; + value: number; +} + +const DEFAULT_LABEL_SIZE = 16; + +export function BarGraphHorizontal(props: BarGraphProps) { + const { + width, + height, + margin, + data, + className, + categoryTickLabelSize = DEFAULT_LABEL_SIZE, + valueTickLabelSize = DEFAULT_LABEL_SIZE, + hoverLabelSize, + categoryAxisLabel, + categoryAxisLabelSize = DEFAULT_LABEL_SIZE, + categoryAxisLabelOffset = 0, + valueAxisLabel, + valueAxisLabelSize = DEFAULT_LABEL_SIZE, + valueAxisLabelOffset = 0, + } = props; + + const barPadding = 0.4; + + const categoryMax = height - margin.top - margin.bottom; + const valueMax = width - margin.left - margin.right; + + const getCategory = (d: BarGraphData) => d.category; + const getValue = (d: BarGraphData) => d.value; + + const categoryScale = scaleBand({ + range: [0, categoryMax], + domain: data.map(getCategory), + padding: barPadding, + }); + + const valueScale = scaleLinear({ + range: [0, valueMax], + nice: true, + domain: [0, Math.max(...data.map(getValue))], + }); + + const categoryPoint = (d: BarGraphData) => categoryScale(getCategory(d)); + const valuePoint = (d: BarGraphData) => valueScale(getValue(d)); + + return ( + + + + {data.map((d, idx) => { + const barName = `${getCategory(d)}-${idx}`; + const barWidth = categoryScale.bandwidth(); + const backgroundBarWidth = barWidth / (1 - barPadding); + return idx % 2 === 0 ? ( + + ) : null; + })} + + + + {data.map((d, idx) => { + const barName = `${getCategory(d)}-${idx}`; + const barLength = valuePoint(d); + const barWidth = categoryScale.bandwidth(); + return ( + + + + {getValue(d)} + + + ); + })} + + + { + return { + ...leftTickLabelProps(), + className: styles.tickLabel, + dx: "-0.5rem", + dy: "0.25rem", + fontSize: `${categoryTickLabelSize / 16}rem`, + }; + }} + label={categoryAxisLabel} + labelClassName={styles.axisLabel} + labelOffset={categoryAxisLabelOffset} + labelProps={{ + fontSize: `${categoryAxisLabelSize / 16}rem`, + }} + /> + { + return { + ...bottomTickLabelProps(), + className: styles.tickLabel, + dy: "0.25rem", + fontSize: `${valueTickLabelSize / 16}rem`, + }; + }} + label={valueAxisLabel} + labelClassName={styles.axisLabel} + labelOffset={valueAxisLabelOffset} + labelProps={{ + fontSize: `${valueAxisLabelSize / 16}rem`, + }} + /> + + ); +} + +export function BarGraphVertical(props: BarGraphProps) { + const { + width, + height, + margin, + data, + className, + categoryTickLabelSize = DEFAULT_LABEL_SIZE, + valueTickLabelSize = DEFAULT_LABEL_SIZE, + hoverLabelSize, + categoryAxisLabel, + categoryAxisLabelSize = DEFAULT_LABEL_SIZE, + categoryAxisLabelOffset = 0, + valueAxisLabel, + valueAxisLabelSize = DEFAULT_LABEL_SIZE, + valueAxisLabelOffset = 0, + } = props; + + const barPadding = 0.4; + + const categoryMax = width - margin.left - margin.right; + const valueMax = height - margin.top - margin.bottom; + + const getCategory = (d: BarGraphData) => d.category; + const getValue = (d: BarGraphData) => d.value; + + const categoryScale = scaleBand({ + range: [0, categoryMax], + domain: data.map(getCategory), + padding: barPadding, + }); + + const valueScale = scaleLinear({ + range: [valueMax, 0], + nice: true, + domain: [0, Math.max(...data.map(getValue))], + }); + + const categoryPoint = (d: BarGraphData) => categoryScale(getCategory(d)); + const valuePoint = (d: BarGraphData) => valueScale(getValue(d)); + + return ( + + + + {data.map((d, idx) => { + const barName = `${getCategory(d)}-${idx}`; + const barWidth = categoryScale.bandwidth(); + const backgroundBarWidth = barWidth / (1 - barPadding); + return idx % 2 === 0 ? ( + + ) : null; + })} + + + + {data.map((d, idx) => { + const barName = `${getCategory(d)}-${idx}`; + const barHeight = valueMax - valuePoint(d); + const barWidth = categoryScale.bandwidth(); + return ( + + + + {getValue(d)} + + + ); + })} + + + { + return { + ...bottomTickLabelProps(), + className: styles.tickLabel, + dy: "-0.25rem", + fontSize: `${categoryTickLabelSize / 16}rem`, + width: categoryScale.bandwidth(), + verticalAnchor: "start", + }; + }} + label={categoryAxisLabel} + labelClassName={styles.axisLabel} + labelOffset={categoryAxisLabelOffset} + labelProps={{ + fontSize: `${categoryAxisLabelSize / 16}rem`, + }} + /> + { + return { + ...leftTickLabelProps(), + className: styles.tickLabel, + dx: "-0.5rem", + dy: "0.25rem", + fontSize: `${valueTickLabelSize / 16}rem`, + }; + }} + label={valueAxisLabel} + labelClassName={styles.axisLabel} + labelOffset={valueAxisLabelOffset} + labelProps={{ + fontSize: `${valueAxisLabelSize / 16}rem`, + }} + /> + + ); +} diff --git a/data/mocks.ts b/data/mocks.ts index f7e9235..ae0a70a 100644 --- a/data/mocks.ts +++ b/data/mocks.ts @@ -3,35 +3,35 @@ */ export const mockCategoricalData = [ { - key: "Roboto", + category: "Roboto", value: 88, }, { - key: "Open Sans", + category: "Open Sans", value: 16, }, { - key: "Lato", + category: "Lato", value: 14, }, { - key: "Montserrat", + category: "Montserrat", value: 73, }, { - key: "Oswald", + category: "Oswald", value: 14, }, { - key: "Source Sans Pro", + category: "Source Sans Pro", value: 8, }, { - key: "Slabo 27px", + category: "Slabo 27px", value: 41, }, { - key: "Raleway", + category: "Raleway", value: 29, }, ]; diff --git a/package-lock.json b/package-lock.json index a0d527f..6990a90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,10 @@ "dependencies": { "@visx/axis": "^2.10.0", "@visx/grid": "^2.10.0", + "@visx/group": "^2.10.0", + "@visx/scale": "^2.2.2", "@visx/shape": "^2.10.0", + "@visx/text": "^2.10.0", "next": "12.1.6", "react": "18.1.0", "react-dom": "18.1.0" diff --git a/package.json b/package.json index 5ac9543..fc11697 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,10 @@ "dependencies": { "@visx/axis": "^2.10.0", "@visx/grid": "^2.10.0", + "@visx/group": "^2.10.0", + "@visx/scale": "^2.2.2", "@visx/shape": "^2.10.0", + "@visx/text": "^2.10.0", "next": "12.1.6", "react": "18.1.0", "react-dom": "18.1.0" diff --git a/pages/playground.module.css b/pages/playground.module.css new file mode 100644 index 0000000..a7aefd8 --- /dev/null +++ b/pages/playground.module.css @@ -0,0 +1,7 @@ +.page { + padding: calc(8rem / 16); +} + +.barGraphDemo { + border: calc(1rem / 16) solid black; +} diff --git a/pages/playground.tsx b/pages/playground.tsx index f430e95..3f8a90b 100644 --- a/pages/playground.tsx +++ b/pages/playground.tsx @@ -1,13 +1,53 @@ +import { BarGraphHorizontal, BarGraphVertical } from "components/BarGraph"; +import { mockCategoricalData } from "data/mocks"; import React from "react"; import { ColorPalette } from "../components/ColorPalette"; +import styles from "./playground.module.css"; + export default function Home() { return ( - <> +

Playground

Show off your components here!

- + +

+ {""} +

+ + +

+ {""} +

+

+ {""} takes the same props as{" "} + {""}. +

+ +
); } From 933833d331e79bf1bf5e61a5ffde6479f96fed9e Mon Sep 17 00:00:00 2001 From: Amy Date: Wed, 27 Jul 2022 20:45:10 -0400 Subject: [PATCH 2/3] Fix monospace fallback (#28) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just realized that we shouldn't have quotes around `monospace` 🤦 Co-authored-by: Amy Wang Reviewed-on: https://git.csclub.uwaterloo.ca/www/cs-2022-class-profile/pulls/28 Reviewed-by: j285he --- components/BarGraph.module.css | 6 +++--- pages/_app.css | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/BarGraph.module.css b/components/BarGraph.module.css index 025976a..7ccdc8b 100644 --- a/components/BarGraph.module.css +++ b/components/BarGraph.module.css @@ -9,7 +9,7 @@ .barText { visibility: hidden; - font-family: "Inconsolata", "monospace"; + font-family: "Inconsolata", monospace; font-weight: 800; fill: var(--label); } @@ -24,13 +24,13 @@ } .tickLabel { - font-family: "Inconsolata", "monospace"; + font-family: "Inconsolata", monospace; font-weight: 800; fill: var(--label); } .axisLabel { - font-family: "Inconsolata", "monospace"; + font-family: "Inconsolata", monospace; font-weight: 800; fill: var(--label); } diff --git a/pages/_app.css b/pages/_app.css index 92ef010..1aa8093 100644 --- a/pages/_app.css +++ b/pages/_app.css @@ -64,7 +64,7 @@ body { background-color: var(--primary-background); color: var(--primary-text); - font-family: "Inconsolata", "monospace"; + font-family: "Inconsolata", monospace; margin: 0; } From b586d52f720c1d57243244c8b29785079a72cbd5 Mon Sep 17 00:00:00 2001 From: Shahan Neda Date: Thu, 4 Aug 2022 02:17:19 -0400 Subject: [PATCH 3/3] Add Wordcloud Component (#27) --- components/ColorPalette.module.css | 2 +- components/WordCloud.module.css | 20 +++ components/WordCloud.tsx | 210 +++++++++++++++++++++++++++++ data/mocks.ts | 29 ++++ package-lock.json | 180 ++++++++++++++++++++++++- package.json | 3 + pages/_app.css | 2 +- pages/font.css | 2 +- pages/playground.tsx | 13 +- 9 files changed, 455 insertions(+), 6 deletions(-) create mode 100644 components/WordCloud.module.css create mode 100644 components/WordCloud.tsx diff --git a/components/ColorPalette.module.css b/components/ColorPalette.module.css index 01bc6a5..b605f28 100644 --- a/components/ColorPalette.module.css +++ b/components/ColorPalette.module.css @@ -16,6 +16,6 @@ flex-direction: column; align-items: center; text-align: center; - margin: calc(10rem / 16); + margin: calc(10rem / 16) calc(60rem / 16); width: calc(150rem / 16); } \ No newline at end of file diff --git a/components/WordCloud.module.css b/components/WordCloud.module.css new file mode 100644 index 0000000..728094a --- /dev/null +++ b/components/WordCloud.module.css @@ -0,0 +1,20 @@ +.word:hover { + text-shadow: var(--primary-accent) 0 0 calc(20rem / 16); + text-anchor: "middle"; + cursor: default; +} + +.tooltip { + font-family: "Inconsolata", monospace; + font-weight: bold; + top: 0; + left: 0; + position: absolute; + background-color: var(--label); + color: var(--primary-background); + box-shadow: 0px calc(1rem / 16) calc(2rem / 16) var(--card-background); + pointer-events: none; + padding: calc(10rem / 16); + font-size: calc(18rem / 16); + border-radius: calc(10rem / 16); +} \ No newline at end of file diff --git a/components/WordCloud.tsx b/components/WordCloud.tsx new file mode 100644 index 0000000..9591dac --- /dev/null +++ b/components/WordCloud.tsx @@ -0,0 +1,210 @@ +import { localPoint } from "@visx/event"; +import { Point } from "@visx/point"; +import { scaleLog } from "@visx/scale"; +import { Text } from "@visx/text"; +import { TooltipWithBounds, useTooltip, withTooltip } from "@visx/tooltip"; +import { Wordcloud as VisxWordcloud } from "@visx/wordcloud"; +import React from "react"; +import { Color } from "utils/Color"; + +import styles from "./WordCloud.module.css"; + +interface WordCloudProps { + data: Array; + /** Width of the graph, in px */ + width?: number; + /** Height of the graph, in px */ + height?: number; + /** Minimum padding between words, in px */ + wordPadding?: number; + /** Weight of the font of the words */ + fontWeight?: number; + /** The desired font size of the smallest word, in px.*/ + minFontSize?: number; + /** The desired font size of the largest word, in px. */ + maxFontSize?: number; + /** A random seed in the range [0, 1) used for placing the words, change this value to get an alternate placement of words */ + randomSeed?: number; + /** Type of spiral used for rendering the words, either rectangular or archimedean */ + spiral?: "rectangular" | "archimedean"; + /** ClassName of the wrapper of the wordcloud */ + className?: string; +} + +interface WordData { + text: string; + value: number; +} + +const wordColors = [Color.primaryAccent, Color.primaryAccentLight]; +const TOOLTIP_HORIZONTAL_SHIFT_SCALER = 12.0; + +export const WordCloud = withTooltip( + ({ + data, + width, + height, + wordPadding, + fontWeight, + minFontSize, + maxFontSize, + randomSeed, + spiral, + className, + }: WordCloudProps) => { + const { + tooltipData, + tooltipLeft, + tooltipTop, + tooltipOpen, + showTooltip, + hideTooltip, + } = useTooltip(); + + return ( +
+ { + showTooltip({ + tooltipData: data, + tooltipLeft: left, + tooltipTop: top, + }); + }} + hideTooltip={hideTooltip} + tooltipLeft={tooltipLeft} + tooltipTop={tooltipTop} + randomSeed={randomSeed} + spiral={spiral} + /> + + {tooltipOpen && tooltipData ? ( + + {tooltipData.text} ({tooltipData.value}) + + ) : null} +
+ ); + } +); + +/** The internal wordcloud component that actually lays out the word needs to be separate from the tooltip to prevent extra rerendering. */ +type WordCloudWordsProps = Omit & { + showTooltip: ( + data: WordData, + tooltipLeft: number, + tooltipTop: number + ) => void; + hideTooltip: () => void; + // tooltipLeft and tooltipTop are used for preventing unnessary renders + tooltipLeft?: number; + tooltipTop?: number; +}; +const WordCloudWords: React.FC = ({ + data, + width = 1000, + height = 500, + wordPadding = 30, + fontWeight = 500, + minFontSize = 20, + maxFontSize = 150, + randomSeed = 0.5, + spiral = "rectangular", + showTooltip, + hideTooltip, +}) => { + const fontScale = scaleLog({ + domain: [ + Math.min(...data.map((w) => w.value)), + Math.max(...data.map((w) => w.value)), + ], + range: [minFontSize, maxFontSize], + }); + const fontSizeSetter = (datum: WordData) => fontScale(datum.value); + const fixedValueGenerator = () => randomSeed; + return ( + + {(cloudWords) => + cloudWords.map((word, index) => { + return ( + ) => { + const eventSvgCoords = localPoint( + // ownerSVGElement is given by visx docs but not recognized by typescript + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + e.target.ownerSVGElement as Element, + e + ) as Point; + + if (word.text) { + showTooltip( + { text: word.text, value: data[index].value }, + eventSvgCoords.x - + word.text.length * TOOLTIP_HORIZONTAL_SHIFT_SCALER, + eventSvgCoords.y + ); + } + }) as React.MouseEventHandler + } + onMouseLeave={(_) => hideTooltip()} + > + {word.text} + + ); + }) + } + + ); +}; + +const shouldNotRerender = ( + prevProps: WordCloudWordsProps, + nextProps: WordCloudWordsProps +) => { + if ( + prevProps.tooltipLeft !== nextProps.tooltipLeft || + prevProps.tooltipTop !== nextProps.tooltipTop || + nextProps.tooltipLeft === undefined || + nextProps.tooltipTop === undefined + ) { + return true; // do not re-render + } + return false; // will re-render +}; + +const WordCloudWordsMemoized = React.memo(WordCloudWords, shouldNotRerender); diff --git a/data/mocks.ts b/data/mocks.ts index ae0a70a..162f5aa 100644 --- a/data/mocks.ts +++ b/data/mocks.ts @@ -35,3 +35,32 @@ export const mockCategoricalData = [ value: 29, }, ]; + +export const moreMockCategoricalData = [ + { key: "Python", value: 29.53 }, + { key: "Java", value: 17.06 }, + { key: "JavaScript", value: 8.56 }, + { key: "C", value: 6.49 }, + { key: "Assembly", value: 6.31 }, + { key: "R", value: 5.83 }, + { key: "Objective C", value: 4.43 }, + { key: "Swift", value: 3.89 }, + { key: "PHP", value: 3.85 }, + { key: "Rust", value: 3.33 }, + { key: "Ruby", value: 3.25 }, + { key: "Matlab", value: 3.06 }, + { key: "TypeScript", value: 3.98 }, + { key: "Go", value: 3.85 }, + { key: "Haskell", value: 3.28 }, + { key: "VBA", value: 3.11 }, + { key: "Scala", value: 2.79 }, + { key: "Lua", value: 2.79 }, + { key: "Julia", value: 2.77 }, + { key: "Kotlin", value: 2.7 }, + { key: "Perl", value: 2.41 }, + { key: "Groovy", value: 2.4 }, + { key: "Abap", value: 2.24 }, + { key: "Cobol", value: 2.23 }, + { key: "Ada", value: 2.21 }, + { key: "Dart", value: 2.21 }, +]; diff --git a/package-lock.json b/package-lock.json index 6990a90..7f2a1f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,14 @@ "version": "0.1.0", "dependencies": { "@visx/axis": "^2.10.0", + "@visx/event": "^2.6.0", "@visx/grid": "^2.10.0", "@visx/group": "^2.10.0", "@visx/scale": "^2.2.2", "@visx/shape": "^2.10.0", "@visx/text": "^2.10.0", + "@visx/tooltip": "^2.10.0", + "@visx/wordcloud": "^2.10.0", "next": "12.1.6", "react": "18.1.0", "react-dom": "18.1.0" @@ -574,6 +577,19 @@ "integrity": "sha512-WiBSI6JBIhC6LRIsB2Kwh8DsGTlbBU+mLRxJmAe3LjHTdkDpwIbEOZgoXBbZilk/vlfjK8i6nKRAvIRn1XaIMw==", "dev": true }, + "node_modules/@types/d3": { + "version": "3.5.47", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-3.5.47.tgz", + "integrity": "sha512-VkWIQoZXLFdcBGe5pdBKJmTU3fmpXvo/KV6ixvTzOMl1yJ2hbTXpfvsziag0kcaerPDwas2T0vxojwQG3YwivQ==" + }, + "node_modules/@types/d3-cloud": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/d3-cloud/-/d3-cloud-1.2.5.tgz", + "integrity": "sha512-vEIER9DsEBUOdpRiwCh3n1qE+cV6h4e1LhxhY2sLt+m8LPNAIkOOhTlqk0JDiBwD+ZPM8ynFAOU3AuPuVYBFBA==", + "dependencies": { + "@types/d3": "^3" + } + }, "node_modules/@types/d3-color": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.2.tgz", @@ -655,7 +671,6 @@ "version": "18.0.5", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.5.tgz", "integrity": "sha512-OWPWTUrY/NIrjsAPkAk1wW9LZeIjSvkXRhclsFO8CZcZGCOg2G0YZy4ft+rOyYxy8B7ui5iZzi9OkDebZ7/QSA==", - "dev": true, "dependencies": { "@types/react": "*" } @@ -889,6 +904,20 @@ "react": "^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0" } }, + "node_modules/@visx/bounds": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@visx/bounds/-/bounds-2.10.0.tgz", + "integrity": "sha512-rY7WFTIjQaXA8tFL45O2qbtSRkyF4yF75HiWz06F7BVmJ9UjF2qlomB3Y1z6gk6ZiFhwQ4zxABjOVjAQPLn7nQ==", + "dependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "prop-types": "^15.5.10" + }, + "peerDependencies": { + "react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0", + "react-dom": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0" + } + }, "node_modules/@visx/curve": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@visx/curve/-/curve-2.1.0.tgz", @@ -898,6 +927,15 @@ "d3-shape": "^1.0.6" } }, + "node_modules/@visx/event": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@visx/event/-/event-2.6.0.tgz", + "integrity": "sha512-WGp91g82s727g3NAnENF1ppC3ZAlvWg+Y+GG0WFg34NmmOZbvPI/PTOqTqZE3x6B8EUn8NJiMxRjxIMbi+IvRw==", + "dependencies": { + "@types/react": "*", + "@visx/point": "2.6.0" + } + }, "node_modules/@visx/grid": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@visx/grid/-/grid-2.10.0.tgz", @@ -985,6 +1023,35 @@ "react": "^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0" } }, + "node_modules/@visx/tooltip": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@visx/tooltip/-/tooltip-2.10.0.tgz", + "integrity": "sha512-6Zrd79MIEfyuLBcZ1ypSeAkpQc8oLRNB7FQnegzl3Lje4LK5lJtuf5ST0mwK6G2Uv+GlOW9REJ6VK4gfAGkq9A==", + "dependencies": { + "@types/react": "*", + "@visx/bounds": "2.10.0", + "classnames": "^2.3.1", + "prop-types": "^15.5.10", + "react-use-measure": "^2.0.4" + }, + "peerDependencies": { + "react": "^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0", + "react-dom": "^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0" + } + }, + "node_modules/@visx/wordcloud": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@visx/wordcloud/-/wordcloud-2.10.0.tgz", + "integrity": "sha512-SN3W9VbnU/qYofPG5xlN0jJWFTMo5v9jlJtWFLgTy9aHV3CtclyyEAQ6/+VPrKWuNR5bgtOSegiE8EJdobrStg==", + "dependencies": { + "@types/d3-cloud": "1.2.5", + "@visx/group": "2.10.0", + "d3-cloud": "^1.2.5" + }, + "peerDependencies": { + "react": "^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0" + } + }, "node_modules/acorn": { "version": "8.7.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", @@ -1432,11 +1499,24 @@ "internmap": "^1.0.0" } }, + "node_modules/d3-cloud": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/d3-cloud/-/d3-cloud-1.2.5.tgz", + "integrity": "sha512-4s2hXZgvs0CoUIw31oBAGrHt9Kt/7P9Ik5HIVzISFiWkD0Ga2VLAuO/emO/z1tYIpE7KG2smB4PhMPfFMJpahw==", + "dependencies": { + "d3-dispatch": "^1.0.3" + } + }, "node_modules/d3-color": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" }, + "node_modules/d3-dispatch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + }, "node_modules/d3-format": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", @@ -1497,6 +1577,11 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3865,6 +3950,18 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-use-measure": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz", + "integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==", + "dependencies": { + "debounce": "^1.2.1" + }, + "peerDependencies": { + "react": ">=16.13", + "react-dom": ">=16.13" + } + }, "node_modules/reduce-css-calc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", @@ -4662,6 +4759,19 @@ "integrity": "sha512-WiBSI6JBIhC6LRIsB2Kwh8DsGTlbBU+mLRxJmAe3LjHTdkDpwIbEOZgoXBbZilk/vlfjK8i6nKRAvIRn1XaIMw==", "dev": true }, + "@types/d3": { + "version": "3.5.47", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-3.5.47.tgz", + "integrity": "sha512-VkWIQoZXLFdcBGe5pdBKJmTU3fmpXvo/KV6ixvTzOMl1yJ2hbTXpfvsziag0kcaerPDwas2T0vxojwQG3YwivQ==" + }, + "@types/d3-cloud": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/d3-cloud/-/d3-cloud-1.2.5.tgz", + "integrity": "sha512-vEIER9DsEBUOdpRiwCh3n1qE+cV6h4e1LhxhY2sLt+m8LPNAIkOOhTlqk0JDiBwD+ZPM8ynFAOU3AuPuVYBFBA==", + "requires": { + "@types/d3": "^3" + } + }, "@types/d3-color": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.2.tgz", @@ -4743,7 +4853,6 @@ "version": "18.0.5", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.5.tgz", "integrity": "sha512-OWPWTUrY/NIrjsAPkAk1wW9LZeIjSvkXRhclsFO8CZcZGCOg2G0YZy4ft+rOyYxy8B7ui5iZzi9OkDebZ7/QSA==", - "dev": true, "requires": { "@types/react": "*" } @@ -4881,6 +4990,16 @@ "prop-types": "^15.6.0" } }, + "@visx/bounds": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@visx/bounds/-/bounds-2.10.0.tgz", + "integrity": "sha512-rY7WFTIjQaXA8tFL45O2qbtSRkyF4yF75HiWz06F7BVmJ9UjF2qlomB3Y1z6gk6ZiFhwQ4zxABjOVjAQPLn7nQ==", + "requires": { + "@types/react": "*", + "@types/react-dom": "*", + "prop-types": "^15.5.10" + } + }, "@visx/curve": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@visx/curve/-/curve-2.1.0.tgz", @@ -4890,6 +5009,15 @@ "d3-shape": "^1.0.6" } }, + "@visx/event": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@visx/event/-/event-2.6.0.tgz", + "integrity": "sha512-WGp91g82s727g3NAnENF1ppC3ZAlvWg+Y+GG0WFg34NmmOZbvPI/PTOqTqZE3x6B8EUn8NJiMxRjxIMbi+IvRw==", + "requires": { + "@types/react": "*", + "@visx/point": "2.6.0" + } + }, "@visx/grid": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@visx/grid/-/grid-2.10.0.tgz", @@ -4965,6 +5093,28 @@ "reduce-css-calc": "^1.3.0" } }, + "@visx/tooltip": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@visx/tooltip/-/tooltip-2.10.0.tgz", + "integrity": "sha512-6Zrd79MIEfyuLBcZ1ypSeAkpQc8oLRNB7FQnegzl3Lje4LK5lJtuf5ST0mwK6G2Uv+GlOW9REJ6VK4gfAGkq9A==", + "requires": { + "@types/react": "*", + "@visx/bounds": "2.10.0", + "classnames": "^2.3.1", + "prop-types": "^15.5.10", + "react-use-measure": "^2.0.4" + } + }, + "@visx/wordcloud": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@visx/wordcloud/-/wordcloud-2.10.0.tgz", + "integrity": "sha512-SN3W9VbnU/qYofPG5xlN0jJWFTMo5v9jlJtWFLgTy9aHV3CtclyyEAQ6/+VPrKWuNR5bgtOSegiE8EJdobrStg==", + "requires": { + "@types/d3-cloud": "1.2.5", + "@visx/group": "2.10.0", + "d3-cloud": "^1.2.5" + } + }, "acorn": { "version": "8.7.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", @@ -5257,11 +5407,24 @@ "internmap": "^1.0.0" } }, + "d3-cloud": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/d3-cloud/-/d3-cloud-1.2.5.tgz", + "integrity": "sha512-4s2hXZgvs0CoUIw31oBAGrHt9Kt/7P9Ik5HIVzISFiWkD0Ga2VLAuO/emO/z1tYIpE7KG2smB4PhMPfFMJpahw==", + "requires": { + "d3-dispatch": "^1.0.3" + } + }, "d3-color": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" }, + "d3-dispatch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + }, "d3-format": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", @@ -5322,6 +5485,11 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -6998,6 +7166,14 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-use-measure": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz", + "integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==", + "requires": { + "debounce": "^1.2.1" + } + }, "reduce-css-calc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", diff --git a/package.json b/package.json index fc11697..6358840 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,13 @@ }, "dependencies": { "@visx/axis": "^2.10.0", + "@visx/event": "^2.6.0", "@visx/grid": "^2.10.0", "@visx/group": "^2.10.0", "@visx/scale": "^2.2.2", "@visx/shape": "^2.10.0", + "@visx/tooltip": "^2.10.0", + "@visx/wordcloud": "^2.10.0", "@visx/text": "^2.10.0", "next": "12.1.6", "react": "18.1.0", diff --git a/pages/_app.css b/pages/_app.css index 1aa8093..0c34108 100644 --- a/pages/_app.css +++ b/pages/_app.css @@ -109,4 +109,4 @@ a:hover { --card-background: var(--dark--card-background); --label: var(--dark--label); } -} +} \ No newline at end of file diff --git a/pages/font.css b/pages/font.css index c6ea327..f8229d1 100644 --- a/pages/font.css +++ b/pages/font.css @@ -77,4 +77,4 @@ src: local(''), url('../public/fonts/inconsolata-v30-latin-900.woff2') format('woff2'), url('../public/fonts/inconsolata-v30-latin-900.woff') format('woff'); -} +} \ No newline at end of file diff --git a/pages/playground.tsx b/pages/playground.tsx index 3f8a90b..5707edb 100644 --- a/pages/playground.tsx +++ b/pages/playground.tsx @@ -1,8 +1,9 @@ import { BarGraphHorizontal, BarGraphVertical } from "components/BarGraph"; -import { mockCategoricalData } from "data/mocks"; +import { mockCategoricalData, moreMockCategoricalData } from "data/mocks"; import React from "react"; import { ColorPalette } from "../components/ColorPalette"; +import { WordCloud } from "../components/WordCloud"; import styles from "./playground.module.css"; @@ -48,6 +49,16 @@ export default function Home() { right: 20, }} /> + +

+ {""} +

+ ({ + text: word.key, + value: word.value, + }))} + /> ); }