Compare commits

..

37 Commits

Author SHA1 Message Date
Shahan Nedadahandeh 2b97ef0021
test 8 months ago
Shahan Nedadahandeh 8712511968
test 8 months ago
Shahan Nedadahandeh 3396de157a
Updated docs 8 months ago
Shahan Nedadahandeh 540132bfd4
Reverted changes 8 months ago
Shahan Nedadahandeh 2a03276419
test change 8 months ago
Shahan Nedadahandeh c7e5fb59b2
Reverted debug 8 months ago
Shahan Nedadahandeh 64c9c57ad7
Debug docker 8 months ago
Shahan Nedadahandeh f36ce140ca
Updated docs 8 months ago
Shahan Nedadahandeh 35db02a0c8
Reverted change to playground and fixed duplicate pr builds 8 months ago
Shahan Nedadahandeh 5c02596938
Updated url preview 8 months ago
Shahan Nedadahandeh 3f855bb634
Updated docs 8 months ago
Shahan Nedadahandeh 6ed1ace6cb
Updated docs and secret naming 8 months ago
Shahan Nedadahandeh f5470f5596
fix typo 8 months ago
Shahan Nedadahandeh e706b22586
update auth 8 months ago
Shahan Nedadahandeh 6a0384e073
Updated auth 8 months ago
Shahan Nedadahandeh 0af1cbc614
Added auth 8 months ago
Shahan Nedadahandeh 5fb3b90031
Udate log 8 months ago
Shahan Nedadahandeh a03f4519e9
Added logging 8 months ago
Shahan Nedadahandeh 0d9dc49167
rebuild test 8 months ago
Shahan Nedadahandeh d20c28f35a
rebuild test 8 months ago
Shahan Nedadahandeh 09d71776b4
Changed ref 8 months ago
Shahan Nedadahandeh f73aa6ca95
test 8 months ago
Shahan Nedadahandeh 213c8f5756
Chagned ref 8 months ago
Shahan Nedadahandeh ca221a2282
changed ref 8 months ago
Shahan Nedadahandeh 62db9d8ad0
test 8 months ago
Shahan Nedadahandeh 36961ad6e6
test 8 months ago
Shahan Nedadahandeh 787aecba1c
reran test 8 months ago
Shahan Nedadahandeh 0949f37142
Reran test 8 months ago
Shahan Nedadahandeh e9f35eaca5
fixed typo 8 months ago
Shahan Nedadahandeh ce345c8441
added webhook 8 months ago
Shahan Nedadahandeh 95dcbf4d7c
test 8 months ago
Shahan Nedadahandeh cd6673b1bf
test 8 months ago
Shahan Nedadahandeh 0f830efee0
Test 8 months ago
Shahan Nedadahandeh 6a9c4016c3
test 8 months ago
Shahan Nedadahandeh 30d9eed4a0
updated repo 8 months ago
Shahan Nedadahandeh 6939c73814
changed registry 8 months ago
Shahan Nedadahandeh a8a2283dfd
added .drone.yml with staging deployement 8 months ago
  1. 19
      .drone.yml
  2. 11
      .eslintrc.js
  3. 10
      .vscode/settings.json
  4. 3
      Dockerfile
  5. 65
      components/About.module.css
  6. 67
      components/About.tsx
  7. 30
      components/BarGraph.module.css
  8. 398
      components/BarGraph.tsx
  9. 15
      components/BlankLink.tsx
  10. 110
      components/BottomNav.module.css
  11. 88
      components/BottomNav.tsx
  12. 9
      components/Boxplot.module.css
  13. 316
      components/Boxplot.tsx
  14. 15
      components/CenterWrapper.module.css
  15. 11
      components/CenterWrapper.tsx
  16. 21
      components/ColorPalette.module.css
  17. 24
      components/ColorPalette.tsx
  18. 100
      components/ComponentWrapper.module.css
  19. 53
      components/ComponentWrapper.tsx
  20. 30
      components/GroupedBarGraph.module.css
  21. 623
      components/GroupedBarGraph.tsx
  22. 137
      components/Header.module.css
  23. 70
      components/Header.tsx
  24. 25
      components/LineGraph.module.css
  25. 231
      components/LineGraph.tsx
  26. 21
      components/PieChart.module.css
  27. 176
      components/PieChart.tsx
  28. 125
      components/QuotationCarousel.module.css
  29. 136
      components/QuotationCarousel.tsx
  30. 32
      components/SectionHeader.module.css
  31. 17
      components/SectionHeader.tsx
  32. 11
      components/SectionWrapper.module.css
  33. 15
      components/SectionWrapper.tsx
  34. 87
      components/Sections.module.css
  35. 55
      components/Sections.tsx
  36. 23
      components/StackedBarGraph.module.css
  37. 449
      components/StackedBarGraph.tsx
  38. 92
      components/Timeline.module.css
  39. 127
      components/Timeline.tsx
  40. 19
      components/Title.tsx
  41. 31
      components/TooltipWrapper.module.css
  42. 85
      components/TooltipWrapper.tsx
  43. 9
      components/WordCloud.module.css
  44. 247
      components/WordCloud.tsx
  45. 1574
      data/academics.ts
  46. 75
      data/contributors.ts
  47. 609
      data/coop.ts
  48. 344
      data/demographics.ts
  49. 487
      data/friends.ts
  50. 426
      data/intimacy-and-drugs.ts
  51. 725
      data/lifestyle-and-interests.ts
  52. 480
      data/mental-health.ts
  53. 380
      data/miscellaneous.ts
  54. 206
      data/mocks.ts
  55. 334
      data/personal.ts
  56. 243
      data/post-grad.ts
  57. 70
      data/routes.ts
  58. 27
      deploy.sh
  59. 2
      docs/CI.md
  60. 13
      next.config.js
  61. 9556
      package-lock.json
  62. 11
      package.json
  63. 174
      pages/_app.css
  64. 16
      pages/_app.tsx
  65. 521
      pages/academics.tsx
  66. 95
      pages/contributors.tsx
  67. 315
      pages/coop.tsx
  68. 233
      pages/demographics.tsx
  69. 80
      pages/font.css
  70. 238
      pages/friends.tsx
  71. 151
      pages/frontpage.module.css
  72. 88
      pages/index.tsx
  73. 224
      pages/intimacy-and-drugs.tsx
  74. 261
      pages/lifestyle-and-interests.tsx
  75. 326
      pages/mental-health.tsx
  76. 130
      pages/miscellaneous.tsx
  77. 112
      pages/personal.tsx
  78. 15
      pages/playground.module.css
  79. 299
      pages/playground.tsx
  80. 145
      pages/post-grad.tsx
  81. 34
      pages/samplePage.module.css
  82. 230
      pages/samplePage.tsx
  83. BIN
      public/fonts/inconsolata-v30-latin-200.woff
  84. BIN
      public/fonts/inconsolata-v30-latin-200.woff2
  85. BIN
      public/fonts/inconsolata-v30-latin-300.woff
  86. BIN
      public/fonts/inconsolata-v30-latin-300.woff2
  87. BIN
      public/fonts/inconsolata-v30-latin-500.woff
  88. BIN
      public/fonts/inconsolata-v30-latin-500.woff2
  89. BIN
      public/fonts/inconsolata-v30-latin-600.woff
  90. BIN
      public/fonts/inconsolata-v30-latin-600.woff2
  91. BIN
      public/fonts/inconsolata-v30-latin-700.woff
  92. BIN
      public/fonts/inconsolata-v30-latin-700.woff2
  93. BIN
      public/fonts/inconsolata-v30-latin-800.woff
  94. BIN
      public/fonts/inconsolata-v30-latin-800.woff2
  95. BIN
      public/fonts/inconsolata-v30-latin-900.woff
  96. BIN
      public/fonts/inconsolata-v30-latin-900.woff2
  97. BIN
      public/fonts/inconsolata-v30-latin-regular.woff
  98. BIN
      public/fonts/inconsolata-v30-latin-regular.woff2
  99. BIN
      public/images/frontPageTitle.png
  100. 5
      public/images/menuIcon.svg
  101. Some files were not shown because too many files have changed in this diff Show More

@ -50,7 +50,7 @@ steps:
registry: registry.cloud.csclub.uwaterloo.ca
repo: registry.cloud.csclub.uwaterloo.ca/snedadah/csc-class-profile-staging
- name: deploy-staging
- name: deploy
image: node:16
depends_on:
- publish
@ -60,22 +60,7 @@ steps:
commands:
- echo "The docker build tag is ${DRONE_BRANCH//\//-}"
- 'curl -H "Authorization: $STAGING_AUTH_TOKEN" https://csclub.uwaterloo.ca/~snedadah/webhooks/cscclassprofilestaging?ref=${DRONE_BRANCH//\//-}'
- name: deploy-production
image: node:16
depends_on:
- export
environment:
SSH_KEY:
from_secret: DEPLOYMENT_SSH_KEY
commands:
- 'echo "$SSH_KEY" > /tmp/ssh_key'
- chmod 600 /tmp/ssh_key
- ssh -4 -i /tmp/ssh_key www@caffeine.csclub.uwaterloo.ca -o StrictHostKeyChecking=no '~/bin/classprofile/deploy-2022.sh'
when:
branch:
- main
trigger:
event:
exclude:

@ -15,15 +15,10 @@ module.exports = {
"plugin:react/recommended",
"plugin:prettier/recommended",
],
plugins: ["@typescript-eslint", "react", "react-hooks", "prettier", "unused-imports"],
plugins: ["@typescript-eslint", "react", "react-hooks", "prettier"],
rules: {
"no-unused-vars": "off",
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": [
"error",
{ "vars": "all", "varsIgnorePattern": "^_", "args": "after-used", "argsIgnorePattern": "^_" }
],
"prettier/prettier": ["error", { "endOfLine": "auto" }],
"prettier/prettier": "error",
"import/first": "error",
"import/order": [
"error",

@ -2,13 +2,11 @@
"typescript.tsdk": "node_modules/typescript/lib",
"eslint.format.enable": true,
"eslint.codeActionsOnSave.mode": "all",
"css.lint.validProperties": ["composes"],
"css.format.spaceAroundSelectorSeparator": true,
"[css]": {
"editor.suggest.insertMode": "replace",
"gitlens.codeLens.scopes": ["document"],
"editor.formatOnSave": true,
"editor.defaultFormatter": "vscode.css-language-features",
"editor.defaultFormatter": "vscode.css-language-features"
},
"[javascript]": {
"editor.formatOnSave": false,
@ -43,11 +41,7 @@
"files.eol": "\n",
"[markdown]": {
"editor.wordWrap": "on",
"editor.quickSuggestions": {
"comments": "off",
"strings": "off",
"other": "off"
},
"editor.quickSuggestions": false,
"editor.tabSize": 4
}
}

@ -1,5 +1,2 @@
FROM nginx
COPY ./out /usr/share/nginx/html
COPY staging-nginx.conf /etc/nginx/conf.d
RUN rm /etc/nginx/conf.d/default.conf

@ -1,65 +0,0 @@
.aboutWrapper {
position: relative;
width: 90%;
}
.about {
display: flex;
flex-direction: row;
padding: calc(45rem / 16);
}
.about h1 {
margin: 0;
}
.about h4 {
margin: 0;
}
.about aside {
flex: 1;
margin-right: calc(40rem / 16);
}
.about aside h1 {
color: var(--secondary-accent)
}
.about aside p {
color: var(--primary-accent-lighter)
}
.about article {
flex: 3;
}
.about article p {
color: var(--primary-text);
}
.angle {
position: absolute;
top: 0;
left: 0;
width: calc(70rem / 16);
height: calc(70rem / 16);
}
.anglePath {
stroke: var(--primary-accent-light)
}
.left.angle {
transform: rotate(180deg);
top: unset;
left: unset;
bottom: 0;
right: 0;
}
@media screen and (max-width: 900px) {
.about {
flex-direction: column;
}
}

@ -1,67 +0,0 @@
import React from "react";
import styles from "./About.module.css";
export function About() {
return (
<div className={styles.aboutWrapper} id="about">
<AngleDecoration isBottom={false} />
<section className={styles.about}>
<aside>
<h1>About the Programs</h1>
<p>Getting an overview of the CS programs in UW</p>
</aside>
<article>
<h4>Computer Science</h4>
<p>
Computer Science (CS) is commonly offered by the Faculty of
Mathematics as a co-op program, with students usually attending 8
school and 6 co-op terms in their degree. However, CS is more
flexible than the other two programs because of the ability to
choose from a wider range and number of electives, to take terms
off, and to change their academic schedules to fit their needs.
</p>
<h4>Computing and Financial Management</h4>
<p>
Computing and Financial Management (CFM) combines the core CS
courses with electives from areas such as accounting, economics, and
financial management. This is a joint offering by the Faculty of
Mathematics and the School of Accounting and Finance. The program is
offered only as a co-op program with 6 co-op terms. The program is
offered only as a co-op program with 6 co-op terms.
</p>
<h4>CS/BBA</h4>
<p>
Joint with Wilfrid Laurier University, the Business Administration
and Computer Science Double Degree (CS/BBA) is an exclusive offering
that allows students to gain experience in CS as well as many
subfields of business. There are 10 school terms and either 4 or 5
co-op terms in the usual schedule, so its a longer degree with more
academic terms than CS or CFM.
</p>
</article>
</section>
<AngleDecoration isBottom={true} />
</div>
);
}
interface AngleDecorationProps {
isBottom: boolean;
}
function AngleDecoration({ isBottom }: AngleDecorationProps) {
return (
<svg
viewBox="0 0 100 100"
className={isBottom ? `${styles.left} ${styles.angle}` : styles.angle}
>
<path
d="M100,2 L2,2 L2,100"
fill="none"
strokeWidth="10"
className={styles.anglePath}
/>
</svg>
);
}

@ -1,30 +0,0 @@
.barBackground {
fill: var(--card-background);
transition: fill 0.5s ease-out;
}
.bar {
fill: var(--primary-accent-light);
transition: fill 0.5s ease-out;
}
.barGroup {
transition: fill 0.5s ease-out;
}
.barGroup:hover .bar {
fill: var(--primary-accent);
filter: drop-shadow(0 0 calc(4rem / 16) var(--primary-accent));
}
.tickLabel {
font-family: "Inconsolata", monospace;
font-weight: 800;
fill: var(--label);
}
.axisLabel {
font-family: "Inconsolata", monospace;
font-weight: 800;
fill: var(--label);
}

@ -1,398 +0,0 @@
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 { withTooltip } from "@visx/tooltip";
import React from "react";
import { Color } from "utils/Color";
import { getTooltipPosition, TooltipWrapper } from "./TooltipWrapper";
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 axis tick labels, in pixels. Default is 16px. */
categoryTickLabelSize?: number;
/** Font size of the value axis tick labels, in pixels. Default is 16px. */
valueTickLabelSize?: 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;
/** Minimum width of the graph. */
minWidth?: number;
/** Breakpoint width of graph where alernating labels are displayed. Only for Vertical graphs */
widthAlternatingLabel?: number;
/** Space added to the bottom of the graph to show overflowing labels. Only for Vertical graphs */
alternatingLabelSpace?: number;
/** Default position of labels in x-axis, in px. */
defaultLabelDy?: string;
/** Position of lower labels in x-axis, in px. Only for Vertical graphs */
lowerLabelDy?: string;
}
interface BarGraphData {
category: string;
value: number;
}
const DEFAULT_LABEL_SIZE = 16;
type TooltipData = string;
export const BarGraphHorizontal = withTooltip<BarGraphProps, TooltipData>(
({
width,
height,
margin,
data,
className,
minWidth = 500,
categoryTickLabelSize = DEFAULT_LABEL_SIZE,
valueTickLabelSize = DEFAULT_LABEL_SIZE,
categoryAxisLabel,
categoryAxisLabelSize = DEFAULT_LABEL_SIZE,
categoryAxisLabelOffset = 0,
valueAxisLabel,
valueAxisLabelSize = DEFAULT_LABEL_SIZE,
valueAxisLabelOffset = 0,
defaultLabelDy = "0",
tooltipOpen,
tooltipLeft,
tooltipTop,
tooltipData,
hideTooltip,
showTooltip,
}) => {
width = width < minWidth ? minWidth : width; // Ensuring graph's width >= minWidth
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 (
<div>
<svg className={className} width={width} height={height}>
<Group top={margin.top} left={margin.left}>
<Group>
{data.map((d, idx) => {
const barName = `${getCategory(d)}-${idx}`;
const barWidth = categoryScale.bandwidth();
const backgroundBarWidth = barWidth / (1 - barPadding);
return idx % 2 === 0 ? (
<Bar
className={styles.barBackground}
key={`bar-${barName}-background`}
x={0}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
y={categoryPoint(d)! - (backgroundBarWidth - barWidth) / 2}
width={valueMax}
height={backgroundBarWidth}
/>
) : null;
})}
</Group>
<GridColumns
scale={valueScale}
height={categoryMax}
numTicks={5}
stroke={Color.label}
strokeWidth={4}
strokeDasharray="10"
strokeLinecap="round"
/>
<Group>
{data.map((d, idx) => {
const barName = `${getCategory(d)}-${idx}`;
const barLength = valuePoint(d);
const barWidth = categoryScale.bandwidth();
return (
<Group className={styles.barGroup} key={`bar-${barName}`}>
<Bar
onMouseMove={(e) => {
const tooltipPos = getTooltipPosition(e);
showTooltip({
tooltipData: getValue(d).toString(),
tooltipLeft: tooltipPos.x,
tooltipTop: tooltipPos.y,
});
}}
onMouseOut={hideTooltip}
className={styles.bar}
x={0}
y={categoryPoint(d)}
width={barLength}
height={barWidth}
/>
</Group>
);
})}
</Group>
<AxisLeft
scale={categoryScale}
hideAxisLine
hideTicks
tickLabelProps={() => {
return {
...leftTickLabelProps(),
className: styles.tickLabel,
fontSize: `${categoryTickLabelSize / 16}rem`,
};
}}
label={categoryAxisLabel}
labelClassName={styles.axisLabel}
labelOffset={categoryAxisLabelOffset}
labelProps={{
fontSize: `${categoryAxisLabelSize / 16}rem`,
}}
/>
<AxisBottom
scale={valueScale}
top={categoryMax}
hideAxisLine
hideTicks
numTicks={5}
tickLabelProps={() => {
return {
...bottomTickLabelProps(),
className: styles.tickLabel,
dy: defaultLabelDy,
fontSize: `${valueTickLabelSize / 16}rem`,
};
}}
label={valueAxisLabel}
labelClassName={styles.axisLabel}
labelOffset={valueAxisLabelOffset}
labelProps={{
fontSize: `${valueAxisLabelSize / 16}rem`,
}}
/>
</Group>
</svg>
{tooltipOpen && (
<TooltipWrapper
top={tooltipTop}
left={tooltipLeft}
header={tooltipData as string}
></TooltipWrapper>
)}
</div>
);
}
);
export const BarGraphVertical = withTooltip<BarGraphProps, TooltipData>(
({
width,
height,
margin,
data,
className,
minWidth = 500,
categoryTickLabelSize = DEFAULT_LABEL_SIZE,
valueTickLabelSize = DEFAULT_LABEL_SIZE,
categoryAxisLabel,
categoryAxisLabelSize = DEFAULT_LABEL_SIZE,
categoryAxisLabelOffset = 0,
valueAxisLabel,
valueAxisLabelSize = DEFAULT_LABEL_SIZE,
valueAxisLabelOffset = 0,
widthAlternatingLabel = 600,
alternatingLabelSpace = 80,
defaultLabelDy = `0px`,
lowerLabelDy = `30px`,
tooltipOpen,
tooltipLeft,
tooltipTop,
tooltipData,
hideTooltip,
showTooltip,
}) => {
width = width < minWidth ? minWidth : width; // Ensuring graph's width >= minWidth
const barPadding = 0.4;
const alternatingLabel = width <= widthAlternatingLabel;
const final_margin_bottom = alternatingLabel
? margin.bottom + alternatingLabelSpace
: margin.bottom;
const categoryMax = width - margin.left - margin.right;
const valueMax = height - margin.top - final_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 (
<div>
<svg className={className} width={width} height={height}>
<Group top={margin.top} left={margin.left}>
<Group>
{data.map((d, idx) => {
const barName = `${getCategory(d)}-${idx}`;
const barWidth = categoryScale.bandwidth();
const backgroundBarWidth = barWidth / (1 - barPadding);
return idx % 2 === 0 ? (
<Bar
className={styles.barBackground}
key={`bar-${barName}-background`}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
x={categoryPoint(d)! - (backgroundBarWidth - barWidth) / 2}
y={0}
width={backgroundBarWidth}
height={valueMax}
/>
) : null;
})}
</Group>
<GridRows
scale={valueScale}
width={categoryMax}
numTicks={5}
stroke={Color.label}
strokeWidth={4}
strokeDasharray="10"
strokeLinecap="round"
/>
<Group>
{data.map((d, idx) => {
const barName = `${getCategory(d)}-${idx}`;
const barHeight = valueMax - valuePoint(d);
const barWidth = categoryScale.bandwidth();
return (
<Group className={styles.barGroup} key={`bar-${barName}`}>
<Bar
onMouseMove={(e) => {
const tooltipPos = getTooltipPosition(e);
showTooltip({
tooltipData: getValue(d).toString(),
tooltipLeft: tooltipPos.x,
tooltipTop: tooltipPos.y,
});
}}
onMouseOut={hideTooltip}
className={styles.bar}
x={categoryPoint(d)}
y={valueMax - barHeight}
width={barWidth}
height={barHeight}
/>
</Group>
);
})}
</Group>
<AxisBottom
scale={categoryScale}
top={valueMax}
hideAxisLine
hideTicks
tickLabelProps={(value, index) => {
const alternatingDy =
index % 2 == 0 ? defaultLabelDy : lowerLabelDy;
return {
...bottomTickLabelProps(),
className: styles.tickLabel,
dy: alternatingLabel ? alternatingDy : defaultLabelDy,
fontSize: `${categoryTickLabelSize / 16}rem`,
width: categoryScale.bandwidth(),
verticalAnchor: "start",
};
}}
label={categoryAxisLabel}
labelClassName={styles.axisLabel}
labelOffset={categoryAxisLabelOffset}
labelProps={{
fontSize: `${categoryAxisLabelSize / 16}rem`,
}}
/>
<AxisLeft
scale={valueScale}
hideAxisLine
hideTicks
numTicks={5}
tickLabelProps={() => {
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`,
}}
/>
</Group>
</svg>
{tooltipOpen && (
<TooltipWrapper
top={tooltipTop}
left={tooltipLeft}
header={tooltipData as string}
></TooltipWrapper>
)}
</div>
);
}
);

@ -1,15 +0,0 @@
import React from "react";
export const BodyLink = ({
targetBlank = true,
children,
href,
}: {
href: string;
targetBlank?: boolean;
children: React.ReactNode;
}) => (
<a href={href} target={targetBlank ? "_blank" : ""} rel="noreferrer">
<b>{children}</b>
</a>
);

@ -1,110 +0,0 @@
.container {
display: flex;
flex-flow: center;
align-items: center;
justify-content: space-between;
margin: calc(40rem / 16) 0;
}
.subBox {
display: inline-block;
}
.leftItem {
text-align: right;
}
.item {
color: var(--primary-text);
font-size: calc(28rem / 16);
position: relative;
margin: calc(24rem / 16);
}
.subBox {
display: flex;
flex-direction: row;
align-items: center;
}
.arrow {
width: calc(250rem / 16);
height: calc(20rem / 16);
display: flex;
align-items: center;
justify-content: center;
}
.item:after {
content: '';
position: absolute;
width: 100%;
transform: scaleX(0);
height: calc(2rem / 16);
bottom: 0;
left: 0;
background-color: var(--primary-accent);
cursor: pointer;
transform-origin: bottom right;
transition: transform 0.25s ease-out;
}
.item:hover:after {
transform: scaleX(1);
transform-origin: bottom left;
}
.linePath {
stroke: var(--primary-text);
}
.arrowPath {
fill: var(--primary-text);
}
.right {
transform: rotate(180deg);
}
@media screen and (max-width: 1000px) {
.subBox {
flex-direction: column;
align-items: flex-start;
}
.subBoxLeft {
flex-direction: column-reverse;
align-items: flex-end;
}
.item {
font-size: calc(20rem / 16);
margin: 0;
margin-bottom: calc(10rem / 16);
}
.arrow {
width: calc(200rem / 16);
}
}
@media screen and (max-width: 500px) {
.container {
justify-content: center;
gap: calc(50rem / 16);
}
.arrow {
width: 100%;
}
}
.containerOnlyRightArrow {
justify-content: flex-end;
}
.containerOnlyLeftArrow {
justify-content: flex-start;
}

@ -1,88 +0,0 @@
import Link from "next/link";
import React from "react";
import styles from "./BottomNav.module.css";
interface PagesInfo {
leftPage?: {
url: string;
name: string;
};
rightPage?: {
url: string;
name: string;
};
}
export function BottomNav(props: PagesInfo) {
const onlyRightArrow = props.rightPage && !props.leftPage;
const onlyLeftArrow = !props.rightPage && props.leftPage;
return (
<div
className={`${styles.container}
${onlyRightArrow ? styles.containerOnlyRightArrow : ""}
${onlyLeftArrow ? styles.containerOnlyLeftArrow : ""}`}
>
{props.leftPage ? (
<div className={styles.subBox + " " + styles.subBoxLeft}>
<Link href={props.leftPage.url}>
<a>
<Arrow />
</a>
</Link>
<Link href={props.leftPage.url}>
<a className={styles.item + " " + styles.leftItem}>
{props.leftPage.name}
</a>
</Link>
</div>
) : null}
{props.rightPage ? (
<div className={styles.subBox}>
<Link href={props.rightPage.url}>
<a className={styles.item}>{props.rightPage.name}</a>
</Link>
<Link href={props.rightPage.url}>
<a>
<Arrow isPointingRight />
</a>
</Link>
</div>
) : null}
</div>
);
}
interface ArrowProps {
isPointingRight?: boolean;
}
function Arrow({ isPointingRight }: ArrowProps) {
return (
<svg className={(isPointingRight ? styles.right : "") + " " + styles.arrow}>
<defs>
<marker
id="arrow"
markerWidth="10"
markerHeight="10"
refX="5"
refY="3"
orient="auto"
markerUnits="strokeWidth"
>
<path d="M0,0 L0,6 L9,3 z" className={styles.arrowPath} />
</marker>
</defs>
<line
x1="250"
y1="10"
x2="100"
y2="10" // half of svg height
strokeWidth="3"
markerEnd="url(#arrow)"
className={styles.linePath}
/>
</svg>
);
}

@ -1,9 +0,0 @@
.boxplot {
fill: var(--primary-accent-light);
transition: fill 0.5s ease-out;
}
.boxplot:hover {
fill: var(--primary-accent);
filter: drop-shadow(0 0 calc(4rem / 16) var(--primary-accent));
}

@ -1,316 +0,0 @@
import { AxisLeft, AxisBottom } from "@visx/axis";
import { GridRows, GridColumns } from "@visx/grid";
import { Group } from "@visx/group";
import { Stats } from "@visx/mock-data/lib/generators/genStats";
import { Point } from "@visx/point";
import { scaleBand, scaleLinear } from "@visx/scale";
import { Line } from "@visx/shape";
import { BoxPlot as VisxBoxPlot } from "@visx/stats";
import { withTooltip } from "@visx/tooltip";
import { WithTooltipProvidedProps } from "@visx/tooltip/lib/enhancers/withTooltip";
import React from "react";
import { Color } from "utils/Color";
import { getTooltipPosition, TooltipWrapper } from "./TooltipWrapper";
import styles from "./Boxplot.module.css";
const DEFAULT_LABEL_SIZE = 16;
const TICK_LABEL_FONT_WEIGHT = 800;
interface BoxPlotData {
category: string;
min: number;
median: number;
max: number;
firstQuartile: number;
thirdQuartile: number;
outliers?: number[];
}
type TooltipData = Omit<BoxPlotData, "outliers">;
export type StatsPlotProps = {
data: BoxPlotData[];
/** Width of the entire graph, in pixels, greater than 10. */
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;
left: number;
};
/** Width of the lines in the graph, in px. */
strokeWidth?: number;
/** Length of the dashes and the gaps in the graph, in px. */
strokeDashArray?: string;
/** Number of ticks for the value (y-)axis */
numTicksLeftAxis?: number;
/** Distance between the boxplot and the top of the grid, in px. */
plotTopOffset?: number;
/** Distance between the left axis labels and the start of the lines of the graph, in px. */
valueAxisLeftOffset?: number;
/** Distance between the top and the first label of the y axis, in px. */
valueAxisLabelTopOffset?: number;
/** Distance between the left and the labels of the y axis, in px. */
valueAxisLabelLeftOffset?: number;
/** Distance between the left and the start of the first label of the x axis, in px. */
categoryAxisLabelLeftOffset?: number;
/** Distance between the top and the column lines of the grid of the graph, in px. */
gridColumnTopOffset?: number;
/** Distance between the top of the point in the boxplot and the start of the tooltip box, in px. */
toolTipTopOffset?: number;
/** Distance between the left of the point in the boxplot and the start of the tooltip box, in px. */
toolTipLeftOffset?: number;
/** Font size of the category (x-)axis labels */
categoryAxisLabelSize?: number;
/** Font size of the value (y-)axis labels */
valueAxisLabelSize?: number;
/** Font size of the text in the tool tip box */
toolTipFontSize?: number;
/** Factor multiplied with the compressed width to determine the box width, in px. */
boxPlotWidthFactor?: number;
/** Factor multiplied with the compressed width to determine the distance between boxes, in px. */
boxPlotLeftOffset?: number;
};
export const BoxPlot = withTooltip<StatsPlotProps, TooltipData>(
({
width,
height,
data,
margin,
tooltipOpen,
tooltipLeft,
tooltipTop,
tooltipData,
showTooltip,
hideTooltip,
strokeWidth = 2.5,
strokeDashArray = "10,4",
numTicksLeftAxis = 6,
plotTopOffset = 10,
valueAxisLeftOffset = 40,
gridColumnTopOffset = -20,
valueAxisLabelTopOffset = 5,
valueAxisLabelLeftOffset = 10,
categoryAxisLabelLeftOffset = 30,
toolTipTopOffset = 0,
toolTipLeftOffset = 0,
categoryAxisLabelSize = DEFAULT_LABEL_SIZE,
valueAxisLabelSize = DEFAULT_LABEL_SIZE,
boxPlotWidthFactor = 0.4,
boxPlotLeftOffset = 0.3,
}: StatsPlotProps & WithTooltipProvidedProps<TooltipData>) => {
// bounds
const xMax = width;
const yMax = height - 120;
// formatting data
const plotData: Stats[] = data.map((d) => {
return {
boxPlot: {
...d,
x: d.category,
outliers: [],
},
binData: [],
};
});
// accessors
const getX = (d: Stats) => d.boxPlot.x;
const getMin = (d: Stats) => d.boxPlot.min;
const getMax = (d: Stats) => d.boxPlot.max;
const getMedian = (d: Stats) => d.boxPlot.median;
const getFirstQuartile = (d: Stats) => d.boxPlot.firstQuartile;
const getThirdQuartile = (d: Stats) => d.boxPlot.thirdQuartile;
// scales
const xScale = scaleBand<string>({
range: [18, xMax - 80], // scaling is needed due to the left offset
round: true,
domain: plotData.map(getX),
padding: 0.3,
});
const values = plotData.reduce((allValues, { boxPlot }) => {
allValues.push(boxPlot.min, boxPlot.max);
return allValues;
}, [] as number[]);
const minYValue = Math.min(...values);
const maxYValue = Math.max(...values);
const yScale = scaleLinear<number>({
range: [yMax, 0],
round: true,
domain: [minYValue, maxYValue],
});
const constrainedWidth = Math.min(200, xScale.bandwidth());
const mouseOverEventHandler =
(d: Stats) =>
(e: React.MouseEvent<SVGLineElement | SVGRectElement, MouseEvent>) => {
const pos = getTooltipPosition(e);
showTooltip({
tooltipLeft: pos.x + toolTipLeftOffset,
tooltipTop: pos.y + toolTipTopOffset,
tooltipData: {
...d.boxPlot,
category: getX(d),
},
});
};
return width < 10 ? null : (
<div>
<svg width={width} height={height}>
<Group top={margin.top} left={margin.left}>
<GridRows
top={plotTopOffset}
left={valueAxisLeftOffset}
scale={yScale}
width={xMax}
numTicks={numTicksLeftAxis}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<GridColumns
scale={xScale}
height={yMax + plotTopOffset - gridColumnTopOffset}
top={gridColumnTopOffset}
left={valueAxisLeftOffset}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<Line
fill={Color.tertiaryBackground}
to={new Point({ x: valueAxisLeftOffset, y: gridColumnTopOffset })}
from={
new Point({ x: valueAxisLeftOffset, y: yMax + plotTopOffset })
}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<Line
fill={Color.tertiaryBackground}
to={
new Point({
x: xMax - margin.left - strokeWidth,
y: gridColumnTopOffset,
})
}
from={
new Point({
x: xMax - margin.left - strokeWidth,
y: yMax + plotTopOffset,
})
}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
<AxisBottom
top={yMax + plotTopOffset - gridColumnTopOffset}
left={categoryAxisLabelLeftOffset}
scale={xScale}
hideAxisLine
hideTicks
labelProps={{
fontSize: `${categoryAxisLabelSize / 16}rem`,
}}
tickLabelProps={() => {
return {
fill: Color.label,
fontWeight: TICK_LABEL_FONT_WEIGHT,
};
}}
/>
<AxisLeft
scale={yScale}
top={plotTopOffset + valueAxisLabelTopOffset}
left={valueAxisLabelLeftOffset}
numTicks={numTicksLeftAxis}
hideAxisLine
labelProps={{
fontSize: `${valueAxisLabelSize / 16}rem`,
}}
tickLabelProps={() => {
return {
fill: Color.label,
fontWeight: TICK_LABEL_FONT_WEIGHT,
};
}}
/>
<Group top={plotTopOffset}>
{plotData.map((d: Stats, i) => (
<Group key={i}>
<VisxBoxPlot
className={styles.boxplot}
min={getMin(d)}
max={getMax(d)}
left={
xScale(getX(d))! +
boxPlotLeftOffset * constrainedWidth +
valueAxisLeftOffset
}
firstQuartile={getFirstQuartile(d)}
thirdQuartile={getThirdQuartile(d)}
median={getMedian(d)}
boxWidth={constrainedWidth * boxPlotWidthFactor}
rx={0}
ry={0}
stroke={Color.label}
strokeWidth={strokeWidth}
valueScale={yScale}