All checks were successful
CI / update (push) Successful in 2m8s
Migrated all components and routes from Svelte 4 to Svelte 5 syntax:
- Converted export let → $props() with generic type syntax
- Replaced createEventDispatcher → callback props
- Migrated $: reactive statements → $derived() and $effect()
- Updated two-way bindings with $bindable()
- Fixed TypeScript syntax: added lang="ts" to script tags
- Converted inline type annotations to generic parameter syntax
- Updated deprecated event directives to Svelte 5 syntax:
- on:click → onclick
- on:submit → onsubmit
- on:change → onchange
- Converted deprecated <slot> elements → {@render children()}
- Updated slot props to Snippet types
- Fixed season/icon selector components with {#snippet} blocks
- Fixed non-reactive state by converting let → $state()
- Fixed infinite loop in EnhancedBalance by converting $effect → $derived
- Fixed Chart.js integration by converting $state proxies to plain arrays
- Updated cospend dashboard and payment pages with proper reactivity
- Migrated 20+ route files from export let data → $props()
- Fixed TypeScript type annotations in page components
- Updated reactive statements in error and cospend routes
- Removed invalid onchange attribute from Toggle component
- Fixed modal ID isolation in CreateIngredientList/CreateStepList
- Fixed dark mode button visibility in TranslationApproval
- Build now succeeds with zero deprecation warnings
All functionality tested and working. No breaking changes to user experience.
267 lines
5.5 KiB
Svelte
267 lines
5.5 KiB
Svelte
<script lang="ts">
|
|
import type { VerseData } from '$lib/data/mysteryDescriptions';
|
|
|
|
let {
|
|
reference = '',
|
|
title = '',
|
|
verseData = null,
|
|
onClose
|
|
}: {
|
|
reference?: string,
|
|
title?: string,
|
|
verseData?: VerseData | null,
|
|
onClose: () => void
|
|
} = $props();
|
|
|
|
let book: string = $state(verseData?.book || '');
|
|
let chapter: number = $state(verseData?.chapter || 0);
|
|
let verses: Array<{ verse: number; text: string }> = $state(verseData?.verses || []);
|
|
let loading = $state(false);
|
|
let error = $state(verseData ? '' : 'Keine Versdaten verfügbar');
|
|
|
|
function handleBackdropClick(event: MouseEvent) {
|
|
if (event.target === event.currentTarget) {
|
|
onClose();
|
|
}
|
|
}
|
|
|
|
function handleKeydown(event: KeyboardEvent) {
|
|
if (event.key === 'Escape') {
|
|
onClose();
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<svelte:window onkeydown={handleKeydown} />
|
|
|
|
<div class="modal-backdrop" onclick={handleBackdropClick} role="presentation">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<div class="header-content">
|
|
{#if title}
|
|
<h3 class="modal-title">
|
|
{#if title.includes(':')}
|
|
{title.split(':')[0]}:<br>{title.split(':')[1]}
|
|
{:else}
|
|
{title}
|
|
{/if}
|
|
</h3>
|
|
{/if}
|
|
<p class="modal-reference">{reference}</p>
|
|
</div>
|
|
<button class="close-button" onclick={onClose} aria-label="Schließen">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="modal-body">
|
|
{#if loading}
|
|
<p class="loading">Lädt...</p>
|
|
{:else if error}
|
|
<p class="error">{error}</p>
|
|
{:else if verses.length > 0}
|
|
<div class="verses">
|
|
{#each verses as verse}
|
|
<p class="verse">
|
|
<span class="verse-number">{verse.verse}</span>
|
|
<span class="verse-text">{verse.text}</span>
|
|
</p>
|
|
{/each}
|
|
</div>
|
|
{:else}
|
|
<p class="error">Keine Verse gefunden</p>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.modal-backdrop {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
backdrop-filter: blur(10px);
|
|
background: rgba(0, 0, 0, 0.3);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 1000;
|
|
padding: 1rem;
|
|
animation: show-backdrop 200ms ease forwards;
|
|
}
|
|
|
|
@keyframes show-backdrop {
|
|
from {
|
|
backdrop-filter: blur(0px);
|
|
background: rgba(0, 0, 0, 0);
|
|
}
|
|
to {
|
|
backdrop-filter: blur(10px);
|
|
background: rgba(0, 0, 0, 0.3);
|
|
}
|
|
}
|
|
|
|
@media(prefers-color-scheme: light) {
|
|
.modal-backdrop {
|
|
background: rgba(255, 255, 255, 0.3);
|
|
}
|
|
|
|
@keyframes show-backdrop {
|
|
from {
|
|
backdrop-filter: blur(0px);
|
|
background: rgba(255, 255, 255, 0);
|
|
}
|
|
to {
|
|
backdrop-filter: blur(10px);
|
|
background: rgba(255, 255, 255, 0.3);
|
|
}
|
|
}
|
|
}
|
|
|
|
.modal-content {
|
|
background: var(--nord0);
|
|
border-radius: 12px;
|
|
max-width: 600px;
|
|
width: 100%;
|
|
max-height: 80vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
|
|
position: relative;
|
|
}
|
|
|
|
@media(prefers-color-scheme: light) {
|
|
.modal-content {
|
|
background: var(--nord6);
|
|
}
|
|
}
|
|
|
|
.modal-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
padding: 1.5rem;
|
|
border-bottom: 1px solid var(--nord3);
|
|
}
|
|
|
|
@media(prefers-color-scheme: light) {
|
|
.modal-header {
|
|
border-bottom: 1px solid var(--nord4);
|
|
}
|
|
}
|
|
|
|
.header-content {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.modal-title {
|
|
margin: 0;
|
|
color: var(--nord10);
|
|
font-size: 1.3rem;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.modal-reference {
|
|
margin: 0;
|
|
color: var(--nord8);
|
|
font-size: 1rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.close-button {
|
|
position: absolute;
|
|
top: -1rem;
|
|
right: -1rem;
|
|
background-color: var(--nord11);
|
|
border: none;
|
|
cursor: pointer;
|
|
padding: 1rem;
|
|
border-radius: 1000px;
|
|
color: white;
|
|
transition: 200ms;
|
|
box-shadow: 0 0 1em 0.2em rgba(0, 0, 0, 0.3);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 1001;
|
|
}
|
|
|
|
.close-button svg {
|
|
width: 2rem;
|
|
height: 2rem;
|
|
}
|
|
|
|
.close-button:hover {
|
|
background-color: var(--nord0);
|
|
transform: scale(1.2, 1.2);
|
|
box-shadow: 0 0 1em 0.4em rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
.close-button:active {
|
|
transition: 50ms;
|
|
scale: 0.8 0.8;
|
|
}
|
|
|
|
.modal-body {
|
|
padding: 1rem;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.loading,
|
|
.error {
|
|
text-align: center;
|
|
color: var(--nord4);
|
|
font-style: italic;
|
|
}
|
|
|
|
@media(prefers-color-scheme: light) {
|
|
.loading,
|
|
.error {
|
|
color: var(--nord2);
|
|
}
|
|
}
|
|
|
|
.error {
|
|
color: var(--nord11);
|
|
}
|
|
|
|
.verses {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0;
|
|
}
|
|
|
|
.verse {
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
line-height: 1.6;
|
|
color: var(--nord4);
|
|
}
|
|
|
|
@media(prefers-color-scheme: light) {
|
|
.verse {
|
|
color: var(--nord0);
|
|
}
|
|
}
|
|
|
|
.verse-number {
|
|
color: var(--nord10);
|
|
font-weight: 700;
|
|
min-width: 2rem;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.verse-text {
|
|
flex: 1;
|
|
font-size: 1.1rem;
|
|
}
|
|
</style>
|