Files
ERP/sophal/js/widget/widget.navmenu.js

448 lines
12 KiB
JavaScript

/*
* Axelor Business Solutions
*
* Copyright (C) 2005-2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
(function() {
"use strict";
var ui = angular.module('axelor.ui');
NavMenuCtrl.$inject = ['$scope', '$element', 'MenuService', 'NavService'];
function NavMenuCtrl($scope, $element, MenuService, NavService) {
$scope.menus = []; // the first four visible menus
$scope.more = []; // rest of the menus
var hasSideBar = axelor.config['view.menubar.location'] !== 'top';
MenuService.all().then(function(response) {
var res = response.data,
data = res.data;
var items = {};
var all = [];
_.each(data, function(item) {
items[item.name] = item;
if (item.children === undefined) {
item.children = [];
}
});
_.each(data, function(item) {
if (hasSideBar && !item.top) {
return;
}
if (!item.parent) {
return all.push(item);
}
var parent = items[item.parent];
if (parent) {
parent.children.push(item);
}
});
$scope.menus = all;
$scope.more = all;
$scope.extra = {
title: 'More',
children: $scope.more
};
});
this.isSubMenu = function(item) {
return item && item.children && item.children.length > 0;
};
this.onItemClick = function(item) {
if (item.action && !this.isSubMenu(item)) {
NavService.openTabByName(item.action);
}
};
$scope.hasImage = function (menu) {
return menu.icon && menu.icon.indexOf('fa-') !== 0 && menu.icon.indexOf('empty') != -1;
};
$scope.hasIcon = function (menu) {
return menu.icon && menu.icon.indexOf('fa-') === 0 && menu.icon.indexOf('empty') != -1;
};
$scope.hasText = function (menu) {
return !menu.icon || menu.icon.indexOf('empty') === -1;
};
}
ui.directive('navMenuBar', function() {
return {
replace: true,
controller: NavMenuCtrl,
scope: true,
link: function(scope, element, attrs, ctrl) {
var elemTop,
elemSub,
elemMore;
var siblingsWidth = 0;
var adjusting = false;
element.hide();
function adjust() {
if (adjusting) {
return;
}
adjusting = true;
var count = 0;
var parentWidth = element.parent().width() - 32;
elemMore.hide();
elemTop.hide();
elemSub.hide();
while (count < elemTop.length) {
var elem = $(elemTop[count]).show();
var width = siblingsWidth + element.width();
if (width > parentWidth) {
elem.hide();
// show more...
elemMore.show();
width = siblingsWidth + element.width();
if (width > parentWidth) {
count--;
$(elemTop[count]).hide();
}
break;
}
count++;
}
if (count === elemTop.length) {
elemMore.hide();
}
while(count < elemTop.length) {
$(elemSub[count++]).show();
}
adjusting = false;
}
function setup() {
element.siblings().each(function () {
siblingsWidth += $(this).width();
});
elemTop = element.find('.nav-menu.dropdown:not(.nav-menu-more)');
elemMore = element.find('.nav-menu.dropdown.nav-menu-more');
elemSub = elemMore.find('.dropdown-menu:first > .dropdown-submenu');
element.show();
adjust();
$(window).on("resize.menubar", adjust);
}
element.on('$destroy', function () {
if (element) {
$(window).off("resize.menubar");
element = null;
}
});
var unwatch = scope.$watch('menus', function navMenusWatch(menus, old) {
if (!menus || menus.length === 0 || menus === old) {
return;
}
unwatch();
setTimeout(setup, 100);
});
},
template:
"<ul class='nav nav-menu-bar'>" +
"<li class='nav-menu dropdown' ng-class='{empty: !hasText(menu)}' ng-repeat='menu in menus track by menu.name'>" +
"<a href='javascript:' class='dropdown-toggle' data-toggle='dropdown'>" +
"<img ng-if='hasImage(menu)' ng-src='{{menu.icon}}'> " +
"<i ng-if='hasIcon(menu)' class='fa {{menu.icon}}'></i> " +
"<span ng-if='hasText(menu)' ng-bind='menu.title'></span> " +
"<b class='caret'></b>" +
"</a>" +
"<ul nav-menu='menu'></ul>" +
"</li>" +
"<li class='nav-menu nav-menu-more dropdown' style='display: none;'>" +
"<a href='javascript:' class='dropdown-toggle' data-toggle='dropdown'>" +
"<span x-translate>More</span>" +
"<b class='caret'></b>" +
"</a>" +
"<ul nav-menu='extra'></ul>" +
"</li>" +
"</ul>"
};
});
ui.directive('navMenu', function() {
return {
replace: true,
require: '^navMenuBar',
scope: {
menu: '=navMenu'
},
link: function(scope, element, attrs, ctrl) {
},
template:
"<ul class='dropdown-menu'>" +
"<li ng-repeat='item in menu.children track by item.name' nav-menu-item='item'>" +
"</ul>"
};
});
ui.directive('navMenuItem', ['$compile', function($compile) {
return {
replace: true,
require: '^navMenuBar',
scope: {
item: '=navMenuItem'
},
link: function(scope, element, attrs, ctrl) {
var item = scope.item;
scope.isSubMenu = ctrl.isSubMenu(item);
scope.isActionMenu = !!item.action;
scope.onClick = function (e, item) {
ctrl.onItemClick(item);
};
if (ctrl.isSubMenu(item)) {
element.addClass("dropdown-submenu");
$compile('<ul nav-menu="item"></ul>')(scope, function(cloned, scope) {
element.append(cloned);
});
}
},
template:
"<li>" +
"<a href='javascript:' ng-click='onClick($event, item)'>{{item.title}}</a>" +
"</li>"
};
}]);
ui.directive('navMenuFav', function() {
return {
replace: true,
controller: ['$scope', '$location', 'DataSource', 'NavService', function ($scope, $location, DataSource, NavService) {
var ds = DataSource.create("com.axelor.meta.db.MetaMenu", {
domain: "self.user = :__user__ and self.link is not null"
});
$scope.items = [];
function update() {
ds.search({
fields: ["id", "name", "title", "link"],
sortBy: ["-priority"]
}).success(function (records, page) {
$scope.items = records;
});
}
function add(values, callback) {
var item = _.findWhere($scope.items, { link: values.link });
if (item && item.title === values.title) {
return callback();
}
if (item) {
item.title = values.title;
} else {
item = values;
item.name = values.link;
item.user = {
id: axelor.config['user.id']
};
item.hidden = true;
}
ds.save(item).success(update).then(callback, callback);
}
$scope.addFav = function () {
var link = $location.path();
if (link === "/") {
return;
}
var tab = NavService.getSelected() || {};
var vs = tab.$viewScope || {};
var title = tab.title || (vs.schema || {}).title || "";
if (vs.record && vs.record.id > 0) {
title = title + " (" + vs.record.id + ")";
}
var item = _.findWhere($scope.items, { link: link });
if (item) {
title = item.title;
}
var dialog = axelor.dialogs.box("<input type='text' style='width: 100%;box-sizing: border-box;height: 28px;margin: 0;'>", {
title: _t('Add to favorites...'),
buttons: [{
text: _t('Cancel'),
'class': 'btn btn-default',
click: function (e) {
$(this).dialog('close');
}
}, {
text: _t('OK'),
'class': 'btn btn-primary',
click: function (e) {
title = dialog.find("input").val();
add({ title: title, link: link }, function () {
dialog.dialog('close');
});
}
}]
});
setTimeout(function () {
dialog.find("input").val(title).focus().select();
});
};
$scope.manageFav = function () {
NavService.openTabByName('menus.fav');
};
function onUpdate(e, _ds) {
if (ds !== _ds && ds._model === _ds._model) {
update();
}
}
$scope.$on("ds:saved", onUpdate);
$scope.$on("ds:removed", onUpdate);
update();
}],
template:
"<ul class='dropdown-menu'>" +
"<li><a href='' ng-click='addFav()' x-translate>Add to favorites...</a></li>" +
"<li class='divider'></li>" +
"<li ng-repeat='item in items track by item.name'><a ng-href='#{{item.link}}'>{{item.title}}</a></li>" +
"<li class='divider'></li>" +
"<li><a href='' ng-click='manageFav()' x-translate>Organize favorites...</a></li>" +
"</ul>"
};
});
ui.directive('navMenuTasks', function() {
return {
replace: true,
controller: ['$scope', '$location', 'TagService', 'NavService', function ($scope, $location, TagService, NavService) {
var TEAM_TASK = "com.axelor.team.db.TeamTask";
function taskText(count) {
var n = count || 0;
if (n <= 0) return _t('no tasks');
return n > 1 ? _t('{0} tasks', n) : _t('{0} task', n);
}
function update(data) {
var counts = data || {};
if (counts.current) {
counts.css = 'badge-primary';
}
if (counts.pending) {
counts.css = 'badge-important';
}
counts.currentText = taskText(counts.current);
counts.pendingText = taskText(counts.pending);
counts.total = Math.min(99, counts.current);
$scope.counts = counts;
}
TagService.listen(function (data) {
update(data.tasks || {});
});
$scope.showTasks = function (type) {
NavService.openTabByName('team.tasks.' + type);
};
function onDataChange(e, ds) {
if (ds._model === TEAM_TASK) {
TagService.find();
}
}
$scope.$on('ds:saved', onDataChange);
$scope.$on('ds:removed', onDataChange);
update({});
}],
template:
"<li class='dropdown'>" +
"<a href='' class='nav-link-tasks dropdown-toggle' data-toggle='dropdown'>" +
"<i class='fa fa-bell'></i>" +
"<span class='badge' ng-show='counts.css' ng-class='counts.css'>{{counts.total}}</span>" +
"</a>" +
"<ul class='dropdown-menu'>" +
"<li>" +
"<a href='' ng-click='showTasks(\"due\")'>" +
"<span class='nav-link-user-name' x-translate>Tasks due</span>" +
"<span class='nav-link-user-sub' ng-class='{\"fg-red\": counts.pending > 0}'>{{counts.pendingText}}</span>" +
"</a>" +
"</li>" +
"<li class='divider'></li>" +
"<li>" +
"<a href='' ng-click='showTasks((\"todo\"))'>" +
"<span class='nav-link-user-name' x-translate>Tasks todo</span>" +
"<span class='nav-link-user-sub'>{{counts.currentText}}</span>" +
"</a>" +
"</li>" +
"</ul>" +
"</li>"
};
});
})();