This commit is contained in:
skidoodle 2024-03-13 00:30:45 +01:00
commit d761a10bf7
102 changed files with 4761 additions and 0 deletions

View file

@ -0,0 +1,52 @@
<script lang="ts">
import { page } from '$app/stores';
import { getUserAvatar } from '$lib/config';
import { editorStore } from '$lib/stores/editor';
export let loadTime: number | undefined = undefined;
export let uniqueId: string | undefined = undefined;
export let align: BioSiteAlign = 'LEFT';
export let small: boolean = false;
export let tiny: boolean = false;
export let veryTiny: boolean = false;
$: noAvatar =
($page.data.bio?.hasAvatar !== true && $editorStore.avatar == null) ||
$editorStore.edits.edits.deleteAvatar != null;
$: isEditor = $page.url.pathname.startsWith('/dashboard');
$: avatar = noAvatar
? undefined
: $editorStore.avatar
? $editorStore.avatar
: uniqueId
? getUserAvatar(uniqueId, isEditor ? loadTime : undefined)
: undefined;
</script>
<div
class="w-24 h-24 rounded-3xl avatar bg-center bg-cover"
class:self-center={align === 'CENTER'}
class:self-end={align === 'RIGHT'}
class:small
class:tiny
class:very-tiny={veryTiny}
style="--avatar: url({avatar ?? '/assets/default/avatar.svg'})" />
<style lang="postcss">
div.avatar {
background-image: var(--avatar);
}
div.small {
@apply w-16 h-16 rounded-2xl;
}
div.tiny {
@apply w-9 h-9 rounded-full;
}
div.very-tiny {
@apply w-6 h-6 rounded-full;
}
</style>

View file

@ -0,0 +1,25 @@
<script lang="ts">
import badges from '$lib/badges';
import Tooltip from '$lib/components/common/Tooltip.svelte';
export let badge: BioSiteBadge;
const badgeObject = badges.find((x) => x.id == badge);
</script>
{#if badgeObject}
<a class="relative inline-block" href={badgeObject.link} aria-label={badgeObject.name}>
<div class="w-5 h-5 bg-cover bg-center peer" style="--icon: url({badgeObject.icon})" />
<div
class="relative opacity-0 peer-hover:opacity-100 -translate-y-5 peer-hover:-translate-y-8 transition-all"
aria-hidden="true">
<Tooltip>{badgeObject.name}</Tooltip>
</div>
</a>
{/if}
<style lang="postcss">
div {
background-image: var(--icon);
}
</style>

View file

@ -0,0 +1,11 @@
<script lang="ts">
import Badge from './Badge.svelte';
export let badges: BioSiteBadge[];
</script>
<div class="flex gap-0.5 items-center flex-wrap">
{#each badges as badge}
<Badge {badge} />
{/each}
</div>

View file

@ -0,0 +1,7 @@
<script lang="ts">
export let description: string | null | undefined = undefined;
</script>
{#if description}
<p class="text-text-primary break-words whitespace-pre-wrap">{description.trim()}</p>
{/if}

View file

@ -0,0 +1,22 @@
<script lang="ts">
import { faBriefcase, faGraduationCap, faLocationPin } from '@fortawesome/free-solid-svg-icons';
import ExtraItem from './ExtraItem.svelte';
export let location: string | undefined = undefined;
export let school: string | undefined = undefined;
export let workplace: string | undefined = undefined;
</script>
{#if location || school || workplace}
<div class="flex flex-wrap gap-x-3 gap-y-2">
{#if location}
<ExtraItem icon={faLocationPin} text={location.trim()} />
{/if}
{#if school}
<ExtraItem icon={faGraduationCap} text={school.trim()} />
{/if}
{#if workplace}
<ExtraItem icon={faBriefcase} text={workplace.trim()} />
{/if}
</div>
{/if}

View file

@ -0,0 +1,12 @@
<script lang="ts">
import type { IconDefinition } from '@fortawesome/free-solid-svg-icons';
import Fa from 'svelte-fa';
export let icon: IconDefinition;
export let text: string;
</script>
<div class="flex gap-1 text-text-secondary items-center">
<Fa {icon} />
<p class="break-words line-clamp-1">{text}</p>
</div>

View file

@ -0,0 +1,11 @@
<script lang="ts">
export let uid: number | null | undefined = undefined;
export let views: number | null | undefined = undefined;
</script>
{#if (uid || views) && uid !== -1}
<div class="border-t border-text-secondary pt-1 flex justify-between text-text-secondary text-xs">
{#if uid}<p>UID: {uid === -2 ? '??' : uid}</p>{/if}
{#if views}<p>VIEWS: {views}</p>{/if}
</div>
{/if}

View file

@ -0,0 +1,72 @@
<script lang="ts">
import BadgeContainer from './Badges.svelte';
export let identifier: string | null | undefined;
export let secondaryIdentifier: string | null | undefined;
export let badges: BioSiteBadge[] = [];
export let align: BioSiteAlign = 'LEFT';
export let placeholder: boolean = false;
</script>
<div class="flex flex-col" class:placeholder>
<div
class="primary-id text-xl font-semibold text-text-header flex gap-2 items-center"
class:justify-center={align === 'CENTER'}
class:justify-end={align === 'RIGHT'}>
<p class="break-words line-clamp-1">{(identifier ?? secondaryIdentifier ?? 'Unknown').trim()}</p>
{#if placeholder}
<div class="placeholder-container" />
{/if}
{#if badges.length > 0 && !placeholder}
<BadgeContainer {badges} />
{/if}
</div>
<div
class="secondary-id text-text-secondary"
class:text-center={align === 'CENTER'}
class:text-right={align === 'RIGHT'}>
<p class="break-words line-clamp-1">{(!identifier ? '' : secondaryIdentifier || '').trim()}</p>
{#if placeholder}
<div class="placeholder-container" />
{/if}
</div>
</div>
<style lang="postcss">
div.placeholder > * {
@apply w-fit px-2 overflow-hidden select-none rounded-md relative opacity-50;
}
div.placeholder > * {
@apply bg-text-teritary !text-text-teritary;
}
div.placeholder > div.primary-id {
@apply mb-[1px];
}
div.placeholder-container {
@apply absolute top-0 left-0 bottom-0 right-0;
}
div.placeholder div.placeholder-container {
animation: slide 1s infinite;
background: linear-gradient(
to right,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.8) 50%,
rgba(128, 186, 232, 0) 99%,
rgba(125, 185, 232, 0) 100%
);
}
@keyframes slide {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
</style>

View file

@ -0,0 +1,31 @@
<script lang="ts">
import type { Site } from '$lib/models/socials';
export let hoverInvert: boolean = false;
export let minimal: boolean = false;
export let site: Site | undefined;
export let social: BioSiteSocialLink;
</script>
{#if site}
<a
class="flex-grow"
style="--color: {site.color}"
href={`${site.type == 'EMAIL' ? 'mailto:' : site.baseUrl}${social.value}`}
target="_blank"
title={site.name}>
<div
class="w-full min-w-[95px] h-10 hover:opacity-80 transition-opacity rounded-lg flex justify-center items-center">
<img class="w-5 h-5" alt="Icon" src={site.icon} />
</div>
</a>
{/if}
<!-- -->
<style lang="postcss">
div {
background: var(--color);
}
</style>

View file

@ -0,0 +1,15 @@
<script lang="ts">
import Button from '$lib/components/dashboard/elements/Button.svelte';
import { toast } from '@zerodevx/svelte-toast';
export let social: BioSiteSocialText;
</script>
<Button
title={social.value}
onClick={async () => {
await navigator.clipboard.writeText(social.value);
toast.push('Copied to clipboard.');
}}>
{social.title}
</Button>

View file

@ -0,0 +1,92 @@
<script lang="ts">
import { getLinkType } from '$lib/models/socials';
import { editorStore } from '$lib/stores/editor';
import SocialLink from './SocialLink.svelte';
import SocialText from './SocialText.svelte';
import { SocialLink as SocialLinkType } from '$lib/models/socials';
import { browser } from '$app/environment';
export let socials: BioSiteSocials | undefined = undefined;
export let storelessSocials: boolean = false;
$: socialLinks = (
[
...(socials?.links ?? [])
.map((x) => {
const remove = $editorStore.edits.edits.deleteSocialLinks.map((x) => x.id).findIndex((y) => y === x.id);
if (remove !== -1) return null;
return x;
})
.filter((x) => x),
...$editorStore.edits.edits.createSocialLinks
.filter(() => !storelessSocials)
.map((x) => ({ ...x, type: Object.values(SocialLinkType)[x.type] } as BioSiteSocialLink)),
].filter((x) => x) as BioSiteSocialLink[]
)
.map((x) => {
const index = $editorStore.edits.edits.indexSocialLinks.findIndex((y) => x.id == y.socialId);
if (index === -1) return x;
return { ...x, index: $editorStore.edits.edits.indexSocialLinks[index].index ?? -1 };
})
.sort((a, b) => a.index - b.index);
$: socialTexts = (
[
...(socials?.texts ?? [])
.map((x) => {
const remove = $editorStore.edits.edits.deleteSocialTexts.map((x) => x.id).findIndex((y) => y === x.id);
if (remove !== -1) return null;
return x;
})
.filter((x) => x),
...$editorStore.edits.edits.createSocialTexts.filter(() => !storelessSocials),
].filter((x) => x) as BioSiteSocialText[]
)
.map((x) => {
const index = $editorStore.edits.edits.indexSocialTexts.findIndex((y) => x.id == y.socialId);
if (index === -1) return x;
return { ...x, index: $editorStore.edits.edits.indexSocialTexts[index].index ?? -1 };
})
.sort((a, b) => a.index - b.index);
</script>
{#key socials}
{#if socials}
{#key socialLinks}
{#if socialLinks.length > 0}
<div class="flex flex-wrap gap-1.5" class:edit={$editorStore.editMode}>
{#each socialLinks as link}
<SocialLink
site={getLinkType(link.type)}
social={link}
hoverInvert={socials.invert}
minimal={socials.minimal} />
{/each}
</div>
{/if}
{/key}
{#if browser}
{#key socialTexts}
{#if socialTexts.length > 0}
<div class="flex flex-col gap-1.5" class:edit={$editorStore.editMode}>
{#each socialTexts as text}
<SocialText social={text} />
{/each}
</div>
{/if}
{/key}
{/if}
{/if}
{/key}
<style lang="postcss">
div {
@apply select-none;
}
div.edit {
@apply pointer-events-none;
}
</style>