2024-08-06 10:48:06 +02:00
|
|
|
<script lang="ts">
|
2024-08-09 07:31:44 +02:00
|
|
|
import { donate } from "$lib/env";
|
|
|
|
import { t } from "$lib/i18n/translations";
|
|
|
|
|
2024-08-09 09:13:48 +02:00
|
|
|
import DonateCardContainer from "$components/donate/DonateCardContainer.svelte";
|
2024-08-09 07:31:44 +02:00
|
|
|
import DonationOption from "$components/donate/DonationOption.svelte";
|
2024-08-06 10:48:06 +02:00
|
|
|
|
2024-08-30 17:33:11 +02:00
|
|
|
import IconCoin from "@tabler/icons-svelte/IconCoin.svelte";
|
|
|
|
import IconCalendarRepeat from "@tabler/icons-svelte/IconCalendarRepeat.svelte";
|
2024-08-06 12:45:25 +02:00
|
|
|
import IconCup from "@tabler/icons-svelte/IconCup.svelte";
|
|
|
|
import IconPizza from "@tabler/icons-svelte/IconPizza.svelte";
|
|
|
|
import IconToolsKitchen2 from "@tabler/icons-svelte/IconToolsKitchen2.svelte";
|
2024-08-30 17:33:11 +02:00
|
|
|
import IconPaperBag from "@tabler/icons-svelte/IconPaperBag.svelte";
|
2024-08-09 07:31:44 +02:00
|
|
|
import IconArrowRight from "@tabler/icons-svelte/IconArrowRight.svelte";
|
2024-08-30 17:33:11 +02:00
|
|
|
import IconSoup from "@tabler/icons-svelte/IconSoup.svelte";
|
|
|
|
import IconFridge from "@tabler/icons-svelte/IconFridge.svelte";
|
|
|
|
import IconArmchair from "@tabler/icons-svelte/IconArmchair.svelte";
|
|
|
|
import IconDeviceLaptop from "@tabler/icons-svelte/IconDeviceLaptop.svelte";
|
|
|
|
import IconApple from "@tabler/icons-svelte/IconApple.svelte";
|
|
|
|
import IconPhoto from "@tabler/icons-svelte/IconPhoto.svelte";
|
|
|
|
import IconWorldWww from "@tabler/icons-svelte/IconWorldWww.svelte";
|
|
|
|
import IconBath from "@tabler/icons-svelte/IconBath.svelte";
|
2024-09-09 14:57:58 +02:00
|
|
|
import OuterLink from "$components/misc/OuterLink.svelte";
|
2024-08-06 10:48:06 +02:00
|
|
|
|
2024-08-06 20:28:54 +02:00
|
|
|
let customInput: HTMLInputElement;
|
2024-08-06 20:26:53 +02:00
|
|
|
let customInputValue: number | null;
|
2024-08-06 20:29:39 +02:00
|
|
|
let customFocused = false;
|
2024-08-06 10:48:06 +02:00
|
|
|
|
2024-08-30 17:33:11 +02:00
|
|
|
const PRESET_DONATION_AMOUNTS = {
|
|
|
|
5: IconCup,
|
|
|
|
10: IconPizza,
|
|
|
|
15: IconSoup,
|
|
|
|
30: IconToolsKitchen2,
|
|
|
|
50: IconPaperBag,
|
|
|
|
100: IconWorldWww,
|
|
|
|
200: IconFridge,
|
|
|
|
500: IconArmchair,
|
|
|
|
1599: IconDeviceLaptop,
|
|
|
|
4900: IconApple,
|
|
|
|
7398: IconDeviceLaptop,
|
|
|
|
8629: IconPhoto,
|
|
|
|
9433: IconBath
|
|
|
|
};
|
|
|
|
|
2024-08-06 20:26:53 +02:00
|
|
|
type Processor = "stripe" | "liberapay";
|
|
|
|
let processor: Processor = "stripe";
|
2024-08-06 10:48:06 +02:00
|
|
|
|
2024-09-09 14:57:58 +02:00
|
|
|
const donationMethods: Record<Processor, (_: number) => string> = {
|
2024-08-06 20:26:53 +02:00
|
|
|
stripe: (amount: number) => {
|
|
|
|
const url = new URL(donate.stripe);
|
|
|
|
url.searchParams.set("__prefilled_amount", amount.toString());
|
2024-09-09 14:57:58 +02:00
|
|
|
return url.toString();
|
2024-08-06 20:26:53 +02:00
|
|
|
},
|
|
|
|
liberapay: (amount: number) => {
|
|
|
|
const url = new URL(donate.liberapay);
|
|
|
|
url.searchParams.set("currency", "USD");
|
|
|
|
url.searchParams.set("period", "monthly");
|
|
|
|
url.searchParams.set("amount", (amount / 100).toString());
|
2024-09-09 14:57:58 +02:00
|
|
|
return url.toString();
|
2024-08-09 07:31:44 +02:00
|
|
|
},
|
2024-08-06 12:45:25 +02:00
|
|
|
};
|
2024-08-06 10:48:06 +02:00
|
|
|
|
2024-08-06 20:28:54 +02:00
|
|
|
const sendCustom = () => {
|
|
|
|
if (!customInput.reportValidity()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-09-09 15:00:06 +02:00
|
|
|
const amount = Math.floor(Number(customInputValue) * 100);
|
|
|
|
return window.open(donationMethods[processor](amount), '_blank');
|
2024-08-09 07:31:44 +02:00
|
|
|
};
|
2024-08-06 10:48:06 +02:00
|
|
|
</script>
|
|
|
|
|
2024-08-09 09:13:48 +02:00
|
|
|
<DonateCardContainer id="donation-box">
|
2024-08-09 08:04:43 +02:00
|
|
|
<div id="donation-types" role="tablist" aria-orientation="horizontal">
|
2024-08-06 20:26:53 +02:00
|
|
|
<button
|
|
|
|
id="donation-type-once"
|
|
|
|
class="donation-type"
|
2024-08-09 07:31:44 +02:00
|
|
|
on:click={() => (processor = "stripe")}
|
|
|
|
class:selected={processor === "stripe"}
|
2024-08-09 08:04:43 +02:00
|
|
|
aria-selected={processor === "stripe"}
|
|
|
|
role="tab"
|
2024-08-06 20:26:53 +02:00
|
|
|
>
|
2024-08-06 12:45:25 +02:00
|
|
|
<div class="donation-type-icon"><IconCoin /></div>
|
2024-08-09 10:35:55 +02:00
|
|
|
<div class="donation-type-text">
|
|
|
|
<div class="donate-card-title">{$t("donate.card.once")}</div>
|
|
|
|
<div class="donate-card-subtitle">
|
2024-09-04 19:01:51 +02:00
|
|
|
{$t("donate.card.processor", { value: 'stripe' })}
|
2024-08-09 10:35:55 +02:00
|
|
|
</div>
|
2024-08-09 07:31:44 +02:00
|
|
|
</div>
|
2024-08-06 12:45:25 +02:00
|
|
|
</button>
|
2024-08-06 20:26:53 +02:00
|
|
|
<button
|
|
|
|
id="donation-type-monthly"
|
|
|
|
class="donation-type"
|
2024-08-09 07:31:44 +02:00
|
|
|
on:click={() => (processor = "liberapay")}
|
|
|
|
class:selected={processor === "liberapay"}
|
2024-08-09 08:04:43 +02:00
|
|
|
aria-selected={processor === "liberapay"}
|
|
|
|
role="tab"
|
2024-08-06 20:26:53 +02:00
|
|
|
>
|
2024-08-06 12:45:25 +02:00
|
|
|
<div class="donation-type-icon"><IconCalendarRepeat /></div>
|
2024-08-09 10:35:55 +02:00
|
|
|
<div class="donation-type-text">
|
|
|
|
<div class="donate-card-title">{$t("donate.card.monthly")}</div>
|
|
|
|
<div class="donate-card-subtitle">
|
2024-09-04 19:01:51 +02:00
|
|
|
{$t("donate.card.processor", { value: 'liberapay' })}
|
2024-08-09 10:35:55 +02:00
|
|
|
</div>
|
2024-08-09 07:31:44 +02:00
|
|
|
</div>
|
2024-08-06 12:45:25 +02:00
|
|
|
</button>
|
2024-08-06 10:48:06 +02:00
|
|
|
</div>
|
2024-08-06 12:45:25 +02:00
|
|
|
<div id="donation-options">
|
2024-08-30 17:33:11 +02:00
|
|
|
{#each Object.entries(PRESET_DONATION_AMOUNTS) as [ amount, component ]}
|
2024-09-09 14:57:58 +02:00
|
|
|
<OuterLink href={donationMethods[processor](+amount * 100)}>
|
|
|
|
<DonationOption price={+amount} desc={$t(`donate.card.option.${amount}`)}>
|
|
|
|
<svelte:component this={component} />
|
|
|
|
</DonationOption>
|
|
|
|
</OuterLink>
|
2024-08-30 17:33:11 +02:00
|
|
|
{/each}
|
2024-08-06 10:48:06 +02:00
|
|
|
</div>
|
2024-08-06 12:45:25 +02:00
|
|
|
<div id="donation-custom">
|
2024-08-09 07:31:44 +02:00
|
|
|
<div id="input-container" class:focused={customFocused}>
|
2024-08-06 20:29:39 +02:00
|
|
|
{#if customInputValue || customInput?.validity.badInput}
|
2024-08-09 07:31:44 +02:00
|
|
|
<span id="input-dollar-sign">$</span>
|
2024-08-06 20:29:39 +02:00
|
|
|
{/if}
|
|
|
|
<input
|
|
|
|
id="donation-custom-input"
|
|
|
|
type="number"
|
|
|
|
min="2"
|
|
|
|
max="10000"
|
|
|
|
step=".01"
|
|
|
|
required
|
2024-08-09 07:31:44 +02:00
|
|
|
placeholder={$t("donate.card.custom")}
|
2024-08-06 20:29:39 +02:00
|
|
|
bind:this={customInput}
|
|
|
|
bind:value={customInputValue}
|
2024-08-09 07:31:44 +02:00
|
|
|
on:input={() => (customFocused = true)}
|
|
|
|
on:focus={() => (customFocused = true)}
|
|
|
|
on:blur={() => (customFocused = false)}
|
|
|
|
on:keydown={(e) => e.key === "Enter" && sendCustom()}
|
2024-08-06 20:29:39 +02:00
|
|
|
/>
|
|
|
|
</div>
|
2024-08-09 08:04:43 +02:00
|
|
|
<button
|
|
|
|
id="donation-custom-submit"
|
|
|
|
on:click={sendCustom}
|
|
|
|
aria-label={$t("donate.card.custom.submit")}
|
|
|
|
type="submit"
|
|
|
|
>
|
2024-08-06 12:45:25 +02:00
|
|
|
<IconArrowRight />
|
|
|
|
</button>
|
2024-08-06 10:48:06 +02:00
|
|
|
</div>
|
2024-08-09 09:13:48 +02:00
|
|
|
<div class="donate-card-subtitle processor-mobile">
|
2024-09-04 19:01:51 +02:00
|
|
|
{$t("donate.card.processor", { value: processor })}
|
2024-08-09 07:31:44 +02:00
|
|
|
</div>
|
2024-08-09 09:13:48 +02:00
|
|
|
</DonateCardContainer>
|
2024-08-06 10:48:06 +02:00
|
|
|
|
|
|
|
<style>
|
2024-08-09 09:13:48 +02:00
|
|
|
:global(#donation-box) {
|
2024-08-09 12:06:19 +02:00
|
|
|
min-width: 300px;
|
2024-08-09 09:13:48 +02:00
|
|
|
padding: var(--donate-card-main-padding) 0;
|
2024-08-06 10:48:06 +02:00
|
|
|
}
|
|
|
|
|
2024-08-09 07:46:56 +02:00
|
|
|
#donation-types,
|
|
|
|
#donation-options,
|
|
|
|
#donation-custom {
|
2024-08-09 09:13:48 +02:00
|
|
|
padding: 0 var(--donate-card-main-padding);
|
2024-08-09 07:46:56 +02:00
|
|
|
}
|
|
|
|
|
2024-08-06 12:45:25 +02:00
|
|
|
#donation-types {
|
|
|
|
display: flex;
|
|
|
|
flex-direction: row;
|
2024-08-09 09:13:48 +02:00
|
|
|
gap: var(--donate-card-padding);
|
2024-08-09 07:31:44 +02:00
|
|
|
overflow: scroll;
|
2024-08-06 12:45:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.donation-type {
|
|
|
|
width: 100%;
|
2024-08-09 07:31:44 +02:00
|
|
|
overflow: hidden;
|
2024-08-09 10:35:55 +02:00
|
|
|
gap: 2px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.donation-type-icon {
|
|
|
|
display: flex;
|
2024-08-06 12:45:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.donation-type-icon :global(svg) {
|
|
|
|
width: 28px;
|
|
|
|
height: 28px;
|
2024-08-09 10:35:55 +02:00
|
|
|
stroke-width: 1.8px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.donation-type-text {
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
2024-08-06 10:48:06 +02:00
|
|
|
}
|
|
|
|
|
2024-08-06 12:45:25 +02:00
|
|
|
#donation-options {
|
2024-08-06 10:48:06 +02:00
|
|
|
display: flex;
|
2024-08-06 12:45:25 +02:00
|
|
|
overflow-x: scroll;
|
|
|
|
gap: 6px;
|
2024-08-09 07:46:56 +02:00
|
|
|
mask-image: linear-gradient(
|
|
|
|
90deg,
|
|
|
|
rgba(0, 0, 0, 0) 0%,
|
|
|
|
rgba(0, 0, 0, 1) 4%,
|
|
|
|
rgba(0, 0, 0, 1) 50%,
|
|
|
|
rgba(0, 0, 0, 1) 96%,
|
|
|
|
rgba(0, 0, 0, 0) 100%
|
|
|
|
);
|
2024-08-06 10:48:06 +02:00
|
|
|
}
|
|
|
|
|
2024-08-06 12:45:25 +02:00
|
|
|
#donation-custom {
|
2024-08-06 10:48:06 +02:00
|
|
|
display: flex;
|
2024-08-06 12:45:25 +02:00
|
|
|
gap: 6px;
|
2024-08-09 07:31:44 +02:00
|
|
|
overflow: scroll;
|
2024-08-06 10:48:06 +02:00
|
|
|
}
|
|
|
|
|
2024-08-06 20:29:39 +02:00
|
|
|
#input-container {
|
2024-08-30 11:11:28 +02:00
|
|
|
padding: 0 18px;
|
2024-08-06 12:45:25 +02:00
|
|
|
width: 100%;
|
|
|
|
border-radius: 12px;
|
|
|
|
color: var(--white);
|
|
|
|
background-color: rgba(255, 255, 255, 0.1);
|
2024-08-06 20:29:39 +02:00
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
gap: 4px;
|
|
|
|
}
|
|
|
|
|
|
|
|
#input-dollar-sign {
|
2024-08-09 07:31:44 +02:00
|
|
|
animation: grow-in 0.05s linear;
|
2024-08-06 20:29:39 +02:00
|
|
|
display: block;
|
|
|
|
}
|
|
|
|
|
|
|
|
@keyframes grow-in {
|
2024-08-09 07:31:44 +02:00
|
|
|
from {
|
|
|
|
font-size: 0;
|
|
|
|
}
|
|
|
|
to {
|
|
|
|
font-size: inherit;
|
|
|
|
}
|
2024-08-06 20:29:39 +02:00
|
|
|
}
|
|
|
|
|
2024-08-09 07:31:44 +02:00
|
|
|
#input-container,
|
|
|
|
#donation-custom-input {
|
2024-08-06 20:29:39 +02:00
|
|
|
font-size: 13px;
|
|
|
|
}
|
|
|
|
|
|
|
|
#donation-custom-input {
|
|
|
|
flex: 1;
|
|
|
|
background-color: transparent;
|
|
|
|
color: var(--white);
|
2024-08-06 10:48:06 +02:00
|
|
|
border: none;
|
2024-08-09 07:31:44 +02:00
|
|
|
padding-block: 0;
|
|
|
|
padding-inline: 0;
|
2024-08-30 11:11:28 +02:00
|
|
|
padding: 12px 0;
|
2024-08-30 11:03:04 +02:00
|
|
|
appearance: textfield;
|
2024-08-09 07:31:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#donation-custom-input::placeholder {
|
2024-08-09 08:27:49 +02:00
|
|
|
color: var(--white);
|
|
|
|
opacity: 0.5;
|
2024-08-06 10:48:06 +02:00
|
|
|
}
|
|
|
|
|
2024-08-06 20:29:39 +02:00
|
|
|
#donation-custom-input:focus-visible {
|
|
|
|
box-shadow: unset !important;
|
|
|
|
}
|
|
|
|
|
|
|
|
#input-container.focused {
|
2024-08-29 16:58:49 +02:00
|
|
|
box-shadow: 0 0 0 2px var(--white) inset;
|
2024-08-06 20:29:39 +02:00
|
|
|
}
|
|
|
|
|
2024-08-06 12:45:25 +02:00
|
|
|
#donation-custom-submit {
|
|
|
|
color: var(--white);
|
|
|
|
background-color: rgba(255, 255, 255, 0.1);
|
|
|
|
aspect-ratio: 1/1;
|
2024-08-09 07:31:44 +02:00
|
|
|
padding: 0px 10px;
|
2024-08-06 10:48:06 +02:00
|
|
|
}
|
|
|
|
|
2024-08-06 12:45:25 +02:00
|
|
|
#donation-custom-submit :global(svg) {
|
|
|
|
width: 24px;
|
|
|
|
height: 24px;
|
2024-08-06 10:48:06 +02:00
|
|
|
}
|
2024-08-06 12:46:32 +02:00
|
|
|
|
|
|
|
#donation-custom-input::-webkit-outer-spin-button,
|
|
|
|
#donation-custom-input::-webkit-inner-spin-button {
|
|
|
|
-webkit-appearance: none;
|
|
|
|
margin: 0;
|
|
|
|
}
|
2024-08-09 07:31:44 +02:00
|
|
|
|
|
|
|
.processor-mobile {
|
|
|
|
display: none;
|
|
|
|
text-align: center;
|
|
|
|
}
|
|
|
|
|
|
|
|
@media screen and (max-width: 550px) {
|
2024-08-09 12:06:19 +02:00
|
|
|
:global(#donation-box) {
|
|
|
|
min-width: unset;
|
|
|
|
}
|
|
|
|
|
2024-08-09 09:13:48 +02:00
|
|
|
:global(#donation-box .donate-card-title) {
|
|
|
|
font-size: 14px;
|
2024-08-09 07:31:44 +02:00
|
|
|
}
|
|
|
|
|
2024-08-09 09:13:48 +02:00
|
|
|
:global(#donation-box .donate-card-subtitle) {
|
|
|
|
font-size: 12px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.donation-type-icon :global(svg) {
|
|
|
|
width: 26px;
|
|
|
|
height: 26px;
|
2024-08-09 07:31:44 +02:00
|
|
|
}
|
|
|
|
|
2024-08-09 09:13:48 +02:00
|
|
|
.donation-type .donate-card-subtitle {
|
2024-08-09 07:31:44 +02:00
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
.processor-mobile {
|
|
|
|
display: block;
|
|
|
|
}
|
|
|
|
}
|
2024-09-09 14:57:58 +02:00
|
|
|
|
|
|
|
#donation-options > :global(a) {
|
|
|
|
text-decoration: none;
|
|
|
|
}
|
2024-08-06 10:48:06 +02:00
|
|
|
</style>
|