/* * Axelor Business Solutions * * Copyright (C) 2005-2019 Axelor (). * * 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 . */ (function() { "use strict"; var ui = angular.module('axelor.ui'); ui.directive('uiNavTree', ['MenuService', 'TagService', function(MenuService, TagService) { return { scope: { itemClick: "&" }, controller: ["$scope", "$q", function ($scope, $q) { var items = []; var menus = []; var nodes = {}; var searchItems = []; var deferred = $q.defer(); var handler = $scope.itemClick(); function canAccept(item) { return item.left || item.left === undefined; } this.onClick = function (e, menu) { if (menu.action && (menu.children||[]).length === 0) { handler(e, menu); } }; this.load = function (data) { if (!data || !data.length) return; items = data; items.forEach(function (item) { nodes[item.name] = item; item.children = []; }); items.forEach(function (item) { var node = nodes[item.parent]; if (node) { node.children.push(item); } else if (canAccept(item)){ menus.push(item); item.icon = item.icon || 'fa-bars'; item.iconBackground = item.iconBackground || 'green'; } }); var markForSidebar = function (item) { item.sidebar = true; item.children.forEach(markForSidebar); }; menus.forEach(markForSidebar); items.forEach(function (item) { if (item.children.length === 0) { delete item.children; var label = item.title; var parent = nodes[item.parent]; var lastParent; while (parent) { lastParent = parent; parent = nodes[parent.parent]; if (parent) { label = lastParent.title + "/" + label; } } searchItems.push(_.extend({ title: item.title, label: label, action: item.action, category: lastParent ? lastParent.name : '', categoryTitle: lastParent ? lastParent.title : '' })); } }); $scope.menus = menus; $scope.searchItems = searchItems; deferred.resolve(); }; this.update = function (data) { if (!data || data.length === 0) return; data.forEach(function (item) { var node = nodes[item.name]; if (node) { node.tag = item.tag; node.tagStyle = item.tagStyle; if (node.tagStyle) { node.tagCss = "label-" + node.tagStyle; } } }); }; var that = this; TagService.listen(function (data) { that.update(data.tags); }); function findProp(node, name) { if (node[name]) { return node[name]; } var parent = nodes[node.parent]; if (parent) { return findProp(parent, name); } return null; } function updateTabStyle(tab) { if (tab.icon || tab.fa) { return; } var node = _.findWhere(nodes, { action: tab.action, sidebar: true }); if (node) { tab.icon = tab.icon || findProp(node, 'icon'); tab.color = tab.color || findProp(node, 'iconBackground'); if (tab.icon && tab.icon.indexOf('fa') === 0) { tab.fa = tab.icon; delete tab.icon; } else { tab.fa = tab.fa || findProp(node, 'fa'); } if (tab.icon) { tab.fa = null; } if (tab.color && tab.color.indexOf('#') != 0) { tab.topCss = 'bg-' + tab.color; tab.fa = tab.fa ? tab.fa + ' fg-' + tab.color : null; tab.color = null; } } } MenuService.updateTabStyle = function (tab) { deferred.promise.then(function () { updateTabStyle(tab); }); }; }], link: function (scope, element, attrs, ctrl) { var input = element.find('input'); scope.showSearch = !!axelor.device.mobile; scope.toggleSearch = function (show) { input.val(''); if (!axelor.device.mobile) { scope.showSearch = show === undefined ? !scope.showSearch : show; } }; scope.onShowSearch = function () { scope.showSearch = true; setTimeout(function () { input.val('').focus(); }); }; input.attr('placeholder', _t('Search...')); input.blur(function (e) { scope.$timeout(function () { scope.toggleSearch(false); }); }); input.keydown(function (e) { if (e.keyCode === 27) { // escape scope.$timeout(function () { scope.toggleSearch(false); }); } }); function search(request, response) { var term = request.term; var items = _.filter(scope.searchItems, function (item) { var text = item.categoryTitle + '/' + item.label; var search = term; if (search[0] === '/') { search = search.substring(1); text = item.title; } text = text.replace('/', '').toLowerCase(); if (search[0] === '"' || search[0] === '=') { search = search.substring(1); if (search.indexOf('"') === search.length - 1) { search = search.substring(0, search.length - 1); } return text.indexOf(search) > -1; } var parts = search.toLowerCase().split(/\s+/); for (var i = 0; i < parts.length; i++) { if (text.indexOf(parts[i]) === -1) { return false; } } return parts.length > 0; }); response(items); } MenuService.all().success(function (res) { ctrl.load(res.data); input.autocomplete({ source: search, select: function (e, ui) { ctrl.onClick(e, ui.item); scope.$timeout(function () { scope.toggleSearch(false); }); }, appendTo: element.parent(), open: function () { element.children('.nav-tree').hide(); }, close: function (e) { element.children('.nav-tree').show(); } }); input.data('autocomplete')._renderMenu = function (ul, items) { var all = _.groupBy(items, 'category'); var that = this; scope.menus.forEach(function (menu) { var found = all[menu.name]; if (found) { ul.append($("
  • ").html(menu.title)); found.forEach(function (item) { that._renderItemData(ul, item); }); } }); }; }); }, replace: true, template: "
    " + "" + "" + "" + "
    " }; }]); ui.directive('uiNavSubTree', ['$compile', function ($compile) { return { scope: { menu: "=" }, require: "^uiNavTree", link: function (scope, element, attrs, ctrl) { var menu = scope.menu; if (menu.icon && menu.icon.indexOf('fa') === 0) { menu.fa = menu.icon; delete menu.icon; } if (menu.tagStyle) { menu.tagCss = "label-" + menu.tagStyle; } if (menu.children) { var sub = $( ""); sub = $compile(sub)(scope); sub.appendTo(element); } setTimeout(function () { var icon = element.find("span.nav-icon:first"); if (menu.iconBackground && icon.length > 0) { var cssName = menu.parent ? 'color' : 'background-color'; var clsName = menu.parent ? 'fg-' : 'bg-'; if (!menu.parent) { icon.addClass("fg-white"); } if (menu.iconBackground.indexOf("#") === 0) { icon.css(cssName, menu.iconBackground); } else { icon.addClass(clsName + menu.iconBackground); } // get computed color value var color = icon.css(cssName); var bright = d3.rgb(color).brighter(.3).toString(); // add hover effect element.hover(function () { icon.css(cssName, bright); }, function () { icon.css(cssName, color); }); // use same color for vertical line if (!menu.parent) { element.css("border-left-color", color); element.hover(function () { element.css("border-left-color", color); }, function () { element.css("border-left-color", bright); }); } } }); var animation = false; function show(el) { var parent = el.parent("li"); if (animation || parent.hasClass('open')) { return; } function done() { parent.addClass('open'); parent.removeClass('animate'); el.height(''); animation = false; } hide(parent.siblings("li.open").children('ul')); animation = true; parent.addClass('animate'); el.height(el[0].scrollHeight); setTimeout(done, 300); } function hide(el) { var parent = el.parent("li"); if (animation || !parent.hasClass('open')) { return; } function done() { parent.removeClass('open'); parent.removeClass('animate'); animation = false; } animation = true; el.height(el.height())[0].offsetHeight; parent.addClass('animate'); el.height(0); setTimeout(done, 300); } element.on('click', '> a', function (e) { e.preventDefault(); if (animation) return; var $list = element.children('ul'); element.parents('.nav-tree').find('li.active').not(element).removeClass('active'); element.addClass('active'); if (menu.action && (menu.children||[]).length === 0) { scope.$applyAsync(function () { ctrl.onClick(e, menu); }); } if ($list.length === 0) return; if (element.hasClass('open')) { hide($list); } else { show($list); } }); if (menu.help) { setTimeout(function () { var tooltip = element.children('a') .addClass('has-help') .tooltip({ html: true, title: menu.help, placement: 'right', delay: { show: 500, hide: 100 }, container: 'body' }); }); } }, replace: true, template: "
  • " + "" + "" + "" + "{{menu.tag}}" + "{{::menu.title}}" + "" + "
  • " }; }]); })();