Merge pull request #47031 from nextcloud/fix/app-menu

fix: Ensure app overflow menu is rendered centered
pull/47034/head
Andy Scherzinger 2024-08-07 22:30:00 +07:00 committed by GitHub
commit 4417f0f328
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 49 additions and 46 deletions

@ -4,7 +4,8 @@
--> -->
<template> <template>
<nav class="app-menu" <nav ref="appMenu"
class="app-menu"
:aria-label="t('core', 'Applications menu')"> :aria-label="t('core', 'Applications menu')">
<ul class="app-menu__list"> <ul class="app-menu__list">
<AppMenuEntry v-for="app in mainAppList" <AppMenuEntry v-for="app in mainAppList"
@ -29,7 +30,8 @@ import type { INavigationEntry } from '../types/navigation'
import { subscribe, unsubscribe } from '@nextcloud/event-bus' import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import { loadState } from '@nextcloud/initial-state' import { loadState } from '@nextcloud/initial-state'
import { n, t } from '@nextcloud/l10n' 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 AppMenuEntry from './AppMenuEntry.vue'
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js' import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
@ -46,40 +48,47 @@ export default defineComponent({
}, },
setup() { setup() {
const appMenu = ref()
const { width: appMenuWidth } = useElementSize(appMenu)
return { return {
t, t,
n, n,
appMenu,
appMenuWidth,
} }
}, },
data() { data() {
const appList = loadState<INavigationEntry[]>('core', 'apps', []) const appList = loadState<INavigationEntry[]>('core', 'apps', [])
return { return {
appList, appList,
appLimit: 0,
observer: null as ResizeObserver | null,
} }
}, },
computed: { 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() { mainAppList() {
return this.appList.slice(0, this.appLimit) return this.appList.slice(0, this.appLimit)
}, },
popoverAppList() { popoverAppList() {
return this.appList.slice(this.appLimit) return this.appList.slice(this.appLimit)
}, },
}, },
mounted() { mounted() {
this.observer = new ResizeObserver(this.resize)
this.observer.observe(this.$el)
this.resize()
subscribe('nextcloud:app-menu.refresh', this.setApps) subscribe('nextcloud:app-menu.refresh', this.setApps)
}, },
beforeDestroy() { beforeDestroy() {
this.observer!.disconnect()
unsubscribe('nextcloud:app-menu.refresh', this.setApps) unsubscribe('nextcloud:app-menu.refresh', this.setApps)
}, },
@ -96,54 +105,44 @@ export default defineComponent({
setApps({ apps }: { apps: INavigationEntry[]}) { setApps({ apps }: { apps: INavigationEntry[]}) {
this.appList = apps 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> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.app-menu { .app-menu {
width: 100%;
display: flex; display: flex;
flex-shrink: 1; flex: 1 1;
flex-wrap: wrap; width: 0;
&__list { &__list {
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
} }
// Adjust the overflow NcActions styles as they are directly rendered on the background &__overflow {
&__overflow :deep(.button-vue--vue-tertiary) { margin-block: auto;
opacity: .7;
margin: 3px;
filter: var(--background-image-invert-if-bright);
/* Remove all background and align text color if not expanded */ // Adjust the overflow NcActions styles as they are directly rendered on the background
&:not([aria-expanded="true"]) { :deep(.button-vue--vue-tertiary) {
color: var(--color-background-plain-text); opacity: .7;
margin: 3px;
filter: var(--background-image-invert-if-bright);
&:hover { /* Remove all background and align text color if not expanded */
opacity: 1; &:not([aria-expanded="true"]) {
background-color: transparent !important; color: var(--color-background-plain-text);
&:hover {
opacity: 1;
background-color: transparent !important;
}
} }
}
&:focus-visible { &:focus-visible {
opacity: 1; opacity: 1;
outline: none !important; outline: none !important;
}
} }
} }

@ -33,6 +33,7 @@ defineProps<{
<style scoped lang="scss"> <style scoped lang="scss">
.app-menu-entry { .app-menu-entry {
--app-menu-entry-font-size: 12px;
width: var(--header-height); width: var(--header-height);
height: var(--header-height); height: var(--header-height);
position: relative; position: relative;
@ -54,8 +55,7 @@ defineProps<{
&__label { &__label {
opacity: 0; opacity: 0;
position: absolute; position: absolute;
font-size: 12px; font-size: var(--app-menu-entry-font-size);
line-height: 1.25;
// this is shown directly on the background // this is shown directly on the background
color: var(--color-background-plain-text); color: var(--color-background-plain-text);
text-align: center; text-align: center;
@ -71,6 +71,10 @@ defineProps<{
letter-spacing: -0.5px; letter-spacing: -0.5px;
} }
&__icon {
font-size: var(--app-menu-entry-font-size);
}
&--active { &--active {
// When hover or focus, show the label and make it bolder than the other entries // When hover or focus, show the label and make it bolder than the other entries
.app-menu-entry__label { .app-menu-entry__label {
@ -117,7 +121,7 @@ defineProps<{
.app-menu__list:focus-within { .app-menu__list:focus-within {
// Move icon up so that the name does not overflow the icon // Move icon up so that the name does not overflow the icon
.app-menu-entry__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 // Make the label visible

4
dist/core-main.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long