From 8656e92c5df4cf66c9cc752b61114e7a06951512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Doma=C5=84ski?= Date: Wed, 15 Apr 2026 21:35:14 +0200 Subject: [PATCH] [autofocus] fix --- src/lib/components/ui/CommandPalette.svelte | 236 ++++++++------ src/lib/components/ui/Sheet.svelte | 22 +- .../(app)/diary/[date]/add-entry/+page.svelte | 16 +- .../[date]/edit-entry/[entry_id]/+page.svelte | 303 ++++++++++-------- src/routes/(app)/presets/+page.svelte | 5 +- 5 files changed, 333 insertions(+), 249 deletions(-) diff --git a/src/lib/components/ui/CommandPalette.svelte b/src/lib/components/ui/CommandPalette.svelte index c43d0bf..3b3b109 100644 --- a/src/lib/components/ui/CommandPalette.svelte +++ b/src/lib/components/ui/CommandPalette.svelte @@ -1,111 +1,159 @@ {#if open} - -
+ +
- -
-
- -
- - - - - esc -
+ +
+
+ +
+ + + + + esc +
- -
    - {#each filtered as cmd, i (cmd.id)} -
  • - -
  • - {/each} + +
      + {#each filtered as cmd, i (cmd.id)} +
    • + +
    • + {/each} - {#if filtered.length === 0} -
    • No commands found
    • - {/if} -
    + {#if filtered.length === 0} +
  • + No commands found +
  • + {/if} +
- -
- ↑↓ navigate - select -
-
-
+ +
+ ↑↓ navigate + select +
+
+
{/if} diff --git a/src/lib/components/ui/Sheet.svelte b/src/lib/components/ui/Sheet.svelte index d6a240f..5053ad8 100644 --- a/src/lib/components/ui/Sheet.svelte +++ b/src/lib/components/ui/Sheet.svelte @@ -10,6 +10,25 @@ let { open, onclose, title, children }: Props = $props(); + let bottomOffset = $state(0); + + onMount(() => { + const vv = window.visualViewport; + if (!vv) return; + + function update() { + bottomOffset = Math.max(0, window.innerHeight - vv!.offsetTop - vv!.height); + } + + vv.addEventListener('resize', update); + vv.addEventListener('scroll', update); + + return () => { + vv.removeEventListener('resize', update); + vv.removeEventListener('scroll', update); + }; + }); + function handleBackdrop(e: MouseEvent) { if (e.target === e.currentTarget) onclose(); } @@ -32,8 +51,9 @@
diff --git a/src/routes/(app)/diary/[date]/add-entry/+page.svelte b/src/routes/(app)/diary/[date]/add-entry/+page.svelte index e8530ee..367edf8 100644 --- a/src/routes/(app)/diary/[date]/add-entry/+page.svelte +++ b/src/routes/(app)/diary/[date]/add-entry/+page.svelte @@ -13,7 +13,9 @@ import { kcal, g } from "$lib/utils/format"; import { today } from "$lib/utils/date"; - const date = $derived(page.params.date === 'today' ? today() : page.params.date!); + const date = $derived( + page.params.date === "today" ? today() : page.params.date!, + ); const mealId = $derived(Number(page.url.searchParams.get("meal_id"))); const queryClient = useQueryClient(); @@ -31,16 +33,8 @@ let scanError = $state(null); let searchInput = $state(null); - $effect(() => { - if (searchInput) setTimeout(() => searchInput?.focus(), 50); - }); let gramsInput = $state(null); - $effect(() => { - if (selectedProduct && gramsInput) { - setTimeout(() => gramsInput?.focus(), 50); - } - }); function handleSearch(value: string) { q = value; @@ -147,6 +141,7 @@ e.preventDefault()} onclick={() => { grams = Math.max(1, grams - 10); - gramsInput?.focus(); }} class="w-11 h-11 rounded-xl bg-zinc-800 hover:bg-zinc-700 transition-colors text-lg font-medium flex items-center justify-center" >− e.currentTarget.select()} onkeydown={(e) => { if (e.key === "Enter") handleAddEntry(); @@ -322,7 +317,6 @@ onpointerdown={(e) => e.preventDefault()} onclick={() => { grams = grams + 10; - gramsInput?.focus(); }} class="w-11 h-11 rounded-xl bg-zinc-800 hover:bg-zinc-700 transition-colors text-lg font-medium flex items-center justify-center" >+ - import { page } from '$app/state'; - import { goto } from '$app/navigation'; - import { useQueryClient } from '@tanstack/svelte-query'; - import { deleteEntry } from '$lib/api/entries'; - import { offlineEditEntry } from '$lib/offline/mutations'; - import { network } from '$lib/offline/network.svelte'; - import TopBar from '$lib/components/ui/TopBar.svelte'; - import { today } from '$lib/utils/date'; + import { page } from "$app/state"; + import { goto } from "$app/navigation"; + import { useQueryClient } from "@tanstack/svelte-query"; + import { deleteEntry } from "$lib/api/entries"; + import { offlineEditEntry } from "$lib/offline/mutations"; + import { network } from "$lib/offline/network.svelte"; + import TopBar from "$lib/components/ui/TopBar.svelte"; + import { today } from "$lib/utils/date"; - const date = $derived(page.params.date === 'today' ? today() : page.params.date!); - const entryId = $derived(Number(page.params.entry_id)); - const queryClient = useQueryClient(); + const date = $derived( + page.params.date === "today" ? today() : page.params.date!, + ); + const entryId = $derived(Number(page.params.entry_id)); + const queryClient = useQueryClient(); - // Read entry from cache — diary was already fetched on the diary page - const cachedDiary = $derived( - queryClient.getQueryData(['diary', date]) - ); - const entry = $derived( - cachedDiary?.meals.flatMap(m => m.entries).find(e => e.id === entryId) - ); + // Read entry from cache — diary was already fetched on the diary page + const cachedDiary = $derived( + queryClient.getQueryData(["diary", date]), + ); + const entry = $derived( + cachedDiary?.meals.flatMap((m) => m.entries).find((e) => e.id === entryId), + ); - let grams = $state(entry?.grams ?? 100); - $effect(() => { if (entry) grams = entry.grams; }); + let grams = $state(entry?.grams ?? 100); + $effect(() => { + if (entry) grams = entry.grams; + }); - let saving = $state(false); - let deleting = $state(false); + let saving = $state(false); + let deleting = $state(false); - let gramsInput = $state(null); - $effect(() => { - if (entry && gramsInput) gramsInput.focus(); - }); + let gramsInput = $state(null); - const preview = $derived(entry ? { - calories: Math.round(entry.product.calories * grams / 100), - protein: Math.round(entry.product.protein * grams / 100 * 10) / 10, - carb: Math.round(entry.product.carb * grams / 100 * 10) / 10, - fat: Math.round(entry.product.fat * grams / 100 * 10) / 10, - } : null); + const preview = $derived( + entry + ? { + calories: Math.round((entry.product.calories * grams) / 100), + protein: + Math.round(((entry.product.protein * grams) / 100) * 10) / 10, + carb: Math.round(((entry.product.carb * grams) / 100) * 10) / 10, + fat: Math.round(((entry.product.fat * grams) / 100) * 10) / 10, + } + : null, + ); - async function handleSave() { - if (!entry) return; - saving = true; - try { - await offlineEditEntry(queryClient, date, entryId, grams); - goto(`/diary/${date}`); - } catch { - goto(`/diary/${date}`); - } finally { - saving = false; - } - } + async function handleSave() { + if (!entry) return; + saving = true; + try { + await offlineEditEntry(queryClient, date, entryId, grams); + goto(`/diary/${date}`); + } catch { + goto(`/diary/${date}`); + } finally { + saving = false; + } + } - async function handleDelete() { - if (!confirm('Remove this entry?')) return; - deleting = true; - try { - await deleteEntry(date, entry!.meal_id, entryId); - await queryClient.invalidateQueries({ queryKey: ['diary', date] }); - goto(`/diary/${date}`); - } catch { - goto(`/diary/${date}`); - } finally { - deleting = false; - } - } + async function handleDelete() { + if (!confirm("Remove this entry?")) return; + deleting = true; + try { + await deleteEntry(date, entry!.meal_id, entryId); + await queryClient.invalidateQueries({ queryKey: ["diary", date] }); + goto(`/diary/${date}`); + } catch { + goto(`/diary/${date}`); + } finally { + deleting = false; + } + }
- - {#snippet action()} - - {/snippet} - + + {#snippet action()} + + {/snippet} + - {#if !entry} -
- Entry not found -
- {:else} -
- -
-

{entry.product.name}

-

- {entry.product.calories} kcal · P {entry.product.protein}g · C {entry.product.carb}g · F {entry.product.fat}g - per 100g -

-
+ {#if !entry} +
+ Entry not found +
+ {:else} +
+ +
+

{entry.product.name}

+

+ {entry.product.calories} kcal · P {entry.product.protein}g · C {entry + .product.carb}g · F {entry.product.fat}g + per 100g +

+
- - {#if preview} -
- {#each [ - { label: 'kcal', value: preview.calories }, - { label: 'protein', value: preview.protein + 'g' }, - { label: 'carbs', value: preview.carb + 'g' }, - { label: 'fat', value: preview.fat + 'g' }, - ] as m} -
-

{m.value}

-

{m.label}

-
- {/each} -
- {/if} + + {#if preview} +
+ {#each [{ label: "kcal", value: preview.calories }, { label: "protein", value: preview.protein + "g" }, { label: "carbs", value: preview.carb + "g" }, { label: "fat", value: preview.fat + "g" }] as m} +
+

{m.value}

+

{m.label}

+
+ {/each} +
+ {/if} - -
- -
- - e.currentTarget.select()} - onkeydown={(e) => { if (e.key === 'Enter') handleSave(); }} - class="flex-1 bg-zinc-900 border border-zinc-700 rounded-xl px-4 py-3 text-center text-2xl font-semibold focus:outline-none focus:border-green-500 transition-colors" - /> - -
-
-
+ +
+ +
+ + e.currentTarget.select()} + onkeydown={(e) => { + if (e.key === "Enter") handleSave(); + }} + class="flex-1 bg-zinc-900 border border-zinc-700 rounded-xl px-4 py-3 text-center text-2xl font-semibold focus:outline-none focus:border-green-500 transition-colors" + /> + +
+
+
-
- -
- {/if} +
+ +
+ {/if}
diff --git a/src/routes/(app)/presets/+page.svelte b/src/routes/(app)/presets/+page.svelte index e369bb5..75b47ce 100644 --- a/src/routes/(app)/presets/+page.svelte +++ b/src/routes/(app)/presets/+page.svelte @@ -547,7 +547,6 @@ onpointerdown={(e) => e.preventDefault()} onclick={() => { editGrams = Math.max(1, editGrams - 10); - editGramsInput?.focus(); }} class="w-11 h-11 rounded-xl bg-zinc-800 hover:bg-zinc-700 transition-colors text-lg font-medium flex items-center justify-center" >− e.currentTarget.select()} class="flex-1 bg-zinc-800 border border-zinc-700 rounded-xl px-4 py-2.5 text-center text-xl font-semibold focus:outline-none focus:border-green-500 transition-colors" @@ -566,7 +566,6 @@ onpointerdown={(e) => e.preventDefault()} onclick={() => { editGrams = editGrams + 10; - editGramsInput?.focus(); }} class="w-11 h-11 rounded-xl bg-zinc-800 hover:bg-zinc-700 transition-colors text-lg font-medium flex items-center justify-center" >+ e.preventDefault()} onclick={() => { addGrams = Math.max(1, addGrams - 10); - addGramsInput?.focus(); }} class="w-11 h-11 rounded-xl bg-zinc-800 hover:bg-zinc-700 transition-colors text-lg font-medium flex items-center justify-center" >− e.preventDefault()} onclick={() => { addGrams = addGrams + 10; - addGramsInput?.focus(); }} class="w-11 h-11 rounded-xl bg-zinc-800 hover:bg-zinc-700 transition-colors text-lg font-medium flex items-center justify-center" >+