JSFiddle - React, Tailwind, and code Playground
by ajai jothi
HTML
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<div ng-app="app" ng-controller="TestCtrl">
<grid data="data" config="config"></grid>
<script type="text/ng-template" id="grid.html">
<div class="grid">
<div class="grid-filter">
<div class="pull-left" ng-transclude></div>
<input type="text" ng-model="search" placeholder="search">
</div>
<table class="table">
<thead>
<tr>
<th ng-repeat="col in columnDefn">
<span ng-click="config.col=col.field;config.sortOrder=!config.sortOrder"><span>{{col.headerName}}</span>
<i class="fa fa-sort" ng-class="{'fa-sort-asc':!config.sortOrder && config.col===col.field, 'fa-sort-desc':config.sortOrder && config.col===col.field}"></i>
</span>
<div class="filter" ng-click="$event.stopPropagation();" ng-if="config.showFilter && col.showFilter!==false">
<i class="fa fa-filter" ng-click="toggleFilter(col)" ng-class="{active:getSelectedFilterValues(col).length}"></i>
<ul class="filter-dropdown" ng-if="col._filterOpen">
<li ng-repeat="(k,v) in col.filterValues">
<label title="{{k}}">
<input type="checkbox" ng-model="col[k]" ng-change="updateFilter(col, k)"> {{k}}
</label>
</li>
</ul>
</div>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in _data | filter:search | orderBy:config.col:config.sortOrder">
<td ng-repeat="col in columnDefn">
<div ng-if="col.rowTemplate" bind-html-compile="col.rowTemplate"></div>
<div ng-if="col.rowTemplateProvider" bind-html-compile="col.rowTemplateProvider(row, col)"></div>
<div ng-if="!col.rowTemplateProvider && !col.rowTemplate">
{{row[col.field]}}
...
SCSS
.grid {
table {
width: 100%;
border: 1px solid #f1f1f1;
background: white;
padding: 0;
margin: 0;
border-spacing: 0px;
border-collapse: collapse;
color: #333;
font-family: arial;
font-size: 12px;
tbody tr {
td {
border: 1px solid #f1f1f1;
border-bottom: 1px solid #eee;
margin: 0;
padding: 5px;
}
&:nth-child(2n) {
background: #f9f9f9;
}
}
}
.grid-filter {
padding: 5px 0;
text-align: right;
&:after {
content: "";
display: block;
clear: both;
}
}
thead th {
background: #eee;
border: 1px solid #f1f1f1;
text-align: left;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
white-space: nowrap;
cursor: pointer;
position: relative;
padding: 5px 40px 5px 10px;
.filter {
position: absolute;
cursor: pointer;
right: 5px;
top: 25%;
.active {
color: #2196F3;
}
}
.filter-dropdown {
position: absolute;
max-height: 250px;
min-width: 80px;
max-width: 200px;
overflow-x: hidden;
;
overflow-y: auto;
background: #f9f9f9;
border: 1px solid #eee;
top: 20px;
right: -10px;
margin: 0;
padding: 0;
list-style: none;
box-shadow: #eee 0 10px 20px;
}
.filter-dropdown li {
border-bottom: 1px solid #eee;
font-weight: lighter;
}
.filter-dropdown li label {
display: block;
padding: 3px;
width: 100%;
-ms-text-overflow: ellipsis;
text-overflow: ellipsis;
overflow: hidden;
}
}
}
JavaScript
(function() {
'use strict';
function GridDirective() {
return {
restrict: 'EA',
replace: true,
transclude: true,
templateUrl: 'grid.html',
scope: {
data: '=',
config: '=?'
},
link: function(scope) {
scope._data = scope.data;
scope.config = scope.config || {};
scope.appScope = scope.appScope || scope.$parent;
scope.columnDefn = scope.config.columnDefn || getDefaultColumnDefn(scope.data[0] || {});
console.log(scope.columnDefn);
scope.$watch('data', function(nv, ov) {
if (nv !== ov) {
scope.columnDefn = scope.config.columnDefn || getDefaultColumnDefn(scope.data[0]);
scope.updatFilterValues();
}
});
scope.updatFilterValues = function() {
scope.columnDefn.forEach(function(c) {
c.filterValues = getFilterValues(scope.data, c.field);
});
};
scope.updatFilterValues();
scope.updateFilter = function(col, key) {
col.filterValues[key] = !col.filterValues[key];
scope.populateGridData();
};
scope.getSelectedFilterValues = getSelectedFilterValues;
scope.populateGridData = function() {
scope._data = scope.data.filter(function(r) {
return scope.columnDefn.every(function(c) {
var selectedFilterValue = (getSelectedFilterValues(c));
return selectedFilterValue.length === 0 || selectedFilterValue.indexOf(r[c.field]) >= 0;
});
});
};
scope.hideFilters = function(col) {
scope.columnDefn.forEach(function(_col) {
if (col !== _col) {
_col._filterOpen = false;
}
});
};
scope.toggleFilter = function(col) {
col._filterOpen = !col._filterOpen;
scope.hideFilters(col);
};
angular.element(document).on('click',...