Skip to content

Commit

Permalink
Add member metadata and flip card
Browse files Browse the repository at this point in the history
Also refactor some Svelte HTML logic for additional styling.
  • Loading branch information
diamondburned committed Sep 28, 2022
1 parent e2c6701 commit 858f34d
Show file tree
Hide file tree
Showing 2 changed files with 312 additions and 24 deletions.
10 changes: 5 additions & 5 deletions src/lib/public/board/types.ts
Expand Up @@ -7,11 +7,11 @@ export enum Term {
}

export enum Social {
website = 'Website',
github = 'GitHub',
discord = 'Discord',
linkedin = 'LinkedIn',
instagram = 'Instagram',
Website = 'website',
GitHub = 'github',
Discord = 'discord',
LinkedIn = 'linkedin',
Instagram = 'instagram',
}

export interface Officer {
Expand Down
326 changes: 307 additions & 19 deletions src/routes/about/officer-profile.svelte
@@ -1,5 +1,12 @@
<script lang="ts">
import type { Officer } from '$lib/public/board/types';
import { Term } from '$lib/public/board/types';
import GitHub from '$lib/components/svg/github.svelte';
import Public from '$lib/components/svg/public.svelte';
import Discord from '$lib/components/svg/discord.svelte';
import LinkedIn from '$lib/components/svg/linkedin.svelte';
import Instagram from '$lib/components/svg/instagram.svelte';
import { toastDo } from '$lib/components/toaster/toasts';
import {
termIndex,
getOfficerTierByTermIndex,
Expand All @@ -10,40 +17,150 @@
export let placeholderPicture = 'placeholder.webp';
export let dev = false;
// earliestTerm returns the earliest term of the current officer. Blame Ethan
// for this whole idea of having an officer ID. OK, maybe some of it was mine.
function earliestTerm(): string {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (const [_, key] of Object.entries(Term)) {
if (info.positions[key]) {
return key;
}
}
return 'null';
}
const officerID = info.fullName.toLowerCase().replace(/[^a-z0-9]/g, '-') + '-' + earliestTerm();
const officerName = info.fullName ?? '';
const officerPicture = info.picture ?? placeholderPicture;
const officerSocials = info.socials ?? {};
$: officerPosition = getPositionByTermIndex(info, $termIndex)?.title || '';
/** @see <https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText> */
function copyDiscord() {
toastDo(() => navigator.clipboard.writeText(officerSocials.discord || ''), {
success: () => `Copied ${officerName}'s Discord tag to clipboard!`,
failure: () => 'Error occured while copy Discord tag to clipboard.',
});
}
const positionClasses = Object.entries({
Algo: 'acm-purple',
Create: 'acm-pink',
Design: 'acm-pink',
Dev: 'acm-bluer',
AI: 'acm-emerald',
Marketing: 'acm-red',
'Special Events': 'acm-yellow',
nodebuds: 'brand-header',
});
$: titleHTML = getPositionByTermIndex(info, $termIndex)
?.title.replace(/Algo\s/, `<b class="acm-purple">Algo&nbsp;</b>`)
.replace(/Create\s/, `<b class="acm-pink">Create&nbsp;</b>`)
.replace(/Design\s/, `<b class="acm-pink">Design&nbsp;</b>`)
.replace(/Dev\s/, `<b class="acm-bluer">Dev&nbsp;</b>`)
.replace(/AI\s/, `<b class="acm-emerald">AI&nbsp;</b>`)
.replace(/Marketing\s/, `<b class="acm-red">Marketing&nbsp;</b>`)
.replace(/Special Events\s/, `<b class="acm-yellow">Special Events&nbsp;</b>`)
.replace(/nodebuds\s/, `<span class="brand-header">Node Buds&nbsp;</span>`);
$: [titleName, titleClass] = positionClasses.find((e) => {
return officerPosition.startsWith(e[0]);
}) || [null, 'acm-dark'];
$: titleHTML = titleName
? officerPosition.replace(titleName, `<b class="${titleClass}">${titleName}</b>`)
: officerPosition;
$: officerTier = dev ? getOfficerTierByTermIndex(info, $termIndex) : '';
</script>

<div class="officer-container">
<img
class="officer-image"
src={`../assets/authors/${officerPicture}`}
alt={`Image of ${officerName}.`}
/>
<label
for="{officerID}-flipcard"
class="officer-container"
class:officer-has-socials={info.socials !== undefined}
style="--accent: var(--{titleClass})"
>
<input type="checkbox" id="{officerID}-flipcard" />
<div class="officer-3d-flipcard">
<div class="officer-flipcard">
<img
class="officer-image"
src={`../assets/authors/${officerPicture}`}
alt={`Image of ${officerName}.`}
/>
<div class="officer-socials-box">
<div class="officer-socials">
<h3>Socials</h3>
<div class="officer-social-scrolls">
<div class="officer-social-links">
{#if officerSocials.website}
<p class="officer-website">
<a
target="blank"
title={officerSocials.website}
href="//{officerSocials.website}"
>
<Public /> <span>Website</span>
</a>
</p>
{/if}
{#if officerSocials.github}
<p class="officer-github">
<a
target="blank"
title={officerSocials.github}
href="https://github.com/{officerSocials.github}"
>
<GitHub /> <span>GitHub</span>
</a>
</p>
{/if}
{#if officerSocials.discord}
<p class="officer-discord">
<a
target="blank"
title={officerSocials.discord}
href={'javascript:void(0)'}
aria-label="Copy username to clipboard"
on:click={(event) => {
copyDiscord();
event.preventDefault();
}}
>
<Discord /> <span>Discord</span>
</a>
</p>
{/if}

<div>
{#if officerSocials.linkedin}
<p class="officer-linkedin">
<a
target="blank"
title={officerSocials.linkedin}
href="https://www.linkedin.com/in/{officerSocials.linkedin}"
>
<LinkedIn /> <span>LinkedIn</span>
</a>
</p>
{/if}
{#if officerSocials.instagram}
<p class="officer-instagram">
<a
target="blank"
title={officerSocials.instagram}
href="https://www.instagram.com/{officerSocials.instagram}/"
>
<Instagram /> <span>Instagram</span>
</a>
</p>
{/if}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="officer-placard">
<h3 class="brand-header">
{officerName}
{#if officerTier}<br />{officerTier}<br />{/if}
</h3>

<p class="brand-med">
{@html titleHTML}
</p>
</div>
</div>
</label>

<style lang="scss">
.officer-container {
Expand All @@ -53,7 +170,7 @@
align-items: center;
text-align: center;
.officer-image {
.officer-flipcard {
width: 200px;
height: auto;
}
Expand All @@ -62,4 +179,175 @@
max-width: 250px;
}
}
.officer-container.officer-has-socials {
cursor: pointer;
input[type='checkbox'] + .officer-3d-flipcard {
.officer-socials-box {
/* Hide the checkbox by default for WebKit workaround. The transitions
* are set below this. */
visibility: hidden;
}
}
input:checked[type='checkbox'] + .officer-3d-flipcard {
.officer-flipcard {
transform: rotateY(180deg);
}
.officer-socials-box {
visibility: visible;
}
}
&:hover .officer-image,
input:checked[type='checkbox'] + .officer-3d-flipcard .officer-image {
/* This hack stays until we can resolve #348. */
filter: brightness(130%) contrast(85%);
}
.officer-placard {
border-bottom: 2px solid transparent;
}
&:hover .officer-placard,
input:checked[type='checkbox'] ~ .officer-placard {
border-bottom: 2px solid var(--accent);
}
}
input[type='checkbox'] {
display: none;
}
.officer-3d-flipcard {
perspective: 2000px;
.officer-flipcard {
position: relative;
height: 200px;
transition: transform 0.3s ease;
.officer-image,
.officer-socials-box {
top: 0;
left: 0;
position: absolute;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.officer-image {
height: 100%;
}
&,
.officer-socials-box {
transform-style: preserve-3d;
}
.officer-socials-box {
transition: transform 0.3s ease, visibility 0s ease;
}
.officer-socials-box {
width: 100%;
height: 100%;
transform: rotateY(180deg);
display: flex;
align-items: center;
justify-content: center;
.officer-socials {
width: 180px;
height: 180px;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 20px;
box-sizing: border-box;
background-color: var(--acm-canvas);
h3 {
line-height: 2.5em;
}
}
.officer-social-scrolls {
width: 100%;
overflyw-x: hidden;
overflow-y: scroll;
scroll-snap-type: y mandatory;
/* Firefox scrollbar styling. */
scrollbar-width: 3px;
scrollbar-color: var(--accent) transparent;
/* Chrome scrollbar styling. */
&::-webkit-scrollbar {
background: none;
width: 3px;
height: 50px;
}
&::-webkit-scrollbar-thumb {
background-color: var(--accent);
border-radius: 3px;
}
&::-webkit-scrollbar-track-piece:end {
/* Pad the scrollbar so it doesn't go into the corners. */
margin-bottom: 20px;
}
display: flex;
flex-direction: column;
align-items: center;
}
.officer-social-links {
display: flex;
flex-direction: column;
gap: 0.35em;
& > * {
scroll-snap-align: start;
}
}
p {
text-align: left;
line-height: 2em;
:global(svg) {
width: 1.65em;
height: 1.65em;
margin-bottom: -0.45em;
margin-right: 0.25em;
}
a {
display: block;
text-decoration: none;
span {
transition-property: color, border-color;
}
}
&:hover a {
span {
color: var(--acm-blue);
border-bottom: 1px solid var(--acm-blue);
}
:global(svg) {
fill: var(--acm-blue);
}
}
}
}
}
}
</style>

0 comments on commit 858f34d

Please sign in to comment.