mirror of
https://github.com/frappe/books.git
synced 2024-11-10 07:40:55 +00:00
Added keyboard navigation and delete checked rows features in table (#97)
* Added keyboard navigation and delete checked rows in invoice-table * cleanup
This commit is contained in:
parent
d1d1410db7
commit
1827d62464
@ -4,7 +4,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" width="60">
|
<th scope="col" width="60">
|
||||||
<input class="mr-2" type="checkbox">
|
<input class="mr-2" type="checkbox" @change="toggleCheckAll">
|
||||||
<span>#</span>
|
<span>#</span>
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" v-for="column in columns" :key="column.fieldname">
|
<th scope="col" v-for="column in columns" :key="column.fieldname">
|
||||||
@ -15,35 +15,51 @@
|
|||||||
<tbody v-if="rows.length">
|
<tbody v-if="rows.length">
|
||||||
<tr v-for="(row, i) in rows" :key="i">
|
<tr v-for="(row, i) in rows" :key="i">
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
<input class="mr-2" type="checkbox" @change="e => onCheck(e, i)">
|
<input
|
||||||
|
class="mr-2"
|
||||||
|
type="checkbox"
|
||||||
|
:checked="checkedRows.includes(i)"
|
||||||
|
@change="e => onCheck(e, i)"
|
||||||
|
>
|
||||||
<span>{{ i + 1 }}</span>
|
<span>{{ i + 1 }}</span>
|
||||||
</th>
|
</th>
|
||||||
<td v-for="column in columns" :key="column.fieldname"
|
<td v-for="column in columns" :key="column.fieldname"
|
||||||
|
tabindex="1"
|
||||||
|
:ref="column.fieldname + i"
|
||||||
|
@click="activateFocus(i, column.fieldname)"
|
||||||
@dblclick="activateEditing(i, 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)"
|
@keydown.enter="enterPressOnCell(i, column.fieldname)"
|
||||||
|
@keydown.shift.tab="focusPreviousCell(i, column.fieldname)"
|
||||||
|
@keydown.tab="focusNextCell(i, column.fieldname)"
|
||||||
|
@keydown.left="focusPreviousCell(i, column.fieldname)"
|
||||||
|
@keydown.right="focusNextCell(i, column.fieldname)"
|
||||||
|
@keydown.up="focusAboveCell(i, column.fieldname)"
|
||||||
|
@keydown.down="focusBelowCell(i, column.fieldname)"
|
||||||
|
@keydown.esc="escOnCell(i, column.fieldname)"
|
||||||
>
|
>
|
||||||
<frappe-control
|
<div class="table-cell" :class="{'active': isFocused(i, column.fieldname)}">
|
||||||
v-if="isEditing(i, column.fieldname)"
|
<frappe-control
|
||||||
:docfield="getDocfield(column.fieldname)"
|
v-if="isEditing(i, column.fieldname)"
|
||||||
:value="row[column.fieldname]"
|
:docfield="getDocfield(column.fieldname)"
|
||||||
:onlyInput="true"
|
:value="row[column.fieldname]"
|
||||||
:doc="row"
|
:onlyInput="true"
|
||||||
:autofocus="true"
|
:doc="row"
|
||||||
@change="onCellChange(i, column.fieldname, $event)"
|
:autofocus="true"
|
||||||
/>
|
@change="onCellChange(i, column.fieldname, $event)"
|
||||||
<span v-else>
|
/>
|
||||||
{{ row[column.fieldname] }}
|
<span v-else>
|
||||||
</span>
|
{{ row[column.fieldname] || ' ' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
<tbody v-else>
|
<tbody v-else>
|
||||||
<tr>
|
<tr>
|
||||||
<td :colspan="columns.length + 1" class="text-center">
|
<td :colspan="columns.length + 1" class="text-center">
|
||||||
No Data
|
<div class="table-cell">
|
||||||
|
No Data
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -56,7 +72,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// import ModelTable from '../ModelTable';
|
|
||||||
import Base from './Base';
|
import Base from './Base';
|
||||||
import Observable from 'frappejs/utils/observable';
|
import Observable from 'frappejs/utils/observable';
|
||||||
|
|
||||||
@ -66,30 +81,79 @@ export default {
|
|||||||
return {
|
return {
|
||||||
columns: [],
|
columns: [],
|
||||||
checkedRows: [],
|
checkedRows: [],
|
||||||
currentlyEditing: {}
|
currentlyEditing: {},
|
||||||
}
|
currentlyFocused: {}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.columns = this.getColumns();
|
this.columns = this.getColumns();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
enterPressOnCell(i, fieldname) {
|
escOnCell(i, fieldname) {
|
||||||
this.deactivateEditing();
|
this.deactivateEditing();
|
||||||
|
this.activateFocus(i, fieldname);
|
||||||
},
|
},
|
||||||
shiftTabPressOnCell(i, fieldname) {
|
enterPressOnCell(i, fieldname) {
|
||||||
if (this.isEditing(i, fieldname)) {
|
if (this.isEditing(i, fieldname)) {
|
||||||
let pos = this.columns.map(c => c.fieldname).indexOf(fieldname);
|
this.deactivateEditing();
|
||||||
|
this.activateFocus(i, fieldname);
|
||||||
|
} else {
|
||||||
|
this.activateEditing(i, fieldname);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
focusPreviousCell(i, fieldname) {
|
||||||
|
if (this.isFocused(i, fieldname) && !this.isEditing(i, fieldname)) {
|
||||||
|
let pos = this._getColumnIndex(fieldname);
|
||||||
pos = pos - 1;
|
pos = pos - 1;
|
||||||
this.activateEditing(i, this.columns[pos].fieldname);
|
if (pos < 0) {
|
||||||
|
i -= 1;
|
||||||
|
pos = this.columns.length - 1;
|
||||||
|
}
|
||||||
|
if (i < 0) {
|
||||||
|
i = 0;
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
this.activateFocus(i, this.columns[pos].fieldname);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tabPressOnCell(i, fieldname) {
|
focusNextCell(i, fieldname) {
|
||||||
if (this.isEditing(i, fieldname)) {
|
if (this.isFocused(i, fieldname) && !this.isEditing(i, fieldname)) {
|
||||||
let pos = this.columns.map(c => c.fieldname).indexOf(fieldname);
|
let pos = this._getColumnIndex(fieldname);
|
||||||
pos = pos + 1;
|
pos = pos + 1;
|
||||||
this.activateEditing(i, this.columns[pos].fieldname);
|
if (pos > this.columns.length - 1) {
|
||||||
|
i += 1;
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
if (i > this.rows.length - 1) {
|
||||||
|
i = this.rows.length - 1;
|
||||||
|
pos = this.columns.length - 1;
|
||||||
|
}
|
||||||
|
this.activateFocus(i, this.columns[pos].fieldname);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
focusAboveCell(i, fieldname) {
|
||||||
|
if (this.isFocused(i, fieldname) && !this.isEditing(i, fieldname)) {
|
||||||
|
let pos = this._getColumnIndex(fieldname);
|
||||||
|
i -= 1;
|
||||||
|
if (i < 0) {
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
this.activateFocus(i, this.columns[pos].fieldname);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
focusBelowCell(i, fieldname) {
|
||||||
|
if (this.isFocused(i, fieldname) && !this.isEditing(i, fieldname)) {
|
||||||
|
let pos = this._getColumnIndex(fieldname);
|
||||||
|
i += 1;
|
||||||
|
if (i > this.rows.length - 1) {
|
||||||
|
i = this.rows.length - 1;
|
||||||
|
}
|
||||||
|
this.activateFocus(i, this.columns[pos].fieldname);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_getColumnIndex(fieldname) {
|
||||||
|
return this.columns.map(c => c.fieldname).indexOf(fieldname);
|
||||||
|
},
|
||||||
onOutsideClick(e) {
|
onOutsideClick(e) {
|
||||||
this.deactivateEditing();
|
this.deactivateEditing();
|
||||||
},
|
},
|
||||||
@ -100,6 +164,13 @@ export default {
|
|||||||
this.checkedRows = this.checkedRows.filter(i => i !== idx);
|
this.checkedRows = this.checkedRows.filter(i => i !== idx);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
toggleCheckAll() {
|
||||||
|
if (this.checkedRows.length === this.rows.length) {
|
||||||
|
this.checkedRows = [];
|
||||||
|
} else {
|
||||||
|
this.checkedRows = this.rows.map((row, i) => i);
|
||||||
|
}
|
||||||
|
},
|
||||||
getDocfield(fieldname) {
|
getDocfield(fieldname) {
|
||||||
return this.meta.getField(fieldname);
|
return this.meta.getField(fieldname);
|
||||||
},
|
},
|
||||||
@ -107,8 +178,16 @@ export default {
|
|||||||
if (this.disabled) {
|
if (this.disabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this.currentlyEditing.index === i &&
|
return (
|
||||||
this.currentlyEditing.fieldname === fieldname;
|
this.currentlyEditing.index === i &&
|
||||||
|
this.currentlyEditing.fieldname === fieldname
|
||||||
|
);
|
||||||
|
},
|
||||||
|
isFocused(i, fieldname) {
|
||||||
|
return (
|
||||||
|
this.currentlyFocused.index === i &&
|
||||||
|
this.currentlyFocused.fieldname === fieldname
|
||||||
|
);
|
||||||
},
|
},
|
||||||
activateEditing(i, fieldname) {
|
activateEditing(i, fieldname) {
|
||||||
const docfield = this.columns.find(c => c.fieldname === fieldname);
|
const docfield = this.columns.find(c => c.fieldname === fieldname);
|
||||||
@ -120,12 +199,27 @@ export default {
|
|||||||
fieldname
|
fieldname
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
activateFocus(i, fieldname) {
|
||||||
|
this.deactivateEditing();
|
||||||
|
const docfield = this.columns.find(c => c.fieldname === fieldname);
|
||||||
|
this.currentlyFocused = {
|
||||||
|
index: i,
|
||||||
|
fieldname
|
||||||
|
};
|
||||||
|
this.$refs[fieldname + i][0].focus();
|
||||||
|
},
|
||||||
deactivateEditing(i, _fieldname) {
|
deactivateEditing(i, _fieldname) {
|
||||||
const { index, fieldname } = this.currentlyEditing;
|
const { index, fieldname } = this.currentlyEditing;
|
||||||
if (!(index === i && fieldname === _fieldname)) {
|
if (!(index === i && fieldname === _fieldname)) {
|
||||||
this.currentlyEditing = {};
|
this.currentlyEditing = {};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
deactivateFocus(i, _fieldname) {
|
||||||
|
const { index, fieldname } = this.currentlyFocused;
|
||||||
|
if (!(index === i && fieldname === _fieldname)) {
|
||||||
|
this.currentlyFocused = {};
|
||||||
|
}
|
||||||
|
},
|
||||||
addRow() {
|
addRow() {
|
||||||
const rows = this.rows.slice();
|
const rows = this.rows.slice();
|
||||||
const newRow = {
|
const newRow = {
|
||||||
@ -154,7 +248,7 @@ export default {
|
|||||||
// make a copy
|
// make a copy
|
||||||
let rows = this.rows.slice();
|
let rows = this.rows.slice();
|
||||||
rows = rows.filter((row, i) => {
|
rows = rows.filter((row, i) => {
|
||||||
return !indices.includes(i)
|
return !indices.includes(i);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.emitChange(rows);
|
this.emitChange(rows);
|
||||||
@ -190,17 +284,34 @@ export default {
|
|||||||
return this.value;
|
return this.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.table .form-control {
|
.table-cell .form-control {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table [data-fieldtype="Link"] .input-group-append {
|
.table td {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-cell {
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table [data-fieldtype='Link'] .input-group-append {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-cell.active {
|
||||||
|
border: 1px solid var(--blue);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user