Merge branch 'main' of https://git.csclub.uwaterloo.ca/www/www-new into feat/meet-the-team-page

pull/241/head
b38peng 1 year ago
commit 69743462f3
  1. 2
      .gitignore
  2. 5
      components/ArchivePage.module.css
  3. 44
      components/ArchivePage.tsx
  4. 2
      components/Code.module.css
  5. 4
      components/ConnectWithUs.tsx
  6. 92
      components/EventCard.module.css
  7. 52
      components/EventCard.tsx
  8. 2
      components/EventSetting.module.css
  9. 4
      components/Image.module.css
  10. 10
      components/Image.tsx
  11. 1
      components/Link.module.css
  12. 91
      components/OrganizedContent.module.css
  13. 187
      components/OrganizedContent.tsx
  14. 13
      components/OrganizedContent/ReadAll.tsx
  15. 10
      components/OrganizedContent/Section.tsx
  16. 20
      components/ShapesBackground.module.css
  17. 308
      components/ShapesBackground.tsx
  18. 3
      components/Theme.tsx
  19. 31
      components/playground.tsx
  20. 19
      content/events/2021/spring/new-website.md
  21. 6
      content/news/2002/fall/2002-09-16-sjdutoit.md
  22. 6
      content/news/2002/fall/2002-09-16-sjdutoit.news.md
  23. 4
      content/news/2002/fall/2002-09-18-sjdutoit.md
  24. 4
      content/news/2002/fall/2002-09-30-sjdutoit.md
  25. 4
      content/news/2002/fall/2002-10-29-sjdutoit.md
  26. 6
      content/news/2002/winter/2002-02-03-sjdutoit.md
  27. 6
      content/news/2002/winter/2002-02-03-sjdutoit.news.md
  28. 6
      content/news/2002/winter/2002-02-04-sjdutoit.md
  29. 6
      content/news/2002/winter/2002-02-04-sjdutoit.news.md
  30. 4
      content/news/2002/winter/2002-04-15-sjdutoit.md
  31. 4
      content/news/2002/winter/2002-04-22-sjdutoit.md
  32. 6
      content/news/2003/fall/2003-09-17-sfllaw.md
  33. 6
      content/news/2003/fall/2003-09-17-sfllaw.news.md
  34. 6
      content/news/2003/spring/2003-05-14-sjdutoit.md
  35. 6
      content/news/2003/spring/2003-05-14-sjdutoit.news.md
  36. 4
      content/news/2003/spring/2003-06-05-sfllaw.md
  37. 6
      content/news/2003/spring/2003-07-03-sfllaw.md
  38. 6
      content/news/2003/spring/2003-07-03-sfllaw.news.md
  39. 4
      content/news/2003/spring/2003-07-09-ja2morri.md
  40. 4
      content/news/2003/spring/2003-08-06-ja2morri.md
  41. 4
      content/news/2003/spring/2003-08-12-ja2morri.md
  42. 6
      content/news/2003/winter/2003-01-13-sfllaw.md
  43. 6
      content/news/2003/winter/2003-01-13-sfllaw.news.md
  44. 4
      content/news/2004/fall/2004-09-15-mbiggs.md
  45. 6
      content/news/2004/fall/2004-09-27-jeperry.md
  46. 6
      content/news/2004/fall/2004-09-27-jeperry.news.md
  47. 6
      content/news/2004/fall/2004-10-23-jeperry.md
  48. 6
      content/news/2004/fall/2004-10-23-jeperry.news.md
  49. 6
      content/news/2004/spring/2004-05-12-mbiggs.md
  50. 6
      content/news/2004/spring/2004-05-12-mbiggs.news.md
  51. 4
      content/news/2004/spring/2004-05-31-zbnichol.md
  52. 6
      content/news/2004/winter/2004-01-22-sfllaw.md
  53. 6
      content/news/2004/winter/2004-01-22-sfllaw.news.md
  54. 6
      content/news/2004/winter/2004-01-25-sfllaw.md
  55. 6
      content/news/2004/winter/2004-01-25-sfllaw.news.md
  56. 4
      content/news/2004/winter/2004-02-02-sfllaw.md
  57. 4
      content/news/2004/winter/2004-02-05-sfllaw.md
  58. 6
      content/news/2004/winter/2004-03-16-sfllaw.md
  59. 6
      content/news/2004/winter/2004-03-16-sfllaw.news.md
  60. 4
      content/news/2004/winter/2004-03-19-sfllaw.md
  61. 4
      content/news/2004/winter/2004-04-01-sfllaw.md
  62. 6
      content/news/2004/winter/2004-04-02-sfllaw.md
  63. 6
      content/news/2004/winter/2004-04-02-sfllaw.news.md
  64. 6
      content/news/2004/winter/2004-04-07-mbiggs.md
  65. 6
      content/news/2004/winter/2004-04-07-mbiggs.news.md
  66. 4
      content/news/2004/winter/2004-04-16-sfllaw.md
  67. 4
      content/news/2004/winter/2004-04-19-sfllaw.md
  68. 6
      content/news/2006/fall/2006-09-21-woconnor.md
  69. 6
      content/news/2006/fall/2006-09-21-woconnor.news.md
  70. 6
      content/news/2006/spring/2006-05-10-hkarau.md
  71. 6
      content/news/2006/spring/2006-05-10-hkarau.news.md
  72. 6
      content/news/2006/spring/2006-05-19-hkarau.md
  73. 6
      content/news/2006/spring/2006-05-19-hkarau.news.md
  74. 4
      content/news/2006/spring/2006-06-04-hkarau.md
  75. 6
      content/news/2006/winter/2006-01-10-ddenisen.md
  76. 6
      content/news/2006/winter/2006-01-10-ddenisen.news.md
  77. 6
      content/news/2007/fall/2007-09-01-dtbartle.md
  78. 6
      content/news/2007/fall/2007-09-01-dtbartle.news.md
  79. 6
      content/news/2007/fall/2007-09-09-dtbartle.md
  80. 6
      content/news/2007/fall/2007-09-09-dtbartle.news.md
  81. 6
      content/news/2007/spring/2007-05-11-mspang.md
  82. 6
      content/news/2007/spring/2007-05-11-mspang.news.md
  83. 6
      content/news/2007/winter/2007-01-12-daltenty.md
  84. 6
      content/news/2007/winter/2007-01-12-daltenty.news.md
  85. 4
      content/news/2008/fall/2008-09-16-dtbartle.md
  86. 4
      content/news/2008/spring/2008-05-04-dtbartle.md
  87. 6
      content/news/2008/spring/2008-05-16-b4taylor.md
  88. 6
      content/news/2008/spring/2008-05-16-b4taylor.news.md
  89. 4
      content/news/2008/winter/2008-01-08-dtbartle.md
  90. 6
      content/news/2008/winter/2008-01-15-dtbartle.md
  91. 6
      content/news/2008/winter/2008-01-15-dtbartle.news.md
  92. 4
      content/news/2009/fall/2009-10-02-ebering.md
  93. 8
      content/news/2009/fall/2009-10-17-ebering.md
  94. 8
      content/news/2009/fall/2009-10-17-ebering.news.md
  95. 4
      content/news/2009/spring/2009-05-04-kspaans.md
  96. 4
      content/news/2009/spring/2009-05-12-kspaans.md
  97. 4
      content/news/2009/spring/2009-05-14-ebering.md
  98. 4
      content/news/2009/spring/2009-05-26-j3parker.md
  99. 4
      content/news/2009/spring/2009-06-18-mgregson.md
  100. 4
      content/news/2009/winter/2009-01-06-b4taylor.md
  101. Some files were not shown because too many files have changed in this diff Show More

2
.gitignore vendored

@ -22,4 +22,4 @@
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn-error.log*

@ -2,6 +2,11 @@
margin-bottom: calc(40rem / 16);
}
.page > h1 {
border-bottom: calc(1rem / 16) solid var(--primary-heading);
padding-bottom: 1rem;
}
.list {
list-style: none;
padding: 0;

@ -0,0 +1,44 @@
import React from "react";
import { Link } from "@/components/Link";
import {
ShapesConfig,
GetShapesConfig,
defaultGetShapesConfig,
} from "@/components/ShapesBackground";
import { capitalize } from "@/utils";
import styles from "./ArchivePage.module.css";
export interface Props {
type: "news" | "events";
items: {
year: string;
terms: string[];
}[];
}
export function ArchivePage({ items, type }: Props) {
return (
<div className={styles.page}>
<h1>{capitalize(type)} Archive</h1>
<ul className={styles.list}>
{items.map(({ year, terms }) =>
terms.map((term) => (
<li key={`/${type}/${year}/${term}`}>
<Link href={`/${type}/${year}/${term}`}>
{capitalize(term)} {year}
</Link>
</li>
))
)}
</ul>
</div>
);
}
ArchivePage.getShapesConfig = ((width, height) => {
return window.innerWidth <= 768
? ({} as ShapesConfig)
: defaultGetShapesConfig(width, height);
}) as GetShapesConfig;

@ -2,5 +2,5 @@
padding: 0 calc(4rem / 16);
background: var(--code-background);
border-radius: calc(5rem / 16);
word-wrap: break-word;
overflow-wrap: break-word;
}

@ -18,9 +18,9 @@ export function ConnectWithUs() {
<SocialLinks color="gradient" size="big" />
</div>
{/* TODO: fix feedback form link */}
<p>
Send feedback through our <Link href="#">Feedback Form</Link>
Send feedback through our{" "}
<Link href="https://bit.ly/uwcsclub-feedback-form">Feedback Form</Link>
</p>
</section>
);

@ -2,7 +2,7 @@
display: flex;
flex-direction: row;
box-sizing: border-box;
padding: calc(24rem / 16);
padding: calc(20rem / 16) 0;
}
.card aside {
@ -12,16 +12,15 @@
.card aside img {
width: 100%;
margin-bottom: 1rem;
}
.spacer {
margin-top: calc(76rem / 16);
.registerButton {
margin-top: 1rem;
font-weight: bold;
}
.registerButton {
.registerButtonWithPoster {
display: block;
font-weight: bold;
}
.content > h1 {
@ -45,3 +44,84 @@
font-size: 1rem;
margin-bottom: calc(14rem / 16);
}
.mobileLearnMore {
display: none;
}
.children h1,
.children h2,
.children h3,
.children h4 {
font-size: 1rem;
margin-top: calc(24rem / 16);
margin-bottom: calc(8rem / 16);
}
@media only screen and (max-width: calc(768rem / 16)) {
.card {
flex-direction: column;
}
.card aside {
margin: 0 auto;
max-width: calc(300rem / 16);
}
.card aside img {
box-sizing: border-box;
border: calc(1rem / 16) solid var(--text);
}
.content {
margin-top: 1rem;
}
.content {
text-align: center;
}
.content > h1,
.content > h2 {
font-size: 1rem;
line-height: calc(30 / 16);
}
.content > h2 {
margin-bottom: 0;
}
.mobileShowDescriptionContent {
text-align: unset;
}
.mobileShowDescriptionContent > h1 {
font-size: calc(24rem / 16);
margin-bottom: calc(8rem / 16);
}
.registerButton {
display: block;
}
.mobileLearnMore {
display: unset;
}
.mobileShowDescriptionContent .mobileLearnMore {
display: none;
}
.children {
display: none;
}
.children ul,
.children ol {
padding-left: 1rem;
}
.mobileShowDescriptionContent .children {
display: unset;
}
}

@ -3,10 +3,11 @@ import React, { ReactNode } from "react";
import { Button } from "./Button";
import { EventSetting } from "./EventSetting";
import { Image } from "./Image";
import { Link } from "./Link";
import styles from "./EventCard.module.css";
interface EventCardProps {
interface BaseProps {
name: string;
short: string;
date: Date;
@ -17,7 +18,12 @@ interface EventCardProps {
children: ReactNode;
}
type EventCardProps =
| (BaseProps & { showDescription?: false; link: string })
| (BaseProps & { showDescription: true; link?: string });
export function EventCard({
link,
name,
date,
online,
@ -25,13 +31,42 @@ export function EventCard({
poster,
registerLink,
children,
showDescription = false,
}: EventCardProps) {
return (
<article className={styles.card}>
<aside>
{poster && <Image alt={name} src={poster} />}
{!poster && <div className={styles.spacer}></div>}
{registerLink && (
{poster && (
<aside>
<Image alt={name} src={poster} />
{registerLink && (
<Button
isLink={true}
href={registerLink}
size="small"
className={`${styles.registerButton} ${styles.registerButtonWithPoster}`}
>
Register
</Button>
)}
</aside>
)}
<section
className={[
styles.content,
showDescription ? styles.mobileShowDescriptionContent : "",
].join(" ")}
>
<h1>{name}</h1>
<h2>
<EventSetting date={date} online={online} location={location} />
</h2>
{!showDescription && link && (
<Link href={link}>
<span className={styles.mobileLearnMore}>Learn more</span>
</Link>
)}
<div className={styles.children}>{children}</div>
{!poster && registerLink && (
<Button
isLink={true}
href={registerLink}
@ -41,13 +76,6 @@ export function EventCard({
Register
</Button>
)}
</aside>
<section className={styles.content}>
<h1>{name}</h1>
<h2>
<EventSetting date={date} online={online} location={location} />
</h2>
<div>{children}</div>
</section>
</article>
);

@ -2,7 +2,7 @@
.separator {
display: none;
}
.setting {
display: block;
}

@ -0,0 +1,4 @@
.image {
/* So that image doesn't overflow on mobile screens */
max-width: 100%;
}

@ -1,8 +1,14 @@
import React, { ImgHTMLAttributes } from "react";
import styles from "./Image.module.css";
export function Image(props: ImgHTMLAttributes<HTMLImageElement>) {
const classes = props.className
? [props.className, styles.image]
: [styles.image];
if (props.src?.startsWith("http://") || props.src?.startsWith("https://")) {
return <img {...props} />;
return <img {...props} className={classes.join(" ")} />;
}
const { src: relativeSrc = "" } = props;
@ -16,5 +22,5 @@ export function Image(props: ImgHTMLAttributes<HTMLImageElement>) {
absoluteSrc += "/" + relativeSrc;
}
return <img {...props} src={absoluteSrc} />;
return <img {...props} src={absoluteSrc} className={classes.join(" ")} />;
}

@ -3,6 +3,7 @@
transition-duration: 0.3s;
text-decoration: none;
white-space: normal;
overflow-wrap: anywhere;
}
.link:hover {

@ -49,7 +49,7 @@
}
.selected {
background-color: var(--primary-accent-dim);
background-color: var(--primary-accent-lightest);
color: var(--primary-accent);
font-weight: 700;
}
@ -124,7 +124,48 @@
text-decoration: none;
}
.burger {
display: none;
}
.mobileNavTitle {
display: none;
}
@media only screen and (max-width: calc(768rem / 16)) {
.burger {
display: flex;
position: fixed;
border: none;
bottom: calc(32rem / 16);
left: calc(16rem / 16);
width: calc(62rem / 16);
height: calc(57rem / 16);
cursor: pointer;
z-index: 9;
background: var(--primary-accent-light);
border-radius: calc(5rem / 16);
transition: transform 0.3s ease-in-out;
transform: translateY(calc(94rem / 16));
padding: calc(11rem / 16) calc(9rem / 16);
}
.burgerVisible {
transform: translateY(0);
}
.burger > svg {
width: 100%;
height: 100%;
stroke: var(--primary-accent);
}
.navItem {
width: auto;
padding: 0 calc(16rem / 16);
}
.content h1 {
font-size: calc(18rem / 16);
}
@ -138,6 +179,52 @@
}
.nav {
display: none;
position: fixed;
top: 0;
left: 0;
overflow-y: auto;
z-index: 30;
margin: 0;
background: var(--primary-accent-lighter);
width: calc(288rem / 16);
transform: translateX(-100vw);
transition: 0.5s;
}
.navMobileBackground {
position: fixed;
visibility: hidden;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 20;
background-color: var(--navbar-page-overlay);
opacity: 0;
transition: 0.5s;
}
.mobileNavOpen {
transform: translateX(0);
}
.mobileNavTitle {
display: flex;
font-size: calc(24rem / 16);
font-weight: 700;
margin: calc(14rem / 16);
margin-top: calc(39rem / 16);
}
.show.navMobileBackground {
visibility: visible;
opacity: 100%;
}
}

@ -1,5 +1,12 @@
import NextLink from "next/link";
import React, { ReactNode, ComponentType } from "react";
import React, {
ReactNode,
ComponentType,
useState,
useRef,
useEffect,
useCallback,
} from "react";
import styles from "./OrganizedContent.module.css";
@ -17,6 +24,7 @@ interface Props {
sections: Section[];
id: string;
children: ReactNode;
pageTitle: string;
link: Link;
}
@ -24,8 +32,10 @@ export function OrganizedContent({
sections,
id,
children,
pageTitle,
link: Link,
}: Props) {
const [mobileNavOpen, setMobileNavOpen] = useState(false);
const currentIndex = sections.findIndex(
({ id: sectionId }) => sectionId === id
);
@ -36,10 +46,34 @@ export function OrganizedContent({
const section = sections[currentIndex];
const isReadAll = section.id === READ_ALL_ID;
const ref = useRef<HTMLDivElement>(null);
const isVisible = useOnScreen(ref.current);
const burgerVisible = useBurger(isVisible);
useEffect(() => {
mobileNavOpen
? (document.body.style.overflow = "hidden")
: (document.body.style.overflow = "visible");
}, [mobileNavOpen]);
return (
<div className={styles.wrapper}>
<Nav sections={sections} currentIndex={currentIndex} link={Link} />
<div className={styles.wrapper} ref={ref}>
<div
className={
mobileNavOpen
? `${styles.navMobileBackground} ${styles.show}`
: styles.navMobileBackground
}
onClick={() => setMobileNavOpen(false)}
/>
<Nav
sections={sections}
currentIndex={currentIndex}
link={Link}
pageTitle={pageTitle}
mobileNavOpen={mobileNavOpen}
setMobileNavOpen={setMobileNavOpen}
/>
<div className={styles.content}>
{isReadAll ? (
children
@ -57,6 +91,14 @@ export function OrganizedContent({
</>
)}
</div>
<button
className={`${styles.burger} ${
burgerVisible ? styles.burgerVisible : ""
}`}
onClick={() => setMobileNavOpen(!mobileNavOpen)}
>
<Burger />
</button>
</div>
);
}
@ -65,11 +107,29 @@ interface NavProps {
sections: Section[];
currentIndex: number;
link: Link;
pageTitle: string;
mobileNavOpen: boolean;
setMobileNavOpen: (mobileNavOpen: boolean) => void;
}
function Nav({ sections, currentIndex, link: Link }: NavProps) {
function Nav({
sections,
currentIndex,
link: Link,
pageTitle,
mobileNavOpen,
setMobileNavOpen,
}: NavProps) {
const navStyles = mobileNavOpen
? [styles.nav, styles.mobileNavOpen]
: [styles.nav];
return (
<nav className={styles.nav}>
<nav
className={navStyles.join(" ")}
onClick={(event) => event.stopPropagation()}
>
<h1 className={styles.mobileNavTitle}>{pageTitle}</h1>
{sections.map((section, index) => {
const classNames = [styles.navItem];
@ -82,14 +142,17 @@ function Nav({ sections, currentIndex, link: Link }: NavProps) {
}
return (
<Link
className={classNames.join(" ")}
id={section.id}
<div
onClick={() => {
setMobileNavOpen(false);
}}
key={section.id}
>
<span className={styles.marker} />
<div>{section.title}</div>
</Link>
<Link className={classNames.join(" ")} id={section.id}>
<span className={styles.marker} />
<div>{section.title}</div>
</Link>
</div>
);
})}
</nav>
@ -137,6 +200,20 @@ function Footer({ sections, currentIndex, link: Link }: FooterProps) {
);
}
function useDebounce(func: () => void, delay = 300) {
const timerRef = useRef<number | undefined>(undefined);
return useCallback(() => {
if (timerRef.current != null) {
return;
}
timerRef.current = window.setTimeout(() => {
func();
timerRef.current = undefined;
}, delay);
}, [func, delay]);
}
export interface SectionWithContent {
section: Section;
Content: ComponentType;
@ -216,3 +293,91 @@ function Arrow({ direction }: { direction: "left" | "right" }) {
</svg>
);
}
function useOnScreen(element: HTMLDivElement | null) {
const [isIntersecting, setIntersecting] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(([entry]) =>
setIntersecting(entry.isIntersecting)
);
if (element) {
observer.observe(element);
}
// Remove the observer as soon as the component is unmounted
return () => {
observer.disconnect();
};
}, [element]);
return isIntersecting;
}
function useBurger(componentIsVisible: boolean): boolean {
const [prevScrollPos, setPrevScrollPos] = useState(0);
const [burgerVisible, setBurgerVisible] = useState(true);
const handleScroll = useDebounce(() => {
// find current scroll position
const currentScrollPos = window.pageYOffset;
setBurgerVisible(
componentIsVisible &&
((prevScrollPos > currentScrollPos &&
prevScrollPos - currentScrollPos > 70) ||
currentScrollPos < 10)
);
// set state to new scroll position
setPrevScrollPos(currentScrollPos);
});
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, [handleScroll]);
return burgerVisible;
}
// Inlining this svg because we want to fill in colors using css variables
function Burger() {
return (
<svg
width="30"
height="23"
viewBox="0 0 30 23"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<line
x1="28"
y1="2"
x2="2"
y2="2"
strokeWidth="4"
strokeLinecap="round"
strokeLinejoin="round"
/>
<line
x1="28"
y1="11.375"
x2="2"
y2="11.375"
strokeWidth="4"
strokeLinecap="round"
strokeLinejoin="round"
/>
<line
x1="28"
y1="20.75"
x2="2"
y2="20.75"
strokeWidth="4"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}

@ -8,6 +8,8 @@ import {
OrganizedContent,
} from "@/components/OrganizedContent";
import { GetShapesConfig } from "../ShapesBackground";
import { Header } from "./Header";
export interface SerializedSection {
@ -26,6 +28,7 @@ export interface Options {
pagePath: string;
title: string;
image: string;
getShapesConfig?: GetShapesConfig;
imagePosition?: "left" | "right";
link?: ComponentType<LinkProps>;
description?: string;
@ -35,13 +38,14 @@ export function createReadAllPage({
title,
image,
pagePath,
getShapesConfig,
link,
description,
imagePosition,
}: Options) {
const Link = link ?? createLink(pagePath);
return function Page({ sections }: Props) {
function Page({ sections }: Props) {
const readAllSection = createReadAllSection(
sections.map(({ section, content }) => ({
section,
@ -65,11 +69,16 @@ export function createReadAllPage({
readAllSection.section,
...sections.map(({ section }) => section),
]}
pageTitle={title}
link={Link}
>
<readAllSection.Content />
</OrganizedContent>
</Header>
);
};
}
Page.getShapesConfig = getShapesConfig;
return Page;
}

@ -21,13 +21,14 @@ export function createSectionPage({
title,
image,
pagePath,
getShapesConfig,
link,
description,
imagePosition,
}: Options) {
const Link = link ?? createLink(pagePath);
return function Page(this: void, { content, sections, current }: Props) {
function Page(this: void, { content, sections, current }: Props) {
return (
<Header
title={title}
@ -38,11 +39,16 @@ export function createSectionPage({
<OrganizedContent
sections={sections}
id={sections[current].id}
pageTitle={title}
link={Link}
>
<MDXRemote {...content} />
</OrganizedContent>
</Header>
);
};
}
Page.getShapesConfig = getShapesConfig;
return Page;
}

@ -0,0 +1,20 @@
.shapesContainer {
position: absolute;
overflow: hidden;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -10;
}
.shape {
--blue: invert(53%) sepia(80%) saturate(4689%) hue-rotate(189deg)
brightness(92%) contrast(93%);
--teal: invert(76%) sepia(39%) saturate(578%) hue-rotate(110deg)
brightness(91%) contrast(88%);
position: absolute;
filter: var(--blue);
opacity: 20%;
}

@ -0,0 +1,308 @@
import { useWindowDimension } from "hooks/useWindowDimension";
import { useRouter } from "next/router";
import React, { CSSProperties, useEffect, useRef, useState } from "react";
import { Image } from "./Image";
import styles from "./ShapesBackground.module.css";
const MOBILE_WIDTH = 768;
interface Props {
getConfig?: GetShapesConfig;
}
export function ShapesBackground({ getConfig }: Props) {
const [config, setConfig] = useState<ShapesConfig>({});
const [prevWidth, setPrevWidth] = useState<number>(-1);
const [prevRoute, setPrevRoute] = useState<string>("");
const { width, height } = useWindowDimension();
const shapesContainerRef = useRef<HTMLDivElement>(null);
const router = useRouter();
useEffect(() => {
const containerWidth = shapesContainerRef.current?.offsetWidth;
const containerHeight = shapesContainerRef.current?.offsetHeight;
// In general, rerun getShapesConfig() if the screen size changes from desktop to mobile (or vice versa)
if (
containerWidth == null ||
containerHeight == null ||
!(
router.asPath === "/" ||
router.asPath !== prevRoute ||
prevWidth < 0 ||
(prevWidth <= MOBILE_WIDTH && MOBILE_WIDTH < width) ||
(prevWidth > MOBILE_WIDTH && MOBILE_WIDTH >= width)
)
) {
return;
}
setPrevWidth(width);
setPrevRoute(router.asPath);
setConfig(getConfig?.(containerWidth, containerHeight) ?? {});
}, [getConfig, width, height, prevWidth, prevRoute, router.asPath]);
return (
<div className={styles.shapesContainer} ref={shapesContainerRef}>
{Object.entries(config).map(([type, instances]) =>
instances.map((attributes, idx) => (
<Shape
key={idx.toString() + type}
type={type as ShapeType}
style={attributes}
/>
))
)}
</div>
);
}
function Shape(props: { type: ShapeType; style: CSSProperties }) {
return (
<Image
src={`/images/shapes/${props.type}.svg`}
className={styles.shape}
style={props.style}
/>
);
}
export type ShapeType =
| "asterisk"
| "circle"
| "cross"
| "dots"
| "hash"
| "plus"
| "ring"
| "triangle"
| "triangleBig"
| "waves"
| "wavesBig";
export type ShapesConfig = {
[key in ShapeType]?: CSSProperties[];
};
export type GetShapesConfig = (width: number, height: number) => ShapesConfig;
type ShapeSize = {
[key in ShapeType]: {
size: "big" | "small";
width: number;
height: number;
// 0 <= minAngle, maxAngle <= 180
minAngle?: number;
maxAngle?: number;
};
};
const shapeTypes: ShapeType[] = [
"asterisk",
"circle",
"cross",
"dots",
"hash",
"plus",
"ring",
"triangle",
"triangleBig",
"waves",
"wavesBig",
];
const shapesSizes: ShapeSize = {
asterisk: {
size: "big",
width: 168,
height: 168,
},
circle: {
size: "big",
width: 132,
height: 132,
},
cross: {
size: "big",
width: 150,
height: 150,
},
dots: {
size: "big",
width: 232,
height: 250,
},
hash: {
size: "small",
width: 60,
height: 60,
},
plus: {
size: "small",
width: 48,
height: 48,
},
ring: {
size: "small",
width: 70,
height: 70,
},
triangle: {
size: "small",
width: 68,
height: 68,
minAngle: 15,
maxAngle: 26,
},
triangleBig: {
size: "big",
width: 138,
height: 138,
minAngle: 15,
maxAngle: 26,
},
waves: {
size: "small",
width: 102,
height: 50,
},
wavesBig: {
size: "big",
width: 252,
height: 132,
},
};
const shapesBySize = {
big: shapeTypes.filter((shape) => shapesSizes[shape]["size"] == "big"),
small: shapeTypes.filter((shape) => shapesSizes[shape]["size"] == "small"),
};
// Used to generate random shapes in the margins
export const defaultGetShapesConfig = ((pageWidth, pageHeight) => {
if (window.innerWidth <= MOBILE_WIDTH) {
return mobileShapesConfig;
}
const defaultConfig: ShapesConfig = {};
const gap = 20;
const minBoxWidth = 150;
const boxWidth = Math.max(minBoxWidth, (pageWidth - 800 - 2 * gap) / 2);
const boxHeight = 400;
const shapeGenerationProbability = 0.85;
for (let y = 0; y + boxHeight <= pageHeight; y += gap + boxHeight) {
for (let x = 0; x <= 1; ++x) {
if (Math.random() > shapeGenerationProbability) {
continue;
}
const size =
boxWidth > minBoxWidth && (y == 0 || y + 2 * boxHeight > pageHeight)
? "big"
: "small";
const shape: ShapeType = getRandomShape(size);
const verticalOffset = getVerticalOffset(boxHeight, shape);
const horizontalOffset = getHorizontalOffset(boxWidth - 2 * gap, shape);
const shapeWidth = shapesSizes[shape]["width"];
const shapeHeight = shapesSizes[shape]["height"];
const angle = getRandomAngle(shape);
const colour = getRandomColour();
const opacity = getRandomOpacity(colour);
defaultConfig[shape] ??= [];
defaultConfig[shape]?.push({
top: `${((y + verticalOffset) / 16).toFixed(5)}rem`,
left:
x == 0
? `${(((horizontalOffset + gap) / window.innerWidth) * 100).toFixed(
5
)}vw`
: "unset",
right:
x == 1
? `${(((horizontalOffset + gap) / window.innerWidth) * 100).toFixed(
5
)}vw`
: "unset",
width: `${(shapeWidth / 16).toFixed(5)}rem`,
height: `${(shapeHeight / 16).toFixed(5)}rem`,
transform: `rotate(${angle}deg)`,
filter: `var(--${colour})`,
opacity: `${opacity}%`,
});
}
}
return defaultConfig;
}) as GetShapesConfig;
function getRandomShape(size: "big" | "small"): ShapeType {
const idx = Math.floor(Math.random() * shapesBySize[size].length);
return shapesBySize[size][idx];
}
function getVerticalOffset(boxHeight: number, shape: ShapeType): number {
const padding = shapesSizes[shape]["height"];
return Math.floor(Math.random() * (boxHeight - padding));
}
function getHorizontalOffset(boxWidth: number, shape: ShapeType): number {
const padding = shapesSizes[shape]["width"];
return shapesSizes[shape]["size"] == "big"
? Math.floor(Math.random() * (boxWidth - padding / 2) - padding / 2)
: Math.floor(Math.random() * (boxWidth - padding));
}
function getRandomAngle(shape: ShapeType): number {
const minAngle = shapesSizes[shape]["minAngle"] ?? 0;
const maxAngle = shapesSizes[shape]["maxAngle"] ?? 0;
const direction = Math.random() < 0.5 ? 1 : -1;
return (
(minAngle + Math.floor(Math.random() * (maxAngle - minAngle + 1))) *
direction
);
}
function getRandomColour(): "blue" | "teal" {
return Math.random() < 0.7 ? "blue" : "teal";
}
function getRandomOpacity(colour: "blue" | "teal"): number {
if (colour === "blue") {
return Math.random() < 0.8 ? 20 : 25;
} else {
return Math.random() < 0.8 ? 25 : 30;
}
}
// Used for most mobile pages
export const mobileShapesConfig = {
dots: [
{
top: "calc(-6rem / 16)",
left: "calc(-95rem / 16)",
width: "calc(166rem / 16)",
height: "calc(150rem / 16)",
},
],
hash: [
{
top: "calc(88rem / 16)",
right: "15vw",
width: "calc(40rem / 16)",
height: "calc(40rem / 16)",
},
],
triangle: [
{
top: "calc(20rem / 16)",
right: "1vw",
width: "calc(45rem / 16)",
height: "calc(45rem / 16)",
transform: "rotate(17deg)",
},
],
};

@ -22,7 +22,8 @@ export const PALETTE_NAMES = [
"--primary-accent",
"--primary-accent-soft",
"--primary-accent-light",
"--primary-accent-dim",
"--primary-accent-lighter",
"--primary-accent-lightest",
"--secondary-accent",
"--secondary-accent-light",

@ -146,13 +146,23 @@ export function EventDescriptionCardDemo() {
export function EventCardDemo() {
return (
<>
{events.map(({ Content, metadata }) => (
<EventCard
{...metadata}
key={metadata.name + metadata.date.toDateString()}
>
<Content />
</EventCard>
{events.map(({ Content, metadata }, idx) => (
<>
<EventCard
{...metadata}
key={metadata.name + metadata.date.toDateString() + "1"}
showDescription
>
<Content />
</EventCard>
<EventCard
{...metadata}
key={metadata.name + metadata.date.toDateString() + "2"}
link="#"
>
<Content />
</EventCard>
</>
))}
</>
);
@ -238,7 +248,12 @@ export function OrganizedContentDemo() {
)!.Content;
return (
<OrganizedContent sections={sections} id={id} link={FakeLink}>
<OrganizedContent
sections={sections}
id={id}
link={FakeLink}
pageTitle="Playground"
>
<Content />
</OrganizedContent>
);

@ -0,0 +1,19 @@
---
name: 'New Website'
short: 'Check out our new website'
date: 'Jun 15 2022 00:00:00 GMT-0400 (Eastern Daylight Time)'
online: true
location: 'Zoom'
poster: 'images/events/2021/spring/new-website.png'
---
Hey everyone! 👋 Today we are finally releasing the new website internally. Feedback from yall would be really appreciated to iron out bugs before we release it to the public on Wednesday, September 1!! 🥳
Link to the beta version: https://csclub.uwaterloo.ca/~a3thakra/csc/main/
## How to give feedback
- Submit this google form: https://forms.gle/YD6MD726KjKwCd1v6 OR
- Create an issue in our repository https://git.csclub.uwaterloo.ca/www/www-new (use your CSC credentials to sign in)
_**(This is a dummy event, and will be removed before publishing the website)**_

@ -0,0 +1,6 @@
---
author: 'sjdutoit'
date: 'Mon Sep 16 2002 01:00:00 GMT-0400 (Eastern Daylight Time)'
---
The Fall elections have occured and the [results](</about/exec>) are in.

@ -1,6 +0,0 @@
---
author: "sjdutoit"
date: "2002-09-16"
---
The Fall elections have occured and the [results](</about/exec>) are in.

@ -1,6 +1,6 @@
---
author: "sjdutoit"
date: "2002-09-18"
author: 'sjdutoit'
date: 'Wed Sep 18 2002 01:00:00 GMT-0400 (Eastern Daylight Time)'
---
We've changed to the new site! Please send your comments to the [webmaster](<mailto:webmaster@csclub.uwaterloo.ca>). The [old site](</old/>) is still available. A few things may not be working quite right yet, but I'm working on it.

@ -1,6 +1,6 @@
---
author: "sjdutoit"
date: "2002-09-30"
author: 'sjdutoit'
date: 'Mon Sep 30 2002 01:00:00 GMT-0400 (Eastern Daylight Time)'
---
The business meeting of 2002-09-30 was concluded and the [constitutional change](</about/constitution-change-20020920>) was approved with a 14:2 majority (and one spoiled ballot). See the new [constitution](</about/constitution>).

@ -1,6 +1,6 @@
---
author: "sjdutoit"
date: "2002-10-29"
author: 'sjdutoit'
date: 'Tue Oct 29 2002 00:00:00 GMT-0500 (Eastern Standard Time)'
---
Due to lack of time for preparation, the Romp Through The Linux Kernel talks were cancelled. Sorry for the inconvenience. Hopefully these talks will happen next term.

@ -0,0 +1,6 @@
---
author: 'sjdutoit'
date: 'Sun Feb 03 2002 00:00:00 GMT-0500 (Eastern Standard Time)'
---
XML goodness.

@ -1,6 +0,0 @@
---
author: "sjdutoit"
date: "2002-02-03"
---
XML goodness.

@ -0,0 +1,6 @@
---
author: 'sjdutoit'
date: 'Mon Feb 04 2002 00:00:00 GMT-0500 (Eastern Standard Time)'
---
About/Memberlist stub up. Made the CSC logo gold. Isn't it nifty?

@ -1,6 +0,0 @@
---
author: "sjdutoit"
date: "2002-02-04"
---
About/Memberlist stub up. Made the CSC logo gold. Isn't it nifty?