web-apps/apps/common/main/lib/component/TreeView.js

400 lines
17 KiB
JavaScript
Raw Normal View History

/*
*
* (c) Copyright Ascensio System SIA 2010-2019
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
/**
* TreeView.js
*
* Created by Julia Radzhabova on 12/14/17
*
*/
if (Common === undefined)
var Common = {};
define([
'common/main/lib/component/DataView'
], function () {
'use strict';
Common.UI.TreeViewModel = Common.UI.DataViewModel.extend({
defaults: function() {
return {
id: Common.UI.getId(),
name: '',
isNotHeader: false,
hasSubItems: false,
hasParent: false,
isEmptyItem: false,
isExpanded: true,
isVisible: true,
selected: false,
allowSelected: true,
disabled: false,
level: 0,
index: 0
}
}
});
Common.UI.TreeViewStore = Backbone.Collection.extend({
model: Common.UI.TreeViewModel,
2017-12-20 11:09:31 +00:00
expandSubItems: function(record) {
var me = this;
var _expand_sub_items = function(idx, expanded, level) {
for (var i=idx+1; i<me.length; i++) {
var item = me.at(i);
var item_level = item.get('level');
if (item_level>level) {
if (expanded)
item.set('isVisible', true);
if (item.get('hasSubItems'))
i = _expand_sub_items(i, item.get('isExpanded'), item_level );
} else {
return (i-1);
}
}
};
record.set('isExpanded', true);
_expand_sub_items(record.get('index'), true, record.get('level'));
},
2017-12-20 11:09:31 +00:00
collapseSubItems: function(record) {
var start_level = record.get('level'),
index = record.get('index');
for (var i=index+1; i<this.length; i++) {
var item = this.at(i);
var item_level = item.get('level');
if (item_level>start_level) {
item.set('isVisible', false);
} else {
break;
}
}
2017-12-20 11:09:31 +00:00
return i-1;
},
expandAll: function() {
this.each(function(item) {
item.set('isExpanded', true);
item.set('isVisible', true);
});
},
collapseAll: function() {
for (var i=0; i<this.length; i++) {
var item = this.at(i);
if (!item.get('isNotHeader')) {
item.set('isExpanded', false);
i = this.collapseSubItems(item);
}
2017-12-20 11:09:31 +00:00
}
2017-12-20 12:34:17 +00:00
},
expandToLevel: function(expandLevel) {
var me = this;
var _expand_sub_items = function(idx, level) {
var parent = me.at(idx);
parent.set('isExpanded', false);
for (var i=idx+1; i<me.length; i++) {
var item = me.at(i);
var item_level = item.get('level');
if (item_level>level) {
if (item_level<=expandLevel)
parent.set('isExpanded', true);
item.set('isVisible', item_level<=expandLevel);
if (item.get('hasSubItems'))
i = _expand_sub_items(i, item_level );
} else {
return (i-1);
}
}
};
for (var j=0; j<this.length; j++) {
var item = this.at(j);
if (item.get('level')<=expandLevel || !item.get('hasParent')) {
2017-12-20 12:34:17 +00:00
item.set('isVisible', true);
if (!item.get('isNotHeader'))
j = _expand_sub_items(j, item.get('level'));
2017-12-20 12:34:17 +00:00
}
}
}
});
Common.UI.TreeView = Common.UI.DataView.extend((function() {
return {
options: {
handleSelect: true,
showLast: true,
allowScrollbar: true,
scrollAlwaysVisible: true,
emptyItemText: '',
keyMoveDirection: 'both'
},
template: _.template([
2020-09-27 11:39:29 +00:00
'<div class="treeview inner" style="<%= style %>"></div>'
].join('')),
initialize : function(options) {
options.store = options.store || new Common.UI.TreeViewStore();
2017-12-21 12:01:37 +00:00
options.emptyItemText = options.emptyItemText || '';
options.itemTemplate = options.itemTemplate || _.template([
'<div id="<%= id %>" class="tree-item <% if (!isVisible) { %>' + 'hidden' + '<% } %>" style="display: block;padding-left: <%= level*16 + 24 %>px;">',
'<% if (hasSubItems) { %>',
'<div class="tree-caret img-commonctrl ' + '<% if (!isExpanded) { %>' + 'up' + '<% } %>' + '" style="margin-left: <%= level*16 %>px;"></div>',
'<% } %>',
2017-12-25 09:20:13 +00:00
'<% if (isNotHeader) { %>',
2021-04-26 11:54:55 +00:00
'<div class="name not-header"><%= Common.Utils.String.htmlEncode(name) %></div>',
2017-12-25 09:20:13 +00:00
'<% } else if (isEmptyItem) { %>',
'<div class="name empty">' + options.emptyItemText + '</div>',
2017-12-21 12:01:37 +00:00
'<% } else { %>',
2021-04-26 11:54:55 +00:00
'<div class="name"><%= Common.Utils.String.htmlEncode(name) %></div>',
2017-12-21 12:01:37 +00:00
'<% } %>',
'</div>'
].join(''));
Common.UI.DataView.prototype.initialize.call(this, options);
},
2018-01-11 08:22:50 +00:00
onAddItem: function(record, store, opts) {
var view = new Common.UI.DataViewItem({
template: this.itemTemplate,
model: record
});
if (view) {
2019-07-29 11:24:20 +00:00
var innerEl = (this.$el || $(this.el)).find('.inner').addBack().filter('.inner');
if (innerEl) {
2018-09-18 13:22:30 +00:00
(this.dataViewItems.length<1) && innerEl.find('.empty-text').remove();
2018-01-11 08:22:50 +00:00
2021-07-14 16:47:47 +00:00
if (opts && (typeof opts.at==='number')) {
2018-01-11 08:22:50 +00:00
var idx = opts.at;
var innerDivs = innerEl.find('> div');
if (idx > 0)
$(innerDivs.get(idx - 1)).after(view.render().el);
else {
(innerDivs.length > 0) ? $(innerDivs[idx]).before(view.render().el) : innerEl.append(view.render().el);
}
this.dataViewItems = this.dataViewItems.slice(0, idx).concat(view).concat(this.dataViewItems.slice(idx));
} else {
innerEl.append(view.render().el);
this.dataViewItems.push(view);
}
this.updateTip(view);
this.listenTo(view, 'change', this.onChangeItem);
this.listenTo(view, 'remove', this.onRemoveItem);
this.listenTo(view, 'click', this.onClickItem);
this.listenTo(view, 'dblclick', this.onDblClickItem);
this.listenTo(view, 'select', this.onSelectItem);
this.listenTo(view, 'contextmenu', this.onContextMenuItem);
if (!this.isSuspendEvents)
this.trigger('item:add', this, view, record);
}
}
},
onClickItem: function(view, record, e) {
var btn = $(e.target);
if (btn && btn.hasClass('tree-caret')) {
var tip = view.$el.data('bs.tooltip');
if (tip) (tip.tip()).remove();
var isExpanded = !record.get('isExpanded');
record.set('isExpanded', isExpanded);
2017-12-20 11:09:31 +00:00
this.store[(isExpanded) ? 'expandSubItems' : 'collapseSubItems'](record);
2022-04-05 12:10:58 +00:00
this.scroller.update({minScrollbarLength: this.minScrollbarLength, alwaysVisibleY: this.scrollAlwaysVisible});
} else
Common.UI.DataView.prototype.onClickItem.call(this, view, record, e);
},
expandAll: function() {
2017-12-20 11:09:31 +00:00
this.store.expandAll();
2022-04-05 12:10:58 +00:00
this.scroller.update({minScrollbarLength: this.minScrollbarLength, alwaysVisibleY: this.scrollAlwaysVisible});
},
collapseAll: function() {
2017-12-20 11:09:31 +00:00
this.store.collapseAll();
2022-04-05 12:10:58 +00:00
this.scroller.update({minScrollbarLength: this.minScrollbarLength, alwaysVisibleY: this.scrollAlwaysVisible});
2017-12-20 12:34:17 +00:00
},
expandToLevel: function(expandLevel) {
this.store.expandToLevel(expandLevel);
2022-04-05 12:10:58 +00:00
this.scroller.update({minScrollbarLength: this.minScrollbarLength, alwaysVisibleY: this.scrollAlwaysVisible});
2020-03-10 14:49:42 +00:00
},
expandRecord: function(record) {
if (record) {
record.set('isExpanded', true);
this.store.expandSubItems(record);
2022-04-05 12:10:58 +00:00
this.scroller.update({minScrollbarLength: this.minScrollbarLength, alwaysVisibleY: this.scrollAlwaysVisible});
2020-03-10 14:49:42 +00:00
}
},
collapseRecord: function(record) {
if (record) {
record.set('isExpanded', false);
this.store.collapseSubItems(record);
2022-04-05 12:10:58 +00:00
this.scroller.update({minScrollbarLength: this.minScrollbarLength, alwaysVisibleY: this.scrollAlwaysVisible});
2020-03-10 14:49:42 +00:00
}
},
onKeyDown: function (e, data) {
if ( this.disabled ) return;
if (data===undefined) data = e;
if (_.indexOf(this.moveKeys, data.keyCode)>-1 || data.keyCode==Common.UI.Keys.RETURN) {
data.preventDefault();
data.stopPropagation();
var rec = this.getSelectedRec();
2020-09-26 18:43:53 +00:00
if (this.lastSelectedRec===null)
this.lastSelectedRec = rec;
if (data.keyCode==Common.UI.Keys.RETURN) {
2020-09-26 18:43:53 +00:00
this.lastSelectedRec = null;
if (this.selectedBeforeHideRec) // only for ComboDataView menuPicker
rec = this.selectedBeforeHideRec;
this.trigger('item:click', this, this, rec, e);
2020-09-26 18:43:53 +00:00
this.trigger('item:select', this, this, rec, e);
this.trigger('entervalue', this, rec, e);
if (this.parentMenu)
this.parentMenu.hide();
} else {
var idx = _.indexOf(this.store.models, rec);
if (idx<0) {
if (data.keyCode==Common.UI.Keys.LEFT) {
var target = $(e.target).closest('.dropdown-submenu.over');
if (target.length>0) {
target.removeClass('over');
target.find('> a').focus();
} else
idx = 0;
} else
idx = 0;
} else if (this.options.keyMoveDirection == 'both') {
var hasSubItems = rec.get('hasSubItems');
var hasParent = rec.get('hasParent');
var isExpanded = rec.get('isExpanded');
if (data.keyCode==Common.UI.Keys.LEFT) {
if (hasSubItems && isExpanded)
this.collapseRecord(rec);
} else if (data.keyCode==Common.UI.Keys.RIGHT) {
if (hasSubItems && !isExpanded)
this.expandRecord(rec);
} else {
if (data.keyCode==Common.UI.Keys.DOWN) {
for (var i=idx+1; i<this.store.length; i++) {
if (this.store.at(i).get('isVisible')) {
idx=i;
break;
}
}
} else if (data.keyCode==Common.UI.Keys.UP) {
for (var i=idx-1; i>=0; i--) {
if (this.store.at(i).get('isVisible')) {
idx=i;
break;
}
}
}
}
} else {
idx = (data.keyCode==Common.UI.Keys.UP || data.keyCode==Common.UI.Keys.LEFT)
? Math.max(0, idx-1)
: Math.min(this.store.length - 1, idx + 1) ;
}
if (idx !== undefined && idx>=0) rec = this.store.at(idx);
if (rec) {
this._fromKeyDown = true;
this.selectRecord(rec);
this._fromKeyDown = false;
this.scrollToRecord(rec);
}
}
} else {
this.trigger('item:keydown', this, rec, e);
}
},
focus: function() {
this.cmpEl && this.cmpEl.find('.treeview').focus();
},
updateTip: function(item) {
var record = item.model,
name = record.get('name'),
me = this;
if (name.length > 37 - record.get('level')*2)
record.set('tip', name);
else
record.set('tip', '');
var el = item.$el || $(item.el);
var tip = el.data('bs.tooltip');
if (tip) {
if (tip.dontShow===undefined)
tip.dontShow = true;
el.removeData('bs.tooltip');
}
if (record.get('tip')) {
el.attr('data-toggle', 'tooltip');
el.tooltip({
title : record.get('tip'),
placement : 'cursor',
zIndex : this.tipZIndex
});
if (this.delayRenderTips)
el.one('mouseenter', function(){
el.attr('data-toggle', 'tooltip');
el.tooltip({
title : record.get('tip'),
placement : 'cursor',
zIndex : me.tipZIndex
});
el.mouseenter();
});
else {
el.attr('data-toggle', 'tooltip');
el.tooltip({
title : record.get('tip'),
placement : 'cursor',
zIndex : me.tipZIndex
});
}
}
}
}
})());
});