fix(ui): avoid layout shifts in overflow-menu and repo filter (#37818)

Eliminate two layout shifts in the menu, one related to non-existant
label on page load and one to `0` value rendering.

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
Co-authored-by: bircni <bircni@icloud.com>
This commit is contained in:
silverwind
2026-07-26 12:26:52 +02:00
committed by GitHub
parent b4eb8c03c4
commit 14ca2e7526
3 changed files with 17 additions and 5 deletions

View File

@@ -522,6 +522,11 @@ overflow-menu {
position: relative;
}
/* hide until JS has arranged the items, so the un-collapsed menu is never painted */
overflow-menu:not([data-ready]) {
visibility: hidden;
}
overflow-menu .overflow-menu-popup {
position: absolute;
top: calc(100% + 8px);
@@ -609,6 +614,12 @@ overflow-menu .overflow-menu-button:hover {
overflow-menu .ui.label {
margin-left: 7px !important; /* save some space */
font-variant-numeric: tabular-nums; /* equal-width digits so counts don't reflow the row */
}
/* count not loaded yet, keep the reserved space so filling it in does not shift the row */
overflow-menu .ui.label:empty {
visibility: hidden;
}
.activity-bar-graph {

View File

@@ -63,6 +63,7 @@ export default defineComponent({
finalPage: 1,
searchQuery,
isLoading: false,
initialSearchDone: false,
staticPrefix: assetUrlPrefix,
counts: {} as Record<string, number>,
repoTypes: {
@@ -186,7 +187,6 @@ export default defineComponent({
this.reposFilter = filter;
this.repos = [];
this.page = 1;
this.counts[`${filter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
this.searchRepos();
},
@@ -247,7 +247,6 @@ export default defineComponent({
}
this.page = 1;
this.repos = [];
this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
this.searchRepos();
},
@@ -261,7 +260,6 @@ export default defineComponent({
}
this.page = 1;
this.repos = [];
this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
this.searchRepos();
},
@@ -276,7 +274,6 @@ export default defineComponent({
this.page = 1;
}
this.repos = [];
this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
await this.searchRepos();
},
@@ -308,6 +305,7 @@ export default defineComponent({
} catch {
if (searchedURL === this.searchURL) {
this.isLoading = false;
this.initialSearchDone = true;
}
return;
}
@@ -329,6 +327,7 @@ export default defineComponent({
this.finalPage = Math.ceil(count / this.searchLimit);
this.updateHistory();
this.isLoading = false;
this.initialSearchDone = true;
}
},
@@ -449,7 +448,8 @@ export default defineComponent({
</div>
</div>
</div>
<overflow-menu class="ui secondary pointing tabular borderless menu repos-filter">
<!-- stay hidden until the first count arrives, otherwise the label resizes after paint -->
<overflow-menu class="ui secondary pointing tabular borderless menu repos-filter" :class="{'tw-invisible': !initialSearchDone}">
<div class="overflow-menu-items tw-justify-center">
<a class="item" tabindex="0" :class="{active: reposFilter === 'all'}" @click="changeReposFilter('all')">
{{ textAll }}

View File

@@ -213,6 +213,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement {
if (newWidth !== this.lastWidth) {
requestAnimationFrame(() => {
this.updateItems();
this.setAttribute('data-ready', ''); // reveal via CSS [data-ready]
});
this.lastWidth = newWidth;
}