refactor(ui): unified bottom-stack container for PillNav, QuickInput, TagStrip

Replace 4 independent position:fixed elements with one flex container that
stacks them naturally from bottom to top. Elements push each other
automatically — no more hardcoded offsets or z-index conflicts.

Stack order (bottom → top):
1. PillNavigation (collapsible)
2. TagStrip (togglable)
3. QuickInputBar + toggle button row

Shared-UI changes:
- PillNavigation: add positioning='fixed'|'static' prop
- QuickInputBar: add positioning='fixed'|'static' prop
- TagStrip: add positioning='fixed'|'static' prop
- All default to 'fixed' for backward compatibility

Layout changes:
- Wrap all bottom elements in .bottom-stack (position:fixed, flex-column)
- Remove hardcoded bottomOffset calculations
- Toggle button is now inline next to QuickInputBar (not separately positioned)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-02 23:52:40 +02:00
parent bed2060ba5
commit b415567dfa
4 changed files with 154 additions and 85 deletions

View file

@ -228,6 +228,8 @@
onToggleTheme?: () => void;
/** Whether dark mode is active */
isDark?: boolean;
/** Use 'static' when inside a flex container (bottom-stack pattern). Default: 'fixed'. */
positioning?: 'fixed' | 'static';
/** Whether navigation is collapsed */
isCollapsed?: boolean;
/** Called when collapsed state changes */
@ -318,6 +320,7 @@
onLogout,
onToggleTheme,
isDark = false,
positioning = 'fixed',
isCollapsed: externalCollapsed,
onCollapsedChange,
languageItems = [],
@ -403,6 +406,7 @@
{#if !(externalCollapsed ?? false)}
<nav
class="pill-nav"
class:pill-nav-static={positioning === 'static'}
style="{primaryColor
? `--pill-primary-color: ${primaryColor};`
: ''}--pill-nav-bottom: {bottomOffset}"
@ -828,6 +832,12 @@
container-name: pillnav;
}
.pill-nav-static {
position: relative;
bottom: auto;
z-index: auto;
}
.pill-nav-container {
display: flex;
align-items: center;

View file

@ -34,6 +34,8 @@
createHref?: string;
/** Whether the filter strip below is visible (adjusts bottom position) */
aboveFilterStrip?: boolean;
/** Use 'static' when inside a flex container (bottom-stack pattern). Default: 'fixed'. */
positioning?: 'fixed' | 'static';
}
let {
@ -48,6 +50,7 @@
showCreateButton = true,
createHref,
aboveFilterStrip = false,
positioning = 'fixed',
}: Props = $props();
const resolvedCreateHref = $derived(createHref ?? managementHref + '?new=true');
@ -63,7 +66,11 @@
}
</script>
<div class="tag-strip-wrapper" class:above-filter-strip={aboveFilterStrip}>
<div
class="tag-strip-wrapper"
class:above-filter-strip={aboveFilterStrip}
class:tag-strip-static={positioning === 'static'}
>
<div class="tag-strip-container">
<!-- Clear Filter Button (always rendered to prevent layout shift) -->
<button
@ -140,6 +147,12 @@
left: 0;
right: 0;
z-index: 49;
}
.tag-strip-static {
position: relative;
bottom: auto;
z-index: auto;
display: flex;
flex-direction: column;
align-items: stretch;

View file

@ -79,6 +79,8 @@
highlightPatterns?: HighlightPattern[];
/** Locale for syntax highlighting keywords (e.g., 'de', 'en'). Default: 'de'. */
locale?: string;
/** Use 'static' when inside a flex container (bottom-stack pattern). Default: 'fixed'. */
positioning?: 'fixed' | 'static';
}
let {
@ -107,6 +109,7 @@
leftAction,
highlightPatterns,
locale = 'de',
positioning = 'fixed',
}: Props = $props();
// Use settings for autoFocus
@ -382,6 +385,7 @@
class="quick-input-bar"
class:has-fab-right={hasFabRight}
class:has-fab-left={hasFabLeft}
class:quick-input-static={positioning === 'static'}
style="--bottom-offset: {bottomOffset}"
>
<!-- Results Panel (above input) -->
@ -566,6 +570,12 @@
transition: bottom 0.3s ease;
}
.quick-input-static {
position: relative;
bottom: auto;
z-index: auto;
}
/* Leave space for FAB on mobile */
@media (max-width: 900px) {
.quick-input-bar.has-fab-right {