refactor: migrate to Svelte 5

use runes ($props, $state, $derived, $effect, etc)
This commit is contained in:
2024-08-02 17:50:16 +02:00
parent 245049222b
commit 6f8a619af2
60 changed files with 1120 additions and 859 deletions

View File

@@ -2,20 +2,31 @@
import { cx } from 'classix'
import Image from './Image.svelte'
export let id: string
export let alt: string
export let disabled = false
let {
id,
alt,
disabled = false,
...props
}: {
id: string
alt: string
disabled?: boolean
class?: string
} = $props()
let hovering = false
let timer: ReturnType<typeof setTimeout> | number = null
let hovering = $state(false)
let timer: ReturnType<typeof setTimeout>
$: classes = cx(
const classes = $derived(cx(
hovering ? 'is-hovered' : undefined,
disabled ? 'is-disabled' : undefined,
$$props.class
)
props.class,
))
// Hovering functions
/**
* Hovering functions
*/
const handleMouseEnter = () => {
clearTimeout(timer)
hovering = true
@@ -26,9 +37,10 @@
}
</script>
<figure class={classes}
on:mouseenter={handleMouseEnter}
on:mouseleave={handleMouseLeave}
<figure
class={classes}
onmouseenter={handleMouseEnter}
onmouseleave={handleMouseLeave}
>
<Image
{id}

View File

@@ -23,8 +23,13 @@
</style>
<script lang="ts">
export let text: string
export let size = 'small'
let {
text,
size = 'small',
}: {
text: string
size?: string
} = $props()
</script>
<div class="badge badge--{size}">

View File

@@ -42,7 +42,7 @@
margin-left: 20px;
color: $color-secondary-light;
text-align: left;
font-weight: 300;
font-weight: 400;
@include bp (sm) {
margin-left: 0;

View File

@@ -5,10 +5,17 @@
<script lang="ts">
import Icon from '$components/atoms/Icon.svelte'
export let icon: string
export let alt: string
export let label: string
export let url: string
let {
icon,
alt,
label,
url,
}: {
icon: string
alt: string
label: string
url: string
} = $props()
</script>
<a href={url} class="box-cta">

View File

@@ -6,56 +6,71 @@
import { cx } from 'classix'
import SplitText from '$components/SplitText.svelte'
export let text: string
export let url: string = undefined
export let color: string = undefined
export let size: 'xsmall' | 'small' | 'medium' | 'large'
export let effect = 'link-3d'
export let disabled: boolean = undefined
export let slotPosition = 'before'
let {
text,
url,
color,
size,
effect = 'link-3d',
disabled,
slotPosition = 'before',
onclick,
children,
...props
}: {
text: string
url?: string
color?: string
size: 'xsmall' | 'small' | 'medium' | 'large'
effect?: string
disabled?: boolean
slotPosition?: 'before' | 'after'
onclick?: any
children?: any
class?: string
} = $props()
let tag: 'a' | 'button'
$: tag = url ? 'a' : 'button'
$: classes = cx(
const tag = $derived(url ? 'a' : 'button')
const classes = $derived(cx(
'button',
effect ? effect : undefined,
effect,
...[color, size].map(variant => variant && `button--${variant}`),
Object.keys($$slots).length !== 0 ? `has-icon-${slotPosition}` : undefined,
$$props.class,
)
children && `has-icon-${slotPosition}`,
props.class,
))
// Define external links
$: isExternal = /^(http|https):\/\//i.test(url)
$: isProtocol = /^(mailto|tel):/i.test(url)
$: rel = isExternal ? 'external noopener' : null
$: target = isExternal ? '_blank' : null
const isExternal = $derived(/^(http|https):\/\//i.test(url))
const isProtocol = $derived(/^(mailto|tel):/i.test(url))
const rel = $derived(isExternal ? 'external noopener' : null)
const target = $derived(isExternal ? '_blank' : null)
</script>
{#if tag === 'button'}
<button class={classes} tabindex="0" {disabled} on:click>
{#if slotPosition === 'before'}
<slot />
<button class={classes} tabindex="0" {disabled} {onclick}>
{#if children && slotPosition === 'before'}
{@render children()}
{/if}
<SplitText {text} clone={true} />
{#if slotPosition === 'after'}
<slot />
{#if children && slotPosition === 'after'}
{@render children()}
{/if}
</button>
{:else if tag === 'a'}
<a
href={url} class={classes}
href={url}
class={classes}
{target} {rel}
data-sveltekit-noscroll={isExternal || isProtocol ? 'off' : ''}
tabindex="0"
on:click
{onclick}
>
{#if slotPosition === 'before'}
<slot />
{#if children && slotPosition === 'before'}
{@render children()}
{/if}
<SplitText {text} clone={true} />
{#if slotPosition === 'after'}
<slot />
{#if children && slotPosition === 'after'}
{@render children()}
{/if}
</a>
{/if}

View File

@@ -18,7 +18,7 @@
</script>
<div class="button-cart">
<ButtonCircle color="purple" on:click={openCart}>
<ButtonCircle color="purple" onclick={openCart}>
<Icon icon="bag" label="Cart icon" />
{#if $cartAmount > 0}
<span class="quantity" transition:scale={{ start: 0.6, duration: 400, easing: quartOut }}>{$cartAmount}</span>

View File

@@ -5,42 +5,58 @@
<script lang="ts">
import { cx } from 'classix'
export let tag = 'button'
export let url: string = undefined
export let color: string = undefined
export let size: string = undefined
export let type: 'button' | 'reset' | 'submit' = undefined
export let clone = false
export let disabled: boolean = undefined
export let label: string = undefined
let {
tag = 'button',
url,
color,
size,
type,
clone = false,
disabled,
label,
children,
onclick,
...props
}: {
tag?: string
url?: string
color?: string
size?: string
type?: 'button' | 'reset' | 'submit'
clone?: boolean
disabled?: boolean
label?: string
children?: any
onclick?: any
class?: string
} = $props()
const className = 'button-circle'
$: classes = cx(
className,
...[color, size].map(variant => variant && `${className}--${variant}`),
const buttonClass = 'button-circle'
const classes = $derived(cx(
buttonClass,
...[color, size].map(variant => variant && `${buttonClass}--${variant}`),
clone ? 'has-clone' : null,
$$props.class
)
props.class,
))
</script>
{#snippet content()}
{#if clone}
{#each Array(2) as _}
{@render children()}
{/each}
{:else}
{@render children()}
{/if}
{/snippet}
{#if tag === 'a'}
<a href={url} class={classes} tabindex="0" aria-label={label} on:click>
{#if clone}
{#each Array(2) as _}
<slot />
{/each}
{:else}
<slot />
{/if}
<a href={url} class={classes} tabindex="0" aria-label={label} {onclick}>
{@render content()}
</a>
{:else}
<button {type} class={classes} disabled={disabled} tabindex="0" aria-label={label} on:click>
{#if clone}
{#each Array(2) as _}
<slot />
{/each}
{:else}
<slot />
{/if}
<button {type} class={classes} disabled={disabled} tabindex="0" aria-label={label} {onclick}>
{@render content()}
</button>
{/if}

View File

@@ -1,12 +1,15 @@
<script lang="ts">
import { cx } from 'classix'
export let icon: string
export let label: string = undefined
$: classes = cx($$props.class)
let {
icon,
label,
...props
}: {
icon: string
label?: string
class?: string
} = $props()
</script>
<svg class={classes} aria-label={label} width="32" height="32">
<svg class={props.class} aria-label={label} width="32" height="32">
<use xlink:href="#icon-{icon}" />
</svg>

View File

@@ -18,11 +18,17 @@
</style>
<script lang="ts">
export let color: string = undefined
export let flip = false
let {
color,
flip = false,
}: {
color?: string
flip?: boolean
} = $props()
</script>
<svg width="12" height="14"
<svg
width="12" height="14"
class="arrow arrow--{color}"
class:arrow--flip={flip}
>

View File

@@ -9,12 +9,18 @@
<script lang="ts">
import { cx } from 'classix'
export let animate = false
let {
animate = false,
...props
}: {
animate?: boolean
class?: string
} = $props()
$: classes = cx(
const classes = $derived(cx(
'icon-earth',
$$props.class,
)
props.class,
))
</script>
<svg class={classes} width="48" height="48" viewBox="0 0 48 48" fill="none">

View File

@@ -1,16 +1,31 @@
<script lang="ts">
import { getAssetUrlKey } from '$utils/api'
export let src: string = undefined
export let id: string = undefined
export let sizeKey: string = undefined
export let sizes: Sizes = undefined
export let width: number = sizes?.medium?.width
export let height: number = sizes?.medium?.height
export let ratio: number = undefined
export let alt: string
export let lazy = true
export let decoding: 'auto' | 'sync' | 'async' = 'auto'
let {
src,
id,
sizeKey,
sizes,
width = sizes?.medium?.width,
height = sizes?.medium?.height,
ratio,
alt,
lazy = true,
decoding,
...props
}: {
src?: string
id?: string
sizeKey?: string
sizes?: Sizes
width?: number
height?: number
ratio?: number
alt: string
lazy?: boolean
decoding?: 'auto' | 'sync' | 'async'
class?: string
} = $props()
interface Sizes {
small?: { width?: number; height?: number }
@@ -29,19 +44,19 @@
}
}
$: imgWidth = sizes?.small?.width || width
$: imgHeight = sizes?.small?.height || height
$: imgSrc = id ? getAssetUrlKey(id, `${sizeKey}-small`) : src
$: srcSet = sizes
? [
const imgWidth = $derived(sizes?.small?.width || width)
const imgHeight = $derived(sizes?.small?.height || height)
const imgSrc = $derived(id ? getAssetUrlKey(id, `${sizeKey}-small`) : src)
const srcSet = $derived(
sizes ? [
`${getAssetUrlKey(id, `${sizeKey}-small`)} 345w`,
sizes.medium && `${getAssetUrlKey(id, `${sizeKey}-medium`)} 768w`,
sizes.large && `${getAssetUrlKey(id, `${sizeKey}-large`)} 1280w`,
]
: [getAssetUrlKey(id, sizeKey)]
] : [getAssetUrlKey(id, sizeKey)]
)
</script>
<picture class={$$props.class}>
<picture class={props.class}>
<img
src={imgSrc}
sizes={sizes ? '(min-width: 1200px) 864px, (min-width: 992px) 708px, (min-width: 768px) 540px, 100%' : undefined}
@@ -52,4 +67,4 @@
loading={lazy ? 'lazy' : undefined}
{decoding}
/>
</picture>
</picture>

View File

@@ -12,39 +12,53 @@
import { map } from 'utils/math'
import reveal from '$animations/reveal'
export let tag: string
export let label: string = undefined
export let parallax: number = undefined
export let offsetStart: number = undefined
export let offsetEnd: number = undefined
export let animate = true
let {
tag,
label,
parallax,
offsetStart,
offsetEnd,
animate = true,
children,
...props
}: {
tag: string
label?: string
parallax?: number
offsetStart?: number
offsetEnd?: number
animate?: boolean
class?: string
children?: any
} = $props()
let scrollY: number
let innerWidth: number
let innerHeight: number
let titleEl: HTMLElement
let isLarger: boolean
// Define default values
$: if (titleEl && !offsetStart && !offsetEnd) {
offsetStart = titleEl.offsetTop - innerHeight * (innerWidth < 768 ? 0.2 : 0.75)
offsetEnd = titleEl.offsetTop + innerHeight * (innerWidth < 768 ? 0.5 : 0.5)
}
let scrollY = $state<number>()
let innerWidth = $state<number>()
let innerHeight = $state<number>()
let titleEl = $state<HTMLElement>()
// Check if title is larger than viewport to translate it
$: isLarger = titleEl && titleEl.offsetWidth >= innerWidth
const isLarger = $derived<boolean>(titleEl && titleEl.offsetWidth >= innerWidth)
// Calculate the parallax value
$: if (titleEl) {
const toTranslate = 100 - (innerWidth / titleEl.offsetWidth * 100)
parallax = isLarger ? map(scrollY, offsetStart, offsetEnd, 0, -toTranslate, true) : 0
}
$effect(() => {
// Define default values
if (titleEl && !offsetStart && !offsetEnd) {
offsetStart = titleEl.offsetTop - innerHeight * (innerWidth < 768 ? 0.2 : 0.75)
offsetEnd = titleEl.offsetTop + innerHeight * (innerWidth < 768 ? 0.5 : 0.5)
}
$: classes = cx(
// Calculate the parallax value
if (titleEl) {
const toTranslate = 100 - (innerWidth / titleEl.offsetWidth * 100)
parallax = isLarger ? map(scrollY, offsetStart, offsetEnd, 0, -toTranslate, true) : 0
}
})
const classes = $derived(cx(
'scrolling-title',
'title-huge',
$$props.class
)
props.class,
))
const revealOptions = animate ? {
children: '.char',
@@ -65,9 +79,10 @@
<svelte:element this={tag}
bind:this={titleEl}
class={classes} aria-label={label}
class={classes}
aria-label={label}
style:--parallax-x="{parallax}%"
use:reveal={revealOptions}
>
<slot />
{@render children()}
</svelte:element>

View File

@@ -7,8 +7,13 @@
import reveal from '$animations/reveal'
import { DURATION } from '$utils/constants'
export let variant = 'lines'
export let tag = 'h1'
let {
variant = 'lines',
tag = 'h1',
}: {
variant?: 'inline' | 'lines'
tag?: string
} = $props()
</script>
{#if tag === 'h1'}