New grid component vnTable. #139. CR: Juan & Javi

This commit is contained in:
Joan Sanchez 2018-06-28 14:43:59 +02:00
parent fb07f5e860
commit 45b3a5ce02
7 changed files with 320 additions and 0 deletions

View File

@ -37,3 +37,5 @@ import './paging/paging';
import './auto-paging/auto-paging';
import './pagination/pagination';
import './searchbar/searchbar';
import './table';
import './th';

View File

@ -0,0 +1 @@
<div></div>

View File

@ -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: '<?'
}
});

View File

@ -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);
}
}
}
}
}

View File

@ -0,0 +1,3 @@
<div translate ng-click="$ctrl.onToggleOrder()">
<ng-transclude></ng-transclude>
</div>

View File

@ -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'
}
});

View File

@ -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');
});
});
});