dynamic image urls + animation
continuous-integration/drone/push Build is failing Details

This commit is contained in:
b38peng 2021-08-09 19:45:03 -03:00
parent 80cb6e3897
commit 972b7f4200
8 changed files with 106 additions and 45 deletions

View File

@ -29,11 +29,12 @@
@media only screen and (max-width: calc(768rem / 16)) { @media only screen and (max-width: calc(768rem / 16)) {
.img { .img {
width: calc(85rem / 16); width: 100%;
} }
.caption { .caption {
font-size: calc(11rem / 16); text-align: center;
font-size: calc(14rem / 16);
margin-top: 1rem; margin-top: 1rem;
} }
} }

View File

@ -1,21 +1,21 @@
import React from "react"; import React from "react";
import styles from "./TeamMember.module.css";
import { Image } from "./Image"; import { Image } from "./Image";
import { getMemberImagePath } from "../lib/team"; import styles from "./TeamMember.module.css";
interface TeamMemberProps { interface TeamMemberProps {
name: string; name: string;
role: string; role: string;
image: string;
} }
export const TeamMember: React.FC<TeamMemberProps> = ({ name, role }) => { export const TeamMember: React.FC<TeamMemberProps> = ({
name,
role,
image,
}) => {
return ( return (
<div className={styles.container}> <div className={styles.container}>
<Image <Image className={styles.img} src={image} alt={`Picture of ${name}`} />
className={styles.img}
src={getMemberImagePath(name)}
alt={`Picture of ${name}`}
/>
<div className={styles.caption}> <div className={styles.caption}>
<div className={styles.name}>{name}</div> <div className={styles.name}>{name}</div>
<div className={styles.role}>{role}</div> <div className={styles.role}>{role}</div>

View File

@ -54,26 +54,45 @@
/* Popup */ /* Popup */
@keyframes popup {
0% {
transform: scale(0.4) translate(0, -50%);
}
100% {
transform: scale(1) translate(0, -50%);
}
}
@keyframes revealBg {
0% {
opacity: 0;
}
100% {
opacity: 100%;
}
}
.popupBackground { .popupBackground {
position: fixed; position: fixed;
z-index: 11;
background-color: var(--navbar-page-overlay); background-color: var(--navbar-page-overlay);
width: 100%; width: 100%;
height: 100%; height: 100%;
top: 0; top: 0;
left: 0; left: 0;
z-index: 1; animation: revealBg 0.2s forwards;
} }
.popupContainer { .popupContainer {
position: fixed; position: fixed;
display: flex; display: flex;
z-index: 12;
flex-direction: column; flex-direction: column;
background-color: var(--secondary-background); background-color: var(--secondary-background);
padding: calc(20rem / 16) calc(40rem / 16); padding: calc(20rem / 16) calc(40rem / 16);
left: 0; left: 0;
top: 50%; top: 50%;
transform: translate(0, -50%); animation: popup 0.7s forwards;
z-index: 2;
} }
.closeBtn { .closeBtn {

View File

@ -2,15 +2,20 @@ import React, { useState } from "react";
import { Image } from "./Image"; import { Image } from "./Image";
import { useWindowDimension } from "../hooks/useWindowDimension"; import { useWindowDimension } from "../hooks/useWindowDimension";
import styles from "./TeamMemberCard.module.css"; import styles from "./TeamMemberCard.module.css";
import { getMemberImagePath } from "../lib/team";
export interface TeamMemberCardProps { export interface TeamMemberCardProps {
name: string; name: string;
role: string; role: string;
image: string;
children: React.ReactNode; children: React.ReactNode;
} }
export function TeamMemberCard({ name, role, children }: TeamMemberCardProps) { export function TeamMemberCard({
name,
role,
image,
children,
}: TeamMemberCardProps) {
const { width } = useWindowDimension(); const { width } = useWindowDimension();
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const handleClick = () => { const handleClick = () => {
@ -24,7 +29,7 @@ export function TeamMemberCard({ name, role, children }: TeamMemberCardProps) {
<div className={styles.picture}> <div className={styles.picture}>
<Image <Image
className={styles.image} className={styles.image}
src={getMemberImagePath(name)} src={image}
alt={`Picture of ${name}`} alt={`Picture of ${name}`}
/> />
</div> </div>
@ -33,7 +38,12 @@ export function TeamMemberCard({ name, role, children }: TeamMemberCardProps) {
<div className={styles.description}>{children}</div> <div className={styles.description}>{children}</div>
</article> </article>
{isOpen && ( {isOpen && (
<ExecPopup name={name} role={role} handleClick={handleClick}> <ExecPopup
name={name}
role={role}
image={image}
handleClick={handleClick}
>
{children} {children}
</ExecPopup> </ExecPopup>
)} )}
@ -45,7 +55,7 @@ interface Propup extends TeamMemberCardProps {
handleClick: () => void; handleClick: () => void;
} }
function ExecPopup({ name, role, children, handleClick }: Propup) { function ExecPopup({ name, role, image, children, handleClick }: Propup) {
return ( return (
<> <>
<div className={styles.popupBackground} onClick={handleClick} /> <div className={styles.popupBackground} onClick={handleClick} />
@ -57,7 +67,7 @@ function ExecPopup({ name, role, children, handleClick }: Propup) {
<div className={styles.picture}> <div className={styles.picture}>
<Image <Image
className={styles.popupImage} className={styles.popupImage}
src={getMemberImagePath(name)} src={image}
alt={`Picture of ${name}`} alt={`Picture of ${name}`}
/> />
</div> </div>

View File

@ -9,6 +9,7 @@ const fileType = ".md";
export interface Metadata { export interface Metadata {
name: string; name: string;
role: string; role: string;
image: string;
} }
export async function getExecNames() { export async function getExecNames() {
@ -20,22 +21,34 @@ export async function getExecNames() {
export async function getExec(fileName: string, convert = true) { export async function getExec(fileName: string, convert = true) {
const raw = await readFile(path.join(EXECS_PATH, `${fileName}${fileType}`)); const raw = await readFile(path.join(EXECS_PATH, `${fileName}${fileType}`));
const { content, data: metadata } = matter(raw); const { content, data: metadata } = matter(raw);
const image = await getMemberImagePath(metadata.name);
return { return {
content: convert ? await serialize(content) : content, content: convert ? await serialize(content) : content,
metadata, metadata: { ...metadata, image } as Metadata,
}; };
} }
export function getMemberImagePath(name: string) { async function getImage(imgPath: string) {
const imgPath = "/images/team/" + name.replace(" ", "") + ".jpg"; try {
const placeholder = "/images/team/team-member-placeholder.svg"; await access(path.join("public", imgPath));
let img; return imgPath;
access(imgPath) } catch {
.then(() => { return undefined;
img = imgPath; }
}) }
.catch(() => {
img = placeholder; export async function getMemberImagePath(name: string) {
}); const imgPath = path.join("images", "team", name.replace(" ", ""));
const placeholder = path.join(
"images",
"team",
"team-member-placeholder.svg"
);
const img =
(await getImage(imgPath + ".jpg")) ??
(await getImage(imgPath + ".png")) ??
(await getImage(imgPath + ".gif")) ??
placeholder;
return img; return img;
} }

View File

@ -95,17 +95,12 @@
padding-bottom: calc(15rem / 16); padding-bottom: calc(15rem / 16);
} }
.execs { .execs, .members {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(calc(132rem / 16), 1fr)); grid-template-columns: repeat(auto-fill, minmax(calc(130rem / 16), 1fr));
justify-items: center; justify-items: center;
} }
.members {
row-gap: calc(24rem / 16);
column-gap: calc(1rem / 16);
}
.elections { .elections {
padding: 2rem; padding: 2rem;
} }

View File

@ -1,5 +1,10 @@
import React from "react"; import React from "react";
import { getExec, getExecNames, Metadata } from "../../lib/team"; import {
getExec,
getExecNames,
Metadata,
getMemberImagePath,
} from "../../lib/team";
import { MDXRemote, MDXRemoteSerializeResult } from "next-mdx-remote"; import { MDXRemote, MDXRemoteSerializeResult } from "next-mdx-remote";
import { GetStaticProps } from "next"; import { GetStaticProps } from "next";
import { Image } from "../../components/Image"; import { Image } from "../../components/Image";
@ -7,9 +12,9 @@ import { TeamMemberCard } from "../../components/TeamMemberCard";
import { TeamMember } from "../../components/TeamMember"; import { TeamMember } from "../../components/TeamMember";
import { Link } from "../../components/Link"; import { Link } from "../../components/Link";
import { Bubble } from "../../components/Bubble"; import { Bubble } from "../../components/Bubble";
import programme from "../../content/meet-the-team/programme-committee.json"; import programmeData from "../../content/meet-the-team/programme-committee.json";
import website from "../../content/meet-the-team/website-committee.json"; import websiteData from "../../content/meet-the-team/website-committee.json";
import systems from "../../content/meet-the-team/systems-committee.json"; import systemsData from "../../content/meet-the-team/systems-committee.json";
import styles from "./team.module.css"; import styles from "./team.module.css";
// TODO: fix hotdog for elections // TODO: fix hotdog for elections
@ -22,9 +27,12 @@ interface SerializedExec {
interface Props { interface Props {
execs: SerializedExec[]; execs: SerializedExec[];
programme: Metadata[];
website: Metadata[];
systems: Metadata[];
} }
export default function Team({ execs }: Props) { export default function Team({ execs, programme, website, systems }: Props) {
return ( return (
<> <>
<div className={styles.headerContainer}> <div className={styles.headerContainer}>
@ -82,7 +90,7 @@ export default function Team({ execs }: Props) {
} }
interface MembersProps { interface MembersProps {
team: Array<Metadata>; team: Metadata[];
} }
function MembersList(props: MembersProps) { function MembersList(props: MembersProps) {
@ -95,13 +103,28 @@ function MembersList(props: MembersProps) {
); );
} }
async function getMemberImage(team: Omit<Metadata, "image">[]) {
return await Promise.all(
team.map(async (member) => {
const image = await getMemberImagePath(member.name);
return {
...member,
image,
};
})
);
}
export const getStaticProps: GetStaticProps<Props> = async () => { export const getStaticProps: GetStaticProps<Props> = async () => {
const execNames = await getExecNames(); const execNames = await getExecNames();
const execs = (await Promise.all( const execs = (await Promise.all(
execNames.map((name) => getExec(name)) execNames.map((name) => getExec(name))
)) as SerializedExec[]; )) as SerializedExec[];
const programme = await getMemberImage(programmeData);
const website = await getMemberImage(websiteData);
const systems = await getMemberImage(systemsData);
return { return {
props: { execs }, props: { execs, programme, website, systems },
}; };
}; };

View File

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB