Created
September 3, 2016 08:38
-
-
Save PrimozRome/5250ef06f66e6a9ea70914fa50919955 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div id="datagrid-template" class="ui segment auto-overflow" :class="{ 'loading': loading }"> | |
<table id="{{ id }}" class="table table-hover table-condensed table-ultra-condensed datagrid no-margin"> | |
<thead> | |
<tr> | |
<th class="datagrid-toggle-column" v-if="allowSelection"> | |
<div class="checkbox check-success no-margin"> | |
<input type="checkbox" id="allrows" name="allrows" v-model="selectAll"> | |
<label for="allrows" class="no-margin"></label> | |
</div> | |
</th> | |
<th v-for="(index, column) in columns" | |
:style="{ width: getCellWidth(column) }" | |
class="{{ column.class }}" | |
v-if="column.visible" | |
nowrap=""> | |
<div class="control-group"> | |
<div class="datagrid-header control control-fill" @click="sortBy(column)"> | |
<span>{{ column.label }}</span> | |
<i class="fa" | |
:class="{ 'fa-angle-up': sortingDirection === 1, 'fa-angle-down': sortingDirection === -1 }" | |
v-show="sortingKey === column.key"> | |
</i> | |
</div> | |
</div> | |
</th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr v-for="(index, row) in data" | |
:class="{ 'selected': selectedRows.indexOf(row) !== -1 }" | |
@click="addRowToSelection(index)"> | |
<td class="datagrid-toggle-column" v-if="allowSelection"> | |
<div class="checkbox check-success"> | |
<input | |
type="checkbox" | |
id="{{ getControlId(groupName, index) }}" | |
name="{{ getControlId(groupName, index) }}" | |
:value="row" | |
v-model="selectedRows"> | |
<label for="{{ getControlId(groupName, index) }}" class="no-margin"></label> | |
</div> | |
</td> | |
<td v-for="(indexCol, column) in columns" | |
:class="[column.class, isCellSelected(index, indexCol) ? 'selected' : '', (row == editedRow && column == editedColumn) ? 'editing' : '', (row == editedRow && column == editedColumn && column.form.type == 'select') ? 'overflow-visible' : '']" | |
@dblclick="editCell(row, column)" | |
v-if="column.visible" | |
:nowrap="column.nowrap ? true : null"> | |
<partial :name="getCellTemplate(column)"></partial> | |
<formelement | |
v-if="allowEdit && row == editedRow" | |
:value.sync="editedCellValue" | |
:type="column.form.type" | |
:placeholder="column.form.placeholder" | |
:name="column.form.name" | |
:required="column.form.required" | |
:resource="column.form.resource" | |
:preload="column.form.preload" | |
:multiple="column.form.multiple" | |
:create="column.form.create" | |
:animation="column.form.animation" | |
:value-key="column.form.valueKey" | |
:text-key="column.form.textKey" | |
:add-label="column.form.addLabel" | |
:helper-message="column.form.helperMessage" | |
:no-results-label="column.form.noResultsLabel" | |
:class="['editCell', 'overflow-visible']" | |
v-cell-focus="row == editedRow && column == editedColumn" | |
@blur="doneEdit(row, column, index)" | |
@keydown.enter.stop="doneEdit(row, column, index)" | |
@keydown.esc.stop="cancelEdit(row, column, index)"> | |
</td> | |
</tr> | |
</tbody> | |
<tfoot> | |
<tr> | |
<th v-if="allowSelection"></th> | |
<th | |
v-for="(index, column) in columns" | |
class="{{ column.class }}" | |
v-if="column.visible"> | |
<div v-if="column.showSum"> | |
{{ calculateSum(column) }} | |
</div> | |
</th> | |
</tr> | |
</tfoot> | |
</table> | |
<div class="row no-margin"> | |
<div class="col-md-7 col-sm-12 p-t-10 p-l-20 text-center-xs text-center-sm"> | |
<p class="normal-text"> | |
<span>Showing | |
<strong>{{ showingFrom }} to {{ showingTo }}</strong> of {{ paginationData.total }} entries</span> | |
<span v-if="selectedRows.length"> | </span> | |
<span v-if="selectedRows.length"> | |
<strong>{{ selectedRows.length }}</strong> rows selected | |
<a @click.prevent="resetSelection()" href="#">remove selection</a> | |
</span> | |
<span v-if="dataFilter"> | </span> | |
<span v-if="dataFilter"> | |
Filtering on <strong>{{ dataFilter }}</strong> | |
<a @click.prevent="resetFilter()" href="#">remove filter</a> | |
</span> | |
<span v-if="groupingColumn"> | </span> | |
<span v-if="groupingColumn"> | |
Grouping on <strong>{{ groupingColumn.name }}</strong> | |
<a @click.prevent="resetGrouping()" href="#">remove grouping</a> | |
</span> | |
</p> | |
</div> | |
<div class="col-md-5 col-sm-12 p-t-10 p-r-20 p-b-10 text-center-sm text-center-xs text-right-md text-right-lg"> | |
<pagination :pagination.sync="paginationData"></pagination> | |
</div> | |
</div> | |
</template> | |
<script> | |
import Vue from 'vue' | |
import pagination from './pagination' | |
import api from '../services/api' | |
import helpers from '../services/helpers' | |
import { groupBy, formatDate, formatMoney } from '../extensions/filters' | |
import formelement from './form/types/formelement' | |
Vue.filter('groupBy', groupBy) | |
Vue.filter('formatDate', formatDate) | |
Vue.filter('formatMoney', formatMoney) | |
Vue.partial('defaultGridCell', '<span class="cellContent">{{ formatData(column, getCellValue(row, column.key)) }}</span>') | |
Vue.partial('editableGridCell', '<input type="text" class="form-control" v-model="getCellValue(row, column.key)" lazy />') | |
Vue.partial('linkedGridCell', '<a @click.stop="" class="cellContent" v-link="{ path: [\'\', resource, getCellValue(row, \'id\')].join(\'/\') }"><partial name="defaultGridCell"></partial></a>') | |
export default { | |
components: { | |
pagination, | |
formelement | |
}, | |
props: { | |
id: { | |
type: String, | |
required: true | |
}, | |
resource: { | |
type: String, | |
required: true | |
}, | |
includes: { | |
type: String, | |
required: false | |
}, | |
columns: { | |
type: Array, | |
required: true | |
}, | |
sortKey: { | |
type: String, | |
required: false | |
}, | |
sortDirection: { | |
type: Number, | |
required: false, | |
default: 1 | |
}, | |
cellTemplate: { | |
type: String, | |
required: false, | |
default: 'defaultGridCell' | |
}, | |
allowSelection: { | |
type: Boolean, | |
required: false, | |
default: false | |
}, | |
allowEdit: { | |
type: Boolean, | |
required: false, | |
default: false | |
}, | |
showDefaultOptions: { | |
type: Boolean, | |
required: false, | |
default: true | |
}, | |
showAdvancedOptions: { | |
type: Boolean, | |
required: false, | |
default: false | |
} | |
}, | |
computed: { | |
sortDirectionString: function () { | |
return this.sortingDirection === 1 ? 'ASC' : 'DESC' | |
}, | |
columnSpan: function () { | |
return this.allowSelection ? this.columns.length + 1 : this.columns.length | |
}, | |
showOptions: function () { | |
return this.showDefaultOptions || this.showAdvancedOptions | |
}, | |
showFooter: function () { | |
return this.dataFilter || this.groupingColumn || this.selectedRows.length > 0 | |
}, | |
showingFrom: function () { | |
return this.paginationData.current_page * this.paginationData.per_page - this.paginationData.per_page + 1 | |
}, | |
showingTo: function () { | |
if (this.paginationData.current_page === this.paginationData.total_pages) { | |
return this.paginationData.total | |
} else { | |
return this.paginationData.current_page * this.paginationData.per_page | |
} | |
} | |
}, | |
data: function () { | |
return { | |
data: [], | |
sortingKey: this.sortKey || this.columns[0].key, | |
sortingDirection: this.sortDirection, | |
groupingColumn: '', | |
dataFilter: null, | |
selectedRows: [], | |
selectAll: false, | |
paginationData: { | |
per_page: 25, | |
current_page: 1, | |
count: 0, | |
total_pages: 0, | |
total: 0 | |
}, | |
selectedRow: null, | |
selectedCol: null, | |
editedCell: null, | |
editedRow: null, | |
editedColumn: null, | |
loading: false, | |
editedCellValue: null | |
} | |
}, | |
vuex: { | |
getters: { | |
searchQuery: state => state.globals.searchQuery, | |
filterQuery: state => state.globals.filterQuery | |
} | |
}, | |
created: function () { | |
// console.log(this.columns) | |
window.addEventListener('keydown', this.keyboardHandler) | |
this.fetchData() | |
}, | |
beforeDestroy: function () { | |
window.removeEventListener('keydown', this.keyboardHandler) | |
}, | |
ready: function () { | |
}, | |
methods: { | |
testing: function (data) { | |
console.log('from testing') | |
console.log(data) | |
}, | |
getCellValue: function (row, key) { | |
console.time('Vue') | |
return key.split('.').reduce((a, b) => (a !== undefined) ? a[b] : a, row) | |
}, | |
isCellSelected: function (rowIndex, colIndex) { | |
if (this.selectedRow === rowIndex && this.selectedCol === colIndex) { | |
return true | |
} else { | |
return false | |
} | |
}, | |
editCell: function (row, column) { | |
if (!this.allowEdit) { | |
return false | |
} | |
let cell | |
if (column !== undefined && row !== undefined) { | |
cell = this.getCellValue(row, column.key) | |
} | |
this.editedCellValue = cell | |
this.editedRow = row | |
this.editedColumn = column | |
}, | |
doneEdit: function (row, column, index) { | |
if (!this.editedRow && !this.editedColumn) { | |
return | |
} | |
this.editedRow = null | |
this.editedColumn = null | |
if (this.editedCellValue !== this.getCellValue(row, column.key)) { | |
this.loading = true | |
let params | |
if (column.hasOwnProperty('filterKey')) { | |
params = helpers.apiUpdateCell.convertData({}, column.filterKey, this.editedCellValue) | |
} else { | |
params = helpers.apiUpdateCell.convertData({}, column.key, this.editedCellValue) | |
} | |
if (column.hasOwnProperty('id')) { | |
params = helpers.apiUpdateCell.convertData(params, column.id, this.getCellValue(row, column.id)) | |
} | |
api.updateData(this.resource + '/' + row.id, params).then((response) => { | |
helpers.apiUpdateCell.putData(column.key, this.editedCellValue, this.data[index]) | |
this.loading = false | |
}, (err) => { | |
// handle error | |
console.log(err) | |
this.loading = false | |
}) | |
} | |
/* | |
todo.title = todo.title.trim(); | |
if (!todo.title) { | |
this.removeTodo(todo); | |
} | |
*/ | |
}, | |
cancelEdit: function (row, column, index) { | |
this.editedRow = null | |
this.editedColumn = null | |
// todo.title = this.beforeEditCache; | |
}, | |
keyboardHandler: function (e) { | |
var inputs = ['input', 'select', 'button', 'textarea'] | |
var activeElement = document.activeElement | |
if (activeElement && inputs.indexOf(activeElement.tagName.toLowerCase()) !== -1) { | |
return false | |
} | |
switch (e.keyCode) { | |
case 13: | |
e.preventDefault() | |
e.stopPropagation() | |
this.editCell(this.data[this.selectedRow], this.columns[this.selectedCol]) | |
break | |
case 32: | |
if (this.selectedRow) { | |
e.preventDefault() | |
this.addRowToSelection(this.selectedRow) | |
} | |
break | |
case 37: | |
e.preventDefault() | |
this.keyLeftPressed() | |
break | |
case 38: | |
e.preventDefault() | |
this.keyUpPressed() | |
break | |
case 39: | |
e.preventDefault() | |
this.keyRightPressed() | |
break | |
case 40: | |
e.preventDefault() | |
this.keyDownPressed() | |
break | |
} | |
}, | |
keyUpPressed: function () { | |
if (this.selectedRow > 0) { | |
this.selectedRow-- | |
} | |
}, | |
keyDownPressed: function () { | |
if (this.selectedRow === null) { | |
this.selectedRow = 0 | |
this.selectedCol = 0 | |
} else { | |
if (this.selectedRow < this.paginationData.count - 1) { | |
this.selectedRow++ | |
} | |
} | |
}, | |
keyRightPressed: function () { | |
if (this.selectedCol === null) { | |
this.selectedCol = 0 | |
this.selectedRow = 0 | |
} else { | |
if (this.selectedCol < this.columns.length - 1) { | |
this.selectedCol++ | |
} else if (this.selectedCol === this.columns.length - 1) { | |
if (this.selectedRow < this.paginationData.count - 1) { | |
this.selectedCol = 0 | |
this.selectedRow++ | |
} | |
} | |
} | |
}, | |
keyLeftPressed: function () { | |
if (this.selectedCol > 0) { | |
this.selectedCol-- | |
} else if (this.selectedCol === 0) { | |
if (this.selectedRow > 0) { | |
this.selectedCol = this.columns.length - 1 | |
this.selectedRow-- | |
} | |
} | |
}, | |
addRowToSelection: function (index) { | |
if (this.selectedRows.indexOf(this.data[index]) === -1) { | |
this.selectedRows.push(this.data[index]) | |
} else { | |
this.selectedRows.splice(this.selectedRows.indexOf(this.data[index]), 1) | |
} | |
}, | |
fetchData: function () { | |
this.loading = true | |
var params = { | |
page: this.paginationData.current_page, | |
per_page: this.paginationData.per_page, | |
include: this.includes, | |
sortby: this.sortingKey, | |
sortdir: this.sortDirectionString, | |
q: this.searchQuery, | |
filters: this.getFilterQueryData() | |
} | |
api.fetchData(this.resource, params).then((response) => { | |
this.data = response.data.data | |
this.paginationData = response.data.meta.pagination | |
this.loading = false | |
}, (err) => { | |
// handle error | |
console.log(err) | |
this.loading = false | |
}) | |
}, | |
getFilterQueryData: function () { | |
var result = {} | |
this.filterQuery.forEach(function (value) { | |
result[value.filterKey] = value.value | |
}) | |
for (var i = 0; i < this.filterQuery.length; i++) { | |
result[this.filterQuery[i].filterKey] = this.filterQuery[i].form.value | |
} | |
return result | |
}, | |
getCellTemplate: function (column) { | |
// return this.allowEdit ? 'editableGridCell' : (column.template || this.cellTemplate) | |
// console.log([column, column.template, this.cellTemplate]) | |
return false ? 'editableGridCell' : (column.template || this.cellTemplate) | |
}, | |
getCellWidth: function (column) { | |
if (!column.width) { | |
return | |
} | |
return column.width + (isNaN(column.width) ? '' : '%') | |
}, | |
getControlId: function (groupName, index, suffix) { | |
return groupName + '-' + index + (suffix ? '-' + suffix : '') | |
}, | |
sortBy: function (column) { | |
this.sortingKey = column.key | |
this.sortingDirection *= -1 | |
this.fetchData() | |
return | |
}, | |
groupBy: function (column) { | |
this.groupingColumn = column | |
}, | |
resetFilter: function () { | |
this.dataFilter = null | |
}, | |
resetGrouping: function () { | |
this.groupingColumn = null | |
}, | |
resetSelection: function () { | |
this.selectedRows = [] | |
this.selectAll = false | |
}, | |
formatData: function (column, value) { | |
if (column.hasOwnProperty('filter')) { | |
var filter = Vue.filter(column.filter.name) | |
var args = [].concat(value, column.filter.args) | |
return filter.apply(this, args) | |
} | |
return value | |
}, | |
calculateSum: function (column) { | |
var sum = this.data.reduce(function (a, b) { | |
return a + b[column.key] | |
}, 0) | |
if (column.hasOwnProperty('filter')) { | |
var filter = Vue.filter(column.filter.name) | |
return filter.apply(this, [sum]) | |
} | |
return sum | |
} | |
}, | |
events: { | |
'refreshData': function () { | |
this.selectedRow = null | |
this.selectedCol = null | |
this.resetSelection() | |
this.fetchData() | |
}, | |
'paginationNext': function () { | |
console.time('Vue') | |
} | |
}, | |
watch: { | |
'selectAll': function (value) { | |
this.selectedRows = value ? [].concat(this.data) : [] | |
}, | |
'searchQuery': function (value) { | |
this.paginationData.current_page = 1 | |
this.fetchData() | |
}, | |
'filterQuery': function (value) { | |
this.paginationData.current_page = 1 | |
this.fetchData() | |
} | |
}, | |
directives: { | |
'cell-focus': function (value) { | |
if (!value) { | |
return | |
} | |
var el = this.el | |
Vue.nextTick(function () { | |
el.focus() | |
}) | |
} | |
} | |
} | |
</script> | |
<style lang="stylus"> | |
.overflow-visible | |
overflow: visible !important; | |
.datagrid-toggle-column | |
width: 58px | |
.table thead tr th | |
padding-top: 10px | |
padding-bottom: 10px | |
&.datagrid-toggle-column .checkbox | |
height: 18px | |
width: 19px | |
.editCell | |
display: none | |
background-color: #f5f5f5 | |
border: 1px solid #eee | |
width: 100% | |
height: 33px | |
.editing | |
.cellContent | |
display: none | |
.editCell | |
display: block | |
padding: 5px 8px | |
.ui.segment | |
position: relative | |
.ui.segment:first-child | |
margin-top: 0em | |
.ui.segment:last-child | |
margin-bottom: 0em | |
.ui.loading.segment | |
position: relative | |
cursor: default | |
point-events: none | |
text-shadow: none !important | |
color: transparent !important | |
-webkit-transition: all 0s linear | |
transition: all 0s linear | |
.ui.loading.segment:before | |
position: absolute | |
content: '' | |
top: 0% | |
left: 0% | |
background: rgba(255, 255, 255, 0.8) | |
width: 100% | |
height: 100% | |
border-radius: 0.28571429rem | |
z-index: 100 | |
.ui.loading.segment:after | |
position: absolute | |
content: '' | |
top: 50% | |
left: 50% | |
margin: -1.5em 0em 0em -1.5em | |
width: 3em | |
height: 3em | |
-webkit-animation: segment-spin 0.6s linear | |
animation: segment-spin 0.6s linear | |
-webkit-animation-iteration-count: infinite | |
animation-iteration-count: infinite | |
border-radius: 500rem | |
border-color: #767676 rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) | |
border-style: solid | |
border-width: 0.2em | |
box-shadow: 0px 0px 0px 1px transparent | |
visibility: visible | |
z-index: 101 | |
@-webkit-keyframes segment-spin | |
from | |
-webkit-transform: rotate(0deg) | |
transform: rotate(0deg) | |
to | |
-webkit-transform: rotate(360deg) | |
transform: rotate(360deg) | |
@keyframes segment-spin | |
from | |
-webkit-transform: rotate(0deg) | |
transform: rotate(0deg) | |
to | |
-webkit-transform: rotate(360deg) | |
transform: rotate(360deg) | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment