Update to svelte 5

This commit is contained in:
2025-10-13 16:05:50 +02:00
parent f936e4c0f2
commit 6d89c5ddd9
129 changed files with 2443 additions and 2180 deletions

View File

@@ -1,20 +1,39 @@
<script lang="ts">
export let highlight = false;
export let highlight_on_click = false
export let red = false;
export let round = false;
export let flat = false;
export let disabled = false;
export let icon = ""
export let icon_small = false;
export let label = ""
export let title = null
export let link_href = ""
export let link_target = "_self"
export let click: (e?: MouseEvent) => void = null
export let style = null
export let type = null
export let form = null
let {
highlight = $bindable(false),
highlight_on_click = false,
red = $bindable(false),
round = false,
flat = false,
disabled = false,
icon = "",
icon_small = false,
label = "",
title = null,
link_href = "",
link_target = "_self",
click = null,
style = null,
type = null,
form = null
}: {
highlight?: boolean;
highlight_on_click?: boolean;
red?: boolean;
round?: boolean;
flat?: boolean;
disabled?: boolean;
icon?: string;
icon_small?: boolean;
label?: string;
title?: any;
link_href?: string;
link_target?: string;
click?: (e?: MouseEvent) => void;
style?: any;
type?: any;
form?: any;
} = $props();
let click_int = (e: MouseEvent) => {
if (highlight_on_click) {
@@ -33,7 +52,7 @@ let click_int = (e: MouseEvent) => {
{#if link_href === ""}
<button
on:click={click_int}
onclick={click_int}
class="button"
class:button_highlight={highlight}
class:button_red={red}
@@ -54,7 +73,7 @@ let click_int = (e: MouseEvent) => {
</button>
{:else}
<a
on:click={click_int}
onclick={click_int}
href="{link_href}"
target={link_target}
class="button"

View File

@@ -1,12 +1,22 @@
<script lang="ts">
import { copy_text } from "util/Util.svelte";
import { copy_text } from "util/Util";
export let text = ""
export let style = ""
export let large_icon = false
export let small_icon = false
let failed = false
let success = false
let {
text = "",
style = "",
large_icon = false,
small_icon = false,
children
}: {
text?: string;
style?: string;
large_icon?: boolean;
small_icon?: boolean;
children?: import('svelte').Snippet;
} = $props();
let failed = $state(false)
let success = $state(false)
// Exported so it can be called manually
export const copy = () => {
@@ -26,7 +36,7 @@ export const copy = () => {
</script>
<button
on:click={copy}
onclick={copy}
style={style}
class="button"
class:button_highlight={success}
@@ -43,7 +53,7 @@ export const copy = () => {
{:else if failed}
Copy failed
{:else}
<slot></slot>
{@render children?.()}
{/if}
</span>
</button>

View File

@@ -1,5 +1,8 @@
<script lang="ts">
let dialog: HTMLDialogElement
let { children }: {
children?: import('svelte').Snippet;
} = $props();
let dialog: HTMLDialogElement = $state()
export const open = (button_rect: DOMRect) => {
// Show the window so we can get the location
@@ -37,10 +40,10 @@ const click = (e: MouseEvent) => {
}
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<dialog bind:this={dialog} on:click={click}>
<slot></slot>
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
<dialog bind:this={dialog} onclick={click}>
{@render children?.()}
</dialog>
<style>

View File

@@ -1,5 +1,7 @@
<script lang="ts">
export let title = ""
let { title = "" }: {
title?: string;
} = $props();
</script>
<h1>{title}</h1>

View File

@@ -8,10 +8,13 @@ import Reddit from "icons/Reddit.svelte";
import { formatDataVolumeBits } from "util/Formatting";
import { get_endpoint, get_hostname } from "lib/PixeldrainAPI";
export let nobg = false
let server_tx = 0
let cache_tx = 0
let storage_tx = 0
let { nobg = false }: {
nobg?: boolean;
} = $props();
let server_tx = $state(0)
let cache_tx = $state(0)
let storage_tx = $state(0)
onMount(async () => {
try {
const resp = await fetch(get_endpoint()+"/misc/cluster_speed")

View File

@@ -1,11 +1,15 @@
<script lang="ts">
export let toggle = false;
let {
toggle = $bindable(false)
}: {
toggle?: boolean;
} = $props();
</script>
<button class="button small_button round"
class:button_highlight={toggle}
style="margin: 0;"
on:click={() => toggle = !toggle}
onclick={() => toggle = !toggle}
>
<i class="icon">help</i>
</button>

View File

@@ -1,12 +1,19 @@
<script lang="ts">
export let icon_href = ""
export let width = "750px"
let {
icon_href = "",
width = "750px",
children
}: {
icon_href?: string;
width?: string;
children?: import('svelte').Snippet;
} = $props();
</script>
<div class="block" style="width: {width}; max-width: 100%">
<img src={icon_href} alt="File icon" class="icon">
<div class="description">
<slot></slot>
{@render children?.()}
</div>
</div>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { user } from "lib/UserStore";
let nav: HTMLElement;
let nav: HTMLElement = $state();
export const toggle = () => {
var body = document.getElementById("page_body");
@@ -21,7 +21,7 @@ export const reset = () => {
}
</script>
<button id="button_toggle_navigation" class="button_toggle_navigation" on:click={toggle}>☰</button>
<button id="button_toggle_navigation" class="button_toggle_navigation" onclick={toggle}>☰</button>
<nav bind:this={nav} id="page_navigation" class="page_navigation">
<a href="/#">Home</a>
<a href="/#prepaid">For Creators</a>

View File

@@ -1,15 +1,24 @@
<script>
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { formatDataVolume, formatDuration } from "util/Formatting";
import { stats } from "lib/StatsSocket"
import TextBlock from "./TextBlock.svelte"
import IconBlock from "./IconBlock.svelte";
import { user } from "lib/UserStore";
let dispatch = createEventDispatcher()
export let file_size = 0
export let file_name = ""
export let file_type = ""
export let icon_href = ""
let {
file_size = 0,
file_name = "",
file_type = "",
icon_href = ""
}: {
file_size?: number;
file_name?: string;
file_type?: string;
icon_href?: string;
} = $props();
</script>
<TextBlock>
@@ -41,7 +50,7 @@ export let icon_href = ""
<i class="icon">bolt</i> Get Premium
</a>
and earn my eternal gratitude
{#if !window.user_authenticated}
{#if $user.username === undefined || $user.username === ""}
(you will need a <a href="/register">pixeldrain account</a> to
receive the benefits)
{/if}
@@ -57,7 +66,7 @@ export let icon_href = ""
<tr><td>Size</td><td>{formatDataVolume(file_size, 3)}</td></tr>
</tbody>
</table>
<button on:click={() => {dispatch("download")}}>
<button onclick={() => {dispatch("download")}}>
<i class="icon">download</i> Download
</button>
</IconBlock>

View File

@@ -1,15 +1,24 @@
<script lang="ts">
export let field = ""
export let active_field = ""
export let asc = true
export let sort_func: (field: string) => void
let {
field = "",
active_field = "",
asc = true,
sort_func,
children
}: {
field?: string;
active_field?: string;
asc?: boolean;
sort_func: (field: string) => void;
children?: import('svelte').Snippet;
} = $props();
</script>
<button on:click={() => sort_func(field)}>
<button onclick={() => sort_func(field)}>
{#if active_field === field}
{#if asc}↓{:else}↑{/if}
{/if}
<slot></slot>
{@render children?.()}
</button>
<style>

View File

@@ -1,12 +1,13 @@
<script lang="ts" context="module">
<script lang="ts" module>
// svelte-ignore non_reactive_update
export enum FieldType {
Text,
Bytes,
Bits,
Number,
Euro,
HTML,
Func,
Text = 1,
Bytes = 2,
Bits = 3,
Number = 4,
Euro = 5,
HTML = 6,
Func = 7,
}
export type SortColumn = {
field: string,
@@ -20,13 +21,21 @@ import SortButton from "layout/SortButton.svelte";
import Euro from "util/Euro.svelte";
import { formatDataVolume, formatDataVolumeBits, formatThousands } from "util/Formatting";
export let index_field = ""
export let rows = [];
export let columns: SortColumn[] = []
export let sort_field = index_field
export let totals = false
let {
index_field = "",
rows = $bindable([]),
columns = [],
sort_field = $bindable(index_field),
totals = false
}: {
index_field?: string;
rows?: any;
columns?: SortColumn[];
sort_field?: any;
totals?: boolean;
} = $props();
let asc = true
let asc = $state(true)
const sort = (field: string) => {
if (field !== "" && field === sort_field) {
asc = !asc

View File

@@ -1,10 +1,17 @@
<script>
export let width = "750px"
export let center = false
<script lang="ts">
let {
width = "750px",
center = false,
children
}: {
width?: string;
center?: boolean;
children?: import('svelte').Snippet;
} = $props();
</script>
<div class="block" class:center style="width: {width};">
<slot></slot>
{@render children?.()}
</div>
<style>

View File

@@ -1,11 +1,23 @@
<script lang="ts">
export let on = false
export let icon_on = "check"
export let icon_off = "close"
export let group_first = false
export let group_middle = false
export let group_last = false
export let action = (e: MouseEvent) => {}
let {
on = $bindable(),
icon_on = "check",
icon_off = "close",
group_first = false,
group_middle = false,
group_last = false,
action,
children,
}: {
on?: boolean;
icon_on?: string;
icon_off?: string;
group_first?: boolean;
group_middle?: boolean;
group_last?: boolean;
action?: (e: MouseEvent) => void;
children?: import('svelte').Snippet;
} = $props();
const click = (e: MouseEvent) => {
on = !on
@@ -16,7 +28,7 @@ const click = (e: MouseEvent) => {
</script>
<button
on:click={click}
onclick={click}
type="button"
class="button"
class:button_highlight={on}
@@ -29,7 +41,7 @@ const click = (e: MouseEvent) => {
{:else}
<i class="icon">{icon_off}</i>
{/if}
<slot></slot>
{@render children?.()}
</button>
<style>

View File

@@ -1,19 +1,24 @@
<script lang="ts">
import Dialog from "./Dialog.svelte";
let button: HTMLButtonElement
let dialog: Dialog
let { label, children }: {
label?: import('svelte').Snippet;
children?: import('svelte').Snippet;
} = $props();
let button: HTMLButtonElement = $state()
let dialog: Dialog = $state()
const open = () => dialog.open(button.getBoundingClientRect())
</script>
<button bind:this={button} on:click={open} class="button round">
<i class="icon">info</i><slot name="label"></slot>
<button bind:this={button} onclick={open} class="button round">
<i class="icon">info</i>{@render label?.()}
</button>
<Dialog bind:this={dialog}>
<div class="menu">
<slot></slot>
{@render children?.()}
</div>
</Dialog>

View File

@@ -8,7 +8,7 @@ import { get_misc_vat_rate, get_user } from "lib/PixeldrainAPI";
import { countries } from "country-data-list";
import PickProvider from "./PickProvider.svelte";
let state: CheckoutState = {country: null, provider: null, amount: 0, vat: 0, name: ""}
let status: CheckoutState = $state({country: null, provider: null, amount: 0, vat: 0, name: ""})
onMount(async () => {
try {
@@ -17,21 +17,21 @@ onMount(async () => {
// Get the country from the user profile
if (user.checkout_country !== "" && countries[user.checkout_country] !== undefined) {
const vat_rate = await get_misc_vat_rate(user.checkout_country)
state.vat = vat_rate.vat*100
state.country = countries[user.checkout_country]
status.vat = vat_rate.vat*100
status.country = countries[user.checkout_country]
}
// Get the provider from the user profile
for (const p of payment_providers) {
if (p.name === user.checkout_provider) {
state.provider = p
status.provider = p
break
}
}
// Get the checkout name from the user profile
if (user.checkout_name !== "") {
state.name = user.checkout_name
status.name = user.checkout_name
}
}catch (err) {
alert("Failed to get user:"+err)
@@ -40,45 +40,45 @@ onMount(async () => {
</script>
<div class="highlight_border">
{#if state.country !== null}
{#if status.country !== null}
<div class="navbar">
{#if state.country !== null}
<button on:click={() => state.country = null}>
{#if status.country !== null}
<button onclick={() => status.country = null}>
Country:
<span style="font-size: 1.5em; line-height: 1em;">{state.country.emoji}</span>
{state.country.name}
<span style="font-size: 1.5em; line-height: 1em;">{status.country.emoji}</span>
{status.country.name}
</button>
{/if}
{#if state.provider !== null}
<button on:click={() => state.provider = null}>
{#if status.provider !== null}
<button onclick={() => status.provider = null}>
Provider:
<img
class="provider_icon"
src="/res/img/payment_providers/{state.provider.icon}.svg"
alt={state.provider.label}
title={state.provider.label}/>
{state.provider.label}
src="/res/img/payment_providers/{status.provider.icon}.svg"
alt={status.provider.label}
title={status.provider.label}/>
{status.provider.label}
</button>
{/if}
{#if state.name !== ""}
<button on:click={() => state.name = ""}>
Name: {state.name}
{#if status.name !== ""}
<button onclick={() => status.name = ""}>
Name: {status.name}
</button>
{/if}
</div>
<hr/>
{/if}
{#if state.country === null}
<PickCountry bind:state/>
{:else if state.provider === null}
<PickProvider bind:state/>
{:else if state.provider.need_name === true && state.name === ""}
<PickName bind:state/>
{#if status.country === null}
<PickCountry bind:status/>
{:else if status.provider === null}
<PickProvider bind:status/>
{:else if status.provider.need_name === true && status.name === ""}
<PickName bind:status/>
{:else}
<PickAmount bind:state/>
<PickAmount bind:status/>
{/if}
</div>

View File

@@ -1,27 +1,30 @@
<script lang="ts">
import { preventDefault } from 'svelte/legacy';
import Euro from "util/Euro.svelte";
import { checkout, type CheckoutState } from "./CheckoutLib";
import { loading_finish, loading_start } from "lib/Loading";
export let state: CheckoutState
let { status = $bindable() }: {
status: CheckoutState;
} = $props();
const amounts = [10, 20, 50, 100, 200, 500, 1000, 2000, 5000]
let amount = 20
let amount = $state(20)
const submit = async (e: SubmitEvent) => {
e.preventDefault()
state.amount = amount
status.amount = amount
loading_start()
try {
await checkout(state)
await checkout(status)
}finally {
loading_finish()
}
}
</script>
{#if state.provider.crypto === true}
{#if status.provider.crypto === true}
<p style="text-align: initial;" class="highlight_blue">
When paying with cryptocurrencies it is important that you pay
the <b>exact amount</b> stated on the order. If you pay too
@@ -33,11 +36,11 @@ const submit = async (e: SubmitEvent) => {
</p>
{/if}
<form class="amount_grid" on:submit={submit}>
<form class="amount_grid" onsubmit={submit}>
<div class="span3">Please choose an amount</div>
{#each amounts as a}
<button
on:click|preventDefault={() => amount = a}
onclick={preventDefault(() => amount = a)}
class="amount_button"
class:button_highlight={amount === a}
>
@@ -52,7 +55,7 @@ const submit = async (e: SubmitEvent) => {
<div class="span2" style="text-align: initial;">
Total including VAT:
<Euro amount={(amount*1e6) + (amount*1e6)*(state.vat/100)}/>
<Euro amount={(amount*1e6) + (amount*1e6)*(status.vat/100)}/>
</div>
<button type="submit" class="button_highlight">

View File

@@ -1,11 +1,14 @@
<script lang="ts">
import { preventDefault } from 'svelte/legacy';
import { countries } from "country-data-list";
import { get_misc_vat_rate, put_user } from "lib/PixeldrainAPI";
import { payment_providers, type CheckoutState } from "./CheckoutLib";
import { loading_finish, loading_start } from "lib/Loading";
export let state: CheckoutState
let country_input = ""
let { status = $bindable() }: {
status: CheckoutState;
} = $props();
let country_input = $state("")
const set_country = async (e?: Event) => {
if (e !== undefined) {
@@ -21,7 +24,7 @@ const set_country = async (e?: Event) => {
// We always clear the provider field after picking a country, as providers
// vary per country. If we did not clear the provider then we may end up
// with an invalid combination
state.provider = null
status.provider = null
loading_start()
try {
@@ -29,8 +32,8 @@ const set_country = async (e?: Event) => {
await put_user({checkout_country: c.alpha3})
const vat_rate = await get_misc_vat_rate(c.alpha3)
state.vat = vat_rate.vat*100
state.country = c
status.vat = vat_rate.vat*100
status.country = c
} catch (err) {
alert(err)
} finally {
@@ -56,7 +59,7 @@ const format_country = (c: typeof countries.all[0]) => {
Please pick your country of residence
</div>
<div>
<form on:submit|preventDefault={set_country} class="country_form">
<form onsubmit={preventDefault(set_country)} class="country_form">
<div class="country_search">
<datalist id="country_picker">
{#each countries.all.filter(c => c.status === "assigned") as c}
@@ -74,7 +77,7 @@ const format_country = (c: typeof countries.all[0]) => {
Continue
</button>
</div>
<select bind:value={country_input} on:dblclick={set_country} style="padding: 0;" size="10">
<select bind:value={country_input} ondblclick={set_country} style="padding: 0;" size="10">
{#each countries.all.filter(c => c.status === "assigned") as c}
<option value={c.alpha2}>{format_country(c)}</option>
{/each}

View File

@@ -2,8 +2,11 @@
import { put_user } from "lib/PixeldrainAPI";
import { type CheckoutState } from "./CheckoutLib";
export let state: CheckoutState
let name: string
let { status = $bindable() }: {
status: CheckoutState;
} = $props();
let name: string = $state()
const submit = async (e: SubmitEvent) => {
e.preventDefault()
@@ -14,11 +17,11 @@ const submit = async (e: SubmitEvent) => {
alert("Failed to update user:"+ err)
}
state.name = name
status.name = name
}
</script>
<form class="name_form" on:submit={submit}>
<form class="name_form" onsubmit={submit}>
<div>
This payment provider requires a name for checkout. Please enter your
full name below

View File

@@ -2,7 +2,9 @@
import { put_user } from "lib/PixeldrainAPI";
import { payment_providers, type CheckoutState, type PaymentProvider } from "./CheckoutLib";
export let state: CheckoutState
let { status = $bindable() }: {
status: CheckoutState;
} = $props();
const set_provider = async (p: PaymentProvider) => {
try {
@@ -11,7 +13,7 @@ const set_provider = async (p: PaymentProvider) => {
alert("Failed to update user:"+ err)
}
state.provider = p
status.provider = p
}
</script>
@@ -19,8 +21,8 @@ const set_provider = async (p: PaymentProvider) => {
<div class="providers">
{#each payment_providers as p (p.name)}
{#if p.country_filter === undefined || p.country_filter.includes(state.country.alpha2)}
<button on:click={() => set_provider(p)}>
{#if p.country_filter === undefined || p.country_filter.includes(status.country.alpha2)}
<button onclick={() => set_provider(p)}>
<img src="/res/img/payment_providers/{p.icon}.svg" alt={p.label} title={p.label}/>
{p.label}
</button>