editable-grid
Version:
Bootstrap grid with CRUD functionality.
237 lines (191 loc) • 7.21 kB
JavaScript
var $ = require('jquery'),
_ = require('underscore'),
rowFactory = require('./rowFactory'),
utils = require('./gridUtils'),
listeners = require('./gridListeners'),
depthFirstTreeTraversal = require('./depthFirstTreeTraversal'),
Ears = require('elephant-ears');
var Grid = function (options) {
var grid = this;
grid.ears = new Ears();
grid.ears.on('editable-delete-mode', function (activate) {
if (activate) {
grid._createDeleteRows();
} else {
grid._removeDeleteRows();
}
});
// add default props and funcs to options
options = _.defaults(options, {
sortConfig: [],
id: 'id',
borders: true,
rows: {
link: false,
newRow: false,
totalRow: false
},
stateManager: {
isEditable: function () {
return false;
}
},
addListeners: function () {
},
treeMode: false,
childData: function () {
return $.Deferred().resolve([]);
},
launchLinksNewTab: false
});
// add default props and fncs to columns
options.columns = _.map(options.columns, function (column) {
var formatter = function (id, value) {
return value;
};
var parser = function (id, value) {
return value;
};
var validate = function () {
return '';
};
return _.defaults(column, {
formatter: formatter,
parser: parser,
validate: validate,
sortable: false,
sortCompare: null,
sortType: null,
nullable: false,
type: 'text',
link: null,
alignment: 'left',
titleAlignment: 'left',
classes: [],
preCreateCallback: function () {
}
});
});
// store the original order of the data
grid.dataOrder = _.clone(options.data);
var createHeaderTable = function (columns) {
return '<div class="editable-header-table">' +
'<table class="table table-bordered">' +
'<thead>' + rowFactory.createTableHeaderRow(
{
columns: columns,
isColumnSorted: grid._isColumnSorted
}) +
'</thead>' +
'</table>' +
'</div>';
};
var createBodyTable = function () {
var hasTreeState = ('_treeState' in grid) && _.keys(grid._treeState);
var rowsMarkup = depthFirstTreeTraversal.call(grid, options, options.data, hasTreeState, 0);
var tableClazzes = 'table table-bordered';
if (options.rows.link) {
tableClazzes += ' table-hover';
}
return '<div class="editable-body-table">' +
'<table class="' + tableClazzes + '">' +
'<tbody>' + rowsMarkup +
'</tbody>' +
'</table>' +
'</div>';
};
var createFooterTable = function (options) {
// opening tags
var markup = '<div class="editable-footer-table">';
markup += '<table class="table table-bordered"><tfoot>';
if (options.rows.totalRow) {
markup += rowFactory.createTableFooterTotalRow({
columns: options.columns,
data: options.data
});
}
if (options.rows.newRow) {
markup += rowFactory.createTableFooterAddRow({columns: options.columns});
}
// closing tags
markup += '</tfoot></table>';
if (options.rows.newRow) {
markup += '<div>';
markup += '<button type="button" class="new-row pull-right btn btn-link">Add</button>';
markup += '</div>';
}
markup += '</div>';
return markup;
};
// attach grid functions
_.extend(grid, utils.call(grid, options));
_.extend(grid, {
trigger: function (name, params) {
grid.ears.trigger(name, params);
},
on: function (name, callback) {
grid.ears.on(name, callback);
},
off: function (name) {
grid.ears.off(name);
},
destroy: function () {
// remove any previous content
options.el.empty();
},
get options () {
return options;
},
clearAllValidation: function () {
this.bodyTableContainer.find('.validation-error').remove();
this.bodyTableContainer.find('.has-error').removeClass('has-error');
},
addValidation: function (recordId, propertyName, message) {
var td = this.bodyTableContainer.find('tr[data-row-id="' + recordId + '"] ' +
'td[data-col-id="' + propertyName + '"]');
td.addClass('has-error');
td.append('<span class="validation-error help-block small">' + message + '</div>');
},
render: function () {
grid.ears.trigger('editable-pre-render');
this.destroy();
// get the height before any contents is rendered
var height = options.el.height();
var markup = createHeaderTable(options.columns);
markup += createBodyTable();
markup += createFooterTable(options);
options.el.append(markup);
this.headerTableContainer = options.el.find('.editable-header-table');
this.headerTable = this.headerTableContainer.find('table');
this.bodyTableContainer = options.el.find('.editable-body-table');
this.bodyTable = this.bodyTableContainer.find('table');
this.footerTableContainer = options.el.find('.editable-footer-table');
this.footerTable = this.footerTableContainer.find('table');
if (options.borders === false) {
this.headerTable.addClass('no-borders');
this.bodyTable.addClass('no-borders');
this.footerTable.addClass('no-borders');
}
if (height > 0) {
// only set the body height if the outer containing div has a height set
this.bodyTableContainer.height(options.el.height() -
this.headerTableContainer.height() - this.footerTableContainer.height());
this.headerTable.width(this.bodyTable.width() + 1);
}
// the below line enforces the browser to calculate heights and widths
if (options.data.length > 0) {
this.bodyTableContainer.get(0).scrollHeight >
this.bodyTableContainer.get(0).clientHeight;
} else {
this.bodyTableContainer.height(0);
}
// attach grid listeners
listeners.call(grid, this.headerTableContainer,
this.bodyTableContainer, this.footerTableContainer, options);
// allow the user to add custom listeners
options.addListeners(options.el);
grid.ears.trigger('editable-post-render');
}
});
};
module.exports = Grid;