mirror of
https://github.com/imputnet/cobalt.git
synced 2024-12-28 18:46:09 +00:00
web/DonateOptionsCard: add scroll buttons to the options container
cuz users without touchpads couldn't scroll it without tabbing
This commit is contained in:
parent
5a4be4890b
commit
6ba27f8369
|
@ -1,5 +1,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import settings from "$lib/state/settings";
|
||||||
|
|
||||||
import { donate } from "$lib/env";
|
import { donate } from "$lib/env";
|
||||||
|
import { device } from "$lib/device";
|
||||||
import { t } from "$lib/i18n/translations";
|
import { t } from "$lib/i18n/translations";
|
||||||
|
|
||||||
import DonateCardContainer from "$components/donate/DonateCardContainer.svelte";
|
import DonateCardContainer from "$components/donate/DonateCardContainer.svelte";
|
||||||
|
@ -11,7 +14,6 @@
|
||||||
import IconPizza from "@tabler/icons-svelte/IconPizza.svelte";
|
import IconPizza from "@tabler/icons-svelte/IconPizza.svelte";
|
||||||
import IconToolsKitchen2 from "@tabler/icons-svelte/IconToolsKitchen2.svelte";
|
import IconToolsKitchen2 from "@tabler/icons-svelte/IconToolsKitchen2.svelte";
|
||||||
import IconPaperBag from "@tabler/icons-svelte/IconPaperBag.svelte";
|
import IconPaperBag from "@tabler/icons-svelte/IconPaperBag.svelte";
|
||||||
import IconArrowRight from "@tabler/icons-svelte/IconArrowRight.svelte";
|
|
||||||
import IconSoup from "@tabler/icons-svelte/IconSoup.svelte";
|
import IconSoup from "@tabler/icons-svelte/IconSoup.svelte";
|
||||||
import IconFridge from "@tabler/icons-svelte/IconFridge.svelte";
|
import IconFridge from "@tabler/icons-svelte/IconFridge.svelte";
|
||||||
import IconArmchair from "@tabler/icons-svelte/IconArmchair.svelte";
|
import IconArmchair from "@tabler/icons-svelte/IconArmchair.svelte";
|
||||||
|
@ -20,7 +22,11 @@
|
||||||
import IconPhoto from "@tabler/icons-svelte/IconPhoto.svelte";
|
import IconPhoto from "@tabler/icons-svelte/IconPhoto.svelte";
|
||||||
import IconWorldWww from "@tabler/icons-svelte/IconWorldWww.svelte";
|
import IconWorldWww from "@tabler/icons-svelte/IconWorldWww.svelte";
|
||||||
import IconBath from "@tabler/icons-svelte/IconBath.svelte";
|
import IconBath from "@tabler/icons-svelte/IconBath.svelte";
|
||||||
import OuterLink from "$components/misc/OuterLink.svelte";
|
|
||||||
|
import IconArrowLeft from "@tabler/icons-svelte/IconArrowLeft.svelte";
|
||||||
|
import IconArrowRight from "@tabler/icons-svelte/IconArrowRight.svelte";
|
||||||
|
|
||||||
|
let donateList: HTMLElement;
|
||||||
|
|
||||||
let customInput: HTMLInputElement;
|
let customInput: HTMLInputElement;
|
||||||
let customInputValue: number | null;
|
let customInputValue: number | null;
|
||||||
|
@ -39,7 +45,7 @@
|
||||||
4900: IconApple,
|
4900: IconApple,
|
||||||
7398: IconDeviceLaptop,
|
7398: IconDeviceLaptop,
|
||||||
8629: IconPhoto,
|
8629: IconPhoto,
|
||||||
9433: IconBath
|
9433: IconBath,
|
||||||
};
|
};
|
||||||
|
|
||||||
type Processor = "stripe" | "liberapay";
|
type Processor = "stripe" | "liberapay";
|
||||||
|
@ -66,7 +72,30 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const amount = Math.floor(Number(customInputValue) * 100);
|
const amount = Math.floor(Number(customInputValue) * 100);
|
||||||
return window.open(donationMethods[processor](amount), '_blank');
|
return window.open(donationMethods[processor](amount), "_blank");
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrollBehavior = $settings.appearance.reduceMotion
|
||||||
|
? "instant"
|
||||||
|
: "smooth";
|
||||||
|
|
||||||
|
$: showLeftScroll = false;
|
||||||
|
$: showRightScroll = true;
|
||||||
|
|
||||||
|
const scroll = (direction: "left" | "right") => {
|
||||||
|
const currentPos = donateList.scrollLeft;
|
||||||
|
const newPos = direction === "left" ? currentPos - 150 : currentPos + 150;
|
||||||
|
const maxPos = donateList.scrollWidth - donateList.getBoundingClientRect().width;
|
||||||
|
|
||||||
|
donateList.scroll({
|
||||||
|
left: newPos,
|
||||||
|
behavior: scrollBehavior,
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
showLeftScroll = newPos > 0;
|
||||||
|
showRightScroll = newPos < maxPos && newPos !== maxPos;
|
||||||
|
}, 150)
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -84,10 +113,11 @@
|
||||||
<div class="donation-type-text">
|
<div class="donation-type-text">
|
||||||
<div class="donate-card-title">{$t("donate.card.once")}</div>
|
<div class="donate-card-title">{$t("donate.card.once")}</div>
|
||||||
<div class="donate-card-subtitle">
|
<div class="donate-card-subtitle">
|
||||||
{$t("donate.card.processor", { value: 'stripe' })}
|
{$t("donate.card.processor", { value: "stripe" })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
id="donation-type-monthly"
|
id="donation-type-monthly"
|
||||||
class="donation-type"
|
class="donation-type"
|
||||||
|
@ -100,27 +130,65 @@
|
||||||
<div class="donation-type-text">
|
<div class="donation-type-text">
|
||||||
<div class="donate-card-title">{$t("donate.card.monthly")}</div>
|
<div class="donate-card-title">{$t("donate.card.monthly")}</div>
|
||||||
<div class="donate-card-subtitle">
|
<div class="donate-card-subtitle">
|
||||||
{$t("donate.card.processor", { value: 'liberapay' })}
|
{$t("donate.card.processor", { value: "liberapay" })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="donation-options">
|
|
||||||
{#each Object.entries(PRESET_DONATION_AMOUNTS) as [ amount, component ]}
|
<div
|
||||||
<DonationOption
|
id="donation-options-container"
|
||||||
price={+amount}
|
aria-hidden="true"
|
||||||
desc={$t(`donate.card.option.${amount}`)}
|
class:mask-both={!device.is.mobile && showLeftScroll && showRightScroll}
|
||||||
href={donationMethods[processor](+amount * 100)}
|
class:mask-left={!device.is.mobile && showLeftScroll && !showRightScroll}
|
||||||
>
|
class:mask-right={!device.is.mobile && showRightScroll && !showLeftScroll}
|
||||||
<svelte:component this={component} />
|
>
|
||||||
</DonationOption>
|
{#if !device.is.mobile}
|
||||||
{/each}
|
<div id="donation-options-scroll">
|
||||||
|
<button
|
||||||
|
class="scroll-button left"
|
||||||
|
class:hidden={!showLeftScroll}
|
||||||
|
on:click={() => {
|
||||||
|
scroll("left");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconArrowLeft />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="scroll-button right"
|
||||||
|
class:hidden={!showRightScroll}
|
||||||
|
on:click={() => {
|
||||||
|
scroll("right");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconArrowRight />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="donation-options"
|
||||||
|
bind:this={donateList}
|
||||||
|
on:scroll={(e) => {}}
|
||||||
|
>
|
||||||
|
{#each Object.entries(PRESET_DONATION_AMOUNTS) as [amount, component]}
|
||||||
|
<DonationOption
|
||||||
|
price={+amount}
|
||||||
|
desc={$t(`donate.card.option.${amount}`)}
|
||||||
|
href={donationMethods[processor](+amount * 100)}
|
||||||
|
>
|
||||||
|
<svelte:component this={component} />
|
||||||
|
</DonationOption>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="donation-custom">
|
<div id="donation-custom">
|
||||||
<div id="input-container" class:focused={customFocused}>
|
<div id="input-container" class:focused={customFocused}>
|
||||||
{#if customInputValue || customInput?.validity.badInput}
|
{#if customInputValue || customInput?.validity.badInput}
|
||||||
<span id="input-dollar-sign">$</span>
|
<span id="input-dollar-sign">$</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<input
|
<input
|
||||||
id="donation-custom-input"
|
id="donation-custom-input"
|
||||||
type="number"
|
type="number"
|
||||||
|
@ -137,6 +205,7 @@
|
||||||
on:keydown={(e) => e.key === "Enter" && sendCustom()}
|
on:keydown={(e) => e.key === "Enter" && sendCustom()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
id="donation-custom-submit"
|
id="donation-custom-submit"
|
||||||
on:click={sendCustom}
|
on:click={sendCustom}
|
||||||
|
@ -146,6 +215,7 @@
|
||||||
<IconArrowRight />
|
<IconArrowRight />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="donate-card-subtitle processor-mobile">
|
<div class="donate-card-subtitle processor-mobile">
|
||||||
{$t("donate.card.processor", { value: processor })}
|
{$t("donate.card.processor", { value: processor })}
|
||||||
</div>
|
</div>
|
||||||
|
@ -288,6 +358,79 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#donation-options-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: calc(var(--donate-card-main-padding) / 2);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#donation-options-scroll {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 3;
|
||||||
|
overflow: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-button {
|
||||||
|
pointer-events: all;
|
||||||
|
color: white;
|
||||||
|
padding: 0 16px;
|
||||||
|
background-color: transparent;
|
||||||
|
height: 100%;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#donation-options-container:hover #donation-options-scroll {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-button.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#donation-options-container.mask-both:hover #donation-options {
|
||||||
|
mask-image: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
rgba(0, 0, 0, 0) 0%,
|
||||||
|
rgba(0, 0, 0, 1) 20%,
|
||||||
|
rgba(0, 0, 0, 1) 50%,
|
||||||
|
rgba(0, 0, 0, 1) 80%,
|
||||||
|
rgba(0, 0, 0, 0) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#donation-options-container.mask-left:hover #donation-options {
|
||||||
|
mask-image: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
rgba(0, 0, 0, 0) 0%,
|
||||||
|
rgba(0, 0, 0, 1) 20%,
|
||||||
|
rgba(0, 0, 0, 1) 50%,
|
||||||
|
rgba(0, 0, 0, 1) 96%,
|
||||||
|
rgba(0, 0, 0, 0) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#donation-options-container.mask-right:hover #donation-options {
|
||||||
|
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) 80%,
|
||||||
|
rgba(0, 0, 0, 0) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 550px) {
|
@media screen and (max-width: 550px) {
|
||||||
:global(#donation-box) {
|
:global(#donation-box) {
|
||||||
min-width: unset;
|
min-width: unset;
|
||||||
|
|
Loading…
Reference in a new issue