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