New grid component vnTable. #139. CR: Juan & Javi
This commit is contained in:
parent
fb07f5e860
commit
45b3a5ce02
|
@ -37,3 +37,5 @@ import './paging/paging';
|
||||||
import './auto-paging/auto-paging';
|
import './auto-paging/auto-paging';
|
||||||
import './pagination/pagination';
|
import './pagination/pagination';
|
||||||
import './searchbar/searchbar';
|
import './searchbar/searchbar';
|
||||||
|
import './table';
|
||||||
|
import './th';
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<div></div>
|
|
@ -0,0 +1,53 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
export default class Table {
|
||||||
|
constructor($scope, $element, $transclude) {
|
||||||
|
this.$scope = $scope;
|
||||||
|
this.table = $element[0];
|
||||||
|
this.field = null;
|
||||||
|
this.order = null;
|
||||||
|
|
||||||
|
$transclude($scope.$parent, clone => {
|
||||||
|
angular.element($element[0].querySelector('div')).append(clone);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setOrder(field, order) {
|
||||||
|
this.field = field;
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyFilter(field = this.field, order = this.order) {
|
||||||
|
this.model.filter.order = `${field} ${order}`;
|
||||||
|
this.model.refresh();
|
||||||
|
this.setActiveArrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
$onChanges() {
|
||||||
|
if (this.model)
|
||||||
|
this.applyFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveArrow() {
|
||||||
|
let columns = this.table.querySelectorAll('vn-thead vn-th');
|
||||||
|
columns.forEach(column => {
|
||||||
|
column.classList.remove('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
let selector = `vn-thead vn-th[field="${this.field}"]`;
|
||||||
|
let activeColumn = this.table.querySelector(selector);
|
||||||
|
activeColumn.classList.add('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Table.$inject = ['$scope', '$element', '$transclude'];
|
||||||
|
|
||||||
|
ngModule.component('vnTable', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
transclude: true,
|
||||||
|
controller: Table,
|
||||||
|
bindings: {
|
||||||
|
model: '<?'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,117 @@
|
||||||
|
@import "effects";
|
||||||
|
|
||||||
|
vn-table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
width: 100%;
|
||||||
|
display: table;
|
||||||
|
|
||||||
|
vn-thead {
|
||||||
|
display: table-header-group;
|
||||||
|
|
||||||
|
vn-th[field] {
|
||||||
|
cursor: pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-th[field] > * {
|
||||||
|
padding-right: 20px
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-th[field] > :after {
|
||||||
|
font-family: 'Material Icons';
|
||||||
|
content: 'arrow_drop_down';
|
||||||
|
position: absolute;
|
||||||
|
padding-left: 2px;
|
||||||
|
color: $lines;
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-th[field] > :hover:after {
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-th[field].active > :after {
|
||||||
|
color: $main-font-color;
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-th[field].desc > :after {
|
||||||
|
content: 'arrow_drop_down';
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-th[field].asc > :after {
|
||||||
|
content: 'arrow_drop_up';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-tbody {
|
||||||
|
display: table-row-group
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-tfoot {
|
||||||
|
display: table-footer-group
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-tr {
|
||||||
|
display: table-row
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-empty-rows {
|
||||||
|
display: table-caption;
|
||||||
|
caption-side: bottom;
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-thead,
|
||||||
|
vn-tbody,
|
||||||
|
vn-tfoot {
|
||||||
|
vn-tr {
|
||||||
|
display: table-row;
|
||||||
|
|
||||||
|
vn-th {
|
||||||
|
font-weight: bold
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-td, vn-th {
|
||||||
|
display: table-cell;
|
||||||
|
text-align: left;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
&[number]{
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-thead, vn-tbody, vn-empty-rows {
|
||||||
|
border-bottom: 3px solid $lines;
|
||||||
|
}
|
||||||
|
vn-tbody > vn-tr {
|
||||||
|
border-bottom: 1px solid $lines;
|
||||||
|
transition: background-color 200ms ease-in-out;
|
||||||
|
|
||||||
|
&.clickable {
|
||||||
|
@extend %clickable;
|
||||||
|
}
|
||||||
|
&.success {
|
||||||
|
background-color: rgba(163, 209, 49, 0.3);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(163, 209, 49, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.warning {
|
||||||
|
background-color: rgba(247, 147, 30, 0.3);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(247, 147, 30, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div translate ng-click="$ctrl.onToggleOrder()">
|
||||||
|
<ng-transclude></ng-transclude>
|
||||||
|
</div>
|
|
@ -0,0 +1,84 @@
|
||||||
|
import ngModule from '../../module';
|
||||||
|
|
||||||
|
export default class Th {
|
||||||
|
constructor($element) {
|
||||||
|
this.column = $element[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the order if the cell has a field and defaultOrder property
|
||||||
|
*/
|
||||||
|
$onInit() {
|
||||||
|
if (!this.field) return;
|
||||||
|
|
||||||
|
if (this.defaultOrder)
|
||||||
|
this.order = this.defaultOrder;
|
||||||
|
|
||||||
|
this.updateArrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
set order(order) {
|
||||||
|
this._order = order;
|
||||||
|
|
||||||
|
this.table.setOrder(this.field, order);
|
||||||
|
}
|
||||||
|
|
||||||
|
get order() {
|
||||||
|
return this._order;
|
||||||
|
}
|
||||||
|
|
||||||
|
get field() {
|
||||||
|
return this.column.getAttribute('field');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle order ASC/DESC
|
||||||
|
*/
|
||||||
|
toggleOrder() {
|
||||||
|
if (this.order === 'ASC') {
|
||||||
|
this.order = 'DESC';
|
||||||
|
} else {
|
||||||
|
this.order = 'ASC';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a new filter order to the model and
|
||||||
|
* updates the cell arrow
|
||||||
|
*/
|
||||||
|
onToggleOrder() {
|
||||||
|
if (!this.field) return;
|
||||||
|
|
||||||
|
this.toggleOrder();
|
||||||
|
this.updateArrow();
|
||||||
|
|
||||||
|
this.table.applyFilter(this.field, this.order);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set cell class to asc/desc
|
||||||
|
*/
|
||||||
|
updateArrow() {
|
||||||
|
this.column.classList.remove('asc', 'desc');
|
||||||
|
|
||||||
|
if (this.order === 'DESC') {
|
||||||
|
this.column.classList.add('desc');
|
||||||
|
} else {
|
||||||
|
this.column.classList.add('asc');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Th.$inject = ['$element'];
|
||||||
|
|
||||||
|
ngModule.component('vnTh', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
transclude: true,
|
||||||
|
controller: Th,
|
||||||
|
bindings: {
|
||||||
|
defaultOrder: '@?'
|
||||||
|
},
|
||||||
|
require: {
|
||||||
|
table: '^^vnTable'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,60 @@
|
||||||
|
import './index.js';
|
||||||
|
import template from './index.html';
|
||||||
|
|
||||||
|
fdescribe('Component vnTh', () => {
|
||||||
|
let $componentController;
|
||||||
|
let controller;
|
||||||
|
let $element;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
angular.mock.module('client');
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(angular.mock.inject(_$componentController_ => {
|
||||||
|
$componentController = _$componentController_;
|
||||||
|
$element = angular.element(`<div>${template}</div>`);
|
||||||
|
controller = $componentController('vnTh', {$element: $element});
|
||||||
|
controller.table = {setOrder: () => {}};
|
||||||
|
controller.column = {
|
||||||
|
getAttribute: () => 'MyField',
|
||||||
|
classList: {
|
||||||
|
add: () => {},
|
||||||
|
remove: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('toggleOrder()', () => {
|
||||||
|
it(`should change the ordenation to DESC (descendant) if it was ASC (ascendant)`, () => {
|
||||||
|
controller.order = 'ASC';
|
||||||
|
controller.toggleOrder();
|
||||||
|
|
||||||
|
expect(controller.order).toEqual('DESC');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should change the ordenation to ASC (ascendant) if it wasnt ASC`, () => {
|
||||||
|
controller.order = 'DESC or any other value that might occur';
|
||||||
|
controller.toggleOrder();
|
||||||
|
|
||||||
|
expect(controller.order).toEqual('ASC');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should call the setOrder() function after changing a value`, () => {
|
||||||
|
spyOn(controller.table, 'setOrder');
|
||||||
|
controller.order = 'Change me!';
|
||||||
|
|
||||||
|
expect(controller.table.setOrder).toHaveBeenCalledWith('MyField', 'Change me!');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onInit()', () => {
|
||||||
|
it(`should define controllers order as per defaultOrder then call setOrder()`, () => {
|
||||||
|
controller.defaultOrder = 'DESC';
|
||||||
|
spyOn(controller.table, 'setOrder');
|
||||||
|
controller.$onInit();
|
||||||
|
|
||||||
|
expect(controller.order).toEqual('DESC');
|
||||||
|
expect(controller.table.setOrder).toHaveBeenCalledWith('MyField', 'DESC');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue