Extend treelist.js with search function and saving collapse/expand states in localStorage

pull/190/head
Thomas Bruederli 10 years ago
parent 2b4ee2235b
commit e3f84735d7

@ -44,6 +44,7 @@ function rcube_treelist_widget(node, p)
scroll_delay: 500,
scroll_step: 5,
scroll_speed: 20,
save_state: false,
check_droptarget: function(node){ return !node.virtual }
}, p || {});
@ -52,6 +53,8 @@ function rcube_treelist_widget(node, p)
indexbyid = {},
selection = null,
drag_active = false,
search_active = false,
last_search = '',
box_coords = {},
item_coords = [],
autoexpand_timer,
@ -59,6 +62,9 @@ function rcube_treelist_widget(node, p)
body_scroll_top = 0,
list_scroll_top = 0,
scroll_timer,
searchfield,
tree_state,
list_id = (container.attr('id') || id_prefix || '0'),
me = this;
@ -105,6 +111,43 @@ function rcube_treelist_widget(node, p)
}
});
// activate search function
if (p.searchbox) {
searchfield = $(p.searchbox).on('keyup', function(e) {
var key = rcube_event.get_keycode(e),
mod = rcube_event.get_modifier(e);
switch (key) {
case 9: // tab
break;
case 13: // enter
search(this.value, true);
return rcube_event.cancel(e);
case 27: // escape
reset_search();
break;
case 38: // arrow up
case 37: // left
case 39: // right
case 40: // arrow down
return; // ignore arrow keys
default:
search(this.value, false);
break;
}
}).attr('autocomplete', 'off');
// find the reset button for this search field
searchfield.parent().find('a.reset').click(function(e) {
reset_search();
return false;
})
}
/////// private methods
@ -126,6 +169,7 @@ function rcube_treelist_widget(node, p)
}
me.triggerEvent(node.collapsed ? 'collapse' : 'expand', node);
save_state(id, node.collapsed);
}
}
@ -191,13 +235,21 @@ function rcube_treelist_widget(node, p)
function insert(node, parent_id, sort)
{
var li, parent_li,
parent_node = parent_id ? indexbyid[parent_id] : null;
parent_node = parent_id ? indexbyid[parent_id] : null
search_ = search_active;
// apply saved state
state = get_state(node.id, node.collapsed);
if (state !== undefined) {
node.collapsed = state;
}
// insert as child of an existing node
if (parent_node) {
if (!parent_node.children)
parent_node.children = [];
search_active = false;
parent_node.children.push(node);
parent_li = id2dom(parent_id);
@ -210,6 +262,19 @@ function rcube_treelist_widget(node, p)
// append new node to parent's child list
li = render_node(node, parent_li.children('ul').first());
}
// list is in search mode
if (search_) {
search_active = search_;
// add clone to current search results (top level)
$('<li>')
.attr('id', li.attr('id') + '--xsR')
.attr('class', li.attr('class'))
.addClass('searchresult__')
.append(li.children().first().clone(true, true))
.appendTo(container);
}
}
// insert at top level
else {
@ -335,6 +400,80 @@ function rcube_treelist_widget(node, p)
drag_active = false;
container.html('');
reset_search();
}
/**
*
*/
function search(q, enter)
{
q = String(q).toLowerCase();
if (!q.length)
return reset_search();
else if (q == last_search && !enter)
return 0;
var hits = [];
var search_tree = function(items) {
$.each(items, function(i, node) {
var li, sli;
if (!node.virtual && !node.deleted && String(node.text).toLowerCase().indexOf(q) >= 0) {
li = id2dom(node.id);
sli = $('<li>')
.attr('id', li.attr('id') + '--xsR')
.attr('class', li.attr('class'))
.addClass('searchresult__')
.append(li.children().first().clone(true, true))
.appendTo(container);
hits.push(node.id);
}
if (node.children && node.children.length) {
search_tree(node.children);
}
});
};
// reset old search results
if (search_active) {
$(container).children('li.searchresult__').remove();
search_active = false;
}
// hide all list items
$(container).children('li').hide().removeClass('selected');
// search recursively in tree (to keep sorting order)
search_tree(data);
search_active = true;
last_search = q;
me.triggerEvent('search', { query: q, last: last_search, count: hits.length, ids: hits, execute: enter||false });
return hits.count;
}
/**
*
*/
function reset_search()
{
if (searchfield)
searchfield.val('');
$(container).children('li.searchresult__').remove();
$(container).children('li').show();
search_active = false;
me.triggerEvent('search', { query: false, last: last_search });
last_search = '';
if (selection)
select(selection);
}
/**
@ -378,6 +517,9 @@ function rcube_treelist_widget(node, p)
else if (typeof node.html == 'object')
li.append(node.html);
if (!node.text)
node.text = li.children().first().text();
if (node.virtual)
li.addClass('virtual');
if (node.id == selection)
@ -406,12 +548,13 @@ function rcube_treelist_widget(node, p)
{
var result = [];
ul.children('li').each(function(i,e){
var li = $(e), sublist = li.children('ul');
var state, li = $(e), sublist = li.children('ul');
var node = {
id: dom2id(li),
classes: String(li.attr('class')).split(' '),
virtual: li.hasClass('virtual'),
html: li.children().first().get(0).outerHTML,
text: li.children().first().text(),
children: walk_list(sublist)
}
@ -420,6 +563,14 @@ function rcube_treelist_widget(node, p)
}
if (node.children.length) {
node.collapsed = sublist.css('display') == 'none';
// apply saved state
state = get_state(node.id, node.collapsed);
if (state !== undefined) {
node.collapsed = state;
sublist[(state?'hide':'show')]();
}
if (!li.children('div.treetoggle').length)
$('<div class="treetoggle '+(node.collapsed ? 'collapsed' : 'expanded') + '">&nbsp;</div>').appendTo(li);
}
@ -452,7 +603,7 @@ function rcube_treelist_widget(node, p)
*/
function dom2id(li)
{
var domid = li.attr('id').replace(new RegExp('^' + (p.id_prefix) || '%'), '');
var domid = li.attr('id').replace(new RegExp('^' + (p.id_prefix) || '%'), '').replace(/--xsR$/, '');
return p.id_decode ? p.id_decode(domid) : domid;
}
@ -461,8 +612,9 @@ function rcube_treelist_widget(node, p)
*/
function id2dom(id)
{
var domid = p.id_encode ? p.id_encode(id) : id;
return $('#' + p.id_prefix + domid);
var domid = p.id_encode ? p.id_encode(id) : id,
suffix = search_active ? '--xsR' : '';
return $('#' + p.id_prefix + domid + suffix);
}
/**
@ -478,6 +630,40 @@ function rcube_treelist_widget(node, p)
scroller.scrollTop(rel_offset + current_offset);
}
/**
* Save node collapse state to localStorage
*/
function save_state(id, collapsed)
{
if (p.save_state && window.rcmail) {
var key = 'treelist-' + list_id;
if (!tree_state) {
tree_state = rcmail.local_storage_get_item(key, {});
}
if (tree_state[id] != collapsed) {
tree_state[id] = collapsed;
rcmail.local_storage_set_item(key, tree_state);
}
}
}
/**
* Read node collapse state from localStorage
*/
function get_state(id)
{
if (p.save_state && window.rcmail) {
if (!tree_state) {
tree_state = rcmail.local_storage_get_item('treelist-' + list_id, {});
}
return tree_state[id];
}
return undefined;
}
///// drag & drop support
/**

Loading…
Cancel
Save