Refactor body into multiple files
This commit is contained in:
parent
36db4c2c0c
commit
a513e3786c
10 changed files with 168 additions and 151 deletions
150
src/Body.tsx
150
src/Body.tsx
|
@ -1,150 +0,0 @@
|
||||||
import "./style.css";
|
|
||||||
|
|
||||||
import type { PropsWithChildren } from "react";
|
|
||||||
|
|
||||||
import hero from "./assets/MichaelBradley.jpeg?format=avif;webp;jpeg&as=picture";
|
|
||||||
|
|
||||||
import CV from "./assets/cv.svg?react";
|
|
||||||
import Git from "./assets/git.svg?react";
|
|
||||||
import LinkedIn from "./assets/linkedin.svg?react";
|
|
||||||
import Mastodon from "./assets/mastodon.svg?react";
|
|
||||||
import DarkModeToggleIcon from "./assets/darkmodetoggle.svg?react";
|
|
||||||
|
|
||||||
type ColouredBlockProps = {
|
|
||||||
colorNum: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ColouredBlock = ({ colorNum }: ColouredBlockProps) => (
|
|
||||||
<span style={{ color: `var(--color${colorNum})` }}>███</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
type ColouredLineProps = {
|
|
||||||
light: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ProfilePicture = () => (
|
|
||||||
<div id="michael-photo-container">
|
|
||||||
<picture>
|
|
||||||
{Object.entries(hero.sources).map(([format, src]) => (
|
|
||||||
<source srcSet={src} type={`image/${format}`} key={format} />
|
|
||||||
))}
|
|
||||||
<img src={hero.img.src} alt="A picture of me in a suit smiling" id="michael-photo" />
|
|
||||||
</picture>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Highlight = ({ children }: PropsWithChildren) => <span className="text-highlight">{children}</span>;
|
|
||||||
|
|
||||||
type FetchHeaderProps = {
|
|
||||||
user: string;
|
|
||||||
hostname: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const FetchHeader = ({ user, hostname }: FetchHeaderProps) => (
|
|
||||||
<>
|
|
||||||
<p>
|
|
||||||
<Highlight>{user}</Highlight>@<Highlight>{hostname}</Highlight>
|
|
||||||
</p>
|
|
||||||
<p>{"-".repeat(user.length + 1 + hostname.length)}</p>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
type InfoLineProps = {
|
|
||||||
label: string;
|
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const InfoLine = ({ label, value }: InfoLineProps) => (
|
|
||||||
<p>
|
|
||||||
<span className="text-highlight">{label}</span>: {value}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
|
|
||||||
const BlankLine = () => (
|
|
||||||
<p>
|
|
||||||
<br />
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Description = () => (
|
|
||||||
<div className="invisible-div">
|
|
||||||
<FetchHeader user="website" hostname="MichaelBradley" />
|
|
||||||
<InfoLine label="Degree" value="Bachelor of Computer Science" />
|
|
||||||
<InfoLine label="University" value="Carleton" />
|
|
||||||
<InfoLine label="Major CGPA" value="11.52/12 (A+)" />
|
|
||||||
<InfoLine label="Languages" value="C/C++, Python, TypeScript" />
|
|
||||||
<InfoLine label="Skills" value="Linux, Git, Testing" />
|
|
||||||
<InfoLine label="Work Experience" value="2 years" />
|
|
||||||
<InfoLine label="Applying for" value="Full-time job" />
|
|
||||||
<InfoLine label="Location" value="Toronto or Remote" />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const ColouredLine = ({ light }: ColouredLineProps) => {
|
|
||||||
const startIndex = light ? 8 : 0;
|
|
||||||
return (
|
|
||||||
<p className="blocks">
|
|
||||||
{Array.from({ length: 8 }).map(
|
|
||||||
(
|
|
||||||
_value,
|
|
||||||
index, // Apparently this is the best way to map a range in JavaScript
|
|
||||||
) => (
|
|
||||||
<ColouredBlock colorNum={startIndex + index} key={index} />
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Automate screenshot of info for thumbnail?
|
|
||||||
const Fetch = () => {
|
|
||||||
return (
|
|
||||||
<div id="fetch-container">
|
|
||||||
<div id="fetch">
|
|
||||||
<ProfilePicture />
|
|
||||||
<div id="basic-info">
|
|
||||||
<Description />
|
|
||||||
<BlankLine />
|
|
||||||
<ColouredLine light={false} />
|
|
||||||
<ColouredLine light={true} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
type ImageLinkProps = {
|
|
||||||
href: string;
|
|
||||||
title: string;
|
|
||||||
Image: typeof Git; // Not ideal, but I can't figure out how to import the type directly from the declared wildcard module
|
|
||||||
};
|
|
||||||
|
|
||||||
const ImageLink = ({ href, title, Image }: ImageLinkProps) => (
|
|
||||||
<a rel="me" href={href} title={title}>
|
|
||||||
<Image width="100%" />
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Links = () => (
|
|
||||||
<div className="links">
|
|
||||||
<ImageLink href="https://git.mmbradley.ca/MichaelBradley/" title="Forgejo instance" Image={Git} />
|
|
||||||
<ImageLink href="https://www.linkedin.com/in/michaelmbradley/" title="LinkedIn account" Image={LinkedIn} />
|
|
||||||
<ImageLink href="https://mmbradley.ca/resume.pdf" title="My Résumé" Image={CV} />
|
|
||||||
<ImageLink href="https://mstdn.ca/@michaelbradley/" title="Mastodon account" Image={Mastodon} />
|
|
||||||
<input id="dark-mode-toggle" type="checkbox" aria-label="Toggle dark mode" />
|
|
||||||
<label htmlFor="dark-mode-toggle" id="dark-mode-toggle-label" title="Toggle dark mode">
|
|
||||||
<DarkModeToggleIcon width="100%" />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Body = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Fetch />
|
|
||||||
<Links />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Body;
|
|
29
src/Body/Fetch/ColouredLine.tsx
Normal file
29
src/Body/Fetch/ColouredLine.tsx
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
type ColouredBlockProps = {
|
||||||
|
colorNum: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ColouredBlock = ({ colorNum }: ColouredBlockProps) => (
|
||||||
|
<span style={{ color: `var(--color${colorNum})` }}>███</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
type ColouredLineProps = {
|
||||||
|
light: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ColouredLine = ({ light }: ColouredLineProps) => {
|
||||||
|
const startIndex = light ? 8 : 0;
|
||||||
|
return (
|
||||||
|
<p className="blocks">
|
||||||
|
{Array.from({ length: 8 }).map(
|
||||||
|
(
|
||||||
|
_value,
|
||||||
|
index, // Apparently this is the best way to map a range in JavaScript
|
||||||
|
) => (
|
||||||
|
<ColouredBlock colorNum={startIndex + index} key={index} />
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ColouredLine;
|
42
src/Body/Fetch/Description.tsx
Normal file
42
src/Body/Fetch/Description.tsx
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import { Highlight } from "./utils";
|
||||||
|
|
||||||
|
type HeaderProps = {
|
||||||
|
user: string;
|
||||||
|
hostname: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Header = ({ user, hostname }: HeaderProps) => (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
<Highlight>{user}</Highlight>@<Highlight>{hostname}</Highlight>
|
||||||
|
</p>
|
||||||
|
<p>{"-".repeat(user.length + 1 + hostname.length)}</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
type InfoLineProps = {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const InfoLine = ({ label, value }: InfoLineProps) => (
|
||||||
|
<p>
|
||||||
|
<span className="text-highlight">{label}</span>: {value}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Description = () => (
|
||||||
|
<div className="invisible-div">
|
||||||
|
<Header user="website" hostname="MichaelBradley" />
|
||||||
|
<InfoLine label="Degree" value="Bachelor of Computer Science" />
|
||||||
|
<InfoLine label="University" value="Carleton" />
|
||||||
|
<InfoLine label="Major CGPA" value="11.52/12 (A+)" />
|
||||||
|
<InfoLine label="Languages" value="C/C++, Python, TypeScript" />
|
||||||
|
<InfoLine label="Skills" value="Linux, Git, Testing" />
|
||||||
|
<InfoLine label="Work Experience" value="2 years" />
|
||||||
|
<InfoLine label="Applying for" value="Full-time job" />
|
||||||
|
<InfoLine label="Location" value="Toronto or Remote" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Description;
|
14
src/Body/Fetch/ProfilePicture.tsx
Normal file
14
src/Body/Fetch/ProfilePicture.tsx
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import hero from "../../assets/MichaelBradley.jpeg?format=avif;webp;jpeg&as=picture";
|
||||||
|
|
||||||
|
const ProfilePicture = () => (
|
||||||
|
<div id="michael-photo-container">
|
||||||
|
<picture>
|
||||||
|
{Object.entries(hero.sources).map(([format, src]) => (
|
||||||
|
<source srcSet={src} type={`image/${format}`} key={format} />
|
||||||
|
))}
|
||||||
|
<img src={hero.img.src} alt="A picture of me in a suit smiling" id="michael-photo" />
|
||||||
|
</picture>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default ProfilePicture;
|
23
src/Body/Fetch/index.tsx
Normal file
23
src/Body/Fetch/index.tsx
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import ColouredLine from "./ColouredLine";
|
||||||
|
import Description from "./Description";
|
||||||
|
import ProfilePicture from "./ProfilePicture";
|
||||||
|
import { BlankLine } from "./utils";
|
||||||
|
|
||||||
|
// TODO: Automate screenshot of info for thumbnail?
|
||||||
|
const Fetch = () => {
|
||||||
|
return (
|
||||||
|
<div id="fetch-container">
|
||||||
|
<div id="fetch">
|
||||||
|
<ProfilePicture />
|
||||||
|
<div id="basic-info">
|
||||||
|
<Description />
|
||||||
|
<BlankLine />
|
||||||
|
<ColouredLine light={false} />
|
||||||
|
<ColouredLine light={true} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Fetch;
|
9
src/Body/Fetch/utils.tsx
Normal file
9
src/Body/Fetch/utils.tsx
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import type { PropsWithChildren } from "react";
|
||||||
|
|
||||||
|
export const Highlight = ({ children }: PropsWithChildren) => <span className="text-highlight">{children}</span>;
|
||||||
|
|
||||||
|
export const BlankLine = () => (
|
||||||
|
<p>
|
||||||
|
<br />
|
||||||
|
</p>
|
||||||
|
);
|
32
src/Body/Links.tsx
Normal file
32
src/Body/Links.tsx
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import CV from "../assets/cv.svg?react";
|
||||||
|
import Git from "../assets/git.svg?react";
|
||||||
|
import LinkedIn from "../assets/linkedin.svg?react";
|
||||||
|
import Mastodon from "../assets/mastodon.svg?react";
|
||||||
|
import DarkModeToggleIcon from "../assets/darkmodetoggle.svg?react";
|
||||||
|
|
||||||
|
type ImageLinkProps = {
|
||||||
|
href: string;
|
||||||
|
title: string;
|
||||||
|
Image: typeof Git; // Not ideal, but I can't figure out how to import the type directly from the declared wildcard module
|
||||||
|
};
|
||||||
|
|
||||||
|
const ImageLink = ({ href, title, Image }: ImageLinkProps) => (
|
||||||
|
<a rel="me" href={href} title={title}>
|
||||||
|
<Image width="100%" />
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Links = () => (
|
||||||
|
<div className="links">
|
||||||
|
<ImageLink href="https://git.mmbradley.ca/MichaelBradley/" title="Forgejo instance" Image={Git} />
|
||||||
|
<ImageLink href="https://www.linkedin.com/in/michaelmbradley/" title="LinkedIn account" Image={LinkedIn} />
|
||||||
|
<ImageLink href="https://mmbradley.ca/resume.pdf" title="My Résumé" Image={CV} />
|
||||||
|
<ImageLink href="https://mstdn.ca/@michaelbradley/" title="Mastodon account" Image={Mastodon} />
|
||||||
|
<input id="dark-mode-toggle" type="checkbox" aria-label="Toggle dark mode" />
|
||||||
|
<label htmlFor="dark-mode-toggle" id="dark-mode-toggle-label" title="Toggle dark mode">
|
||||||
|
<DarkModeToggleIcon width="100%" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Links;
|
13
src/Body/index.tsx
Normal file
13
src/Body/index.tsx
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import Fetch from "./Fetch";
|
||||||
|
import Links from "./Links";
|
||||||
|
|
||||||
|
const Body = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Fetch />
|
||||||
|
<Links />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Body;
|
|
@ -1,6 +1,9 @@
|
||||||
// Entry point for development
|
// Entry point for development
|
||||||
|
import "./style.css";
|
||||||
|
|
||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
|
|
||||||
import Body from "./Body";
|
import Body from "./Body";
|
||||||
import Head from "./Head";
|
import Head from "./Head";
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
// Entry point for SSG
|
// Entry point for SSG
|
||||||
|
import style from "./style.css?inline";
|
||||||
|
|
||||||
import { renderToString } from "react-dom/server";
|
import { renderToString } from "react-dom/server";
|
||||||
|
|
||||||
import Body from "./Body";
|
import Body from "./Body";
|
||||||
import Head from "./Head";
|
import Head from "./Head";
|
||||||
import style from "./style.css?inline";
|
|
||||||
|
|
||||||
export const render = () => {
|
export const render = () => {
|
||||||
const html =
|
const html =
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue