Give selections a permanent row so the app stops jumping under the cursor
Some checks are pending
build-gate / build (push) Waiting to run
Some checks are pending
build-gate / build (push) Waiting to run
The selections row left the DOM whenever nothing was selected, so it appeared on every select and vanished on every clear — and the map, the table and the grid moved each time. That is what made the dashboard hard to use: not where the chips sat, but that everything below them shifted while you were looking at it. The row now always renders and only its contents change. Empty it reads "Selected World", which is also true rather than merely a placeholder: nothing filtered really does mean the whole world is in scope. Clear-all is simply absent when there is nothing to clear, instead of the row collapsing around it. Height is pinned and the row no longer wraps, because wrapping was the other way it could grow; a long selection scrolls sideways instead of taking a second line. Slim enough to read as a status strip rather than a tray. Measured across all three states — empty, selected, cleared — the row height stays 28px and the map top stays at 186px. Nothing below the row moves.
This commit is contained in:
parent
0f5ffa28b2
commit
6189966a89
1 changed files with 37 additions and 8 deletions
|
|
@ -174,13 +174,28 @@ function onClearAll() { emit('clear-all') }
|
|||
>{{ t.label }}</button>
|
||||
</div>
|
||||
|
||||
<!-- active-filter chips (removable) -->
|
||||
<div v-if="useFilters && chips.length" class="sfh__chips">
|
||||
<span v-for="c in chips" :key="c.name" class="sfh__chip">
|
||||
{{ c.label }}
|
||||
<button class="sfh__chip-x" type="button" :aria-label="`Remove ${c.label}`" @click="onRemoveChip(c.name)">×</button>
|
||||
</span>
|
||||
<button class="sfh__clear" type="button" @click="onClearAll">{{ L.clear_all }}</button>
|
||||
<!-- ★ THE SELECTIONS ROW IS PERMANENT — it must never leave the DOM.
|
||||
This was v-if="useFilters && chips.length", so the row DISAPPEARED whenever nothing was
|
||||
selected. That is the actual bug, and it is layout shift rather than chip placement:
|
||||
"whenever I deselect it, it removes that space from the second header... the decompression
|
||||
of the height causes the ENTIRE LOWER APP TO SHIFT UPWARD, which makes it really hard to
|
||||
track with your eyes." (Driver, 2026-09-22)
|
||||
A row that appears on select and vanishes on clear moves the map, the table and the grid on
|
||||
every single click. The row now always renders and only its CONTENTS change, so nothing below
|
||||
it can move.
|
||||
The empty state is "World" — the Driver's word, and it reads correctly too: no filter really
|
||||
does mean the whole world is in scope. Clear-all is omitted rather than the row collapsing,
|
||||
because there is nothing to clear; the row keeps its height either way. -->
|
||||
<div v-if="useFilters" class="sfh__chips" :class="{ 'is-empty': !chips.length }">
|
||||
<span class="sfh__sel-label">{{ L.selected || 'Selected' }}</span>
|
||||
<template v-if="chips.length">
|
||||
<span v-for="c in chips" :key="c.name" class="sfh__chip">
|
||||
{{ c.label }}
|
||||
<button class="sfh__chip-x" type="button" :aria-label="`Remove ${c.label}`" @click="onRemoveChip(c.name)">×</button>
|
||||
</span>
|
||||
<button class="sfh__clear" type="button" @click="onClearAll">{{ L.clear_all }}</button>
|
||||
</template>
|
||||
<span v-else class="sfh__sel-world">{{ L.world || 'World' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -240,7 +255,21 @@ function onClearAll() { emit('clear-all') }
|
|||
.sfh__pill:hover { border-color: var(--brand); }
|
||||
.sfh__pill[data-active] { background-color: var(--dx-accent-weak, rgba(146, 177, 149, .18)); border-color: var(--brand); color: var(--ink); }
|
||||
|
||||
.sfh__chips { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
||||
/* ★ FIXED HEIGHT IS THE WHOLE POINT — this row must be the same size empty or full, or everything
|
||||
below it jumps on every click. min-height pins it; nowrap keeps a long selection on ONE line rather
|
||||
than growing to a second (flex-wrap was the other way this row could change height).
|
||||
28px is the Driver's "a bit slimmer": tall enough for a chip and its remove button, short enough
|
||||
that it reads as a status strip rather than a tray. overflow-x:auto lets a long selection scroll
|
||||
sideways instead of ever adding a row. */
|
||||
.sfh__chips { display: flex; flex-wrap: nowrap; gap: 8px; align-items: center;
|
||||
min-height: 28px; overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; }
|
||||
/* The persistent prefix. flex:0 0 auto so it never shrinks away and the row always starts the same
|
||||
way, which is what makes the strip scannable at a glance. */
|
||||
.sfh__sel-label { flex: 0 0 auto; font-size: 0.75rem; font-weight: 700; letter-spacing: .4px;
|
||||
text-transform: uppercase; color: var(--mut, #6d766e); }
|
||||
/* The empty state. Muted rather than hidden: "World" is real information (nothing is filtered), and
|
||||
rendering it keeps the row's content box occupied so the height cannot collapse. */
|
||||
.sfh__sel-world { flex: 0 0 auto; font-size: 0.875rem; color: var(--mut, #6d766e); }
|
||||
.sfh__chip {
|
||||
display: inline-flex; gap: 6px; align-items: center; background-color: var(--dx-accent-weak, rgba(146, 177, 149, .18));
|
||||
color: var(--ink); border: 1px solid var(--line); border-radius: 100px; padding: 4px 12px; font-size: 0.85rem;
|
||||
|
|
|
|||
Loading…
Reference in a new issue