diff --git a/core/css/header.css b/core/css/header.css
index 4dbd41f5ec7..2c1850389dc 100644
Binary files a/core/css/header.css and b/core/css/header.css differ
diff --git a/core/css/header.css.map b/core/css/header.css.map
index 5970b18a6a3..824350e4a9a 100644
Binary files a/core/css/header.css.map and b/core/css/header.css.map differ
diff --git a/core/css/header.scss b/core/css/header.scss
index 5f3c4356d32..8ab0b019437 100644
--- a/core/css/header.scss
+++ b/core/css/header.scss
@@ -16,7 +16,6 @@
/* prevent ugly selection effect on accidental selection */
#header,
-#navigation,
#expanddiv {
-webkit-user-select: none;
-moz-user-select: none;
@@ -72,7 +71,6 @@
/* Header menu */
$header-menu-entry-height: 44px;
- .header-left > nav > .menu,
.header-right > div > .menu {
background-color: var(--color-main-background);
filter: drop-shadow(0 1px 5px var(--color-box-shadow));
@@ -103,7 +101,6 @@
right: 10px;
}
- #apps > ul,
& > div,
& > ul {
overflow-y: auto;
@@ -111,8 +108,7 @@
@include header-menu-height();
}
- /* Use by the apps menu and the settings right menu */
- #apps > ul,
+ /* Use by the settings right menu */
&.settings-menu > ul {
li {
a {
@@ -182,16 +178,6 @@
padding-right: 10px;
flex-shrink: 0;
}
- /* show caret indicator next to logo to make clear it is tappable */
- .icon-caret {
- display: inline-block;
- width: 12px;
- height: 12px;
- margin: 0;
- margin-top: -21px;
- padding: 0;
- vertical-align: middle;
- }
#header-left, .header-left,
#header-right, .header-right {
@@ -245,27 +231,6 @@
opacity: .75;
}
-.menutoggle {
- .icon-caret {
- opacity: .75;
- }
- &:hover {
- .header-appname, .icon-caret {
- opacity: 1;
- }
- }
- &:focus {
- .header-appname, .icon-caret {
- opacity: 1;
- }
- }
- &.active {
- .header-appname, .icon-caret {
- opacity: 1;
- }
- }
-}
-
/* TODO: move into minimal css file for public shared template */
/* only used for public share pages now as we have the app icons when logged in */
.header-appname {
@@ -291,56 +256,6 @@
text-overflow: ellipsis;
}
-/* do not show menu toggle on public share links as there is no menu */
-#body-public #header .icon-caret {
- display: none;
-}
-
-/* NAVIGATION --------------------------------------------------------------- */
-nav[role='navigation'] {
- display: inline-block;
- width: variables.$header-height;
- height: variables.$header-height;
- margin-left: -#{variables.$header-height};
- position: relative;
-}
-
-#header .header-left > nav > #navigation {
- position: relative;
- left: 25px; /* half the togglemenu */
- transform: translateX(-50%);
- width: 160px;
-}
-
-#header .header-left > nav > #navigation,
-.ui-datepicker,
-.ui-timepicker.ui-widget {
- background-color: var(--color-main-background);
- filter: drop-shadow(0 1px 10px var(--color-box-shadow));
- &:after {
- /* position of dropdown arrow */
- left: 50%;
- bottom: 100%;
- border: solid transparent;
- content: ' ';
- height: 0;
- width: 0;
- position: absolute;
- pointer-events: none;
- border-color: rgba(0, 0, 0, 0);
- border-bottom-color: var(--color-main-background);
- border-width: 10px;
- margin-left: -10px; /* border width */
- }
-}
-
-#navigation {
- box-sizing: border-box;
- .in-header {
- display: none;
- }
-}
-
/* USER MENU -----------------------------------------------------------------*/
#settings {
display: inline-block;
@@ -420,220 +335,6 @@ nav[role='navigation'] {
}
}
-/* Apps menu */
-#appmenu {
- display: inline-flex;
- min-width: variables.$header-height;
- z-index: 2;
-
- li {
- position: relative;
- cursor: pointer;
- padding: 0 2px;
- display: flex;
- justify-content: center;
-
- a {
- position: relative;
- display: flex;
- margin: 0;
- height: calc(variables.$header-height - 6px);
- width: variables.$header-height;
- align-items: center;
- justify-content: center;
- opacity: .85;
- // Make sure most app names don’t ellipsize
- letter-spacing: -0.5px;
- font-size: 12px;
- margin: 2px;
- }
-
- /* focused app visual feedback */
- &:hover a,
- a:focus,
- a.active {
- opacity: 1;
- font-weight: bold;
- }
-
- // Text size back to normal for hover/focus
- &:hover a,
- a:focus {
- font-size: 14px;
- }
-
- &:hover a + span,
- a:focus + span,
- &:hover span,
- &:focus span,
- a:focus span,
- a.active span {
- display: inline-block;
- text-overflow: initial;
- width: auto;
- overflow: hidden;
- padding: 0 5px;
- z-index: 2;
- }
-
- /* hidden apps menu */
- img,
- .icon-more-white {
- display: inline-block;
- width: 20px;
- height: 20px;
- }
-
- .icon-more-white {
- background-image: url('../img/actions/more-white.svg?v=1');
- }
-
- /* App title */
- span {
- opacity: 0;
- position: absolute;
- color: var(--color-primary-text);
- bottom: 2px;
- width: 100%;
- text-align: center;
- overflow: hidden;
- text-overflow: ellipsis;
- transition: all var(--animation-quick) ease;
- pointer-events: none;
- }
-
- /* Set up transitions for showing app titles on hover */
- /* App icon */
- svg,
- .icon-more-white {
- transition: transform var(--animation-quick) ease;
- // If the primary is too bright, invert the app icons
- filter: var(--primary-invert-if-bright);
- }
-
- /* Triangle */
- a::before {
- transition: border var(--animation-quick) ease;
- }
- }
-
- /* Show all app titles on hovering app menu area */
- &:hover {
- li {
- /* Move up app icon */
- svg,
- .icon-more,
- .icon-more-white,
- .icon-loading-small,
- .icon-loading-small-dark {
- transform: translateY(-7px);
- }
-
- /* Show app title */
- span {
- opacity: 1;
- bottom: 2px;
- z-index: -1; /* fix clickability issue - otherwise we need to move the span into the link */
- }
-
- /* Prominent app title for current and hovered/focused app */
- &:hover span,
- &:focus span,
- .active + span {
- opacity: 1;
- }
-
- /* Smaller triangle because of limited space */
- a::before {
- border-width: 5px;
- }
- }
- }
-
- /* Also show app title on focusing single entry (showing all on focus is only possible with CSS4 and parent selectors) */
- li a:focus {
- /* Move up app icon */
- svg,
- .icon-more,
- .icon-more-white,
- .icon-loading-small,
- .icon-loading-small-dark {
- transform: translateY(-7px);
- }
-
- /* Show app title */
- & + span,
- span {
- opacity: 1;
- bottom: 2px;
- }
-
- /* Smaller triangle because of limited space */
- &::before {
- border-width: 5px;
- }
- }
-
- /* show triangle below active app */
- li a::before {
- content: ' ';
- height: 0;
- width: 0;
- position: absolute;
- pointer-events: none;
- border: 0 solid transparent;
- border-bottom-color: var(--color-main-background);
- border-width: 10px;
- transform: translateX(-50%);
- left: 50%;
- bottom: -5px;
- display: none;
- }
-
- /* triangle focus feedback */
- li a.active::before,
- li:hover a::before,
- li:hover a.active::before,
- li a:focus::before {
- display: block;
- }
- li a.active::before {
- z-index: 99;
- }
- li:hover a::before,
- li a.active:hover::before,
- li a:focus::before {
- z-index: 101;
- }
-
- li.hidden {
- display: none;
- }
-
- #more-apps {
- z-index: 3;
- }
-}
-
-.unread-counter {
- display: none;
-}
-#apps .app-icon-notification,
-#appmenu .app-icon-notification {
- fill: var(--color-error);
-}
-
-#apps svg:not(.has-unread),
-#appmenu svg:not(.has-unread) {
- .app-icon-notification-mask {
- display: none;
- }
- .app-icon-notification {
- display: none;
- }
-}
-
-
/* Skip navigation links – show only on keyboard focus */
#skip-actions {
position: absolute;
diff --git a/core/css/server.css b/core/css/server.css
index d8d603379ea..dd6cb730927 100644
Binary files a/core/css/server.css and b/core/css/server.css differ
diff --git a/core/css/server.css.map b/core/css/server.css.map
index 5cb4a0f4074..5a67a8ebf86 100644
Binary files a/core/css/server.css.map and b/core/css/server.css.map differ
diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js
index 7f5a8b10d89..93d282c5d5e 100644
--- a/core/js/tests/specs/coreSpec.js
+++ b/core/js/tests/specs/coreSpec.js
@@ -308,48 +308,6 @@ describe('Core base tests', function() {
expect(OC.generateUrl('apps/files/download{file}')).toEqual(OC.getRootPath() + '/index.php/apps/files/download%7Bfile%7D');
});
});
- describe('Main menu mobile toggle', function() {
- var clock;
- var $toggle;
- var $navigation;
-
- beforeEach(function() {
- jQuery.fx.off = true;
- clock = sinon.useFakeTimers();
- $('#testArea').append('
' +
- '');
- $toggle = $('#header').find('.menutoggle');
- $navigation = $('#navigation');
- });
- afterEach(function() {
- jQuery.fx.off = false;
- clock.restore();
- $(document).off('ajaxError');
- });
- it('Sets up menu toggle', function() {
- window.initCore();
- expect($navigation.hasClass('menu')).toEqual(true);
- });
- it('Clicking menu toggle toggles navigation in', function() {
- window.initCore();
- // fore show more apps icon since otherwise it would be hidden since no icons are available
- clock.tick(1 * 1000);
- $('#more-apps').show();
-
- expect($navigation.is(':visible')).toEqual(false);
- $toggle.click();
- clock.tick(1 * 1000);
- expect($navigation.is(':visible')).toEqual(true);
- $toggle.click();
- clock.tick(1 * 1000);
- expect($navigation.is(':visible')).toEqual(false);
- });
- });
describe('Util', function() {
describe('computerFileSize', function() {
it('correctly parses file sizes from a human readable formated string', function() {
diff --git a/core/src/components/AppMenu.vue b/core/src/components/AppMenu.vue
new file mode 100644
index 00000000000..0a337340ccb
--- /dev/null
+++ b/core/src/components/AppMenu.vue
@@ -0,0 +1,289 @@
+
+
+
+
+
+
+
+
+
diff --git a/core/src/components/MainMenu.js b/core/src/components/MainMenu.js
index 603338d05b3..267a3d9a361 100644
--- a/core/src/components/MainMenu.js
+++ b/core/src/components/MainMenu.js
@@ -22,99 +22,27 @@
*
*/
-import $ from 'jquery'
+import { translate as t, translatePlural as n } from '@nextcloud/l10n'
+import Vue from 'vue'
-import OC from '../OC'
+import AppMenu from './AppMenu.vue'
-/**
- * Set up the main menu toggle to react to media query changes.
- * If the screen is small enough, the main menu becomes a toggle.
- * If the screen is bigger, the main menu is not a toggle any more.
- */
export const setUp = () => {
+ Vue.mixin({
+ methods: {
+ t,
+ n,
+ },
+ })
+
+ const AppMenuApp = Vue.extend(AppMenu)
+ const appMenu = new AppMenuApp({}).$mount('#header-left__appmenu')
+
Object.assign(OC, {
setNavigationCounter(id, counter) {
- const appmenuElement = document.getElementById('appmenu').querySelector('[data-id="' + id + '"] svg')
- const appsElement = document.getElementById('apps').querySelector('[data-id="' + id + '"] svg')
- if (counter === 0) {
- appmenuElement.classList.remove('has-unread')
- appsElement.classList.remove('has-unread')
- appmenuElement.getElementsByTagName('image')[0].style.mask = ''
- appsElement.getElementsByTagName('image')[0].style.mask = ''
- } else {
- appmenuElement.classList.add('has-unread')
- appsElement.classList.add('has-unread')
- appmenuElement.getElementsByTagName('image')[0].style.mask = 'url(#hole-appmenu-' + id + ')'
- appsElement.getElementsByTagName('image')[0].style.mask = 'url(#hole-' + id + ')'
- }
- document.getElementById('appmenu').querySelector('[data-id="' + id + '"] .unread-counter').textContent = counter
- document.getElementById('apps').querySelector('[data-id="' + id + '"] .unread-counter').textContent = counter
+ appMenu.setNavigationCounter(id, counter)
},
})
- // init the more-apps menu
- OC.registerMenu($('#more-apps > a'), $('#navigation'))
- // toggle the navigation
- const $toggle = $('#header .header-appname-container')
- const $navigation = $('#navigation')
- const $appmenu = $('#appmenu')
-
- // init the menu
- OC.registerMenu($toggle, $navigation)
- $toggle.data('oldhref', $toggle.attr('href'))
- $toggle.attr('href', '#')
- $navigation.hide()
-
- // show loading feedback on more apps list
- $navigation.delegate('a', 'click', event => {
- let $app = $(event.target)
- if (!$app.is('a')) {
- $app = $app.closest('a')
- }
- if (event.which === 1 && !event.ctrlKey && !event.metaKey && $app.attr('target') !== '_blank') {
- $app.find('svg').remove()
- $app.find('div').remove() // prevent odd double-clicks
- // no need for theming, loader is already inverted on dark mode
- // but we need it over the primary colour
- $app.prepend($('').addClass('icon-loading-small'))
- } else {
- // Close navigation when opening app in
- // a new tab
- OC.hideMenus(() => false)
- }
- })
-
- $navigation.delegate('a', 'mouseup', event => {
- if (event.which === 2) {
- // Close navigation when opening app in
- // a new tab via middle click
- OC.hideMenus(() => false)
- }
- })
-
- // show loading feedback on visible apps list
- $appmenu.delegate('li:not(#more-apps) > a', 'click', event => {
- let $app = $(event.target)
- if (!$app.is('a')) {
- $app = $app.closest('a')
- }
-
- if (event.which === 1 && !event.ctrlKey && !event.metaKey && $app.parent('#more-apps').length === 0 && $app.attr('target') !== '_blank') {
- $app.find('svg').remove()
- $app.find('div').remove() // prevent odd double-clicks
- $app.prepend($('').addClass(
- OCA.Theming && OCA.Theming.inverted
- ? 'icon-loading-small'
- : 'icon-loading-small-dark'
- ))
- // trigger redirect
- // needed for ie, but also works for every browser
- window.location = $app.attr('href')
- } else {
- // Close navigation when opening app in
- // a new tab
- OC.hideMenus(() => false)
- }
- })
}
diff --git a/core/src/init.js b/core/src/init.js
index 507f5bbb35f..ae8db0abf49 100644
--- a/core/src/init.js
+++ b/core/src/init.js
@@ -39,60 +39,6 @@ import PasswordConfirmation from './OC/password-confirmation'
// keep in sync with core/css/variables.scss
const breakpointMobileWidth = 1024
-const resizeMenu = () => {
- const appList = $('#appmenu li')
- const rightHeaderWidth = $('.header-right').outerWidth()
- const headerWidth = $('header').outerWidth()
- const usePercentualAppMenuLimit = 0.67
- const minAppsDesktop = 12
- let availableWidth = headerWidth - $('#nextcloud').outerWidth() - (rightHeaderWidth > 210 ? rightHeaderWidth : 210)
- const isMobile = $(window).width() < breakpointMobileWidth
- if (!isMobile) {
- availableWidth = availableWidth * usePercentualAppMenuLimit
- }
- let appCount = Math.floor((availableWidth / $(appList).width()))
- if (isMobile && appCount > minAppsDesktop) {
- appCount = minAppsDesktop
- }
- if (!isMobile && appCount < minAppsDesktop) {
- appCount = minAppsDesktop
- }
-
- // show at least 2 apps in the popover
- if (appList.length - 1 - appCount >= 1) {
- appCount--
- }
-
- $('#more-apps a').removeClass('active')
- let lastShownApp
- for (let k = 0; k < appList.length - 1; k++) {
- const name = $(appList[k]).data('id')
- if (k < appCount) {
- $(appList[k]).removeClass('hidden')
- $('#apps li[data-id=' + name + ']').addClass('in-header')
- lastShownApp = appList[k]
- } else {
- $(appList[k]).addClass('hidden')
- $('#apps li[data-id=' + name + ']').removeClass('in-header')
- // move active app to last position if it is active
- if (appCount > 0 && $(appList[k]).children('a').hasClass('active')) {
- $(lastShownApp).addClass('hidden')
- $('#apps li[data-id=' + $(lastShownApp).data('id') + ']').removeClass('in-header')
- $(appList[k]).removeClass('hidden')
- $('#apps li[data-id=' + name + ']').addClass('in-header')
- }
- }
- }
-
- // show/hide more apps icon
- if ($('#apps li:not(.in-header)').length === 0) {
- $('#more-apps').hide()
- $('#navigation').hide()
- } else {
- $('#more-apps').show()
- }
-}
-
const initLiveTimestamps = () => {
// Update live timestamps every 30 seconds
setInterval(() => {
@@ -179,30 +125,6 @@ export const initCore = () => {
setUpUserMenu()
setUpContactsMenu()
- // move triangle of apps dropdown to align with app name triangle
- // 2 is the additional offset between the triangles
- if ($('#navigation').length) {
- $('#header #nextcloud + .menutoggle').on('click', () => {
- $('#menu-css-helper').remove()
- const caretPosition = $('.header-appname + .icon-caret').offset().left - 2
- if (caretPosition > 255) {
- // if the app name is longer than the menu, just put the triangle in the middle
-
- } else {
- $('head').append('')
- }
- })
- $('#header #appmenu .menutoggle').on('click', () => {
- $('#appmenu').toggleClass('menu-open')
- if ($('#appmenu').is(':visible')) {
- $('#menu-css-helper').remove()
- }
- })
- }
-
- $(window).resize(resizeMenu)
- setTimeout(resizeMenu, 0)
-
// just add snapper for logged in users
// and if the app doesn't handle the nav slider itself
if ($('#app-navigation').length && !$('html').hasClass('lte9')
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
index c7eb2fde5ad..05698b3aa67 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -65,70 +65,7 @@ $getUserAvatar = static function (int $size) use ($_): string {
-
-
-
-
+