Merge pull request #4439 from nextcloud/appmenu-responsive
Responsive app menu
This commit is contained in:
commit
db94b5d4af
|
@ -191,17 +191,18 @@
|
|||
|
||||
/* NAVIGATION --------------------------------------------------------------- */
|
||||
nav {
|
||||
margin-top: auto;
|
||||
display: inline-block;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
margin-left: -54px;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
position: relative;
|
||||
top: 45px;
|
||||
left: -100%;
|
||||
width: 265px;
|
||||
max-height: 85%;
|
||||
width: 160px;
|
||||
margin-top: 0;
|
||||
padding-bottom: 10px;
|
||||
background-color: $color-main-background;
|
||||
box-shadow: 0 1px 10px $color-box-shadow;
|
||||
border-radius: 3px;
|
||||
|
@ -242,12 +243,6 @@ nav {
|
|||
margin-left: -10px;
|
||||
}
|
||||
|
||||
/* position of dropdown arrow */
|
||||
|
||||
#navigation:after {
|
||||
left: 242px;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
box-sizing: border-box;
|
||||
* {
|
||||
|
@ -258,18 +253,14 @@ nav {
|
|||
}
|
||||
a {
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
display: block;
|
||||
padding: 10px 12px;
|
||||
height:40px;
|
||||
vertical-align: text-bottom;
|
||||
span {
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
padding-bottom: 0;
|
||||
padding-left: 0;
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
padding-left: 10px;
|
||||
color: $color-main-text;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
@ -280,6 +271,9 @@ nav {
|
|||
-ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)';
|
||||
opacity: .5;
|
||||
}
|
||||
svg {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
&:hover svg,
|
||||
&:focus svg,
|
||||
&:hover span,
|
||||
|
@ -300,20 +294,21 @@ nav {
|
|||
max-height: 32px;
|
||||
max-width: 32px;
|
||||
}
|
||||
/* loading feedback for apps */
|
||||
.app-loading {
|
||||
.icon-loading-dark {
|
||||
display: inline !important;
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 24px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
.app-icon {
|
||||
-ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=0)';
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* loading feedback for apps */
|
||||
.app-loading {
|
||||
.icon-loading-small-dark {
|
||||
display: inline !important;
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.app-icon {
|
||||
-ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=0)';
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -454,10 +449,13 @@ nav {
|
|||
width: auto;
|
||||
clear: both;
|
||||
height: 44px;
|
||||
flex-shrink: 0;
|
||||
|
||||
li {
|
||||
float: left;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
vertical-align: top !important;
|
||||
height: 45px;
|
||||
cursor: pointer;
|
||||
|
||||
a {
|
||||
|
@ -471,11 +469,14 @@ nav {
|
|||
opacity: .6;
|
||||
}
|
||||
}
|
||||
.app-loading .icon-loading-small-dark {
|
||||
top:12px;
|
||||
}
|
||||
|
||||
|
||||
li:hover a,
|
||||
li a.active {
|
||||
opacity: 1;
|
||||
|
||||
}
|
||||
|
||||
li img,
|
||||
|
@ -541,31 +542,4 @@ nav {
|
|||
li.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* use popover menu on mobile and small screens */
|
||||
@media only screen and (max-width: 680px) {
|
||||
|
||||
#header .header-appname-container {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
#appmenu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#apps .in-header {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
position: fixed;
|
||||
top: 45px;
|
||||
left: 10px;
|
||||
&:after {
|
||||
left: 214px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1398,6 +1398,7 @@ function initCore() {
|
|||
// toggle the navigation
|
||||
var $toggle = $('#header .header-appname-container');
|
||||
var $navigation = $('#navigation');
|
||||
var $appmenu = $('#appmenu');
|
||||
|
||||
// init the menu
|
||||
OC.registerMenu($toggle, $navigation);
|
||||
|
@ -1427,6 +1428,20 @@ function initCore() {
|
|||
OC.hideMenus(function(){return false});
|
||||
}
|
||||
});
|
||||
|
||||
$appmenu.delegate('a', 'click', function(event) {
|
||||
var $app = $(event.target);
|
||||
if(!$app.is('a')) {
|
||||
$app = $app.closest('a');
|
||||
}
|
||||
if(event.which === 1 && !event.ctrlKey && !event.metaKey) {
|
||||
$app.addClass('app-loading');
|
||||
} else {
|
||||
// Close navigation when opening app in
|
||||
// a new tab
|
||||
OC.hideMenus(function(){return false});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setupUserMenu() {
|
||||
|
@ -1482,6 +1497,53 @@ function initCore() {
|
|||
});
|
||||
}
|
||||
|
||||
var resizeMenu = function() {
|
||||
var maxApps = 8;
|
||||
var appList = $('#appmenu li');
|
||||
var availableWidth = $('#header-left').width() - $('#nextcloud').width() - 44;
|
||||
var appCount = Math.floor((availableWidth)/44);
|
||||
console.log(appCount);
|
||||
// show a maximum of 8 apps
|
||||
if(appCount >= maxApps) {
|
||||
appCount = maxApps;
|
||||
}
|
||||
// show at least 2 apps in the popover
|
||||
if(appList.length-1-appCount >= 1) {
|
||||
appCount--;
|
||||
}
|
||||
|
||||
$('#more-apps a').removeClass('active');
|
||||
var lastShownApp;
|
||||
for (var k = 0; k < appList.length-1; k++) {
|
||||
var 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();
|
||||
}
|
||||
};
|
||||
$(window).resize(resizeMenu);
|
||||
resizeMenu();
|
||||
|
||||
// just add snapper for logged in users
|
||||
if($('#app-navigation').length && !$('html').hasClass('lte9')) {
|
||||
|
||||
|
|
|
@ -60,54 +60,51 @@
|
|||
<div class="icon-caret"></div>
|
||||
</a>
|
||||
|
||||
<div id="appmenu">
|
||||
<ul>
|
||||
<?php foreach($_['headernavigation'] as $entry): ?>
|
||||
<li data-id="<?php p($entry['id']); ?>">
|
||||
<a href="<?php print_unescaped($entry['href']); ?>" tabindex="3"
|
||||
<?php if( $entry['active'] ): ?> class="active"<?php endif; ?>>
|
||||
<img src="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>" class="app-icon" />
|
||||
<div class="icon-loading-dark" style="display:none;"></div>
|
||||
<span>
|
||||
<ul id="appmenu">
|
||||
<?php foreach ($_['navigation'] as $entry): ?>
|
||||
<li data-id="<?php p($entry['id']); ?>" class="hidden">
|
||||
<a href="<?php print_unescaped($entry['href']); ?>"
|
||||
tabindex="3"
|
||||
<?php if ($entry['active']): ?> class="active"<?php endif; ?>>
|
||||
<img src="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>"
|
||||
class="app-icon"/>
|
||||
<div class="icon-loading-small-dark"
|
||||
style="display:none;"></div>
|
||||
<span>
|
||||
<?php p($entry['name']); ?>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
<li id="more-apps" class="menutoggle<?php if (count($_['navigation']) <= 8): ?> hidden<?php endif; ?>">
|
||||
<a href="#">
|
||||
<div class="icon-more-white"></div>
|
||||
<span><?php p($l->t('More apps')); ?></span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
<li id="more-apps" class="menutoggle">
|
||||
<a href="#">
|
||||
<div class="icon-more-white"></div>
|
||||
<span><?php p($l->t('More apps')); ?></span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<nav role="navigation"><div id="navigation">
|
||||
<nav role="navigation">
|
||||
<div id="navigation">
|
||||
<div id="apps">
|
||||
<ul>
|
||||
<?php foreach($_['navigation'] as $entry): ?>
|
||||
<?php if($entry['showInHeader']): ?>
|
||||
<li data-id="<?php p($entry['id']); ?>" class="in-header">
|
||||
<?php else: ?>
|
||||
<li data-id="<?php p($entry['id']); ?>">
|
||||
<?php endif; ?>
|
||||
<li data-id="<?php p($entry['id']); ?>">
|
||||
<a href="<?php print_unescaped($entry['href']); ?>" tabindex="3"
|
||||
<?php if( $entry['active'] ): ?> class="active"<?php endif; ?>>
|
||||
<svg width="32" height="32" viewBox="0 0 32 32">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16">
|
||||
<defs><filter id="invert-<?php p($entry['id']); ?>"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"></feColorMatrix></filter></defs>
|
||||
<image x="0" y="0" width="32" height="32" preserveAspectRatio="xMinYMin meet" filter="url(#invert-<?php p($entry['id']); ?>)" xlink:href="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>" class="app-icon"></image>
|
||||
<image x="0" y="0" width="16" height="16" preserveAspectRatio="xMinYMin meet" filter="url(#invert-<?php p($entry['id']); ?>)" xlink:href="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>" class="app-icon"></image>
|
||||
</svg>
|
||||
<div class="icon-loading-dark" style="display:none;"></div>
|
||||
<span>
|
||||
<?php p($entry['name']); ?>
|
||||
</span>
|
||||
<div class="icon-loading-small-dark" style="display:none;"></div>
|
||||
<span><?php p($entry['name']); ?></span>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div></nav>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -79,8 +79,6 @@ class TemplateLayout extends \OC_Template {
|
|||
$this->assign( 'appid', $appId );
|
||||
$navigation = \OC_App::getNavigation();
|
||||
$this->assign( 'navigation', $navigation);
|
||||
$navigation = \OC_App::getHeaderNavigation();
|
||||
$this->assign( 'headernavigation', $navigation);
|
||||
$settingsNavigation = \OC_App::getSettingsNavigation();
|
||||
$this->assign( 'settingsnavigation', $settingsNavigation);
|
||||
foreach($navigation as $entry) {
|
||||
|
|
|
@ -468,69 +468,16 @@ class OC_App {
|
|||
}
|
||||
});
|
||||
|
||||
$activeAppIndex = -1;
|
||||
$activeApp = OC::$server->getNavigationManager()->getActiveEntry();
|
||||
foreach ($list as $index => &$navEntry) {
|
||||
$navEntry['showInHeader'] = true;
|
||||
if ($navEntry['id'] == $activeApp) {
|
||||
$navEntry['active'] = true;
|
||||
$activeAppIndex = $index;
|
||||
} else {
|
||||
$navEntry['active'] = false;
|
||||
}
|
||||
}
|
||||
unset($navEntry);
|
||||
|
||||
if (count($list) <= 8) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
$headerIconCount = 7;
|
||||
if($activeAppIndex > ($headerIconCount-1)) {
|
||||
$active = $list[$activeAppIndex];
|
||||
$lastInHeader = $list[$headerIconCount-1];
|
||||
$list[$headerIconCount-1] = $active;
|
||||
$list[$activeAppIndex] = $lastInHeader;
|
||||
}
|
||||
|
||||
foreach ($list as $index => &$navEntry) {
|
||||
if($index >= $headerIconCount) {
|
||||
$navEntry['showInHeader'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public static function proceedAppNavigation($entries) {
|
||||
$activeAppIndex = -1;
|
||||
$list = self::proceedNavigation($entries);
|
||||
|
||||
$activeApp = OC::$server->getNavigationManager()->getActiveEntry();
|
||||
foreach ($list as $index => &$navEntry) {
|
||||
if ($navEntry['id'] == $activeApp) {
|
||||
$navEntry['active'] = true;
|
||||
$activeAppIndex = $index;
|
||||
} else {
|
||||
$navEntry['active'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (count($list) <= 8) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
$headerIconCount = 7;
|
||||
// move active item to last position
|
||||
if($activeAppIndex > ($headerIconCount-1)) {
|
||||
$active = $list[$activeAppIndex];
|
||||
$lastInHeader = $list[$headerIconCount-1];
|
||||
$list[$headerIconCount-1] = $active;
|
||||
$list[$activeAppIndex] = $lastInHeader;
|
||||
}
|
||||
$list = array_slice($list, 0, $headerIconCount);
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
|
@ -722,21 +669,6 @@ class OC_App {
|
|||
return self::proceedNavigation($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the navigation inside the header bar
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* This function returns an array containing all entries added. The
|
||||
* entries are sorted by the key 'order' ascending. Additional to the keys
|
||||
* given for each app the following keys exist:
|
||||
* - active: boolean, signals if the user is on this navigation entry
|
||||
*/
|
||||
public static function getHeaderNavigation() {
|
||||
$entries = OC::$server->getNavigationManager()->getAll();
|
||||
return self::proceedAppNavigation($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Settings Navigation
|
||||
*
|
||||
|
|
|
@ -487,22 +487,22 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
container.children('li[data-id]').each(function (index, el) {
|
||||
var id = $(el).data('id');
|
||||
// remove all apps that are not in the correct order
|
||||
if ((navEntries[index] && navEntries[index].id !== $(el).data('id'))) {
|
||||
if (!navEntries[index] || (navEntries[index] && navEntries[index].id !== $(el).data('id'))) {
|
||||
$(el).remove();
|
||||
$('#appmenu li[data-id='+id+']').remove();
|
||||
}
|
||||
});
|
||||
|
||||
var previousEntry;
|
||||
var previousEntry = {};
|
||||
// add enabled apps to #navigation and #appmenu
|
||||
for (var i = 0; i < navEntries.length; i++) {
|
||||
var entry = navEntries[i];
|
||||
if (container.children('li[data-id="' + entry.id + '"]').length === 0) {
|
||||
var li = $('<li></li>');
|
||||
li.attr('data-id', entry.id);
|
||||
var img = '<svg width="32" height="32" viewBox="0 0 32 32">';
|
||||
var img = '<svg width="16" height="16" viewBox="0 0 16 16">';
|
||||
img += '<defs><filter id="invert"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /></filter></defs>';
|
||||
img += '<image x="0" y="0" width="32" height="32" preserveAspectRatio="xMinYMin meet" filter="url(#invert)" xlink:href="' + entry.icon + '" class="app-icon" /></svg>';
|
||||
img += '<image x="0" y="0" width="16" height="16" preserveAspectRatio="xMinYMin meet" filter="url(#invert)" xlink:href="' + entry.icon + '" class="app-icon" /></svg>';
|
||||
var a = $('<a></a>').attr('href', entry.href);
|
||||
var filename = $('<span></span>');
|
||||
var loading = $('<div class="icon-loading-dark"></div>').css('display', 'none');
|
||||
|
@ -526,9 +526,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
}
|
||||
}
|
||||
|
||||
if ($('#appmenu ul').children('li[data-id="' + entry.id + '"]').length === 0) {
|
||||
// add apps to #appmenu until it is full
|
||||
if ($('#appmenu li').not('.hidden').length < 8) {
|
||||
if ($('#appmenu').children('li[data-id="' + entry.id + '"]').length === 0) {
|
||||
var li = $('<li></li>');
|
||||
li.attr('data-id', entry.id);
|
||||
var img = '<img src="' + entry.icon + '" class="app-icon">';
|
||||
|
@ -547,22 +545,11 @@ OC.Settings.Apps = OC.Settings.Apps || {
|
|||
.animate({opacity: 0.5})
|
||||
.animate({opacity: 1});
|
||||
}
|
||||
}
|
||||
}
|
||||
previousEntry = entry;
|
||||
// do not show apps from #appmenu in #navigation
|
||||
if(i <= 7) {
|
||||
$('#navigation li').eq(i).addClass('in-header');
|
||||
} else {
|
||||
$('#navigation li').eq(i).removeClass('in-header');
|
||||
}
|
||||
}
|
||||
|
||||
if (navEntries.length > 8) {
|
||||
$('#more-apps').show();
|
||||
} else {
|
||||
$('#more-apps').hide();
|
||||
}
|
||||
$(window).trigger('resize');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue