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)) {
|
@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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
37
lib/team.ts
37
lib/team.ts
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 },
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
Loading…
Reference in New Issue