From 2c370363f58b2007a5600f4c779c8052d94441a1 Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Sat, 10 Jan 2026 10:47:55 +0100 Subject: [PATCH] fix: resolve recipe edit modal issues and improve dark mode visibility - Migrate TranslationApproval and edit page to Svelte 5 runes ($props, $state, $derived) - Fix empty modal issue by eagerly initializing editableEnglish from germanData - Fix modal state isolation by adding language-specific modal IDs (en/de) - Resolve cross-contamination where English modals opened German ingredient/instruction editors - Improve button icon visibility in dark mode by setting white fill color - Replace createEventDispatcher with callback props for Svelte 5 compatibility --- .../components/CreateIngredientList.svelte | 18 +- src/lib/components/CreateStepList.svelte | 16 +- src/lib/components/TranslationApproval.svelte | 306 +++++++++--------- .../TranslationFieldComparison.svelte | 32 +- .../edit/[name]/+page.svelte | 110 ++++--- 5 files changed, 249 insertions(+), 233 deletions(-) diff --git a/src/lib/components/CreateIngredientList.svelte b/src/lib/components/CreateIngredientList.svelte index 866effd..e342b98 100644 --- a/src/lib/components/CreateIngredientList.svelte +++ b/src/lib/components/CreateIngredientList.svelte @@ -191,7 +191,7 @@ function editItemFromReference(list_index: number, position: 'before' | 'after', ingredient_index: "", }; - const modal_el = document.querySelector("#edit_ingredient_modal") as HTMLDialogElement; + const modal_el = document.querySelector(`#edit_ingredient_modal-${lang}`) as HTMLDialogElement; if (modal_el) { modal_el.showModal(); } @@ -214,7 +214,7 @@ function openAddToReferenceModal(list_index: number, position: 'before' | 'after list_index: "", ingredient_index: "", }; - const modal_el = document.querySelector("#edit_ingredient_modal") as HTMLDialogElement; + const modal_el = document.querySelector(`#edit_ingredient_modal-${lang}`) as HTMLDialogElement; if (modal_el) { modal_el.showModal(); } @@ -231,12 +231,12 @@ function get_sublist_index(sublist_name, list){ export function show_modal_edit_subheading_ingredient(list_index){ edit_heading.name = ingredients[list_index].name edit_heading.list_index = list_index - const el = document.querySelector('#edit_subheading_ingredient_modal') + const el = document.querySelector(`#edit_subheading_ingredient_modal-${lang}`) el.showModal() } export function edit_subheading_and_close_modal(){ ingredients[edit_heading.list_index].name = edit_heading.name - const el = document.querySelector('#edit_subheading_ingredient_modal') + const el = document.querySelector(`#edit_subheading_ingredient_modal-${lang}`) el.close() } @@ -286,7 +286,7 @@ export function show_modal_edit_ingredient(list_index, ingredient_index){ edit_ingredient.list_index = list_index edit_ingredient.ingredient_index = ingredient_index edit_ingredient.sublist = ingredients[list_index].name - const modal_el = document.querySelector("#edit_ingredient_modal"); + const modal_el = document.querySelector(`#edit_ingredient_modal-${lang}`); modal_el.showModal(); } export function edit_ingredient_and_close_modal(){ @@ -301,7 +301,7 @@ export function edit_ingredient_and_close_modal(){ editing: false, item_index: -1 }; - const modal_el = document.querySelector("#edit_ingredient_modal") as HTMLDialogElement; + const modal_el = document.querySelector(`#edit_ingredient_modal-${lang}`) as HTMLDialogElement; if (modal_el) { setTimeout(() => modal_el.close(), 0); } @@ -341,7 +341,7 @@ export function edit_ingredient_and_close_modal(){ } ingredients[edit_ingredient.list_index].name = edit_ingredient.sublist } - const modal_el = document.querySelector("#edit_ingredient_modal") as HTMLDialogElement; + const modal_el = document.querySelector(`#edit_ingredient_modal-${lang}`) as HTMLDialogElement; if (modal_el) { // Defer closing to next tick to ensure all bindings are updated setTimeout(() => modal_el.close(), 0); @@ -887,7 +887,7 @@ h3{ - +

{t[lang].editIngredient}

@@ -902,7 +902,7 @@ h3{
- +

{t[lang].renameCategory}

do_on_key(event, 'Enter', false, edit_subheading_and_close_modal)} > diff --git a/src/lib/components/CreateStepList.svelte b/src/lib/components/CreateStepList.svelte index fbbaaf1..4f06f04 100644 --- a/src/lib/components/CreateStepList.svelte +++ b/src/lib/components/CreateStepList.svelte @@ -186,7 +186,7 @@ function editStepFromReference(list_index: number, position: 'before' | 'after', step_index: 0, }; - const modal_el = document.querySelector("#edit_step_modal") as HTMLDialogElement; + const modal_el = document.querySelector(`#edit_step_modal-${lang}`) as HTMLDialogElement; if (modal_el) { modal_el.showModal(); } @@ -207,7 +207,7 @@ function openAddToReferenceModal(list_index: number, position: 'before' | 'after list_index: 0, step_index: 0, }; - const modal_el = document.querySelector("#edit_step_modal") as HTMLDialogElement; + const modal_el = document.querySelector(`#edit_step_modal-${lang}`) as HTMLDialogElement; if (modal_el) { modal_el.showModal(); } @@ -270,7 +270,7 @@ export function show_modal_edit_step(list_index, step_index){ } edit_step.list_index = list_index edit_step.step_index = step_index - const modal_el = document.querySelector("#edit_step_modal"); + const modal_el = document.querySelector(`#edit_step_modal-${lang}`); modal_el.showModal(); } @@ -286,7 +286,7 @@ export function edit_step_and_close_modal(){ editing: false, step_index: -1 }; - const modal_el = document.querySelector("#edit_step_modal") as HTMLDialogElement; + const modal_el = document.querySelector(`#edit_step_modal-${lang}`) as HTMLDialogElement; if (modal_el) { setTimeout(() => modal_el.close(), 0); } @@ -315,7 +315,7 @@ export function edit_step_and_close_modal(){ // Normal edit behavior instructions[edit_step.list_index].steps[edit_step.step_index] = edit_step.step } - const modal_el = document.querySelector("#edit_step_modal") as HTMLDialogElement; + const modal_el = document.querySelector(`#edit_step_modal-${lang}`) as HTMLDialogElement; if (modal_el) { // Defer closing to next tick to ensure all bindings are updated setTimeout(() => modal_el.close(), 0); @@ -325,7 +325,7 @@ export function edit_step_and_close_modal(){ export function show_modal_edit_subheading_step(list_index){ edit_heading.name = instructions[list_index].name edit_heading.list_index = list_index - const el = document.querySelector('#edit_subheading_steps_modal') + const el = document.querySelector(`#edit_subheading_steps_modal-${lang}`) el.showModal() } @@ -942,7 +942,7 @@ h3{
- +

{t[lang].editStep}

do_on_key(event, 'Enter', false , edit_step_and_close_modal)}> @@ -955,7 +955,7 @@ h3{
- +

{t[lang].renameCategory}

do_on_key(event, 'Enter', false, edit_subheading_steps_and_close_modal)}> diff --git a/src/lib/components/TranslationApproval.svelte b/src/lib/components/TranslationApproval.svelte index b0eb5a0..f72557d 100644 --- a/src/lib/components/TranslationApproval.svelte +++ b/src/lib/components/TranslationApproval.svelte @@ -1,22 +1,38 @@ diff --git a/src/routes/[recipeLang=recipeLang]/edit/[name]/+page.svelte b/src/routes/[recipeLang=recipeLang]/edit/[name]/+page.svelte index ad0c6b4..0a24fa1 100644 --- a/src/routes/[recipeLang=recipeLang]/edit/[name]/+page.svelte +++ b/src/routes/[recipeLang=recipeLang]/edit/[name]/+page.svelte @@ -8,53 +8,64 @@ import '$lib/css/nordtheme.css' import { redirect } from '@sveltejs/kit'; import EditRecipeNote from '$lib/components/EditRecipeNote.svelte'; + import type { PageData } from './$types'; + import CardAdd from '$lib/components/CardAdd.svelte'; + import CreateIngredientList from '$lib/components/CreateIngredientList.svelte'; + import CreateStepList from '$lib/components/CreateStepList.svelte'; + import { season } from '$lib/js/season_store'; + import { portions } from '$lib/js/portions_store'; + import { img } from '$lib/js/img_store'; - export let data: PageData; - let preamble = data.recipe.preamble - let addendum = data.recipe.addendum - let image_preview_url="https://bocken.org/static/rezepte/thumb/" + (data.recipe.images?.[0]?.mediapath || `${data.recipe.short_name}.webp`); - let note = data.recipe.note + let { data }: { data: PageData } = $props(); + + let preamble = $state(data.recipe.preamble); + let addendum = $state(data.recipe.addendum); + let image_preview_url = $state("https://bocken.org/static/rezepte/thumb/" + (data.recipe.images?.[0]?.mediapath || `${data.recipe.short_name}.webp`)); + let note = $state(data.recipe.note); // Translation workflow state - let showTranslationWorkflow = false; - let translationData: any = data.recipe.translations?.en || null; - let changedFields: string[] = []; + let showTranslationWorkflow = $state(false); + let translationData = $state(data.recipe.translations?.en || null); + let changedFields = $state([]); // Store original recipe data for change detection const originalRecipe = JSON.parse(JSON.stringify(data.recipe)); - import { season } from '$lib/js/season_store'; - import { portions } from '$lib/js/portions_store'; - - portions.update(() => data.recipe.portions) - let portions_local - portions.subscribe((p) => { - portions_local = p + portions.update(() => data.recipe.portions); + let portions_local = $state(data.recipe.portions); + $effect(() => { + portions.subscribe((p) => { + portions_local = p; }); - - season.update(() => data.recipe.season) - let season_local - season.subscribe((s) => { - season_local = s }); + season.update(() => data.recipe.season); + let season_local = $state(data.recipe.season); + $effect(() => { + season.subscribe((s) => { + season_local = s; + }); + }); - import { img } from '$lib/js/img_store'; - let img_local - img.update(() => "") - img.subscribe((i) => { - img_local = i}); + let img_local = $state(''); + img.update(() => ''); + $effect(() => { + img.subscribe((i) => { + img_local = i; + }); + }); - let old_short_name = data.recipe.short_name + let old_short_name = $state(data.recipe.short_name); - export let card_data ={ + let card_data = $state({ icon: data.recipe.icon, category: data.recipe.category, name: data.recipe.name, description: data.recipe.description, tags: data.recipe.tags, - } - export let add_info ={ + }); + + let add_info = $state({ preparation: data.recipe.preparation, fermentation: { bulk: data.recipe.fermentation.bulk, @@ -67,23 +78,17 @@ }, total_time: data.recipe.total_time, cooking: data.recipe.cooking, - } + }); - let images = data.recipe.images + let images = $state(data.recipe.images); - let short_name = data.recipe.short_name - let datecreated = data.recipe.datecreated - let datemodified = new Date() - let isBaseRecipe = data.recipe.isBaseRecipe || false + let short_name = $state(data.recipe.short_name); + let datecreated = $state(data.recipe.datecreated); + let datemodified = $state(new Date()); + let isBaseRecipe = $state(data.recipe.isBaseRecipe || false); - import type { PageData } from './$types'; - import CardAdd from '$lib/components/CardAdd.svelte'; - - import CreateIngredientList from '$lib/components/CreateIngredientList.svelte'; - export let ingredients = data.recipe.ingredients - - import CreateStepList from '$lib/components/CreateStepList.svelte'; - export let instructions = data.recipe.instructions + let ingredients = $state(data.recipe.ingredients); + let instructions = $state(data.recipe.instructions); function get_season(){ @@ -389,6 +394,17 @@ input:focus-visible flex-direction: column; } } + +/* Fix button icon visibility in dark mode */ +@media (prefers-color-scheme: dark) { + .list_wrapper :global(svg) { + fill: white !important; + } + .list_wrapper :global(.button_arrow) { + fill: var(--nord4) !important; + } +} + h1{ text-align: center; margin-bottom: 2rem; @@ -609,10 +625,10 @@ button.action_button{ oldRecipeData={originalRecipe} {changedFields} isEditMode={true} - on:approved={handleTranslationApproved} - on:skipped={handleTranslationSkipped} - on:cancelled={handleTranslationCancelled} - on:forceFullRetranslation={forceFullRetranslation} + onapproved={handleTranslationApproved} + onskipped={handleTranslationSkipped} + oncancelled={handleTranslationCancelled} + onforceFullRetranslation={forceFullRetranslation} />
{/if}