2
0
mirror of https://github.com/frappe/books.git synced 2024-12-23 11:29:03 +00:00

Replace Datatable with Vue component

This commit is contained in:
Faris Ansari 2018-09-28 18:40:02 +05:30
parent 81f48eab83
commit db767edb32
3 changed files with 201 additions and 14 deletions

View File

@ -13,7 +13,13 @@ export default {
type: Boolean,
default: false
},
disabled: Boolean
disabled: Boolean,
autofocus: Boolean
},
mounted() {
if (this.autofocus) {
this.$refs.input.focus();
}
},
computed: {
id() {
@ -33,9 +39,10 @@ export default {
methods: {
getWrapperElement(h) {
return h('div', {
class: ['form-group', ...this.wrapperClass],
class: ['form-group', this.onlyInput ? 'mb-0' : '', ...this.wrapperClass],
attrs: {
'data-fieldname': this.docfield.fieldname
'data-fieldname': this.docfield.fieldname,
'data-fieldtype': this.docfield.fieldtype
}
}, this.getChildrenElement(h));
},

View File

@ -5,6 +5,7 @@
:value="value"
:onlyInput="onlyInput"
:disabled="isDisabled"
:autofocus="autofocus"
@change="$emit('change', $event)"
/>
</template>
@ -28,7 +29,7 @@ import Text from './Text';
import Time from './Time';
export default {
props: ['docfield', 'value', 'onlyInput', 'doc'],
props: ['docfield', 'value', 'onlyInput', 'doc', 'autofocus'],
computed: {
component() {
if (this.docfield.template) {

View File

@ -1,27 +1,206 @@
<template>
<div class="form-group">
<model-table
:doctype="docfield.childtype"
:rows="value"
:disabled="disabled"
@update:rows="emitChange"
/>
<div class="form-group" v-on-outside-click="onOutsideClick">
<table class="table table-bordered" style="table-layout: fixed">
<thead>
<tr>
<th scope="col" width="60">
<input class="mr-2" type="checkbox">
<span>#</span>
</th>
<th scope="col" v-for="column in columns" :key="column.fieldname">
{{ column.label }}
</th>
</tr>
</thead>
<tbody v-if="rows.length">
<tr v-for="(row, i) in rows" :key="i">
<th scope="row">
<input class="mr-2" type="checkbox" @change="e => onCheck(e, i)">
<span>{{ i + 1 }}</span>
</th>
<td v-for="column in columns" :key="column.fieldname"
@dblclick="activateEditing(i, column.fieldname)"
@click="deactivateEditing(i, column.fieldname)"
@keydown.shift.tab="shiftTabPressOnCell(i, column.fieldname)"
@keydown.tab="tabPressOnCell(i, column.fieldname)"
@keydown.enter="enterPressOnCell(i, column.fieldname)"
>
<frappe-control
v-if="isEditing(i, column.fieldname)"
:docfield="getDocfield(column.fieldname)"
:value="row[column.fieldname]"
:onlyInput="true"
:doc="row"
:autofocus="true"
@change="onCellChange(i, column.fieldname, $event)"
/>
<span v-else>
{{ row[column.fieldname] }}
</span>
</td>
</tr>
</tbody>
<tbody v-else>
<tr>
<td :colspan="columns.length + 1" class="text-center">
No Data
</td>
</tr>
</tbody>
</table>
<div class="table-actions" v-if="!disabled">
<f-button danger @click="removeCheckedRows" v-if="checkedRows.length">Remove</f-button>
<f-button light @click="addRow" v-if="!checkedRows.length">Add Row</f-button>
</div>
</div>
</template>
<script>
import ModelTable from '../ModelTable';
// import ModelTable from '../ModelTable';
import Base from './Base';
import Observable from 'frappejs/utils/observable';
export default {
extends: Base,
components: {
ModelTable
data() {
return {
columns: [],
checkedRows: [],
currentlyEditing: {}
}
},
mounted() {
this.columns = this.getColumns();
},
methods: {
enterPressOnCell(i, fieldname) {
this.deactivateEditing();
},
shiftTabPressOnCell(i, fieldname) {
if (this.isEditing(i, fieldname)) {
let pos = this.columns.map(c => c.fieldname).indexOf(fieldname);
pos = pos - 1;
this.activateEditing(i, this.columns[pos].fieldname);
}
},
tabPressOnCell(i, fieldname) {
if (this.isEditing(i, fieldname)) {
let pos = this.columns.map(c => c.fieldname).indexOf(fieldname);
pos = pos + 1;
this.activateEditing(i, this.columns[pos].fieldname);
}
},
onOutsideClick(e) {
this.deactivateEditing();
},
onCheck(e, idx) {
if (e.target.checked) {
this.checkedRows.push(idx);
} else {
this.checkedRows = this.checkedRows.filter(i => i !== idx);
}
},
getDocfield(fieldname) {
return this.meta.getField(fieldname);
},
isEditing(i, fieldname) {
if (this.disabled) {
return false;
}
return this.currentlyEditing.index === i &&
this.currentlyEditing.fieldname === fieldname;
},
activateEditing(i, fieldname) {
const docfield = this.columns.find(c => c.fieldname === fieldname);
if (docfield.readOnly || docfield.disabled) {
return;
}
this.currentlyEditing = {
index: i,
fieldname
};
},
deactivateEditing(i, _fieldname) {
const { index, fieldname } = this.currentlyEditing;
if (!(index === i && fieldname === _fieldname)) {
this.currentlyEditing = {};
}
},
addRow() {
const rows = this.rows.slice();
const newRow = {
idx: rows.length
};
for (let column of this.columns) {
newRow[column.fieldname] = null;
}
rows.push(newRow);
this.emitChange(rows, newRow);
},
removeCheckedRows() {
this.removeRows(this.checkedRows);
this.checkedRows = [];
},
removeRows(indices) {
// convert to array
if (!Array.isArray(indices)) {
indices = [indices];
}
// convert string to number
indices = indices.map(i => parseInt(i, 10));
// make a copy
let rows = this.rows.slice();
rows = rows.filter((row, i) => {
return !indices.includes(i)
});
this.emitChange(rows);
},
getRows() {
return (this.docs || []).map((row, i) => {
const doc = new Observable();
doc.set('idx', i);
for (let fieldname in row) {
doc.set(fieldname, row[fieldname]);
}
return doc;
});
},
getColumns() {
const fieldsToShow = this.meta.fields.filter(df => !df.hidden);
return fieldsToShow;
},
onCellChange(idx, fieldname, value) {
const rows = this.value.slice();
rows[idx][fieldname] = value;
this.emitChange(rows, rows[idx]);
},
emitChange(rows, rowDoc) {
this.$emit('change', rows, rowDoc);
}
},
computed: {
meta() {
return frappe.getMeta(this.docfield.childtype);
},
rows() {
return this.value;
}
}
}
</script>
<style>
.table .form-control {
padding: 0;
border: none;
box-shadow: none;
outline: none;
}
.table [data-fieldtype="Link"] .input-group-append {
display: none;
}
</style>