Merge main

pull/49/head
Jared He 6 months ago
commit 34e05db3fc
  1. 75
      components/Timeline.module.css
  2. 142
      components/Timeline.tsx
  3. 27
      data/mocks.ts
  4. 8
      pages/playground.tsx

@ -0,0 +1,75 @@
.wrapper {
position: relative;
}
.line {
position: absolute;
height: 100%;
border-radius: calc(10rem / 16);
background-color: var(--secondary-accent);
z-index: -1;
}
.timelineSections {
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
gap: calc(20rem / 16);
}
.timelineSection {
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
}
.time {
margin: 0;
text-align: right;
font-size: calc(30rem / 16);
font-weight: 700;
color: var(--secondary-accent);
word-wrap: break-word;
}
.circle {
background-color: var(--secondary-accent);
box-shadow: calc(0rem / 16) calc(0rem / 16) calc(30rem / 16) var(--secondary-accent);
display: flex;
justify-content: center;
align-items: center;
}
.innerCircle {
background-color: var(--label);
display: none;
}
.text {
height: fit-content;
margin: 0;
padding: calc(15rem / 16);
border-radius: calc(10rem / 16);
font-size: calc(20rem / 16);
font-weight: 700;
color: var(--label);
border: calc(2rem / 16) solid var(--card-background);
background-color: var(--card-background);
word-wrap: break-word;
box-sizing: border-box;
}
.timelineSection:hover .time {
color: var(--secondary-accent-light);
}
.timelineSection:hover .innerCircle {
display: inline;
}
.timelineSection:hover .text {
border: calc(2rem / 16) solid var(--secondary-accent-light);
box-shadow: calc(0rem / 16) calc(0rem / 16) calc(20rem / 16) var(--secondary-accent);
}

@ -0,0 +1,142 @@
import React from "react";
import styles from "./Timeline.module.css";
interface TimelineData {
time: string;
text: string;
}
interface TimelineProps {
data: TimelineData[];
/** Whether the time is transformed to uppercase. */
isTimeUppercase?: boolean;
/** Width of the middle timeline line, in pixels */
lineWidth?: number;
/** Width of the outer circles on the timeline, in pixels. */
outerCircleWidth?: number;
/** Width of the inner circles on the timeline, in pixels. */
innerCircleWidth?: number;
/** Width of time label, in pixels. */
timeWidth?: number;
/** Width of text label, in pixels. */
textWidth?: number;
/** Distance between the time label AND the text label to middle line, in pixels. */
gap?: number;
className?: string;
}
export function Timeline({
data,
isTimeUppercase = true,
lineWidth = 5,
outerCircleWidth = 30,
innerCircleWidth = 15,
timeWidth = 200,
textWidth = 300,
gap = 50,
className,
}: TimelineProps) {
const largerMiddleElement =
outerCircleWidth > lineWidth ? outerCircleWidth : lineWidth;
const width = timeWidth + gap + largerMiddleElement + gap + textWidth;
if (innerCircleWidth > outerCircleWidth) {
throw new Error(
`<Timeline /> - innerCircleWidth (${innerCircleWidth}) is larger than outerCircleWidth (${outerCircleWidth})`
);
}
return (
<div
className={
className ? `${className} ${styles.wrapper}` : `${styles.wrapper}`
}
style={{ width: width }}
>
<div
className={styles.line}
style={{
width: lineWidth,
left: width / 2 - lineWidth / 2,
}}
/>
<div className={styles.timelineSections}>
{data.map((datum) => (
<TimelineSection
key={datum.time}
datum={datum}
width={width}
isTimeUppercase={isTimeUppercase}
outerCircleWidth={outerCircleWidth}
innerCircleWidth={innerCircleWidth}
timeWidth={timeWidth}
textWidth={textWidth}
gap={gap}
/>
))}
</div>
</div>
);
}
interface TimelineSectionProps {
datum: TimelineData;
width: number;
isTimeUppercase: boolean;
outerCircleWidth: number;
innerCircleWidth: number;
timeWidth: number;
textWidth: number;
gap: number;
}
function TimelineSection({
datum,
width,
isTimeUppercase,
outerCircleWidth,
innerCircleWidth,
timeWidth,
textWidth,
gap,
}: TimelineSectionProps) {
return (
<div className={styles.timelineSection} style={{ gap: gap }}>
<div
className={styles.time}
style={{
width: timeWidth,
marginLeft: (width - 2 * gap - outerCircleWidth) / 2 - timeWidth,
}}
>
{isTimeUppercase ? datum.time.toUpperCase() : datum.time}
</div>
<div
className={styles.circle}
style={{
width: outerCircleWidth,
height: outerCircleWidth,
borderRadius: outerCircleWidth,
}}
>
<div
className={styles.innerCircle}
style={{
width: innerCircleWidth,
height: innerCircleWidth,
borderRadius: innerCircleWidth,
}}
/>
</div>
<div
className={styles.text}
style={{
width: textWidth,
marginRight: (width - 2 * gap - outerCircleWidth) / 2 - textWidth,
}}
>
{datum.text}
</div>
</div>
);
}

@ -80,6 +80,33 @@ export const moreMockCategoricalData = [
{ key: "Dart", value: 2.21 },
];
export const mockTimelineData = [
{
time: "Fall 2020",
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
},
{
time: "Winter 2021",
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad",
},
{
time: "Spring 2021",
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor i",
},
{
time: "Fall 2021",
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proid",
},
{
time: "Winter 2022",
text: "Lorem ipsum dolor sit amet, consectetur adipi",
},
{
time: "Spring 2022",
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut en",
},
];
export const mockBoxPlotData = [
{
category: "1A",

@ -7,6 +7,7 @@ import {
mockQuoteData,
mockQuoteDataLong,
mockPieData,
mockTimelineData,
} from "data/mocks";
import { sectionsData } from "data/routes";
import React from "react";
@ -15,6 +16,7 @@ import { About } from "@/components/About";
import { PieChart } from "@/components/PieChart";
import { QuotationCarousel } from "@/components/QuotationCarousel";
import { Sections } from "@/components/Sections";
import { Timeline } from "@/components/Timeline";
import { CenterWrapper } from "../components/CenterWrapper";
import { ColorPalette } from "../components/ColorPalette";
@ -29,6 +31,7 @@ export default function Home() {
<p>Show off your components here!</p>
<ColorPalette />
<h2>
<code>Text Styles</code>
</h2>
@ -92,6 +95,11 @@ export default function Home() {
}))}
/>
<h2>
<code>{"<Timeline />"}</code>
</h2>
<Timeline data={mockTimelineData} />
<h2>
<code>{"<Textbox />"}</code>
</h2>

Loading…
Cancel
Save