diff --git a/components/CenterWrapper.module.css b/components/CenterWrapper.module.css new file mode 100644 index 0000000..6370ab3 --- /dev/null +++ b/components/CenterWrapper.module.css @@ -0,0 +1,7 @@ +.textbox { + width: 80%; + padding: calc(80rem / 16); + background-color: var(--secondary-background); + border-radius: calc(20rem / 16); + margin: 0 auto; +} diff --git a/components/CenterWrapper.tsx b/components/CenterWrapper.tsx new file mode 100644 index 0000000..377bb14 --- /dev/null +++ b/components/CenterWrapper.tsx @@ -0,0 +1,11 @@ +import React, { ReactNode } from "react"; + +import styles from "./CenterWrapper.module.css"; + +export interface TextboxProps { + children: ReactNode; +} + +export function CenterWrapper({ children }: TextboxProps) { + return
{children}
; +} diff --git a/components/PieChart.module.css b/components/PieChart.module.css new file mode 100644 index 0000000..29bb6dc --- /dev/null +++ b/components/PieChart.module.css @@ -0,0 +1,26 @@ +.piePath { + fill: var(--tertiary-background); +} + +.labelPath { + fill-opacity: 0; +} + +.pieText, +.labelText { + fill: var(--label); + font-weight: 800; +} + +.pieText { + display: none; +} + +.group:hover > .piePath { + fill: var(--primary-accent); + filter: drop-shadow(0px 0px calc(6rem / 16) var(--primary-accent)); +} + +.group:hover .pieText { + display: inline; +} \ No newline at end of file diff --git a/components/PieChart.tsx b/components/PieChart.tsx new file mode 100644 index 0000000..536ccfc --- /dev/null +++ b/components/PieChart.tsx @@ -0,0 +1,180 @@ +import { Group } from "@visx/group"; +import Pie, { ProvidedProps } from "@visx/shape/lib/shapes/Pie"; +import { Text } from "@visx/text"; +import React from "react"; + +import styles from "./PieChart.module.css"; + +interface PieChartProps { + data: PieChartData[]; + /** Width of the entire graph, including labels, in pixels. */ + width: number; + /** Width of the outer ring of labels, in pixels. Label text may be cut off if specified value is too small. */ + labelWidth: number; + /** Distance between pie slices, in pixels. */ + padRadius?: number; + /** Distance of gap in center of pie graph, in pixels. */ + innerRadius?: number; + /** Font size of text inside the pie, in pixels. */ + pieTextSize?: number; + /** X-axis offset of the pie text, in pixels. */ + pieTextXOffset?: number; + /** Y-axis offset of the pie text, in pixels. */ + pieTextYOffset?: number; + /** Accessor function to get value to display as pie text from datum. */ + getPieDisplayValueFromDatum?: (datum: PieChartData) => string; + /** Font size of labels outside the pie, in pixels. */ + labelTextSize?: number; + /** X-axis offset of the label text, in pixels. */ + labelTextXOffset?: number; + /** Y-axis offset of the label text, in pixels. */ + labelTextYOffset?: number; + /** Accessor function to get value to display as label text from datum. */ + getLabelDisplayValueFromDatum?: (datum: PieChartData) => string; + className?: string; +} + +interface PieChartData { + category: string; + value: number; +} + +export function PieChart({ + data, + width, + labelWidth, + padRadius = width * 0.35, + innerRadius = width * 0.015, + pieTextSize = 40, + pieTextXOffset = 0, + pieTextYOffset = 10, + getPieDisplayValueFromDatum = (datum: PieChartData) => `${datum.value}%`, + labelTextSize = 40, + labelTextXOffset = 0, + labelTextYOffset = 0, + getLabelDisplayValueFromDatum = (datum: PieChartData) => `${datum.category}`, + className, +}: PieChartProps) { + const pieWidth = width * 0.5 - labelWidth; + return ( + + + d.value} + cornerRadius={10} + padAngle={0.075} + padRadius={padRadius} + innerRadius={innerRadius} + outerRadius={pieWidth} + > + {(pie) => ( + + )} + + d.value} + innerRadius={pieWidth} + outerRadius={width * 0.5} + > + {(pie) => ( + + )} + + + + ); +} + +type PieSliceProps = ProvidedProps & { + pieTextSize: number; + pieTextXOffset: number; + pieTextYOffset: number; + getPieDisplayValueFromDatum: (datum: PieChartData) => string; +}; + +export function PieSlice({ + path, + arcs, + pieTextSize, + pieTextXOffset, + pieTextYOffset, + getPieDisplayValueFromDatum, +}: PieSliceProps) { + return ( + <> + {arcs.map((arc) => { + const [centroidX, centroidY] = path.centroid(arc); + const pathArc = path(arc) as string; + + return ( + + + + {`${getPieDisplayValueFromDatum(arc.data)}`} + + + ); + })} + + ); +} + +type PieSliceLabelProps = ProvidedProps & { + labelTextSize: number; + labelTextXOffset: number; + labelTextYOffset: number; + getLabelDisplayValueFromDatum: (datum: PieChartData) => string; +}; + +export function PieSliceLabel({ + path, + arcs, + labelTextSize, + labelTextXOffset, + labelTextYOffset, + getLabelDisplayValueFromDatum, +}: PieSliceLabelProps) { + return ( + <> + {arcs.map((arc) => { + const [centroidX, centroidY] = path.centroid(arc); + const pathArc = path(arc) as string; + + return ( + + + + {`${getLabelDisplayValueFromDatum(arc.data)}`} + + + ); + })} + + ); +} diff --git a/data/mocks.ts b/data/mocks.ts index 8a35df7..7d0252b 100644 --- a/data/mocks.ts +++ b/data/mocks.ts @@ -36,6 +36,21 @@ export const mockCategoricalData = [ }, ]; +export const mockPieData = [ + { + category: "Nightingale", + value: 42, + }, + { + category: "Quail", + value: 48, + }, + { + category: "Cuckoo", + value: 10, + }, +]; + export const moreMockCategoricalData = [ { key: "Python", value: 29.53 }, { key: "Java", value: 17.06 }, diff --git a/package-lock.json b/package-lock.json index 443e075..761134e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,6 @@ "requires": true, "packages": { "": { - "name": "cs-2022-class-profile", "version": "0.1.0", "dependencies": { "@visx/axis": "^2.10.0", diff --git a/pages/playground.tsx b/pages/playground.tsx index ae3aebf..cde416f 100644 --- a/pages/playground.tsx +++ b/pages/playground.tsx @@ -6,11 +6,14 @@ import { mockBoxPlotData, mockQuoteData, mockQuoteDataLong, + mockPieData, } from "data/mocks"; import React from "react"; +import { PieChart } from "@/components/PieChart"; import { QuotationCarousel } from "@/components/QuotationCarousel"; +import { CenterWrapper } from "../components/CenterWrapper"; import { ColorPalette } from "../components/ColorPalette"; import { LineGraph } from "../components/LineGraph"; import { WordCloud } from "../components/WordCloud"; @@ -22,8 +25,13 @@ export default function Home() {

Playground

Show off your components here!

+

+ {""} +

+
+ +
-

{""}

@@ -39,7 +47,6 @@ export default function Home() { right: 20, }} /> -

{""}

@@ -59,7 +66,6 @@ export default function Home() { right: 20, }} /> -

{""}

@@ -69,6 +75,37 @@ export default function Home() { value: word.value, }))} /> +

+ {""} +

+ +

Preface

+

+ The CS Class Profile consists of data relevant to CS, CFM, and CS/BBA + students. These were combined with the knowledge that students in + these programs tend to have similar experiences, as many of the same + CS required courses are shared. In the standard co-op offering, CS and + CFM take 4 years and 2 semesters to complete, while CS/BBA can take up + to a full 5 years. +

+

+ Computer Science (and the others) is known to be a very prestigious + program, and is very well known in Canada as well as across the world. + For prospective students or anyone who is interested in learning more + about what the students are like, this CS Class Profile will attempt + to answer some of your questions, and you may even learn a thing or + two you didn’t expect! +

+

+ The survey questions were approved by the Institutional Analysis & + Planning, where all University of Waterloo stakeholders that are + interested in conducting a non-academic research survey involving a + large portion of the UWaterloo population are reviewed and approved. + The entirety of the survey creation and data processing was done by + the UW Computer Science Club, so please check us out if you enjoy what + you see! +

+

{""}