Files
homepage/src/lib/components/BaseRecipeSelector.svelte

275 lines
5.5 KiB
Svelte

<script lang="ts">
import { onMount } from 'svelte';
import { browser } from '$app/environment';
import { do_on_key } from '$lib/components/do_on_key.js'
import Check from '$lib/assets/icons/Check.svelte'
let {
type = 'ingredients' as 'ingredients' | 'instructions',
onSelect,
open = $bindable(false)
}: {
type?: 'ingredients' | 'instructions',
onSelect: (recipe: any, options: any) => void,
open?: boolean
} = $props();
// Unique dialog ID based on type to prevent conflicts when both are on the same page
const dialogId = `base-recipe-selector-modal-${type}`;
let baseRecipes: any[] = $state([]);
let selectedRecipe: any = $state(null);
let options = $state({
includeIngredients: false,
includeInstructions: false,
showLabel: true,
labelOverride: '',
baseMultiplier: 1
});
// Reset options whenever type or modal state changes
$effect(() => {
if (open || type) {
options.includeIngredients = type === 'ingredients';
options.includeInstructions = type === 'instructions';
}
});
onMount(async () => {
const res = await fetch('/api/rezepte/base-recipes');
baseRecipes = await res.json();
});
function handleInsert() {
if (selectedRecipe) {
onSelect(selectedRecipe, options);
// Reset modal
selectedRecipe = null;
options.labelOverride = '';
options.showLabel = true;
options.baseMultiplier = 1;
closeModal();
}
}
function closeModal() {
open = false;
if (browser) {
const modal = document.querySelector(`#${dialogId}`) as HTMLDialogElement;
if (modal) {
modal.close();
}
}
}
function openModal() {
if (browser) {
const modal = document.querySelector(`#${dialogId}`) as HTMLDialogElement;
if (modal) {
modal.showModal();
}
}
}
$effect(() => {
if (browser) {
if (open) {
setTimeout(openModal, 0);
} else {
closeModal();
}
}
});
</script>
<style>
dialog {
box-sizing: content-box;
width: 100%;
height: 100%;
background-color: transparent;
border: unset;
margin: 0;
transition: 500ms;
}
dialog[open]::backdrop {
animation: show 200ms ease forwards;
}
@keyframes show {
from {
backdrop-filter: blur(0px);
}
to {
backdrop-filter: blur(10px);
}
}
dialog h2 {
font-size: 3rem;
font-family: sans-serif;
color: white;
text-align: center;
margin-top: 30vh;
margin-top: 30dvh;
filter: drop-shadow(0 0 0.4em black)
drop-shadow(0 0 1em black);
}
.selector-content {
box-sizing: border-box;
margin-inline: auto;
margin-top: 2rem;
max-width: 600px;
padding: 2rem;
border-radius: 20px;
background-color: var(--blue);
color: white;
box-shadow: 0 0 1em 0.2em rgba(0,0,0,0.3);
}
.selector-content label {
display: block;
margin-block: 1rem;
font-size: 1.1rem;
}
.selector-content select,
.selector-content input[type="text"],
.selector-content input[type="number"] {
width: 100%;
padding: 0.5em 1em;
margin-top: 0.5em;
border-radius: 1000px;
border: 2px solid var(--nord4);
background-color: white;
color: var(--nord0);
font-size: 1rem;
transition: 100ms;
}
.selector-content select:hover,
.selector-content select:focus,
.selector-content input[type="text"]:hover,
.selector-content input[type="text"]:focus,
.selector-content input[type="number"]:hover,
.selector-content input[type="number"]:focus {
border-color: var(--nord9);
transform: scale(1.02, 1.02);
}
.selector-content input[type="checkbox"] {
width: 1.2em;
height: 1.2em;
margin-right: 0.5em;
vertical-align: middle;
}
.button-group {
display: flex;
gap: 1rem;
margin-top: 2rem;
justify-content: center;
}
.button-group button {
padding: 0.75em 2em;
font-size: 1.1rem;
border-radius: 1000px;
border: none;
cursor: pointer;
transition: 200ms;
font-weight: bold;
}
.button-insert {
background-color: var(--nord14);
color: var(--nord0);
}
.button-cancel {
background-color: var(--nord3);
color: white;
}
.button-group button:hover {
transform: scale(1.1, 1.1);
box-shadow: 0 0 1em 0.3em rgba(0,0,0,0.3);
}
@media (prefers-color-scheme: dark) {
.selector-content {
background-color: var(--nord1);
}
}
</style>
<dialog id={dialogId}>
<h2>Basisrezept einfügen</h2>
<div class="selector-content">
<label>
Basisrezept auswählen:
<select bind:value={selectedRecipe}>
<option value={null}>-- Auswählen --</option>
{#each baseRecipes as recipe}
<option value={recipe}>{recipe.icon} {recipe.name}</option>
{/each}
</select>
</label>
{#if type === 'ingredients'}
<label>
<input type="checkbox" bind:checked={options.includeIngredients} />
Zutaten einbeziehen
</label>
{/if}
{#if type === 'instructions'}
<label>
<input type="checkbox" bind:checked={options.includeInstructions} />
Zubereitungsschritte einbeziehen
</label>
{/if}
<label>
<input type="checkbox" bind:checked={options.showLabel} />
Rezeptname als Überschrift anzeigen
</label>
{#if options.showLabel}
<label>
Eigene Überschrift (optional):
<input
type="text"
bind:value={options.labelOverride}
placeholder={selectedRecipe?.name || 'Überschrift eingeben...'}
onkeydown={(event) => do_on_key(event, 'Enter', false, handleInsert)}
/>
</label>
{/if}
<label>
Mengenfaktor (Multiplikator):
<input
type="number"
bind:value={options.baseMultiplier}
min="0"
step="any"
placeholder="1"
onkeydown={(event) => do_on_key(event, 'Enter', false, handleInsert)}
/>
</label>
<div class="button-group">
<button class="button-insert" onclick={handleInsert} disabled={!selectedRecipe}>
Einfügen
</button>
<button class="button-cancel" onclick={closeModal}>
Abbrechen
</button>
</div>
</div>
</dialog>