aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-07-28 18:14:56 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-07-28 18:14:56 +0200
commit3ce6da65adf9d8c657893edb1ee1278a18da3e4e (patch)
tree6f175a6384be290b17c22f863bfb7959c45f8b62
parent4bace68be5cbbdb37ba800cd285ff656a661c81b (diff)
feat: keep keyboard open on mobile when adding items to list
-rw-r--r--src/routes/+page.server.ts3
-rw-r--r--src/routes/+page.svelte99
2 files changed, 84 insertions, 18 deletions
diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts
index 9675ad8..b0ddff0 100644
--- a/src/routes/+page.server.ts
+++ b/src/routes/+page.server.ts
@@ -33,7 +33,8 @@ export const actions: Actions = {
}
await addOrUncheck(name, categoryId);
- return { success: true };
+ const { items } = await loadUnchecked();
+ return { success: true, items };
},
toggle: async ({ request }) => {
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 7a367c1..6d9d85f 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -1,45 +1,110 @@
<script lang="ts">
+ import { deserialize } from '$app/forms';
+ import { goto } from '$app/navigation';
import { enhance } from '$app/forms';
let { data, form } = $props();
let name = $state('');
+ let inputRef: HTMLInputElement;
+ // eslint-disable-next-line svelte/prefer-writable-derived
+ let localItems = $state(data?.items ?? []);
+ let addError = $state('');
- let listItems = $derived(data?.items ?? []);
+ // Sync local list with server data when it changes externally (toggle, navigation)
+ $effect(() => {
+ localItems = data?.items ?? [];
+ });
- function groupByCategory(items: typeof listItems) {
- const groups: Record<string, typeof listItems> = {};
+ function groupByCategory(items: typeof localItems) {
+ const groups: Record<string, typeof localItems> = {};
for (const item of items) {
const key = item.categoryName ?? 'Non catégorisé';
(groups[key] ??= []).push(item);
}
return Object.entries(groups);
}
+
+ async function handleAdd(redirectToEdit: boolean) {
+ if (!name.trim()) {
+ addError = 'Le nom est requis';
+ return;
+ }
+ addError = '';
+
+ const formData = new FormData();
+ formData.append('name', name);
+
+ const action = redirectToEdit ? '?/addAndEdit' : '?/add';
+ name = '';
+ inputRef.focus();
+
+ try {
+ const response = await fetch(action, {
+ method: 'POST',
+ headers: {
+ accept: 'application/json',
+ 'x-sveltekit-action': 'true',
+ },
+ body: formData,
+ });
+
+ const result = deserialize(await response.text());
+
+ if (result.type === 'error') {
+ addError =
+ typeof result.error === 'string'
+ ? result.error
+ : result.error?.message || 'Erreur';
+ return;
+ }
+
+ if (result.type === 'redirect') {
+ goto(result.location);
+ return;
+ }
+
+ if (result.type === 'success' && result.data?.items) {
+ localItems = result.data.items;
+ }
+ } catch (err) {
+ addError = 'Erreur réseau';
+ console.error(err);
+ }
+ }
</script>
-<form class="add-bar" method="POST" action="?/add" use:enhance>
+<div class="add-bar">
<input
+ bind:this={inputRef}
class="form-input"
type="text"
- name="name"
bind:value={name}
placeholder="Ajouter un article..."
autocomplete="off"
+ onkeydown={(e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ handleAdd(false);
+ }
+ }}
/>
- <button type="submit" aria-label="Ajouter">+</button>
- <button type="submit" formaction="?/addAndEdit" aria-label="Modifier"
- >→</button
+ <button type="button" aria-label="Ajouter" onclick={() => handleAdd(false)}
+ >+</button
+ >
+ <button type="button" aria-label="Modifier" onclick={() => handleAdd(true)}
+ >&rarr;</button
>
-</form>
+</div>
-{#if form?.error}
- <p class="error">{form.error}</p>
+{#if addError || form?.error}
+ <p class="error">{addError || form?.error}</p>
{/if}
-{#if listItems.length === 0}
+{#if localItems.length === 0}
<p class="empty">Rien à acheter</p>
{:else}
<form method="POST" action="?/toggle" use:enhance>
- {#each groupByCategory(listItems) as [cat, items] (cat)}
+ {#each groupByCategory(localItems) as [cat, items] (cat)}
<h3 class="category-name">{cat}</h3>
<ul class="item-list">
{#each items as item (item.id)}
@@ -107,25 +172,25 @@
transition: all 0.2s ease;
}
- .add-bar button[type='submit']:not([formaction]) {
+ .add-bar button:nth-child(2) {
background: var(--accent);
color: var(--white);
box-shadow: 0 1px 2px var(--shadow);
}
- .add-bar button[type='submit']:not([formaction]):hover {
+ .add-bar button:nth-child(2):hover {
background: var(--accent-hover);
transform: translateY(-1px);
box-shadow: 0 4px 8px var(--shadow-accent-lg);
}
- .add-bar button[formaction] {
+ .add-bar button:nth-child(3) {
background: var(--surface);
color: var(--text-secondary);
border: 1.5px solid var(--border);
}
- .add-bar button[formaction]:hover {
+ .add-bar button:nth-child(3):hover {
border-color: var(--accent);
color: var(--text);
}