fix(bulk-upload): no layout shift, no autofocus on date field
Replace JS navHeight measurement with CSS var(--header-height) so the fixed
panel renders in its final position on first paint — no onMount shift.
Add autofocus prop to WhoWhenSection (default true, preserves document-edit
behaviour) and pass autofocus={false} from BulkDocumentEditLayout so the date
field does not steal focus before the user has even dropped any files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { SvelteMap } from 'svelte/reactivity';
|
import { SvelteMap } from 'svelte/reactivity';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { onMount, onDestroy, untrack } from 'svelte';
|
import { onDestroy, untrack } from 'svelte';
|
||||||
import { m } from '$lib/paraglide/messages.js';
|
import { m } from '$lib/paraglide/messages.js';
|
||||||
import BulkDropZone from './BulkDropZone.svelte';
|
import BulkDropZone from './BulkDropZone.svelte';
|
||||||
import FileSwitcherStrip from './FileSwitcherStrip.svelte';
|
import FileSwitcherStrip from './FileSwitcherStrip.svelte';
|
||||||
@@ -27,12 +27,6 @@ let {
|
|||||||
initialReceivers?: Person[];
|
initialReceivers?: Person[];
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
// --- Layout ---
|
|
||||||
let navHeight = $state(0);
|
|
||||||
onMount(() => {
|
|
||||||
navHeight = document.querySelector('header')?.getBoundingClientRect().height ?? 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
// --- File state ---
|
// --- File state ---
|
||||||
let files = new SvelteMap<string, FileEntry>();
|
let files = new SvelteMap<string, FileEntry>();
|
||||||
let activeId = $state<string | null>(null);
|
let activeId = $state<string | null>(null);
|
||||||
@@ -124,7 +118,7 @@ async function save() {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="fixed inset-x-0 bottom-0 flex flex-col" style="top: {navHeight}px">
|
<div class="fixed inset-x-0 bottom-0 flex flex-col" style="top: var(--header-height)">
|
||||||
<!-- Topbar -->
|
<!-- Topbar -->
|
||||||
<div class="flex shrink-0 items-center gap-3 border-b border-line bg-surface px-6 py-3">
|
<div class="flex shrink-0 items-center gap-3 border-b border-line bg-surface px-6 py-3">
|
||||||
<a
|
<a
|
||||||
@@ -226,6 +220,7 @@ async function save() {
|
|||||||
bind:selectedReceivers={selectedReceivers}
|
bind:selectedReceivers={selectedReceivers}
|
||||||
bind:dateIso={dateIso}
|
bind:dateIso={dateIso}
|
||||||
initialSenderName={initialSenderName}
|
initialSenderName={initialSenderName}
|
||||||
|
autofocus={false}
|
||||||
/>
|
/>
|
||||||
<DescriptionSection bind:tags={tags} hideTitle />
|
<DescriptionSection bind:tags={tags} hideTitle />
|
||||||
</ScopeCard>
|
</ScopeCard>
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ let {
|
|||||||
initialLocation = '',
|
initialLocation = '',
|
||||||
initialSenderName = '',
|
initialSenderName = '',
|
||||||
suggestedDateIso = '',
|
suggestedDateIso = '',
|
||||||
suggestedSenderName = ''
|
suggestedSenderName = '',
|
||||||
|
autofocus = true
|
||||||
}: {
|
}: {
|
||||||
senderId?: string;
|
senderId?: string;
|
||||||
selectedReceivers?: Person[];
|
selectedReceivers?: Person[];
|
||||||
@@ -26,6 +27,7 @@ let {
|
|||||||
initialSenderName?: string;
|
initialSenderName?: string;
|
||||||
suggestedDateIso?: string;
|
suggestedDateIso?: string;
|
||||||
suggestedSenderName?: string;
|
suggestedSenderName?: string;
|
||||||
|
autofocus?: boolean;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
let dateDisplay = $state(untrack(() => isoToGerman(initialDateIso)));
|
let dateDisplay = $state(untrack(() => isoToGerman(initialDateIso)));
|
||||||
@@ -69,7 +71,7 @@ $effect(() => {
|
|||||||
oninput={handleDateInput}
|
oninput={handleDateInput}
|
||||||
placeholder={m.form_placeholder_date()}
|
placeholder={m.form_placeholder_date()}
|
||||||
maxlength="10"
|
maxlength="10"
|
||||||
autofocus={!initialDateIso}
|
autofocus={autofocus && !initialDateIso}
|
||||||
class="block w-full rounded border border-line p-2 text-sm shadow-sm
|
class="block w-full rounded border border-line p-2 text-sm shadow-sm
|
||||||
{dateInvalid ? 'border-red-400 focus:outline-none focus-visible:ring-2 focus-visible:ring-red-500' : 'focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring'}"
|
{dateInvalid ? 'border-red-400 focus:outline-none focus-visible:ring-2 focus-visible:ring-red-500' : 'focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring'}"
|
||||||
aria-describedby={dateInvalid ? 'date-error' : undefined}
|
aria-describedby={dateInvalid ? 'date-error' : undefined}
|
||||||
@@ -89,7 +91,7 @@ $effect(() => {
|
|||||||
bind:value={senderId}
|
bind:value={senderId}
|
||||||
initialName={initialSenderName}
|
initialName={initialSenderName}
|
||||||
suggestedName={suggestedSenderName}
|
suggestedName={suggestedSenderName}
|
||||||
autofocus={!!initialDateIso}
|
autofocus={autofocus && !!initialDateIso}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user