1012 lines
26 KiB
JavaScript
1012 lines
26 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');
|
|
|
|
function acceptNumber(value) {
|
|
if (value === null || value === undefined) {
|
|
return value;
|
|
}
|
|
if (_.isNumber(value)) {
|
|
return +value;
|
|
}
|
|
if (/^(-)?\d+(\.\d+)?$/.test(value)) {
|
|
return +value;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function parseNumber(field, value) {
|
|
if (value === null || value === undefined) {
|
|
return value;
|
|
}
|
|
if (!field || ['integer', 'long'].indexOf(field.serverType) === -1) {
|
|
return value;
|
|
}
|
|
var num = +value;
|
|
if (isNaN(num)) {
|
|
return value;
|
|
}
|
|
return num;
|
|
}
|
|
|
|
ui.formWidget('BaseSelect', {
|
|
|
|
findInput: function(element) {
|
|
return element.find('input:first:not([ui-panel-editor] input)');
|
|
},
|
|
|
|
init: function(scope) {
|
|
|
|
scope.loadSelection = function(request, response) {
|
|
|
|
};
|
|
|
|
scope.parse = function(value) {
|
|
return value;
|
|
};
|
|
|
|
scope.format = function(value) {
|
|
return this.formatItem(value);
|
|
};
|
|
|
|
scope.formatItem = function(item) {
|
|
return item;
|
|
};
|
|
},
|
|
|
|
link_editable: function (scope, element, attrs, model) {
|
|
|
|
var input = this.findInput(element);
|
|
var showing = false;
|
|
var willShow = false;
|
|
|
|
scope.showSelection = function(delay) {
|
|
if (scope.isReadonly() || showing || willShow) {
|
|
return;
|
|
}
|
|
if (input.is('.x-focus')) {
|
|
input.removeClass('.x-focus');
|
|
return;
|
|
}
|
|
willShow = true;
|
|
input.addClass('.x-focus');
|
|
doSetup(input);
|
|
setTimeout(function () {
|
|
if (input.is(':focus')) {
|
|
input.autocomplete("search" , '');
|
|
input.removeClass('.x-focus');
|
|
}
|
|
willShow = false;
|
|
}, delay || 100);
|
|
};
|
|
|
|
scope.handleClear = function(e) {
|
|
scope.setValue(null, true);
|
|
};
|
|
|
|
scope.handleDelete = function(e) {
|
|
|
|
};
|
|
|
|
scope.handleEnter = function(e) {
|
|
|
|
};
|
|
|
|
scope.handleSelect = function(e, ui) {
|
|
|
|
};
|
|
|
|
scope.handleClose = function(e, ui) {
|
|
|
|
};
|
|
|
|
scope.handleOpen = function(e, ui) {
|
|
|
|
};
|
|
|
|
function renderItem(ul, item) {
|
|
var el = $("<li>").append( $("<a>").html(item.label)).appendTo(ul);
|
|
if (item.click) {
|
|
el.addClass("tag-select-action");
|
|
ul.addClass("tag-select-action-menu");
|
|
}
|
|
return el;
|
|
}
|
|
|
|
var doSetup = _.once(function (input) {
|
|
|
|
var loading = false;
|
|
var pending = null;
|
|
|
|
function doLoad(request, response) {
|
|
if (loading) {
|
|
return pending = _.partial(doLoad, request, response);
|
|
}
|
|
loading = true;
|
|
scope.loadSelection(request, function() {
|
|
loading = false;
|
|
response.apply(null, arguments);
|
|
if (pending) {
|
|
pending();
|
|
pending = null;
|
|
}
|
|
});
|
|
}
|
|
|
|
input.autocomplete({
|
|
|
|
minLength: 0,
|
|
|
|
position: { collision: "flip" },
|
|
|
|
source: doLoad,
|
|
|
|
focus: function(event, ui) {
|
|
return false;
|
|
},
|
|
|
|
select: function(event, ui) {
|
|
// do not select with tab key, to prevent unexpected result on editable grid
|
|
if (event.keyCode === 9) {
|
|
return false;
|
|
}
|
|
var ret = scope.handleSelect(event, ui);
|
|
if (ret !== undefined) {
|
|
return ret;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
open: function(event, ui) {
|
|
showing = true;
|
|
scope.handleOpen(event, ui);
|
|
},
|
|
|
|
close: function(event, ui) {
|
|
showing = false;
|
|
scope.handleClose(event, ui);
|
|
}
|
|
});
|
|
|
|
input.data('ui-autocomplete')._renderItem = scope.renderSelectItem || renderItem;
|
|
|
|
scope.$onAdjust('size scroll', function (e) {
|
|
if (e.type === 'adjust:size' && e.target !== document) {
|
|
return;
|
|
}
|
|
if (showing) {
|
|
input.autocomplete('close');
|
|
}
|
|
});
|
|
});
|
|
|
|
input.focus(function(e) {
|
|
element.addClass('focus');
|
|
doSetup(input);
|
|
}).blur(function() {
|
|
element.removeClass('focus');
|
|
if (showing) {
|
|
input.autocomplete('close');
|
|
}
|
|
}).keyup(function(e) {
|
|
// if TAB key
|
|
if (e.which === 9) {
|
|
scope.showSelection(300);
|
|
}
|
|
}).keydown(function(e) {
|
|
var KEY = $.ui.keyCode;
|
|
switch(e.keyCode) {
|
|
case KEY.DELETE:
|
|
case KEY.BACKSPACE:
|
|
scope.handleDelete(e);
|
|
break;
|
|
case KEY.ENTER:
|
|
scope.handleEnter(e);
|
|
break;
|
|
}
|
|
}).click(function() {
|
|
scope.showSelection();
|
|
});
|
|
|
|
if (axelor.browser.mozilla) {
|
|
input.mousedown(function () {
|
|
if (!input.is(':focus')) {
|
|
scope.showSelection(300);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
template_editable:
|
|
'<span class="picker-input">'+
|
|
'<input type="text" autocomplete="off">'+
|
|
'<span class="picker-icons picker-icons-2">'+
|
|
'<i class="fa fa-times" ng-show="text" ng-click="handleClear()"></i>'+
|
|
'<i class="fa fa-caret-down" ng-click="showSelection()"></i>'+
|
|
'</span>'+
|
|
'</span>'
|
|
});
|
|
|
|
function filterSelection(scope, field, selection, current) {
|
|
var selectionIn = scope.attr('selection-in') || field.selectionIn;
|
|
if (_.isEmpty(selection)) return selection;
|
|
if (_.isEmpty(selectionIn)) return selection;
|
|
|
|
var context = (scope.getContext || angular.noop)() || {};
|
|
var list = selectionIn;
|
|
|
|
if (_.isString(selectionIn)) {
|
|
var expr = selectionIn.trim();
|
|
if (expr.indexOf('[') !== 0) {
|
|
expr = '[' + expr + ']';
|
|
}
|
|
list = axelor.$eval(scope, expr, context);
|
|
}
|
|
|
|
var value = acceptNumber(current);
|
|
|
|
if (_.isEmpty(list)) {
|
|
return selection;
|
|
}
|
|
|
|
list = _.map(list, acceptNumber);
|
|
|
|
return _.filter(selection, function (item) {
|
|
var val = acceptNumber(item.value);
|
|
return val === value || list.indexOf(val) > -1;
|
|
});
|
|
}
|
|
|
|
ui.formInput('Select', 'BaseSelect', {
|
|
|
|
css: 'select-item',
|
|
cellCss: 'form-item select-item',
|
|
|
|
init: function(scope) {
|
|
|
|
this._super(scope);
|
|
|
|
var field = scope.field,
|
|
selectionList = field.selectionList || [],
|
|
selectionMap = {};
|
|
|
|
var data = _.map(selectionList, function(item) {
|
|
var value = "" + item.value;
|
|
selectionMap[value] = item.title;
|
|
return {
|
|
value: value,
|
|
label: item.title || " "
|
|
};
|
|
});
|
|
|
|
var dataSource = null;
|
|
function getDataSource() {
|
|
if (dataSource || !field.selection || !field.domain) {
|
|
return dataSource;
|
|
}
|
|
return dataSource = scope._dataSource._new('com.axelor.meta.db.MetaSelectItem', {
|
|
domain: "(self.select.name = :_select) AND (" + field.domain + ")",
|
|
context: {
|
|
_select: field.selection
|
|
}
|
|
});
|
|
}
|
|
|
|
scope.loadSelection = function(request, response) {
|
|
|
|
var ds = getDataSource();
|
|
|
|
function select(records) {
|
|
var items = _.filter(records, function(item) {
|
|
var label = item.label || "",
|
|
term = request.term || "";
|
|
return label.toLowerCase().indexOf(term.toLowerCase()) > -1;
|
|
});
|
|
items = filterSelection(scope, field, items);
|
|
return response(items);
|
|
}
|
|
|
|
if (ds) {
|
|
return ds.search({
|
|
fields: ['value', 'title'],
|
|
context: scope.getContext ? scope.getContext() : undefined
|
|
}).success(function (records) {
|
|
_.each(records, function (item) {
|
|
item.label = selectionMap[item.value] || item.title;
|
|
});
|
|
return select(records);
|
|
});
|
|
}
|
|
return select(data);
|
|
};
|
|
|
|
scope.formatItem = function(item) {
|
|
var key = _.isNumber(item) ? "" + item : item;
|
|
if (!key) {
|
|
return item;
|
|
}
|
|
if (_.isString(key)) {
|
|
return selectionMap[key] || "";
|
|
}
|
|
return item.label;
|
|
};
|
|
|
|
if (field.enumType) {
|
|
var __enumValues = {};
|
|
var __hasValue = false;
|
|
|
|
_.each(selectionList, function (item) {
|
|
__enumValues[item.value] = (item.data || {}).value;
|
|
__hasValue = __hasValue || __enumValues[item.value] !== undefined;
|
|
});
|
|
|
|
if (__hasValue) {
|
|
scope.$watch('record.' + field.name, function selectFieldNameWatch(value, old) {
|
|
if (value && value !== old) {
|
|
var enumValue = __enumValues[value];
|
|
if (scope.record && enumValue !== value) {
|
|
scope.record[field.name + '$value'] = enumValue;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
link_editable: function(scope, element, attrs, model) {
|
|
this._super.apply(this, arguments);
|
|
|
|
var input = this.findInput(element);
|
|
|
|
function update(value) {
|
|
var val = parseNumber(scope.field, value);
|
|
scope.setValue(val, true);
|
|
scope.$applyAsync();
|
|
}
|
|
|
|
scope.handleDelete = function(e) {
|
|
if (e.keyCode === 46) { // DELETE
|
|
update(null);
|
|
}
|
|
if (e.keyCode === 8) { // BACKSPACE
|
|
var value = scope.getValue();
|
|
if (value || (e.target.value||'').length < 2) {
|
|
update(null);
|
|
}
|
|
}
|
|
};
|
|
|
|
scope.handleSelect = function(e, ui) {
|
|
update(ui.item.value);
|
|
};
|
|
|
|
scope.$render_editable = function() {
|
|
input.val(this.getText());
|
|
};
|
|
|
|
scope.$on('on:edit', function () {
|
|
// force update input text, fixes #5965
|
|
scope.$render_editable();
|
|
});
|
|
}
|
|
});
|
|
|
|
ui.formInput('Enum', 'Select');
|
|
|
|
ui.formInput('ImageSelect', 'Select', {
|
|
|
|
metaWidget: true,
|
|
|
|
BLANK: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
|
|
|
|
link: function(scope, element, attrs) {
|
|
this._super(scope, element, attrs);
|
|
|
|
var field = scope.field;
|
|
var formatItem = scope.formatItem;
|
|
var selectIcons = {};
|
|
|
|
_.each(field.selectionList, function (item) {
|
|
selectIcons[item.value] = item.icon || item.value;
|
|
});
|
|
|
|
scope.canShowText = function () {
|
|
return field.labels === undefined || field.labels;
|
|
};
|
|
|
|
scope.formatItem = function (item) {
|
|
if (scope.canShowText()) {
|
|
return formatItem(item);
|
|
}
|
|
return "";
|
|
};
|
|
|
|
scope.findImage = function (value) {
|
|
return selectIcons[value] || this.BLANK;
|
|
};
|
|
|
|
scope.$watch('getValue()', function selectFieldValueWatch(value, old) {
|
|
scope.image = scope.findImage(value);
|
|
scope.isIcon = scope.image && scope.image.indexOf('fa-') === 0;
|
|
element.toggleClass('empty', !value);
|
|
}.bind(this));
|
|
},
|
|
|
|
link_editable: function(scope, element, attrs) {
|
|
this._super(scope, element, attrs);
|
|
var input = this.findInput(element);
|
|
var selects = {};
|
|
_.each(scope.field.selectionList, function (item) {
|
|
selects[item.value] = (item.data||{}).icon || item.value;
|
|
});
|
|
|
|
scope.renderSelectItem = function(ul, item) {
|
|
var a = $("<a>");
|
|
var el = $("<li>").addClass("image-select-item").append(a).appendTo(ul);
|
|
var image = scope.findImage(item.value);
|
|
|
|
if (image && image.indexOf('fa-') === 0) {
|
|
a.append($("<i>").addClass("fa").addClass(image));
|
|
} else {
|
|
a.append($("<img>").attr("src", image));
|
|
}
|
|
|
|
if (scope.canShowText()) {
|
|
a.append($("<span></span>").html(item.label));
|
|
}
|
|
|
|
return el;
|
|
};
|
|
|
|
var $render_editable = scope.$render_editable;
|
|
scope.$render_editable = function () {
|
|
$render_editable.apply(scope, arguments);
|
|
setTimeout(function () {
|
|
element.find('input').css('padding-left', element.find('i.image,img').width());
|
|
});
|
|
};
|
|
},
|
|
template_readonly:
|
|
'<span class="image-select readonly">'+
|
|
'<i ng-if="isIcon" class="fa" ng-class="image"></i>'+
|
|
'<img ng-if="!isIcon" ng-src="{{image}}"></img> <span ng-show="canShowText()">{{text}}</span>' +
|
|
'</span>',
|
|
|
|
template_editable:
|
|
'<span class="picker-input image-select">'+
|
|
'<i ng-if="isIcon" class="fa" ng-class="image"></i>'+
|
|
'<img ng-if="!isIcon" ng-src="{{image}}"></img>' +
|
|
'<input type="text" autocomplete="off">'+
|
|
'<span class="picker-icons">'+
|
|
'<i class="fa fa-caret-down" ng-click="showSelection()"></i>'+
|
|
'</span>'+
|
|
'</span>'
|
|
});
|
|
|
|
ui.formInput('MultiSelect', 'Select', {
|
|
|
|
css: 'multi-select-item',
|
|
cellCss: 'form-item multi-select-item',
|
|
metaWidget: true,
|
|
|
|
init: function(scope) {
|
|
this._super(scope);
|
|
|
|
var __parse = scope.parse;
|
|
scope.parse = function(value) {
|
|
if (_.isArray(value)) {
|
|
return value.join(', ');
|
|
}
|
|
return __parse(value);
|
|
};
|
|
|
|
scope.format = function(value) {
|
|
var items = value,
|
|
values = [];
|
|
if (!value) {
|
|
scope.items = [];
|
|
return value;
|
|
}
|
|
if (!_.isArray(items)) items = items.split(/,\s*/);
|
|
values = _.map(items, function(item) {
|
|
return {
|
|
value: item,
|
|
title: scope.formatItem(item)
|
|
};
|
|
});
|
|
scope.items = values;
|
|
return _.pluck(values, 'title').join(', ');
|
|
};
|
|
|
|
scope.matchValues = function(a, b) {
|
|
if (a === b) return true;
|
|
if (!a) return false;
|
|
if (!b) return false;
|
|
if (_.isString(a)) return a === b;
|
|
return a.value === b.value;
|
|
};
|
|
|
|
scope.getSelection = function() {
|
|
return this.items;
|
|
};
|
|
|
|
var max = +(scope.field.max);
|
|
scope.limited = function(items) {
|
|
if (max && items && items.length > max) {
|
|
scope.more = _t("and {0} more", items.length - max);
|
|
return _.first(items, max);
|
|
}
|
|
scope.more = null;
|
|
return items;
|
|
};
|
|
},
|
|
|
|
link_editable: function(scope, element, attrs, model) {
|
|
this._super.apply(this, arguments);
|
|
|
|
var input = this.findInput(element);
|
|
|
|
input.focus(function() {
|
|
scaleInput();
|
|
}).blur(function() {
|
|
scaleInput(50);
|
|
input.val('');
|
|
});
|
|
|
|
var placeholder = null;
|
|
if (scope.field.placeholder) {
|
|
placeholder = $('<span class="tag-select-placeholder hidden"></span>')
|
|
.text(scope.field.placeholder)
|
|
.appendTo(element)
|
|
.click(function (e) {
|
|
scope.showSelection();
|
|
});
|
|
}
|
|
|
|
function scaleInput(width) {
|
|
|
|
var elem = element.find('.tag-selector'),
|
|
pos = elem.position();
|
|
|
|
if (width) {
|
|
input.css('position', '');
|
|
elem.width('');
|
|
return input.width(width);
|
|
}
|
|
|
|
var top = pos.top,
|
|
left = pos.left;
|
|
|
|
width = element.innerWidth() - left;
|
|
|
|
elem.width(50);
|
|
|
|
input.css('width', width - 24);
|
|
}
|
|
|
|
function update(value) {
|
|
var val = parseNumber(scope.field, value);
|
|
scope.setValue(val, true);
|
|
scope.$applyAsync();
|
|
setTimeout(function () {
|
|
scaleInput(50);
|
|
});
|
|
}
|
|
|
|
scope.removeItem = function(item) {
|
|
var items = this.getSelection(),
|
|
value = _.isString(item) ? item : (item||{}).value;
|
|
|
|
items = _.chain(items)
|
|
.pluck('value')
|
|
.filter(function(v){
|
|
return !scope.matchValues(v, value);
|
|
})
|
|
.value();
|
|
|
|
update(items);
|
|
};
|
|
|
|
scope.onShowSelection = function(e) {
|
|
if (e && $(e.target || e.srcElement).is('input,li,i,span.tag-text')) {
|
|
return;
|
|
}
|
|
input.focus();
|
|
setTimeout(function() {
|
|
scope.showSelection();
|
|
});
|
|
};
|
|
|
|
scope.handleDelete = function(e) {
|
|
if (input.val()) {
|
|
return;
|
|
}
|
|
var items = this.getSelection();
|
|
this.removeItem(_.last(items));
|
|
};
|
|
|
|
scope.handleSelect = function(e, ui) {
|
|
var items = this.getSelection(),
|
|
values = _.pluck(items, 'value');
|
|
var found = _.find(values, function(v){
|
|
return scope.matchValues(v, ui.item.value);
|
|
});
|
|
if (found) {
|
|
return false;
|
|
}
|
|
values.push(ui.item.value);
|
|
update(values);
|
|
scaleInput(50);
|
|
};
|
|
|
|
scope.handleOpen = function(e, ui) {
|
|
input.data('autocomplete')
|
|
.menu
|
|
.element
|
|
.position({
|
|
my: "left top",
|
|
at: "left bottom",
|
|
of: element
|
|
})
|
|
.width(element.width() - 4);
|
|
};
|
|
|
|
scope.$render_editable = function() {
|
|
if (placeholder) {
|
|
placeholder.toggleClass('hidden', !!scope.getValue());
|
|
}
|
|
return input.val('');
|
|
};
|
|
|
|
input.on("input blur", function () {
|
|
if (placeholder) {
|
|
placeholder.toggleClass('hidden', !!(input.val() || scope.getValue()));
|
|
}
|
|
});
|
|
|
|
scope.$watch('items.length', function selectItemsLengthWatch(value, old) {
|
|
setTimeout(function () {
|
|
scaleInput(50);
|
|
});
|
|
});
|
|
},
|
|
template_editable:
|
|
'<div class="tag-select picker-input" ng-click="onShowSelection($event)">'+
|
|
'<ul>'+
|
|
'<li class="tag-item label label-primary" ng-repeat="item in items">'+
|
|
'<span ng-class="{\'tag-link\': handleClick}" class="tag-text" ng-click="handleClick($event, item.value)">{{item.title}}</span> '+
|
|
'<i class="fa fa-times fa-small" ng-click="removeItem(item)"></i>'+
|
|
'</li>'+
|
|
'<li class="tag-selector">'+
|
|
'<input type="text" autocomplete="off">'+
|
|
'</li>'+
|
|
'</ul>'+
|
|
'<span class="picker-icons">'+
|
|
'<i class="fa fa-caret-down" ng-click="onShowSelection()"></i>'+
|
|
'</span>'+
|
|
'</div>',
|
|
template_readonly:
|
|
'<div class="tag-select">'+
|
|
'<span class="label label-primary" ng-repeat="item in limited(items)">'+
|
|
'<span ng-class="{\'tag-link\': handleClick}" class="tag-text" ng-click="handleClick($event, item.value)">{{item.title}}</span>'+
|
|
'</span>'+
|
|
'<span ng-show="more"> {{more}}</span>'+
|
|
'</div>'
|
|
});
|
|
|
|
ui.formInput('SelectQuery', 'Select', {
|
|
|
|
link_editable: function(scope, element, attrs, model) {
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
var current = {};
|
|
|
|
function update(value) {
|
|
scope.setValue(value);
|
|
scope.$applyAsync();
|
|
}
|
|
|
|
scope.format = function(value) {
|
|
if (!value) return "";
|
|
if (_.isString(value)) {
|
|
return current.label || value;
|
|
}
|
|
current = value;
|
|
return value.label;
|
|
};
|
|
|
|
scope.parse = function(value) {
|
|
if (!value || _.isString(value)) return value;
|
|
return value.value;
|
|
};
|
|
|
|
scope.handleSelect = function(e, ui) {
|
|
update(ui.item);
|
|
};
|
|
|
|
var query = scope.$eval(attrs.query);
|
|
|
|
scope.loadSelection = function(request, response) {
|
|
return query(request, response);
|
|
};
|
|
}
|
|
});
|
|
|
|
ui.formInput('RadioSelect', {
|
|
|
|
css: "radio-select",
|
|
metaWidget: true,
|
|
|
|
link: function(scope, element, attrs, model) {
|
|
|
|
var field = scope.field;
|
|
var selection = field.selectionList || [];
|
|
|
|
scope.getSelection = function () {
|
|
return filterSelection(scope, field, selection, scope.getValue());
|
|
};
|
|
|
|
element.on("change", ":input", function(e) {
|
|
var val = parseNumber(scope.field, $(e.target).val());
|
|
scope.setValue(val, true);
|
|
scope.$applyAsync();
|
|
});
|
|
|
|
if (field.direction === "vertical" || field.dir === "vert") {
|
|
setTimeout(function(){
|
|
element.addClass("radio-select-vertical");
|
|
});
|
|
}
|
|
},
|
|
template_editable: null,
|
|
template_readonly: null,
|
|
template:
|
|
'<ul ng-class="{ readonly: isReadonly() }">'+
|
|
'<li ng-repeat="select in getSelection()">'+
|
|
'<label class="ibox round">'+
|
|
'<input type="radio" name="radio_{{$parent.$id}}" value="{{select.value}}"'+
|
|
' ng-disabled="isReadonly()"'+
|
|
' ng-checked="getValue() == select.value">'+
|
|
'<span class="box"></span>'+
|
|
'<span class="title">{{select.title}}</span>'+
|
|
'</label>'+
|
|
'</li>'+
|
|
'</ul>'
|
|
});
|
|
|
|
ui.formInput('CheckboxSelect', {
|
|
|
|
css: "checkbox-select",
|
|
metaWidget: true,
|
|
|
|
link: function(scope, element, attrs, model) {
|
|
|
|
var field = scope.field;
|
|
var selection = field.selectionList || [];
|
|
|
|
scope.getSelection = function () {
|
|
return filterSelection(scope, field, selection, scope.getValue());
|
|
};
|
|
|
|
scope.isSelected = function (select) {
|
|
var value = scope.getValue();
|
|
var current = ("" + value).split(",").map(function (val) {
|
|
return parseNumber(scope.field, val);
|
|
});
|
|
return current.indexOf(select.value) > -1;
|
|
};
|
|
|
|
element.on("change", ":input", function(e) {
|
|
var all = element.find("input:checked");
|
|
var selected = [];
|
|
all.each(function () {
|
|
var val = parseNumber(scope.field, $(this).val());
|
|
selected.push(val);
|
|
});
|
|
var value = selected.length === 0 ? null : selected.join(",");
|
|
scope.setValue(value, true);
|
|
scope.$applyAsync();
|
|
});
|
|
|
|
if (field.direction === "vertical" || field.dir === "vert") {
|
|
setTimeout(function(){
|
|
element.addClass("checkbox-select-vertical");
|
|
});
|
|
}
|
|
},
|
|
template_editable: null,
|
|
template_readonly: null,
|
|
template:
|
|
'<ul ng-class="{ readonly: isReadonly() }">'+
|
|
'<li ng-repeat="select in getSelection()">'+
|
|
'<label class="ibox">'+
|
|
'<input type="checkbox" value="{{select.value}}"'+
|
|
' ng-disabled="isReadonly()"'+
|
|
' ng-checked="isSelected(select)">'+
|
|
'<span class="box"></span>'+
|
|
'<span class="title">{{select.title}}</span>'+
|
|
'</label>'+
|
|
'</li>'+
|
|
'</ul>'
|
|
});
|
|
|
|
ui.formInput('NavSelect', {
|
|
|
|
css: "nav-select",
|
|
metaWidget: true,
|
|
|
|
link: function(scope, element, attrs, model) {
|
|
|
|
var field = scope.field;
|
|
var selection = field.selectionList || [];
|
|
|
|
scope.getSelection = function () {
|
|
return filterSelection(scope, field, selection, scope.getValue()) || [];
|
|
};
|
|
|
|
scope.$watch('text', function navSelectTextWatch(text, old) {
|
|
adjust();
|
|
});
|
|
|
|
scope.onSelect = function(select) {
|
|
if (scope.attr('readonly')) {
|
|
return;
|
|
}
|
|
var val = parseNumber(scope.field, select.value);
|
|
this.setValue(val, true);
|
|
|
|
elemNavs.removeClass('open');
|
|
elemMenu.removeClass('open');
|
|
|
|
// if selection change is used to show/hide some elements
|
|
// the layout should be adjusted
|
|
axelor.$adjustSize();
|
|
};
|
|
|
|
scope.isSelected = function (select) {
|
|
return select && scope.getValue() == select.value;
|
|
};
|
|
|
|
var lastWidth = 0;
|
|
var lastValue = null;
|
|
var elemNavs = null;
|
|
var elemMenu = null;
|
|
var elemMenuTitle = null;
|
|
var elemMenuItems = null;
|
|
|
|
function setup() {
|
|
elemNavs = element.children('.nav-steps').children('li:not(.dropdown,.ignore)');
|
|
elemMenu = element.children('.nav-steps').children('li.dropdown');
|
|
elemMenuTitle = elemMenu.find('a.nav-label > span');
|
|
elemMenuItems = elemMenu.find('li');
|
|
adjust();
|
|
}
|
|
|
|
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 (elemNavs === null || element.is(":hidden")) {
|
|
return;
|
|
}
|
|
var currentValue = scope.getValue();
|
|
var parentWidth = element.width() - 16;
|
|
if (parentWidth === lastWidth && currentValue === lastValue) {
|
|
return;
|
|
}
|
|
lastWidth = parentWidth;
|
|
lastValue = currentValue;
|
|
|
|
elemNavs.parent().css('visibility', 'hidden');
|
|
elemNavs.show();
|
|
elemMenu.hide();
|
|
|
|
if (elemNavs.parent().width() <= parentWidth) {
|
|
elemNavs.parent().css('visibility', '');
|
|
return;
|
|
}
|
|
|
|
var navs = scope.getSelection();
|
|
var selected = _.find(navs, scope.isSelected.bind(scope));
|
|
var selectedIndex = navs.indexOf(selected);
|
|
|
|
var elem = null;
|
|
var index = navs.length;
|
|
|
|
setMenuTitle(null);
|
|
|
|
while (elemNavs.parent().width() > parentWidth) {
|
|
elem = $(elemNavs[--index]);
|
|
elem.hide();
|
|
if (index === selectedIndex) {
|
|
setMenuTitle(selected);
|
|
}
|
|
}
|
|
|
|
elemMenuItems.hide();
|
|
while(index < navs.length) {
|
|
$(elemMenuItems[index++]).show();
|
|
}
|
|
|
|
elemNavs.parent().css('visibility', '');
|
|
}
|
|
|
|
scope.$onAdjust(adjust);
|
|
scope.$callWhen(setup, function () {
|
|
return element.is(':visible');
|
|
});
|
|
},
|
|
template_editable: null,
|
|
template_readonly: null,
|
|
template:
|
|
"<div class='nav-select'>" +
|
|
"<ul class='nav-steps' style='display: inline-flex; visibility: hidden;'>" +
|
|
"<li class='nav-step' ng-repeat='select in getSelection()' ng-class='{ active: isSelected(select), last: $last }'>" +
|
|
"<a href='' class='nav-label' ng-click='onSelect(select)' ng-bind-html='select.title'></a>" +
|
|
"</li>" +
|
|
"<li class='nav-step dropdown'>" +
|
|
"<a href='' class='nav-label dropdown-toggle' data-toggle='dropdown'><span></span></a>" +
|
|
"<ul class='dropdown-menu pull-right'>" +
|
|
"<li ng-repeat='select in getSelection()' ng-class='{active: getValue() == select.value}'>" +
|
|
"<a tabindex='-1' href='' ng-click='onSelect(select)' ng-bind-html='select.title'></a>" +
|
|
"</li>" +
|
|
"</ul>" +
|
|
"</li>" +
|
|
"</ul>"+
|
|
"</div>"
|
|
});
|
|
|
|
ui.formInput('ThemeSelect', 'Select', {
|
|
|
|
init: function (scope) {
|
|
scope.field.selectionList = _.map(axelor.config['application.themes'], function (name) {
|
|
return { value: name, title: _.titleize(name) };
|
|
});
|
|
scope.field.selectionList.unshift({
|
|
value: "default",
|
|
title: "Default"
|
|
});
|
|
this._super(scope);
|
|
}
|
|
});
|
|
|
|
ui.formInput('WidgetSelect', 'Select', {
|
|
|
|
init: function (scope) {
|
|
scope.field.selectionList = _.map(ui.getMetaWidgets(), function (name) {
|
|
return { value: name, title: name };
|
|
});
|
|
this._super(scope);
|
|
}
|
|
});
|
|
|
|
})();
|