mirror of
https://github.com/nextcloud/server.git
synced 2026-06-10 17:23:59 -04:00
fix: Ensure app overflow menu is rendered centered
1. Fix app menu overflow button to be centered 2. Ensure the overflow calculation always just removed one element (incl. refactor to use `@vueuse`) 3. Ensure we use 1.5 line height for app menu, otherwise some languages look squashed under the app icon Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
609fa7d5db
commit
91f78936c9
2 changed files with 46 additions and 43 deletions
|
|
@ -4,7 +4,8 @@
|
|||
-->
|
||||
|
||||
<template>
|
||||
<nav class="app-menu"
|
||||
<nav ref="appMenu"
|
||||
class="app-menu"
|
||||
:aria-label="t('core', 'Applications menu')">
|
||||
<ul class="app-menu__list">
|
||||
<AppMenuEntry v-for="app in mainAppList"
|
||||
|
|
@ -29,7 +30,8 @@ import type { INavigationEntry } from '../types/navigation'
|
|||
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { n, t } from '@nextcloud/l10n'
|
||||
import { defineComponent } from 'vue'
|
||||
import { useElementSize } from '@vueuse/core'
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
import AppMenuEntry from './AppMenuEntry.vue'
|
||||
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
|
||||
|
|
@ -46,40 +48,47 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
setup() {
|
||||
const appMenu = ref()
|
||||
const { width: appMenuWidth } = useElementSize(appMenu)
|
||||
return {
|
||||
t,
|
||||
n,
|
||||
appMenu,
|
||||
appMenuWidth,
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
const appList = loadState<INavigationEntry[]>('core', 'apps', [])
|
||||
|
||||
return {
|
||||
appList,
|
||||
appLimit: 0,
|
||||
observer: null as ResizeObserver | null,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
appLimit() {
|
||||
const maxApps = Math.floor(this.appMenuWidth / 50)
|
||||
if (maxApps < this.appList.length) {
|
||||
// Ensure there is space for the overflow menu
|
||||
return Math.max(maxApps - 1, 0)
|
||||
}
|
||||
return maxApps
|
||||
},
|
||||
|
||||
mainAppList() {
|
||||
return this.appList.slice(0, this.appLimit)
|
||||
},
|
||||
|
||||
popoverAppList() {
|
||||
return this.appList.slice(this.appLimit)
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.observer = new ResizeObserver(this.resize)
|
||||
this.observer.observe(this.$el)
|
||||
this.resize()
|
||||
subscribe('nextcloud:app-menu.refresh', this.setApps)
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.observer!.disconnect()
|
||||
unsubscribe('nextcloud:app-menu.refresh', this.setApps)
|
||||
},
|
||||
|
||||
|
|
@ -96,54 +105,44 @@ export default defineComponent({
|
|||
setApps({ apps }: { apps: INavigationEntry[]}) {
|
||||
this.appList = apps
|
||||
},
|
||||
|
||||
resize() {
|
||||
const availableWidth = (this.$el as HTMLElement).offsetWidth
|
||||
let appCount = Math.floor(availableWidth / 50) - 1
|
||||
const popoverAppCount = this.appList.length - appCount
|
||||
if (popoverAppCount === 1) {
|
||||
appCount--
|
||||
}
|
||||
if (appCount < 1) {
|
||||
appCount = 0
|
||||
}
|
||||
this.appLimit = appCount
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.app-menu {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-shrink: 1;
|
||||
flex-wrap: wrap;
|
||||
flex: 1 1;
|
||||
width: 0;
|
||||
|
||||
&__list {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
// Adjust the overflow NcActions styles as they are directly rendered on the background
|
||||
&__overflow :deep(.button-vue--vue-tertiary) {
|
||||
opacity: .7;
|
||||
margin: 3px;
|
||||
filter: var(--background-image-invert-if-bright);
|
||||
&__overflow {
|
||||
margin-block: auto;
|
||||
|
||||
/* Remove all background and align text color if not expanded */
|
||||
&:not([aria-expanded="true"]) {
|
||||
color: var(--color-background-plain-text);
|
||||
// Adjust the overflow NcActions styles as they are directly rendered on the background
|
||||
:deep(.button-vue--vue-tertiary) {
|
||||
opacity: .7;
|
||||
margin: 3px;
|
||||
filter: var(--background-image-invert-if-bright);
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
background-color: transparent !important;
|
||||
/* Remove all background and align text color if not expanded */
|
||||
&:not([aria-expanded="true"]) {
|
||||
color: var(--color-background-plain-text);
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
opacity: 1;
|
||||
outline: none !important;
|
||||
&:focus-visible {
|
||||
opacity: 1;
|
||||
outline: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ defineProps<{
|
|||
|
||||
<style scoped lang="scss">
|
||||
.app-menu-entry {
|
||||
--app-menu-entry-font-size: 12px;
|
||||
width: var(--header-height);
|
||||
height: var(--header-height);
|
||||
position: relative;
|
||||
|
|
@ -54,8 +55,7 @@ defineProps<{
|
|||
&__label {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
font-size: 12px;
|
||||
line-height: 1.25;
|
||||
font-size: var(--app-menu-entry-font-size);
|
||||
// this is shown directly on the background
|
||||
color: var(--color-background-plain-text);
|
||||
text-align: center;
|
||||
|
|
@ -71,6 +71,10 @@ defineProps<{
|
|||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
font-size: var(--app-menu-entry-font-size);
|
||||
}
|
||||
|
||||
&--active {
|
||||
// When hover or focus, show the label and make it bolder than the other entries
|
||||
.app-menu-entry__label {
|
||||
|
|
@ -117,7 +121,7 @@ defineProps<{
|
|||
.app-menu__list:focus-within {
|
||||
// Move icon up so that the name does not overflow the icon
|
||||
.app-menu-entry__icon {
|
||||
margin-block-end: calc(1.5 * 12px); // font size of label * line height
|
||||
margin-block-end: 1lh;
|
||||
}
|
||||
|
||||
// Make the label visible
|
||||
|
|
|
|||
Loading…
Reference in a new issue