zui
Version:
一个基于 Bootstrap 深度定制开源前端实践方案,帮助你快速构建现代跨屏应用。
412 lines (372 loc) • 15.1 kB
JavaScript
/* ========================================================================
* ZUI: tree.js [1.4.0+]
* http://zui.sexy
* ========================================================================
* Copyright (c) 2016 cnezsoft.com; Licensed MIT
* ======================================================================== */
(function($) {
'use strict';
var name = 'zui.tree'; // modal name
var globalId = 0;
// The tree modal class
var Tree = function(element, options) {
this.name = name;
this.$ = $(element);
this.getOptions(options);
this._init();
};
var DETAULT_ACTIONS = {
sort: {
template: '<a class="sort-handler" href="javascript:;"><i class="icon icon-move"></i></a>'
},
add: {
template: '<a href="javascript:;"><i class="icon icon-plus"></i></a>'
},
edit: {
template: '<a href="javascript:;"><i class="icon icon-pencil"></i></a>'
},
"delete": {
template: '<a href="javascript:;"><i class="icon icon-trash"></i></a>'
}
};
function formatActions(actions, parentActions) {
if(actions === false) return actions;
if(!actions) return parentActions;
if(actions === true) {
actions = {add: true, "delete": true, edit: true, sort: true};
} else if(typeof actions === 'string') {
actions = actions.split(',');
}
var _actions;
if($.isArray(actions)) {
_actions = {};
$.each(actions, function(idx, action) {
if($.isPlainObject(action)) {
_actions[action.action] = action;
} else {
_actions[action] = true;
}
});
actions = _actions;
}
if($.isPlainObject(actions)) {
_actions = {};
$.each(actions, function(name, action) {
if(action) {
_actions[name] = $.extend({type: name}, DETAULT_ACTIONS[name], $.isPlainObject(action) ? action : null);
} else {
_actions[name] = false;
}
});
actions = _actions;
}
return parentActions ? $.extend(true, {}, parentActions, actions) : actions;
}
function createActionEle(action, name, template) {
name = name || action.type;
return $(template || action.template).addClass('tree-action').attr($.extend({'data-type': name, title: action.title || ''}, action.attr)).data('action', action);
}
// default options
Tree.DEFAULTS = {
animate: null,
initialState: 'normal', // 'normal' | 'preserve' | 'expand' | 'collapse',
toggleTemplate: '<i class="list-toggle icon"></i>',
// sortable: false, //
};
Tree.prototype.add = function(rootEle, items, expand, disabledAnimate, notStore) {
var $e = $(rootEle), $ul, options = this.options;
if($e.is('li')) {
$ul = $e.children('ul');
if(!$ul.length) {
$ul = $('<ul/>');
$e.append($ul);
this._initList($ul, $e);
}
} else {
$ul = $e;
}
if($ul) {
var that = this;
if(!$.isArray(items)) {
items = [items];
}
$.each(items, function(idx, item) {
var $li = $('<li/>').data(item).appendTo($ul);
if(item.id !== undefined) $li.attr('data-id', item.id);
var $wrapper = options.itemWrapper ? $(options.itemWrapper === true ? '<div class="tree-item-wrapper"/>' : options.itemWrapper).appendTo($li) : $li;
if(item.html) {
$wrapper.html(item.html)
} else if($.isFunction(that.options.itemCreator)) {
var itemContent = that.options.itemCreator($li, item);
if(itemContent !== true && itemContent !== false) $wrapper.html(itemContent);
} else if(item.url) {
$wrapper.append($('<a/>', {href: item.url}).text(item.title || item.name));
} else {
$wrapper.append($('<span/>').text(item.title || item.name));
}
that._initItem($li, item.idx || idx, $ul, item);
if(item.children && item.children.length) {
that.add($li, item.children);
}
});
this._initList($ul);
if(expand && !$ul.hasClass('tree')) {
that.expand($ul.parent('li'), disabledAnimate, notStore);
}
}
};
Tree.prototype.reload = function(data) {
var that = this;
if(data) {
that.$.empty();
that.add(that.$, data);
}
if(that.isPreserve)
{
if(that.store.time) {
that.$.find('li:not(.tree-action-item)').each(function() {
var $li= $(this);
that[that.store[$li.data('id')] ? 'expand' : 'collapse']($li, true, true);
});
}
}
};
Tree.prototype._initList = function($list, $parentItem, idx, data) {
var that = this;
if(!$list.hasClass('tree')) {
$parentItem = ($parentItem || $list.closest('li')).addClass('has-list');
if(!$parentItem.find('.list-toggle').length) {
$parentItem.prepend(this.options.toggleTemplate);
}
idx = idx || $parentItem.data('idx');
} else {
idx = 0;
$parentItem = null;
}
$list.removeClass('has-active-item');
var $children = $list.attr('data-idx', idx || 0).children('li:not(.tree-action-item)').each(function(index) {
that._initItem($(this), index + 1, $list);
});
if($children.length === 1 && !$children.find('ul').length)
{
$children.addClass('tree-single-item');
}
data = data || ($parentItem ? $parentItem.data() : null);
var actions = formatActions(data ? data.actions : null, this.actions);
if(actions) {
if(actions.add && actions.add.templateInList !== false) {
var $actionItem = $list.children('li.tree-action-item');
if(!$actionItem.length) {
$('<li class="tree-action-item"/>').append(createActionEle(actions.add, 'add', actions.add.templateInList)).appendTo($list);
} else {
$actionItem.detach().appendTo($list);
}
}
if(actions.sort) {
$list.sortable($.extend({
dragCssClass: 'tree-drag-holder',
trigger: '.sort-handler',
selector: 'li:not(.tree-action-item)',
finish: function(e) {
that.callEvent('action', {action: actions.sort, $list: $list, target: e.target, item: data});
}
}, actions.sort.options, $.isPlainObject(this.options.sortable) ? this.options.sortable : null));
}
}
if($parentItem && ($parentItem.hasClass('open') || (data && data.open))) {
$parentItem.addClass('open in');
}
};
Tree.prototype._initItem = function($item, idx, $parentList, data) {
if(idx === undefined) {
var $pre = $item.prev('li');
idx = $pre.length ? ($pre.data('idx') + 1) : 1;
}
$parentList = $parentList || $item.closest('ul');
$item.attr('data-idx', idx).removeClass('tree-single-item');
if(!$item.data('id')) {
var id = idx;
if(!$parentList.hasClass('tree')) {
id = $parentList.parent('li').data('id') + '-' + id;
}
$item.attr('data-id', id);
}
if ($item.hasClass('active')) {
$parentList.parent('li').addClass('has-active-item');
}
data = data || $item.data();
var actions = formatActions(data.actions, this.actions);
if(actions) {
var $actions = $item.find('.tree-actions');
if(!$actions.length) {
$actions = $('<div class="tree-actions"/>').appendTo(this.options.itemWrapper ? $item.find('.tree-item-wrapper') : $item);
$.each(actions, function(actionName, action) {
if(action) $actions.append(createActionEle(action, actionName));
});
}
}
var $children = $item.children('ul');
if($children.length) {
this._initList($children, $item, idx, data);
}
};
Tree.prototype._init = function() {
var options = this.options, that = this;
this.actions = formatActions(options.actions);
this.$.addClass('tree');
if(options.animate) this.$.addClass('tree-animate');
this._initList(this.$);
var initialState = options.initialState;
var isPreserveEnable = $.zui && $.zui.store && $.zui.store.enable;
if(isPreserveEnable) {
this.selector = name + '::' + (options.name || '') + '#' + (this.$.attr('id') || globalId++);
this.store = $.zui.store[options.name ? 'get' : 'pageGet'](this.selector, {});
}
if(initialState === 'preserve') {
if(isPreserveEnable) this.isPreserve = true;
else this.options.initialState = initialState = 'normal';
}
// init data
this.reload(options.data);
if(isPreserveEnable) this.isPreserve = true;
if(initialState === 'expand') {
this.expand();
} else if(initialState === 'collapse') {
this.collapse();
}
// Bind event
this.$.on('click', '.list-toggle,a[href="#"],.tree-toggle', function(e) {
var $this = $(this);
var $li = $this.parent('li');
that.callEvent('hit', {target: $li, item: $li.data()});
that.toggle($li);
if($this.is('a')) e.preventDefault();
}).on('click', '.tree-action', function() {
var $action = $(this);
var action = $action.data();
if(action.action) action = action.action;
if(action.type === 'sort') return;
var $li = $action.closest('li:not(.tree-action-item)');
that.callEvent('action', {action: action, target: this, $item: $li, item: $li.data()});
});
};
Tree.prototype.preserve = function($li, id, expand) {
if(!this.isPreserve) return;
if($li) {
id = id || $li.data('id');
expand = expand === undefined ? $li.hasClass('open') : false;
if(expand) this.store[id] = expand;
else delete this.store[id];
this.store.time = new Date().getTime();
$.zui.store[this.options.name ? 'set' : 'pageSet'](this.selector, this.store);
} else {
var that = this;
this.store = {};
this.$.find('li').each(function() {
that.preserve($(this));
});
}
};
Tree.prototype.expand = function($li, disabledAnimate, notStore) {
if($li) {
$li.addClass('open');
if(!disabledAnimate && this.options.animate) {
setTimeout(function() {
$li.addClass('in');
}, 10);
} else {
$li.addClass('in');
}
} else {
$li = this.$.find('li.has-list').addClass('open in');
}
if(!notStore) this.preserve($li);
this.callEvent('expand', $li, this);
};
Tree.prototype.show = function($lis, disabledAnimate, notStore) {
var that = this;
$lis.each(function() {
var $li = $(this);
that.expand($li, disabledAnimate, notStore);
if($li) {
var $ul = $li.parent('ul');
while($ul && $ul.length && !$ul.hasClass('tree')) {
var $parentLi = $ul.parent('li');
if($parentLi.length) {
that.expand($parentLi, disabledAnimate, notStore);
$ul = $parentLi.parent('ul');
} else {
$ul = false;
}
}
}
});
};
Tree.prototype.collapse = function($li, disabledAnimate, notStore) {
if($li) {
if(!disabledAnimate && this.options.animate) {
$li.removeClass('in');
setTimeout(function() {
$li.removeClass('open');
}, 300);
} else {
$li.removeClass('open in');
}
} else {
$li = this.$.find('li.has-list').removeClass('open in');
}
if(!notStore) this.preserve($li);
this.callEvent('collapse', $li, this);
};
Tree.prototype.toggle = function($li) {
var collapse = ($li && $li.hasClass('open')) || $li === false || ($li === undefined && this.$.find('li.has-list.open').length);
this[collapse ? 'collapse' : 'expand']($li);
};
// Get and init options
Tree.prototype.getOptions = function(options) {
this.options = $.extend({}, Tree.DEFAULTS, this.$.data(), options);
if(this.options.animate === null && this.$.hasClass('tree-animate')) {
this.options.animate = true;
}
};
Tree.prototype.toData = function($ul, filter) {
if($.isFunction($ul)) {
filter = $ul;
$ul = null;
}
$ul = $ul || this.$;
var that = this;
return $ul.children('li:not(.tree-action-item)').map(function() {
var $li = $(this);
var data = $li.data();
delete data['zui.droppable'];
var $children = $li.children('ul');
if($children.length) data.children = that.toData($children);
return $.isFunction(filter) ? filter(data, $li) : data;
}).get();
};
// Call event helper
Tree.prototype.callEvent = function(name, params) {
var result;
if($.isFunction(this.options[name])) {
result = this.options[name](params, this);
}
this.$.trigger($.Event(name + '.' + this.name, params));
return result;
};
// Extense jquery element
$.fn.tree = function(option, params) {
return this.each(function() {
var $this = $(this);
var data = $this.data(name);
var options = typeof option == 'object' && option;
if(!data) $this.data(name, (data = new Tree(this, options)));
if(typeof option == 'string') data[option](params);
});
};
$.fn.tree.Constructor = Tree;
// Auto call tree after document load complete
$(function() {
$('[data-ride="tree"]').tree();
});
}(jQuery));