dynamic image urls + animation
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
80cb6e3897
commit
972b7f4200
|
@ -29,11 +29,12 @@
|
|||
|
||||
@media only screen and (max-width: calc(768rem / 16)) {
|
||||
.img {
|
||||
width: calc(85rem / 16);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.caption {
|
||||
font-size: calc(11rem / 16);
|
||||
text-align: center;
|
||||
font-size: calc(14rem / 16);
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import React from "react";
|
||||
import styles from "./TeamMember.module.css";
|
||||
import { Image } from "./Image";
|
||||
import { getMemberImagePath } from "../lib/team";
|
||||
import styles from "./TeamMember.module.css";
|
||||
|
||||
interface TeamMemberProps {
|
||||
name: string;
|
||||
role: string;
|
||||
image: string;
|
||||
}
|
||||
|
||||
export const TeamMember: React.FC<TeamMemberProps> = ({ name, role }) => {
|
||||
export const TeamMember: React.FC<TeamMemberProps> = ({
|
||||
name,
|
||||
role,
|
||||
image,
|
||||
}) => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Image
|
||||
className={styles.img}
|
||||
src={getMemberImagePath(name)}
|
||||
alt={`Picture of ${name}`}
|
||||
/>
|
||||
<Image className={styles.img} src={image} alt={`Picture of ${name}`} />
|
||||
<div className={styles.caption}>
|
||||
<div className={styles.name}>{name}</div>
|
||||
<div className={styles.role}>{role}</div>
|
||||
|
|
|
@ -54,26 +54,45 @@
|
|||
|
||||
/* 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 {
|
||||
position: fixed;
|
||||
z-index: 11;
|
||||
background-color: var(--navbar-page-overlay);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
animation: revealBg 0.2s forwards;
|
||||
}
|
||||
|
||||
.popupContainer {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
z-index: 12;
|
||||
flex-direction: column;
|
||||
background-color: var(--secondary-background);
|
||||
padding: calc(20rem / 16) calc(40rem / 16);
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
z-index: 2;
|
||||
animation: popup 0.7s forwards;
|
||||
}
|
||||
|
||||
.closeBtn {
|
||||
|
|
|
@ -2,15 +2,20 @@ import React, { useState } from "react";
|
|||
import { Image } from "./Image";
|
||||
import { useWindowDimension } from "../hooks/useWindowDimension";
|
||||
import styles from "./TeamMemberCard.module.css";
|
||||
import { getMemberImagePath } from "../lib/team";
|
||||
|
||||
export interface TeamMemberCardProps {
|
||||
name: string;
|
||||
role: string;
|
||||
image: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function TeamMemberCard({ name, role, children }: TeamMemberCardProps) {
|
||||
export function TeamMemberCard({
|
||||
name,
|
||||
role,
|
||||
image,
|
||||
children,
|
||||
}: TeamMemberCardProps) {
|
||||
const { width } = useWindowDimension();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const handleClick = () => {
|
||||
|
@ -24,7 +29,7 @@ export function TeamMemberCard({ name, role, children }: TeamMemberCardProps) {
|
|||
<div className={styles.picture}>
|
||||
<Image
|
||||
className={styles.image}
|
||||
src={getMemberImagePath(name)}
|
||||
src={image}
|
||||
alt={`Picture of ${name}`}
|
||||
/>
|
||||
</div>
|
||||
|
@ -33,7 +38,12 @@ export function TeamMemberCard({ name, role, children }: TeamMemberCardProps) {
|
|||
<div className={styles.description}>{children}</div>
|
||||
</article>
|
||||
{isOpen && (
|
||||
<ExecPopup name={name} role={role} handleClick={handleClick}>
|
||||
<ExecPopup
|
||||
name={name}
|
||||
role={role}
|
||||
image={image}
|
||||
handleClick={handleClick}
|
||||
>
|
||||
{children}
|
||||
</ExecPopup>
|
||||
)}
|
||||
|
@ -45,7 +55,7 @@ interface Propup extends TeamMemberCardProps {
|
|||
handleClick: () => void;
|
||||
}
|
||||
|
||||
function ExecPopup({ name, role, children, handleClick }: Propup) {
|
||||
function ExecPopup({ name, role, image, children, handleClick }: Propup) {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.popupBackground} onClick={handleClick} />
|
||||
|
@ -57,7 +67,7 @@ function ExecPopup({ name, role, children, handleClick }: Propup) {
|
|||
<div className={styles.picture}>
|
||||
<Image
|
||||
className={styles.popupImage}
|
||||
src={getMemberImagePath(name)}
|
||||
src={image}
|
||||
alt={`Picture of ${name}`}
|
||||
/>
|
||||
</div>
|
||||
|
|
37
lib/team.ts
37
lib/team.ts
|
@ -9,6 +9,7 @@ const fileType = ".md";
|
|||
export interface Metadata {
|
||||
name: string;
|
||||
role: string;
|
||||
image: string;
|
||||
}
|
||||
|
||||
export async function getExecNames() {
|
||||
|
@ -20,22 +21,34 @@ export async function getExecNames() {
|
|||
export async function getExec(fileName: string, convert = true) {
|
||||
const raw = await readFile(path.join(EXECS_PATH, `${fileName}${fileType}`));
|
||||
const { content, data: metadata } = matter(raw);
|
||||
const image = await getMemberImagePath(metadata.name);
|
||||
|
||||
return {
|
||||
content: convert ? await serialize(content) : content,
|
||||
metadata,
|
||||
metadata: { ...metadata, image } as Metadata,
|
||||
};
|
||||
}
|
||||
|
||||
export function getMemberImagePath(name: string) {
|
||||
const imgPath = "/images/team/" + name.replace(" ", "") + ".jpg";
|
||||
const placeholder = "/images/team/team-member-placeholder.svg";
|
||||
let img;
|
||||
access(imgPath)
|
||||
.then(() => {
|
||||
img = imgPath;
|
||||
})
|
||||
.catch(() => {
|
||||
img = placeholder;
|
||||
});
|
||||
async function getImage(imgPath: string) {
|
||||
try {
|
||||
await access(path.join("public", imgPath));
|
||||
return imgPath;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -95,17 +95,12 @@
|
|||
padding-bottom: calc(15rem / 16);
|
||||
}
|
||||
|
||||
.execs {
|
||||
.execs, .members {
|
||||
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;
|
||||
}
|
||||
|
||||
.members {
|
||||
row-gap: calc(24rem / 16);
|
||||
column-gap: calc(1rem / 16);
|
||||
}
|
||||
|
||||
.elections {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
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 { GetStaticProps } from "next";
|
||||
import { Image } from "../../components/Image";
|
||||
|
@ -7,9 +12,9 @@ import { TeamMemberCard } from "../../components/TeamMemberCard";
|
|||
import { TeamMember } from "../../components/TeamMember";
|
||||
import { Link } from "../../components/Link";
|
||||
import { Bubble } from "../../components/Bubble";
|
||||
import programme from "../../content/meet-the-team/programme-committee.json";
|
||||
import website from "../../content/meet-the-team/website-committee.json";
|
||||
import systems from "../../content/meet-the-team/systems-committee.json";
|
||||
import programmeData from "../../content/meet-the-team/programme-committee.json";
|
||||
import websiteData from "../../content/meet-the-team/website-committee.json";
|
||||
import systemsData from "../../content/meet-the-team/systems-committee.json";
|
||||
import styles from "./team.module.css";
|
||||
|
||||
// TODO: fix hotdog for elections
|
||||
|
@ -22,9 +27,12 @@ interface SerializedExec {
|
|||
|
||||
interface Props {
|
||||
execs: SerializedExec[];
|
||||
programme: Metadata[];
|
||||
website: Metadata[];
|
||||
systems: Metadata[];
|
||||
}
|
||||
|
||||
export default function Team({ execs }: Props) {
|
||||
export default function Team({ execs, programme, website, systems }: Props) {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.headerContainer}>
|
||||
|
@ -82,7 +90,7 @@ export default function Team({ execs }: Props) {
|
|||
}
|
||||
|
||||
interface MembersProps {
|
||||
team: Array<Metadata>;
|
||||
team: Metadata[];
|
||||
}
|
||||
|
||||
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 () => {
|
||||
const execNames = await getExecNames();
|
||||
const execs = (await Promise.all(
|
||||
execNames.map((name) => getExec(name))
|
||||
)) as SerializedExec[];
|
||||
const programme = await getMemberImage(programmeData);
|
||||
const website = await getMemberImage(websiteData);
|
||||
const systems = await getMemberImage(systemsData);
|
||||
|
||||
return {
|
||||
props: { execs },
|
||||
props: { execs, programme, website, systems },
|
||||
};
|
||||
};
|
||||
|
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
Loading…
Reference in New Issue