diff --git a/src/App.svelte b/src/App.svelte index 2cb2c86..c6fb9e7 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -166,14 +166,23 @@ const MIN_THUMB = 24; let toolbarClipEl: HTMLDivElement | null = $state(null); let toolbarStackEl: HTMLDivElement | null = $state(null); + // The track lives inside the island (header / expanded row) so its dropdowns can + // paint above it, and is counter-translated so it stays viewport-fixed while the + // stack scrolls. Its width spans the visible island minus an edge inset. + const STACK_PAD_PX = 13.6; // 0.85rem — keep in sync with .toolbar-stack padding + const TRACK_EDGE = 20; // track inset from the island's side edges let toolbarStackWidth = $state(0); // full content width of the stack let toolbarViewWidth = $state(0); // visible (clipped) width let tbScroll = $state(0); // current left offset in px let tbOverflow = $derived(Math.max(0, toolbarStackWidth - toolbarViewWidth)); + let tbTrackW = $derived(Math.max(0, toolbarViewWidth - 2 * (STACK_PAD_PX + TRACK_EDGE))); + // Thumb: proportional, but capped short (25% of the track) so it reads clearly. let thumbWidth = $derived( - toolbarStackWidth > 0 ? Math.max(MIN_THUMB, (toolbarViewWidth / toolbarStackWidth) * toolbarViewWidth) : 0, + toolbarStackWidth > 0 + ? Math.max(MIN_THUMB, Math.min(0.25 * tbTrackW, (toolbarViewWidth / toolbarStackWidth) * tbTrackW)) + : 0, ); - let thumbTravel = $derived(Math.max(0, toolbarViewWidth - thumbWidth)); + let thumbTravel = $derived(Math.max(0, tbTrackW - thumbWidth)); let thumbLeft = $derived(tbOverflow > 0 ? (tbScroll / tbOverflow) * thumbTravel : 0); // Track the stack's content width and the visible width; clamp the offset into @@ -209,6 +218,10 @@ function onScrollbarPointerDown(e: PointerEvent) { if (thumbTravel <= 0) return; const track = e.currentTarget as HTMLElement; + // The track overlays the island: suppress native text selection under the + // drag, and re-focus manually since preventDefault also blocks click-focus. + e.preventDefault(); + track.focus(); const thumb = track.querySelector('.toolbar-scrollbar-thumb'); if (!thumb?.contains(e.target as Node)) { const rect = track.getBoundingClientRect(); @@ -552,7 +565,28 @@ } -
+
+ {#snippet toolbarScrollbar()} + {#if tbOverflow > 0} +
+
+
+ {/if} + {/snippet}
@@ -716,6 +750,9 @@ onchange={handleImportFile} />
+ {#if !toolbarExpanded} + {@render toolbarScrollbar()} + {/if}
+ {@render toolbarScrollbar()} {/if}
- {#if tbOverflow > 0} -
-
-
- {/if}