Skip to content

Instantly share code, notes, and snippets.

@tristanpendergrass
Created November 22, 2016 15:08
Show Gist options
  • Select an option

  • Save tristanpendergrass/9f52985172c0e705ad3ed97fcc0f09e4 to your computer and use it in GitHub Desktop.

Select an option

Save tristanpendergrass/9f52985172c0e705ad3ed97fcc0f09e4 to your computer and use it in GitHub Desktop.
// contentGrid.directive.js
import angular from 'angular';
import gridTpl from './contentGrid.tpl.html';
import angularServices from 'common/services/angular/index';
import coreServices from 'common/services/core/index';
import 'common/vaadinGrid.css!';
function contentGrid() {
return {
restrict: 'E',
template: `
<script src="https://cdn.vaadin.com/vaadin-core-elements/latest/webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="https://cdn.vaadin.com/vaadin-core-elements/latest/vaadin-grid/vaadin-grid.html">
<vaadin-grid
class="content-grid"
selection-mode="multi">
</vaadin-grid>
`,
controller: ($scope, $element) => new ContentGridCtrl($scope, angularServices, coreServices, $element),
controllerAs: 'contentGridCtrl',
bindToController: true,
scope: {
items: '=',
selected: '=',
rowClick: '&',
rowSelect: '&',
query: '='
}
};
}
const contentGridCtrlModule = angular.module('contentGrid.directive.js', [])
.directive('contentGrid', contentGrid);
export default contentGridCtrlModule;
//contentGrid.controller.js
export default class ExpVaadinGridCtrl {
constructor($scope, angularServices, coreServices, $element) {
const { $window, $timeout, $filter, $compile} = angularServices;
const { shiftKey, fileType, color } = coreServices;
this.$scope = $scope;
this.$element = $element;
this.$window = $window;
this.$timeout = $timeout;
this.$filter = $filter;
this.$compile = $compile;
this.shiftKey = shiftKey;
this.fileType = fileType;
this.color = color;
const grid = $element.find('vaadin-grid')[0];
this.grid = grid;
grid.rowClassGenerator = this.rowClassGenerator.bind(this);
grid.columns = [
{
name: 'name',
hidable: true,
hidingToggleText: 'Name',
renderer: this.nameRenderer.bind(this)
// resizable throws a funky error. uncomment to see what i mean.
//resizable: true
},
{
name: 'lastModified',
hidable: true,
hidingToggleText: 'Last Modified',
renderer: this.dateRenderer.bind(this)
},
{
name: 'created',
hidable: true,
hidingToggleText: 'Created',
renderer: this.dateRenderer.bind(this)
}
];
grid.then(() => {
//setTimeout(() => {
console.log('setting sortable to true');
grid.columns.forEach(column => column.sortable = true);
grid.sortOrder = [
{ column: 0, direction: 'asc' }
];
//}, 1000);
});
this.selectedIndices = [];
this.selectionListener = () => {
this.selectedIndices = grid.selection.selected();
$timeout();
};
grid.addEventListener('selected-items-changed', this.selectionListener);
grid.addEventListener('sort-order-changed', (e) => {
console.log('sort-order-changed');
const columnIndex = grid.sortOrder[0].column;
const columnName = grid.columns[columnIndex].name;
const direction = grid.sortOrder[0].direction;
this.query.order = direction === 'asc' ?
columnName :
'-' + columnName;
if (columnIndex === 0) {
// sorting by name, since name is an object rather than string have to do sort manually
e.preventDefault(); // prevents vaadin trying to sort
this.gridItems.sort((a, b) => {
return direction === 'asc' ?
a.name.name.localeCompare(b.name.name) :
b.name.name.localeCompare(a.name.name);
});
}
this.$timeout();
});
$scope.$watch(() => this.items, (newVal, oldVal) => {
this.gridItems = this.items.map((item) => {
return {
uuid: item.uuid,
name: { name: item.name, fileType: fileType.getName(item), fileIcon: fileType.getIcon(item), colorObj: fileType.getType(item).colorVal },
lastModified: item.lastModified,
created: item.created
};
});
}, true);
$scope.$watch(() => this.gridItems, (newVal, oldVal) => {
grid.items = this.gridItems;
if (grid.refreshItems) {
grid.refreshItems();
}
}, true);
$scope.$watch(() => this.selectedIndices, (newVal, oldVal) => {
this.selected = newVal.map(index => this.items[index]);
// support shift-click to select multiple rows at once
if (newVal.length === oldVal.length + 1) {
const selectedIndex = newVal[newVal.length - 1];
this.rowSelect({ item: this.items[selectedIndex] });
if (this.shiftKey.shifted && this.lastSelectedIndex !== undefined) {
// select all rows between lastSelectedIndex and selectedIndex
const lower = Math.min(selectedIndex, this.lastSelectedIndex);
const higher = Math.max(selectedIndex, this.lastSelectedIndex);
// we're about to cause the listener to fire a bunch of times unnecessarily, so remove it for the duration of the for-loop
// this was observed to save as much as 500 ms for a large shift-select operation
grid.removeEventListener('selected-items-changed', this.selectionListener);
for (let i = lower; i <= higher; i++) {
if (!newVal.includes(i)) {
grid.selection.select(i);
}
}
grid.addEventListener('selected-items-changed', this.selectionListener);
this.selectionListener(); // fire this listener once, manually, since it was disabled above
}
this.lastSelectedIndex = selectedIndex;
}
}, true);
}
dateRenderer(cell) {
cell.element.innerHTML = this.$filter('fromNow')(cell.data);
cell.element.classList.add('date-cell');
};
// alternate nameRenderer with much better performance but no md-icon
//nameRenderer(cell) {
//cell.element.innerHTML = cell.data.name;
//}
nameRenderer(cell) {
const scope = this.$scope.$new();
scope.fileName = cell.data.name;
scope.fileType = cell.data.fileType;
scope.fileIcon = cell.data.fileIcon;
scope.color = `rgb(${ this.color.map[cell.data.colorObj[0]][cell.data.colorObj[1] - 400].value.join(', ') })`;
const tpl = `
<span layout="row" layout-align="start center">
<md-icon
size="lg"
md-svg-icon="{{:: fileIcon }}"
alt="{{:: fileType }}"
ng-style="{ color: color }">
</md-icon>
<span>&nbsp;&nbsp;{{:: fileName.replace('.fabric.json', '') }}</span>
</span>`;
const el = this.$compile(tpl)(scope);
cell.element.innerHTML = '';
angular.element(cell.element).append(el);
scope.$digest();
}
rowClassGenerator(row) {
const data = row.data;
const index = row.index;
// don't use addEventListener because this rowClassGenerator function gets called many times per row
row.element.onclick = () => {
// we want clicking on rows to select them as well, so have to do it manually since the vaadin grid doesn't support it
if (this.selectedIndices.length === 0) {
this.grid.selection.select(index);
} else if (this.selectedIndices.length === 1 && this.selectedIndices.includes(index)) {
this.grid.selection.deselect(index);
} else if (this.selectedIndices.length === 1 && !this.selectedIndices.includes(index)) {
if (!this.shiftKey.shifted) {
this.grid.selection.clear();
}
this.grid.selection.select(index);
} else {
this.selectedIndices.includes(index) ?
this.grid.selection.deselect(index) :
this.grid.selection.select(index);
}
this.rowClick({
clickedItem: this.items.find(item => item.uuid === data.uuid)
});
this.$timeout();
}.bind(this);
return '';
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment