JSFiddle - React, Tailwind, and code Playground
HTML
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css">
<div id="grid" style="padding:10px;">
<form class="form-inline">
<div class="input-prepend"> <span class="add-on"><i class="icon-search"></i></span><input class="span2"
type="text" data-bind="value: searchTerm, valueUpdate:'afterkeydown'"/>
</div>
<button class="btn pull-right disabled">Edit</button>
<button class="btn pull-right">Add</button>
</form>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr data-bind="foreach: columns">
<th data-bind="html: $data.labelHTML, click: $parent.sort"></th>
</tr>
</thead>
<tbody data-bind="foreach: models">
<tr>
<td data-bind="text: $data.ID"></td>
<td data-bind="text: $data.FirstName"></td>
<td data-bind="text: $data.LastName"></td>
<td data-bind="text: $data.Username"></td>
</tr>
</tbody>
</table>
<div class="pagination pagination-centered">
<ul data-bind="foreach: ko.utils.range(1,pageCount)">
<li data-bind="css: { active: $data===$root.page()}"><a data-bind="text: $data, click: function() { $root.page($data)}"></a>
</li>
</ul>
</div>
</div>
CSS
i.blank {
display: inline-block;
width: 14px;
height: 14px;
line-height: 14px;
vertical-align: text-top;
margin-top:1px;
}
JavaScript
function Grid(Model, Element, Options) {
var self = this;
self.options = Options || {};
self.sort = function (column) {
if (column === self.sortedColumn) {
self.sortedColumn.toggleSort();
} else {
var oldSort = self.sortedColumn;
oldSort.sortDirection(null);
self.sortedColumn = column;
self.sortedColumn.sortDirection("asc");
}
self.internalModels.sort(function (left, right) {
return self.sortedColumn.sort(left, right, self.sortedColumn.property, self.sortedColumn.sortDirection());
});
};
self.sortedColumn = Model.columns()[0];
self.columns = Model.columns;
self.searchTerm = ko.observable("");
self.internalModels = ko.observableArray(Model.Get());
self.filteredModels = ko.computed(function () {
var searchText = self.searchTerm().toLowerCase();
var internalModels = self.internalModels();
var models = [];
var searchMatches = [];
if (searchText !== "") {
for (var i = 0; i < internalModels.length; i++) {
var obj = internalModels[i];
var matches = findInProperties(searchText,obj,null);
if(matches.length>0) {
models.push(obj);
searchMatches.push({"object": obj, "results": matches});
}
}
} else {
models = self.internalModels().slice(0);
}
return models;
});
function findInProperties(search,obj,field) {
var matches = [];
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
var match = getMatch(search,obj,property);
if(match) matches.push(match);
}
}
return matches;
}
function getMatch(search,obj,field) {
if(field!==null) {
var index =...