Skip to content

Commit f68065c

Browse files
authored
Merge pull request #47236 from nextcloud/backport/47203/stable30
[stable30] fix(AppMenu): Prevent menu entries from jumping on hover
2 parents 9a1339d + f8bbb27 commit f68065c

4 files changed

Lines changed: 37 additions & 31 deletions

File tree

core/src/components/AppMenu.vue

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,6 @@ export default defineComponent({
123123
display: flex;
124124
flex-wrap: nowrap;
125125
margin-inline: calc(var(--app-menu-entry-growth) / 2);
126-
transition: margin-inline var(--animation-quick) ease-in-out;
127-
128-
// Remove padding if the first child is focussed
129-
&:has(.app-menu-entry:hover:first-child, .app-menu-entry:focus-within:first-child) {
130-
margin-inline: 0 calc(var(--app-menu-entry-growth) / 2);
131-
}
132-
// Remove padding if the last child is focussed
133-
&:has(.app-menu-entry:hover:last-child, .app-menu-entry:focus-within:last-child) {
134-
margin-inline: calc(var(--app-menu-entry-growth) / 2) 0;
135-
}
136126
}
137127
138128
&__overflow {

core/src/components/AppMenuEntry.vue

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
-->
55

66
<template>
7-
<li class="app-menu-entry"
7+
<li ref="containerElement"
8+
class="app-menu-entry"
89
:class="{
910
'app-menu-entry--active': app.active,
11+
'app-menu-entry--truncated': needsSpace,
1012
}">
1113
<a class="app-menu-entry__link"
1214
:href="app.href"
@@ -15,7 +17,7 @@
1517
:target="app.target ? '_blank' : undefined"
1618
:rel="app.target ? 'noopener noreferrer' : undefined">
1719
<AppMenuIcon class="app-menu-entry__icon" :app="app" />
18-
<span class="app-menu-entry__label">
20+
<span ref="labelElement" class="app-menu-entry__label">
1921
{{ app.name }}
2022
</span>
2123
</a>
@@ -24,11 +26,26 @@
2426

2527
<script setup lang="ts">
2628
import type { INavigationEntry } from '../types/navigation'
29+
import { onMounted, ref, watch } from 'vue'
2730
import AppMenuIcon from './AppMenuIcon.vue'
2831
29-
defineProps<{
32+
const props = defineProps<{
3033
app: INavigationEntry
3134
}>()
35+
36+
const containerElement = ref<HTMLLIElement>()
37+
const labelElement = ref<HTMLSpanElement>()
38+
const needsSpace = ref(false)
39+
40+
/** Update the space requirements of the app label */
41+
function calculateSize() {
42+
const maxWidth = containerElement.value!.clientWidth
43+
// Also keep the 0.5px letter spacing in mind
44+
needsSpace.value = (maxWidth - props.app.name.length * 0.5) < (labelElement.value!.scrollWidth)
45+
}
46+
// Update size on mounted and when the app name changes
47+
onMounted(calculateSize)
48+
watch(() => props.app.name, calculateSize)
3249
</script>
3350

3451
<style scoped lang="scss">
@@ -37,8 +54,6 @@ defineProps<{
3754
width: var(--header-height);
3855
height: var(--header-height);
3956
position: relative;
40-
// Needed to prevent jumping when hover an entry (keep in sync with :hover styles)
41-
transition: width var(--animation-quick) ease-in-out;
4257
4358
&__link {
4459
position: relative;
@@ -65,9 +80,8 @@ defineProps<{
6580
left: 50%;
6681
top: 50%;
6782
display: block;
68-
min-width: 100%;
6983
transform: translateX(-50%);
70-
width: 100%;
84+
max-width: 100%;
7185
text-overflow: ellipsis;
7286
overflow: hidden;
7387
letter-spacing: -0.5px;
@@ -115,25 +129,27 @@ defineProps<{
115129
116130
// Adjust the width when an entry is focussed
117131
// The focussed / hovered entry should grow, while both neighbors need to shrink
118-
&:hover,
119-
&:focus-within {
120-
width: calc(var(--header-height) + var(--app-menu-entry-growth));
132+
&--truncated:hover,
133+
&--truncated:focus-within {
134+
.app-menu-entry__label {
135+
max-width: calc(var(--header-height) + var(--app-menu-entry-growth));
136+
}
121137
122138
// The next entry needs to shrink half the growth
123139
+ .app-menu-entry {
124-
width: calc(var(--header-height) - (var(--app-menu-entry-growth) / 2));
125-
.app-menu-entry__icon {
126-
margin-inline-end: calc(var(--app-menu-entry-growth) / 2);
140+
.app-menu-entry__label {
141+
font-weight: normal;
142+
max-width: calc(var(--header-height) - var(--app-menu-entry-growth));
127143
}
128144
}
129145
}
130146
131147
// The previous entry needs to shrink half the growth
132-
&:has(+ .app-menu-entry:hover),
133-
&:has(+ .app-menu-entry:focus-within) {
134-
width: calc(var(--header-height) - (var(--app-menu-entry-growth) / 2));
135-
.app-menu-entry__icon {
136-
margin-inline-start: calc(var(--app-menu-entry-growth) / 2);
148+
&:has(+ .app-menu-entry--truncated:hover),
149+
&:has(+ .app-menu-entry--truncated:focus-within) {
150+
.app-menu-entry__label {
151+
font-weight: normal;
152+
max-width: calc(var(--header-height) - var(--app-menu-entry-growth));
137153
}
138154
}
139155
}

dist/core-main.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/core-main.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)