Files
ERP/sophal/js/form/form.container.js

842 lines
21 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');
// this directive is used as a replacement for ng-transclude directive
// which fails to keep scope hierarchy (see: https://github.com/angular/angular.js/issues/1809)
ui.directive('uiTransclude', function() {
return {
compile: function(tElement, tAttrs, transclude) {
return function(scope, element, attrs, ctrl) {
transclude(scope.$new(), function(clone) {
element.append(clone);
});
};
}
};
});
/**
* The Group widget.
*
*/
ui.formWidget('Group', {
css: 'form-item-group',
cellCss: 'form-item v-align-top',
link: function(scope, element, attrs) {
var props = scope.field;
scope.collapsed = false;
scope.canCollapse = function() {
return props.canCollapse || props.collapseIf;
};
scope.setCollapsed = function(collapsed) {
scope.collapsed = collapsed;
element.children('legend').nextAll(':not(br)')[collapsed ? 'hide' : 'show']();
axelor.$adjustSize();
};
scope.toggle = function() {
scope.collapsed = !scope.collapsed;
scope.setCollapsed(scope.collapsed);
};
scope.$watch("attr('collapse')", function groupCollapseWatch(collapsed) {
scope.setCollapsed(collapsed);
});
// if auto title, then don't show it
if (attrs.title === attrs.field) {
attrs.$set('title', '');
}
if (props.showTitle !== false) {
scope.$watch('attr("title")', function groupTitleWatch(value){
scope.title = value;
});
}
},
transclude: true,
template:
'<fieldset ng-class="{\'bordered-box\': title, \'has-title\': title}" x-layout-selector="&gt; div:first">'+
'<legend ng-show="title">'+
'<i ng-show="canCollapse()" ng-click="toggle()" ng-class="{\'fa fa-plus\': collapsed, \'fa fa-minus\': !collapsed}"></i>'+
'<span ng-bind-html="title"></span></legend>'+
'<div ui-transclude></div>'+
'</fieldset>'
});
ui.formWidget('Portlet', {
css: 'form-item-portlet',
cellCss: 'form-item v-align-top',
showTitle: false,
link: function(scope, element, attrs) {
var field = scope.field;
scope.canSearch = field.canSearch !== "false";
scope.actionName = field.action;
if (field.name) {
scope.formPath = field.name;
}
if (field.height) {
element.height(field.height);
}
element.resizable({
handles: 's',
resize: _.debounce(function() {
axelor.$adjustSize();
element.width('auto');
}, 100)
});
},
template:
'<div>'+
'<div ui-view-portlet x-action="{{actionName}}" x-can-search="{{canSearch}}"></div>'+
'</div>'
});
ui.formWidget('Dashlet', {
css: 'dashboard',
showTitle: false,
link: function(scope, element, attrs) {
var field = scope.field;
var dashlet = _.extend({}, scope.field);
scope.dashlet = dashlet;
scope.formPath = field.name || field.action;
scope.$watch('attr("title")', function dashletTitleWatch(title, old) {
if (title === old) {
return;
}
var dashletScope = element.children('[ui-view-dashlet]').scope();
if (dashletScope) {
dashletScope.title = title;
}
});
},
template:
'<div>'+
'<div ui-view-dashlet></div>'+
'</div>'
});
/**
* The Tabs widget (notebook).
*/
ui.formWidget('Tabs', {
cellCss: 'form-item v-align-top',
widgets: ['Notebook'],
controller: ['$scope', '$element', function($scope, $element) {
var tabs = $scope.tabs = [],
selected = -1;
var doOnSelectPending = false;
var doOnSelect = _.debounce(function () {
var select = tabs[selected];
if (doOnSelectPending || !select) {
return;
}
doOnSelectPending = true;
$scope.waitForActions(function () {
if (select.handleSelect) {
select.handleSelect();
}
$scope.waitForActions(function () {
doOnSelectPending = false;
});
});
}, 100);
$scope.select = function(tab) {
var current = selected;
angular.forEach(tabs, function(tab, i){
tab.tabSelected = false;
});
tab.tabSelected = true;
selected = _.indexOf(tabs, tab);
if (current === selected) {
return;
}
setTimeout(function() {
if ($scope.$tabs) {
$scope.$tabs.trigger('adjust:tabs');
}
axelor.$adjustSize();
if(current != selected){
doOnSelect();
}
});
};
$scope.$on('on:edit', function (e, record) {
if ($scope.record === record) {
doOnSelect();
}
});
this.addTab = function(tab) {
if (tabs.length === 0) $scope.select(tab);
tab.index = tabs.length;
tabs.push(tab);
};
function inRange(index) {
return index > -1 && index < tabs.length;
}
function findItem(index) {
return $element.find('ul.nav-tabs:first > li:nth-child(' + (index+1) + ')');
}
this.showTab = function(index) {
if (!inRange(index)) {
return;
}
var tab = tabs[index];
var item = findItem(index);
tab.hidden = false;
item.show();
if (selected == -1 || selected === index) {
return $scope.select(tabs[index]);
}
axelor.$adjustSize();
};
this.hideTab = function(index) {
if (!inRange(index))
return;
var item = findItem(index),
tab = tabs[index];
var wasHidden = item.is(":hidden");
item.hide();
item.removeClass('active');
tab.hidden = true;
tab.tabSelected = false;
if (!wasHidden && selected > -1 && selected !== index)
return axelor.$adjustSize();
for(var i = 0 ; i < tabs.length ; i++) {
tab = tabs[i];
if (!tab.hidden) {
return $scope.select(tabs[i]);
}
}
selected = -1;
};
$scope.setTitle = function(value,index){
var item = findItem(index),
pageScope = item.first().data('$scope');
pageScope.tab.title = value;
};
}],
link: function(scope, elem, attrs) {
var props = scope.field;
scope.$tabs = $(elem).bsTabs({
closable: false
});
elem.on('click', '.dropdown-toggle', function(e){
axelor.$adjustSize();
});
// set height (#1011)
if (props.height) {
elem.children('.tab-content:first').height(props.height);
}
},
transclude: true,
template:
'<div class="tabbable-tabs">' +
'<div class="nav-tabs-wrap">' +
'<div class="nav-tabs-scroll-l"><a tabindex="-1" href="#"><i class="fa fa-chevron-left"></i></a></div>' +
'<div class="nav-tabs-scroll-r"><a tabindex="-1" href="#"><i class="fa fa-chevron-right"></i></a></div>' +
'<div class="nav-tabs-strip">' +
'<ul class="nav nav-tabs">' +
'<li tabindex="-1" ng-repeat="tab in tabs" ng-class="{active:tab.tabSelected}">'+
'<a tabindex="-1" href="" ng-click="select(tab)">'+
'<img class="prefix-icon" ng-show="tab.icon" ng-src="{{tab.icon}}">'+
'<span ng-bind-html="tab.title"></span>'+
'</a>' +
'</li>' +
'</ul>' +
'</div>' +
'<div class="nav-tabs-menu">'+
'<div class="dropdown pull-right">'+
'<a class="dropdown-toggle" data-toggle="dropdown" href="#"><i class="caret"></i></a>'+
'<ul class="dropdown-menu" role="menu">'+
'<li ng-repeat="tab in tabs">'+
'<a tabindex="-1" href="javascript: void(0)" ng-click="select(tab)" ng-bind-html="tab.title"></a>'+
'</li>' +
'</ul>' +
'</a>'+
'</div>'+
'</div>'+
'</div>' +
'<div class="tab-content" ui-transclude></div>' +
'</div>'
});
/**
* The Tab widget (notebook page).
*/
ui.formWidget('Tab', {
require: '^uiTabs',
widgets: ['Page'],
handles: ['isHidden'],
link: function(scope, elem, attrs, tabs) {
scope.tabSelected = false;
scope.icon = scope.field && scope.field.icon;
tabs.addTab(scope);
scope.$watch('attr("title")', function tabTitleWatch(value){
scope.title = value;
});
scope.$watch("isHidden()", function tabHiddenWatch(hidden, old) {
if (hidden) {
return tabs.hideTab(scope.index);
}
return tabs.showTab(scope.index);
});
scope.handleSelect = function () {
var onSelect = scope.$events.onSelect;
if (onSelect && !elem.is(":hidden")) {
onSelect();
}
};
},
cellCss: 'form-item v-align-top',
transclude: true,
template: '<div ui-actions class="tab-pane" ng-class="{active: tabSelected}" x-layout-selector="&gt; div:first">'+
'<div ui-transclude></div>'+
'</div>'
});
ui.formWidget('ButtonGroup', {
link: function (scope, element, attrs) {
function adjustButtons() {
var visible = element.children('.btn:visible').length;
if (visible) {
element.children('.btn:visible')
.css('max-width', (100.00/visible) + '%')
.css('width', (100.00/visible) + '%');
}
}
scope.$watch(adjustButtons);
scope.$callWhen(function () {
return element.is(':visible');
}, adjustButtons);
},
transclude: true,
template_editable: null,
template_readonly: null,
template:
"<div class='btn-group' ui-transclude></div>"
});
ui.formWidget('Panel', {
showTitle: false,
link: function (scope, element, attrs) {
var field = scope.field || {};
var body = element.children(".panel-body");
element.addClass(field.serverType);
if (field.sidebar && !attrs.itemSpan) {
attrs.$set('itemSpan', 12, true, 'x-item-span');
}
scope.menus = null;
if (field.menu) {
scope.menus = [field.menu];
}
scope.canCollapse = function() {
return field.canCollapse || field.collapseIf;
};
scope.setCollapsed = function(collapsed) {
var old = scope.collapsed;
var action = collapsed ? "hide" : "show";
scope.collapsed = collapsed;
scope.collapsedIcon = collapsed ? 'fa-chevron-down' : 'fa-chevron-up';
if (collapsed === old) {
return;
}
element.removeClass("collapsed");
body[action]("blind", 200, function () {
element.toggleClass("collapsed", !!collapsed);
if (body.css('display') !== 'none' && action === 'hide') {
body.hide();
}
axelor.$adjustSize();
});
};
scope.toggle = function() {
if (scope.canCollapse()) {
scope.setCollapsed(!scope.collapsed);
}
};
scope.$watch("attr('collapse')", function panelCollapseWatch(collapsed) {
scope.setCollapsed(collapsed);
});
var nested = element.parents('.panel:first').length > 0;
if (nested) {
element.addClass("panel-nested");
}
if (field.showFrame === false) {
element.addClass('noframe');
}
scope.notitle = field.showFrame === false || field.showTitle === false;
scope.title = field.title;
scope.$watch('attr("title")', function panelTitleWatch(title, old) {
if (title === undefined || title === old) return;
scope.title = title;
});
var icon = field.icon;
var iconBg = field.iconBackground;
if (icon && icon.indexOf('fa-') === 0) {
scope.icon = icon;
} else if (icon) {
scope.image = icon;
}
if (scope.icon && iconBg) {
setTimeout(function() {
var iconElem = element.children('.panel-header').children('.panel-icon');
if (iconBg.indexOf("#") === 0) {
iconElem.css('background-color', iconBg);
} else {
iconElem.addClass('bg-' + iconBg);
}
iconElem.addClass('has-bg');
iconElem.find('i').addClass('fg-white');
});
}
setTimeout(function () {
var nestedJson = element.parents('.panel-json:first').length > 0;
if (nestedJson) {
element.removeClass("panel-nested");
}
});
},
transclude: true,
template:
"<div class='panel panel-default'>" +
"<div class='panel-header' ng-click='toggle()' ng-if='!notitle &amp;&amp; field.title' ng-class=\"{'clickable-header' : canCollapse()}\" tabindex='-1'>" +
"<div class='panel-icon' ng-if='icon'><i class='fa' ng-class='icon'></i></div>" +
"<img class='panel-image' ng-if='image' ng-src='{{image}}'>" +
"<div class='panel-title'>{{title}}</div>" +
"<div ng-if='menus' ui-menu-bar menus='menus' handler='this'></div>" +
"<div ng-show='canCollapse()' class='panel-icons'>" +
"<a href=''><i class='fa' ng-class='collapsedIcon'></i></a>" +
"</div>" +
"</div>" +
"<div class='panel-body' ui-transclude></div>" +
"</div>"
});
ui.formWidget('PanelStack', {
transclude: true,
template: "<div class='panel-stack'>" +
"<span ui-transclude></span>" +
"</div>"
});
ui.formWidget('PanelTabs', {
link: function (scope, element, attrs) {
scope.tabs = [];
scope.more = null;
element.find('> .tab-content > div').each(function (index) {
var elem = $(this);
var tab = {
title: elem.attr('x-title'),
selected: false,
hidden: false,
elem: elem,
tabItem: $(),
menuItem: $()
};
scope.tabs.push(tab);
});
var selected = null;
var adjustPending = false;
function findTab(tab) {
var found = scope.tabs[tab] || tab;
if (!found || _.isNumber(found)) {
return null;
}
return found;
}
var doOnSelectPending = false;
var doOnSelect = _.debounce(function () {
if (doOnSelectPending || !selected || !selected.elem) {
return;
}
doOnSelectPending = true;
scope.waitForActions(function () {
var elemScope = selected.elem.scope();
if (elemScope.handleSelect) {
elemScope.handleSelect();
}
doOnSelectPending = false;
});
}, 100);
scope.selectTab = function(tab) {
var current = selected;
var found = findTab(tab);
if (!found) {
return;
}
scope.tabs.forEach(function (current) {
current.selected = false;
current.elem.hide();
});
selected = found;
found.selected = true;
found.elem.show();
found.elem
.add(found.tabItem)
.add(found.menuItem)
.addClass('active');
setTimeout(function () {
scope.$broadcast('tab:select');
elemTabs.removeClass('open');
elemMenu.removeClass('open');
axelor.$adjustSize();
if (current != selected) {
doOnSelect();
}
});
};
scope.showTab = function (tab) {
var found = findTab(tab);
if (!found) {
return;
}
adjustPending = true;
found.hidden = false;
found.tabItem.show();
if (!selected || selected === found) {
return scope.selectTab(found);
}
found.elem.hide();
axelor.$adjustSize();
};
scope.hideTab = function (tab) {
var found = findTab(tab);
if (!found) {
return;
}
adjustPending = true;
var wasHidden = found.hidden;
found.hidden = true;
found.selected = false;
found.elem.hide();
found.tabItem.add(found.menuItem).hide().removeClass('active');
if (!wasHidden && selected && selected !== found) {
return axelor.$adjustSize();
}
var tabs = scope.tabs;
for(var i = 0 ; i < tabs.length ; i++) {
tab = tabs[i];
if (!tab.hidden) {
return scope.selectTab(tabs[i]);
}
}
selected = null;
};
var lastWidth = 0;
var lastTab = null;
var elemTabs = $();
var elemMenu = $();
var elemMenuTitle = $();
var elemMenuItems = $();
function setup() {
elemTabs = element.children('.nav-tabs').children('li:not(.dropdown)');
elemMenu = element.children('.nav-tabs').children('li.dropdown');
elemMenuTitle = elemMenu.children('a:first').children('span');
elemMenuItems = elemMenu.find('li');
_.each(scope.tabs, function (tab, index) {
tab.tabItem = $(elemTabs[index]);
tab.menuItem = $(elemMenuItems[index]);
});
}
var setMenuTitle = (function() {
var setActive = _.debounce(function(selected) {
elemMenu.toggleClass('active', !!selected);
});
return function setMenuTitle(selected) {
elemMenu.show();
elemMenuTitle.html(selected && selected.title);
setActive(selected);
};
}());
function adjust() {
if (elemTabs === null || !scope.tabs || element.is(":hidden")) {
return;
}
var parentWidth = element.width() - 2;
if (parentWidth === lastWidth && lastTab === selected && !adjustPending) {
return;
}
lastWidth = parentWidth;
lastTab = selected;
elemTabs.parent().css('visibility', 'hidden');
elemMenu.hide();
// show visible tabs
scope.tabs.forEach(function (tab, i) {
if (tab.hidden) {
$(elemTabs[i]).hide();
} else {
$(elemTabs[i]).show();
}
});
if (elemTabs.parent().width() <= parentWidth) {
elemTabs.parent().css('visibility', '');
return;
}
setMenuTitle(null);
var elem = null;
var index = elemTabs.length;
var selectedIndex = scope.tabs.indexOf(selected);
while (elemTabs.parent().width() > parentWidth) {
elem = $(elemTabs[--index]);
elem.hide();
if (index === selectedIndex) {
setMenuTitle(selected);
}
}
elemMenuItems.hide();
var tab = null;
while(index < scope.tabs.length) {
tab = scope.tabs[index++];
if (!tab.hidden) {
tab.menuItem.show();
}
}
elemTabs.parent().css('visibility', '');
}
var adjusting = false;
scope.$onAdjust(function() {
if (adjusting) { return; }
try {
adjusting = true;
adjust();
} finally {
adjusting = false;
adjustPending = false;
}
}, 10);
scope.$timeout(function() {
setup();
var first = _.find(scope.tabs, function (tab) {
return !tab.hidden;
});
if (first) {
scope.selectTab(first);
}
});
scope.$on('on:edit', function (e, record) {
if (scope.record === record && !doOnSelectPending) {
scope.ajaxStop(doOnSelect, 100);
}
});
scope.$watch(function tabsWatch() {
var hidden = scope.attr('hidden');
if (hidden) return;
// show selected tab only
scope.tabs.forEach(function (tab) {
if (!tab.selected) tab.elem.hide();
});
});
},
transclude: true,
template:
"<div class='panel-tabs tabbable-tabs'>" +
"<ul class='nav nav-tabs nav-tabs-responsive'>" +
"<li ng-repeat='tab in tabs' ng-class='{active: tab.selected}'>" +
"<a tabindex='-1' href='' ng-click='selectTab(tab)' ng-bind-html='tab.title'></a>" +
"</li>" +
"<li class='dropdown' style='display: none'>" +
"<a tabindex='-1' href='' class='dropdown-toggle' data-toggle='dropdown'><span></span><b class='caret'></b></a>" +
"<ul class='dropdown-menu pull-right'>" +
"<li ng-repeat='tab in tabs' ng-class='{active: tab.selected}'>" +
"<a tabindex='-1' href='' ng-click='selectTab(tab)' ng-bind-html='tab.title'></a>" +
"</li>" +
"</ul>" +
"</li>" +
"</ul>" +
"<div class='tab-content' ui-transclude></div>" +
"</div>"
});
ui.formWidget('PanelTab', {
link: function (scope, element, attrs) {
var index = element.parent().children().index(element);
var tab = null;
var isHidden = scope.isHidden;
function findTab() {
return tab || (tab = (scope.tabs||[])[index]) || {};
}
scope.handleSelect = function () {
var onTabSelect = scope.$events.onTabSelect;
if (onTabSelect && !element.is(":hidden")) {
onTabSelect();
}
};
scope.isHidden = function () {
var tab = findTab();
return !tab.selected || isHidden.call(scope);
};
scope.$watch("attr('title')", function tabTitleWatch(value) {
var tab = findTab();
tab.title = value;
});
scope.$watch("attr('hidden')", function tabHiddenWatch(hidden, old) {
scope.$evalAsync(function () {
if (hidden) {
return scope.hideTab(index);
}
return scope.showTab(index);
});
});
}
});
})();