feat: create Toast component
This commit is contained in:
111
apps/website/src/components/molecules/Toast.svelte
Normal file
111
apps/website/src/components/molecules/Toast.svelte
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<style lang="scss">
|
||||||
|
@import "../../style/molecules/toast";
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
import { fade, fly } from 'svelte/transition'
|
||||||
|
import { quartOut } from 'svelte/easing'
|
||||||
|
import { browser } from '$app/environment'
|
||||||
|
import { cx } from 'classix'
|
||||||
|
// Components
|
||||||
|
import Button from '$components/atoms/Button.svelte'
|
||||||
|
import Image from '$components/atoms/Image.svelte'
|
||||||
|
|
||||||
|
export let id: string
|
||||||
|
export let type: 'global' | 'local'
|
||||||
|
export let text: string
|
||||||
|
export let cta: {
|
||||||
|
label: string
|
||||||
|
url: string
|
||||||
|
color: string
|
||||||
|
} = undefined
|
||||||
|
export let images: { id: string, title: string }[] = undefined
|
||||||
|
export let show = false
|
||||||
|
|
||||||
|
$: if (browser) {
|
||||||
|
show = !localStorage.getItem(`toast-${id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image rotation
|
||||||
|
let imagesLoop: ReturnType<typeof setTimeout>
|
||||||
|
let currentImageIndex = 0
|
||||||
|
|
||||||
|
const incrementCurrentImageIndex = () => {
|
||||||
|
currentImageIndex = currentImageIndex === images.length - 1 ? 0 : currentImageIndex + 1
|
||||||
|
imagesLoop = setTimeout(() => requestAnimationFrame(incrementCurrentImageIndex), 3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close toast
|
||||||
|
const close = () => {
|
||||||
|
localStorage.setItem(`toast-${id}`, 'closed')
|
||||||
|
show = false
|
||||||
|
}
|
||||||
|
|
||||||
|
$: classes = cx(
|
||||||
|
'toast',
|
||||||
|
`toast--${type}`,
|
||||||
|
'shadow-small',
|
||||||
|
$$props.class,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (images.length > 1) {
|
||||||
|
incrementCurrentImageIndex()
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
// Clear rotating words timeout
|
||||||
|
if (imagesLoop) {
|
||||||
|
clearTimeout(imagesLoop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if show}
|
||||||
|
<div
|
||||||
|
class={classes}
|
||||||
|
in:fly={{ y: '10%', duration: 1200, easing: quartOut }}
|
||||||
|
out:fade={{ duration: 800, easing: quartOut }}
|
||||||
|
>
|
||||||
|
{#if images}
|
||||||
|
<div class="media">
|
||||||
|
<a href={cta.url}>
|
||||||
|
{#each images as { id, title }, index}
|
||||||
|
<Image
|
||||||
|
class={index === currentImageIndex ? 'is-visible' : null}
|
||||||
|
{id}
|
||||||
|
sizeKey="square"
|
||||||
|
sizes={{
|
||||||
|
small: { width: 200, height: 200 },
|
||||||
|
large: { width: 350, height: 350 },
|
||||||
|
}}
|
||||||
|
alt={title}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<p class="text">{@html text}</p>
|
||||||
|
|
||||||
|
{#if cta}
|
||||||
|
<Button
|
||||||
|
size="xsmall"
|
||||||
|
text={cta.label}
|
||||||
|
url={cta.url}
|
||||||
|
color="pink"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="close" on:click={close} title="Close">
|
||||||
|
<svg width="10" height="10">
|
||||||
|
<use xlink:href="#cross" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
120
apps/website/src/style/molecules/_toast.scss
Normal file
120
apps/website/src/style/molecules/_toast.scss
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
.toast {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px;
|
||||||
|
padding-right: 28px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
@include bp (md) {
|
||||||
|
padding-right: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Media
|
||||||
|
.media {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex: 0 0 clamp(40px, 14vw, 64px);
|
||||||
|
aspect-ratio: 1 / 1.25;
|
||||||
|
height: 100%;
|
||||||
|
margin-right: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
@include bp (md) {
|
||||||
|
flex: 0 0 min(7vw, 104px);
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(picture) {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(1.075);
|
||||||
|
transition: opacity 0.8s, transform 1.6s var(--ease-quart);
|
||||||
|
}
|
||||||
|
:global(img) {
|
||||||
|
object-position: center 32%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.is-visible) {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(img) {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
|
||||||
|
@include bp (sm) {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content
|
||||||
|
.content {
|
||||||
|
color: $color-text;
|
||||||
|
font-size: rem(14px);
|
||||||
|
|
||||||
|
@include bp (md) {
|
||||||
|
font-size: rem(16px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
@include bp (md) {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(strong) {
|
||||||
|
font-weight: normal;
|
||||||
|
color: $color-secondary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close
|
||||||
|
.close {
|
||||||
|
--size: 28px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
@include bp (sm) {
|
||||||
|
--size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(svg) {
|
||||||
|
transition: transform 0.6s var(--ease-quart);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hover
|
||||||
|
&:hover {
|
||||||
|
:global(svg) {
|
||||||
|
transform: rotate(90deg) translateZ(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user