Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor copy function into its own folder + fix blog typo #629

Merged
merged 20 commits into from Oct 29, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/lib/components/bw-icon/bw-icon.svelte
@@ -0,0 +1,14 @@
<script lang="ts">
export let src: string;
export let alt: string;
</script>

<img {src} {alt} />

<style lang="scss">
:global(body.dark) {
img {
filter: invert(1);
}
}
</style>
2 changes: 1 addition & 1 deletion src/lib/components/toaster/toaster.svelte
Expand Up @@ -9,7 +9,7 @@
return `--error-rgb`;
case ToastType.Success:
default:
return `--acm-${toast.path}-rgb`;
return `--acm-${toast.teamId}-rgb`;
}
}
</script>
Expand Down
6 changes: 3 additions & 3 deletions src/lib/components/toaster/toasts.ts
Expand Up @@ -17,22 +17,22 @@ export interface Toast {
type?: ToastType;
dismissible?: boolean;
timeout?: number;
path?: string;
teamId?: string;
}

export const toasts = writable<Toast[]>([]);

function makeToast(
id: number,
{ content, type, dismissible, timeout, path }: Omit<Toast, 'id'>
{ content, type, dismissible, timeout, teamId }: Omit<Toast, 'id'>
): Required<Toast> {
return {
id,
content: content,
type: type ?? ToastType.Success,
dismissible: dismissible ?? true,
timeout: timeout ?? TOAST_TIMEOUT,
path: path ?? acmGeneral.slug,
teamId: teamId ?? acmGeneral.slug,
};
}

Expand Down
10 changes: 10 additions & 0 deletions src/lib/public/copy/copy.ts
@@ -0,0 +1,10 @@
import { toast, ToastType } from '$lib/components/toaster/toasts';

/** @see <https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText> */
anhduy1202 marked this conversation as resolved.
Show resolved Hide resolved
/* copy text link and display a toast pop up with a success message */
export function copy(link: string, successMessage: string, errorMessage: string, teamId: string) {
navigator.clipboard
.writeText(link)
.then(() => toast({ content: successMessage, teamId }))
.catch(() => toast({ teamId, type: ToastType.Error, content: errorMessage }));
}
18 changes: 7 additions & 11 deletions src/routes/(site)/about/officer-profile.svelte
Expand Up @@ -6,9 +6,9 @@
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 { toast } from '$lib/components/toaster/toasts';
import { onMount } from 'svelte';
import { termIndex, getPositionByTermIndex } from '$lib/public/board/utils';
import { copy } from '$lib/public/copy/copy';

export let info: Officer;
export let placeholderPicture = 'placeholder.webp';
Expand Down Expand Up @@ -61,17 +61,13 @@
? officerPosition.replace(teamName, `<b class="${teamClass}">${teamName}</b>`)
: officerPosition;

/** @see <https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText> */
function copyDiscord() {
toast(async () => {
try {
await navigator.clipboard.writeText(officerSocials.discord || '');
return `Copied ${officerName}'s Discord tag to clipboard!`;
} catch (err) {
console.error(err);
throw `Error occured while copy Discord tag to clipboard.`;
}
});
copy(
officerSocials.discord || '',
`Copied ${officerName}'s Discord tag to clipboard!`,
'Error occured while copy Discord tag to clipboard.',
''
);
}

onMount(() => {
Expand Down
18 changes: 4 additions & 14 deletions src/routes/(site)/blog/[id]/+page.svelte
Expand Up @@ -6,21 +6,16 @@
import Labels from '$lib/components/blog/labels.svelte';
import BlogBody from './blog-body.svelte';
import { onMount } from 'svelte';
anhduy1202 marked this conversation as resolved.
Show resolved Hide resolved
import { toast, ToastType } from '$lib/components/toaster/toasts';
import { copy } from '$lib/public/copy/copy';
export let data: PageData;
onMount(() => {
anhduy1202 marked this conversation as resolved.
Show resolved Hide resolved
const copyBtns = document.querySelectorAll('.copy-code');
for (let button of copyBtns) {
button.addEventListener('click', (event: any) => {
for (let button of document.querySelectorAll('.copy-code')) {
button.addEventListener('click', (event: Event) => {
const content =
(event.target as HTMLElement).offsetParent?.attributes.getNamedItem(
'data-snippet-clipboard-copy-content'
)?.value ?? '';

navigator.clipboard
.writeText(content)
.then(() => toast({ content: 'Copied' }))
.catch(() => toast({ type: ToastType.Error, content: 'Failed to copy' }));
copy(content, 'Copied', 'Failed to copy', 'general');
});
}
});
Expand All @@ -34,11 +29,6 @@

<section>
<h1 class="headers size-lg">{data.post.title}</h1>

const content = (event.target as HTMLElement).offsetParent?.attributes.getNamedItem(
'data-snippet-clipboard-copy-content' )?.value ?? '' const content = (event.target as
HTMLElement).offsetParent?.attributes.getNamedItem( 'data-snippet-clipboard-copy-content' )?.value
?? '';;
<Spacing --min="16px" --med="16px" --max="16px" />
<img src={data.post.author.picture} alt="" />
<Spacing --min="16px" --med="16px" --max="16px" />
Expand Down
91 changes: 58 additions & 33 deletions src/routes/(site)/blog/[id]/blog-body.svelte
@@ -1,41 +1,41 @@
<script lang="ts">
import { onMount } from 'svelte';

export let data = '';

function observerCallback(body: HTMLBodyElement, copySvg: HTMLImageElement) {
return (mutationList: Array<MutationRecord>) => {
mutationList.forEach((mutation: MutationRecord) => {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
// handle class change
copySvg.src = body?.classList.contains('light')
? '/assets/svg/copy-text.svg'
: '/assets/svg/light/copy-text.svg';
}
});
};
}

onMount(() => {
let pre = document.querySelectorAll('pre');
const svg = `<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"
><rect
x="128"
y="128"
width="336"
height="336"
rx="57"
ry="57"
fill="none"
stroke="currentColor"
stroke-linejoin="round"
stroke-width="32"
/><path
d="M383.5 128l.5-24a56.16 56.16 0 00-56-56H112a64.19 64.19 0 00-64 64v216a56.16 56.16 0 0056 56h24"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/></svg
>`;
const svgCode = encodeURI(svg);
for (let code of pre) {
let parentDiv = code.parentElement;
if (parentDiv) {
parentDiv!.style.position = 'relative';
}
let copyBtn = document.createElement('button');
const body = document.querySelector('body');
if (!body) return;

for (const code of document.querySelectorAll('pre')) {
const copySvg: HTMLImageElement = document.createElement('img');
copySvg.src = body?.classList.contains('light')
? '/assets/svg/copy-text.svg'
: '/assets/svg/light/copy-text.svg';

code.parentElement?.classList.add('copy-code-parent');

const observer = new MutationObserver(observerCallback(body, copySvg));
observer.observe(body, { attributes: true });

const copyBtn: HTMLButtonElement = document.createElement('button');
copyBtn.classList.add('copy-code');
copyBtn.style.cssText =
'position: absolute; padding: 1rem; top: 1rem; right: 1rem; outline: none; background-color: transparent; border:none; cursor: pointer';
copyBtn.style.backgroundImage = 'url(data:image/svg+xml;utf8,' + svgCode + ')';
anhduy1202 marked this conversation as resolved.
Show resolved Hide resolved
parentDiv?.appendChild(copyBtn);
copySvg.classList.add('copy-code-icon');
code.parentElement?.appendChild(copySvg);
code.parentElement?.appendChild(copyBtn);
}
});
</script>
Expand All @@ -60,6 +60,31 @@
margin: 1.5rem 0 0.5rem;
}

:global(.copy-code-parent) {
position: relative;
}

:global(.copy-code) {
position: absolute;
padding: 1rem;
top: 1rem;
right: 1rem;
outline: none;
background-color: transparent;
border: none;
cursor: pointer;
}

:global(.copy-code-icon) {
position: absolute;
top: 1rem;
width: 2em;
height: 2em;
right: 1rem;
border: none;
cursor: pointer;
}

:global(code),
:global(pre) {
font-family: monospace;
Expand Down
16 changes: 4 additions & 12 deletions src/routes/(site)/events/event.svelte
@@ -1,26 +1,18 @@
<script lang="ts">
import { onMount } from 'svelte';
import type { ClubEvent } from '$lib/public/events/event';
import { toast, ToastType } from '$lib/components/toaster/toasts';
import { copy } from '$lib/public/copy/copy';
import CopyLink from '$lib/components/svg/copy-link.svelte';
import CopyText from '$lib/components/svg/copy-text.svelte';
import CalendarGoogle from '$lib/components/svg/calendar-google.svelte';
import CalendarOutlook from '$lib/components/svg/calendar-outlook.svelte';
import BwIcon from '$lib/components/bw-icon/bw-icon.svelte';

export let info: ClubEvent;

let isRecurring: boolean = info.recurring;
let anchor: HTMLElement;
let details: HTMLDetailsElement;

/** @see <https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText> */
function copy(link: string, successMessage: string, errorMessage: string, path: string) {
navigator.clipboard
.writeText(link)
.then(() => toast({ content: successMessage, path }))
.catch(() => toast({ path, type: ToastType.Error, content: errorMessage }));
}

function formatLocation(location?: string | null, hosted = ['Discord', 'Zoom']): string {
// '', null, and undefined are all TBD
location = location?.trim() ?? '';
Expand Down Expand Up @@ -108,7 +100,7 @@
info.acmPath.slug
)}
>
<CopyText />
<BwIcon src="/assets/svg/copy-text.svg" alt="Copy event summary" />
</button>

<button
Expand Down Expand Up @@ -306,7 +298,7 @@
transition: all 0.25s ease-in-out;
border-radius: 30px;
border: 2px solid var(--acm-dark);
background-color: var(--acm-light);
background-color: transparent;
}

.action-item:hover {
Expand Down
5 changes: 2 additions & 3 deletions src/routes/+layout.svelte
Expand Up @@ -7,12 +7,11 @@

import { page } from '$app/stores';
import { browser, dev } from '$app/environment';
import { VERCEL_ANALYTICS_ID } from '$env/static/public';
import { send } from '$lib/public/analytics/vitals';

$: if (browser && !dev && VERCEL_ANALYTICS_ID) {
$: if (browser && !dev) {
send({
id: VERCEL_ANALYTICS_ID,
id: '$VERCEL_ANALYTICS_ID',
path: $page.url.pathname,
params: $page.params,
navigator,
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions static/assets/svg/light/copy-text.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.