2019-08-01 17:22:58 +05:30
|
|
|
<template>
|
2019-08-05 15:03:05 +05:30
|
|
|
<div v-on-outside-click="clearInput">
|
2019-08-01 17:22:58 +05:30
|
|
|
<div class="input-group">
|
|
|
|
<div class="input-group-prepend">
|
2019-08-01 18:30:52 +05:30
|
|
|
<span class="input-group-text pt-1">
|
2019-08-01 17:22:58 +05:30
|
|
|
<feather-icon name="search" style="color: #212529 !important;"></feather-icon>
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
<input
|
|
|
|
type="search"
|
|
|
|
class="form-control"
|
2019-08-20 14:27:27 +05:30
|
|
|
@click="focus(0)"
|
|
|
|
@keydown.down="navigate('down')"
|
|
|
|
@keydown.up="navigate('up')"
|
2019-08-01 17:22:58 +05:30
|
|
|
placeholder="Search..."
|
|
|
|
autocomplete="off"
|
|
|
|
spellcheck="false"
|
|
|
|
v-model="inputValue"
|
2019-08-20 14:27:27 +05:30
|
|
|
@keydown.enter="searchOrSelect"
|
2019-08-01 17:22:58 +05:30
|
|
|
/>
|
|
|
|
</div>
|
2019-08-20 14:27:27 +05:30
|
|
|
<div v-if="inputValue" class="search-list position-absolute shadow-sm" style="width: 98%">
|
2019-08-01 17:22:58 +05:30
|
|
|
<list-row
|
2019-08-20 14:27:27 +05:30
|
|
|
v-for="(doc, i) in suggestion"
|
|
|
|
:key="i+1"
|
|
|
|
:ref="i+1"
|
|
|
|
:route="doc.route"
|
|
|
|
:class="{ 'seperator': doc.seperator, 'item-active': isFocused(i+1) && !doc.seperator }"
|
2019-08-01 17:22:58 +05:30
|
|
|
class="d-flex align-items-center"
|
|
|
|
@click.native="routeTo(doc.route)"
|
|
|
|
>
|
2019-08-05 15:03:05 +05:30
|
|
|
<div :class="doc.seperator ? 'small' : ''">{{ doc.name }}</div>
|
2019-08-01 17:22:58 +05:30
|
|
|
<div class="small ml-auto">{{ doc.doctype }}</div>
|
|
|
|
</list-row>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<script>
|
|
|
|
import frappe from 'frappejs';
|
|
|
|
import ListRow from '../pages/ListView/ListRow';
|
|
|
|
import ListCell from '../pages/ListView/ListCell';
|
|
|
|
|
|
|
|
export default {
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
inputValue: '',
|
2019-08-20 14:27:27 +05:30
|
|
|
suggestion: [],
|
|
|
|
currentlyFocused: 0
|
2019-08-01 17:22:58 +05:30
|
|
|
};
|
|
|
|
},
|
|
|
|
components: {
|
|
|
|
ListRow,
|
|
|
|
ListCell
|
|
|
|
},
|
|
|
|
watch: {
|
|
|
|
inputValue() {
|
|
|
|
if (!this.inputValue.length) this.suggestion = [];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
methods: {
|
2019-08-20 14:27:27 +05:30
|
|
|
focus(key) {
|
|
|
|
this.currentlyFocused = key % (this.suggestion.length + 1);
|
|
|
|
},
|
|
|
|
navigate(dir) {
|
|
|
|
const seperatorIndexes = this.suggestion.map((item, i) => {
|
|
|
|
if (item.seperator) return i;
|
|
|
|
});
|
|
|
|
let nextItem = this.currentlyFocused + (dir === 'up' ? -1 : 1);
|
|
|
|
if (seperatorIndexes.includes(this.currentlyFocused)) {
|
|
|
|
nextItem = this.currentlyFocused + (dir === 'up' ? -2 : 2);
|
|
|
|
}
|
|
|
|
this.focus(nextItem);
|
|
|
|
},
|
|
|
|
isFocused(i) {
|
|
|
|
if (i === this.currentlyFocused) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
async searchOrSelect() {
|
|
|
|
if (this.currentlyFocused != 0) {
|
|
|
|
this.routeTo(this.$refs[this.currentlyFocused][0].$attrs.route);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-01 17:22:58 +05:30
|
|
|
const searchableDoctypes = frappe.getDoctypeList({
|
|
|
|
isSingle: 0,
|
|
|
|
isChild: 0
|
|
|
|
});
|
2019-08-05 15:03:05 +05:30
|
|
|
const documents = await this.getDocuments(searchableDoctypes);
|
|
|
|
const doctypes = this.getDoctypes(searchableDoctypes);
|
|
|
|
const reports = this.getReports();
|
|
|
|
this.suggestion = documents.concat(doctypes).concat(reports);
|
2019-08-01 17:22:58 +05:30
|
|
|
if (this.suggestion.length === 0)
|
2019-08-05 15:03:05 +05:30
|
|
|
this.suggestion = [{ seperator: true, name: 'No results found.' }];
|
2019-08-01 17:22:58 +05:30
|
|
|
},
|
2019-08-05 15:03:05 +05:30
|
|
|
clearInput(e) {
|
2019-08-01 17:22:58 +05:30
|
|
|
this.inputValue = '';
|
|
|
|
this.$emit('change', null);
|
|
|
|
},
|
2019-08-05 15:03:05 +05:30
|
|
|
async getDocuments(searchableDoctypes) {
|
2019-08-01 17:22:58 +05:30
|
|
|
const promises = searchableDoctypes.map(doctype => {
|
|
|
|
return frappe.db.getAll({
|
|
|
|
doctype,
|
2019-08-20 14:27:27 +05:30
|
|
|
filters: {
|
|
|
|
name: ['includes', this.inputValue],
|
|
|
|
keywords: ['like', this.inputValue]
|
|
|
|
},
|
2019-08-01 17:22:58 +05:30
|
|
|
fields: ['name']
|
|
|
|
});
|
|
|
|
});
|
|
|
|
const data = await Promise.all(promises);
|
|
|
|
// data contains list of documents, sorted according to position of its doctype in searchableDoctypes
|
|
|
|
const items = [];
|
|
|
|
items.push({
|
2019-08-05 15:03:05 +05:30
|
|
|
seperator: true,
|
2019-08-01 17:22:58 +05:30
|
|
|
name: 'Documents'
|
|
|
|
});
|
|
|
|
for (let i = 0; i < data.length; i++) {
|
|
|
|
// i represents doctype position in searchableDoctypes
|
|
|
|
if (data[i].length > 0) {
|
|
|
|
for (let doc of data[i]) {
|
|
|
|
let doctype = searchableDoctypes[i];
|
|
|
|
items.push({
|
|
|
|
doctype,
|
|
|
|
name: doc.name,
|
|
|
|
route: `/edit/${doctype}/${doc.name}`
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (items.length !== 1) return items;
|
|
|
|
return [];
|
|
|
|
},
|
2019-08-05 15:03:05 +05:30
|
|
|
getDoctypes(searchableDoctypes) {
|
|
|
|
const items = [{ seperator: true, name: 'Lists' }];
|
2019-08-01 17:22:58 +05:30
|
|
|
let filteredDoctypes = searchableDoctypes.filter(doctype => {
|
2019-08-05 15:03:05 +05:30
|
|
|
return (
|
|
|
|
doctype.toLowerCase().indexOf(this.inputValue.toLowerCase()) != -1
|
|
|
|
);
|
2019-08-01 17:22:58 +05:30
|
|
|
});
|
|
|
|
filteredDoctypes = filteredDoctypes.map(doctype => {
|
2019-08-05 15:03:05 +05:30
|
|
|
var titleCase = doctype.replace(/([A-Z])/g, ' $1');
|
|
|
|
titleCase = titleCase.charAt(0).toUpperCase() + titleCase.slice(1);
|
2019-08-01 17:22:58 +05:30
|
|
|
return {
|
2019-08-05 15:03:05 +05:30
|
|
|
name: titleCase,
|
2019-08-01 17:22:58 +05:30
|
|
|
route: `/list/${doctype}`
|
|
|
|
};
|
|
|
|
});
|
|
|
|
if (filteredDoctypes.length > 0) return items.concat(filteredDoctypes);
|
|
|
|
return [];
|
|
|
|
},
|
2019-08-05 15:03:05 +05:30
|
|
|
getReports() {
|
|
|
|
const items = [{ seperator: true, name: 'Reports' }];
|
|
|
|
let reports = require('../../reports/view');
|
|
|
|
reports = Object.values(reports);
|
|
|
|
let filteredReports = reports.filter(report => {
|
|
|
|
return (
|
|
|
|
report.title.toLowerCase().indexOf(this.inputValue.toLowerCase()) !=
|
|
|
|
-1
|
|
|
|
);
|
|
|
|
});
|
|
|
|
filteredReports = filteredReports.map(report => {
|
|
|
|
return {
|
|
|
|
name: report.title,
|
|
|
|
route: `/report/${report.method}`
|
|
|
|
};
|
|
|
|
});
|
|
|
|
if (filteredReports.length > 0) return items.concat(filteredReports);
|
|
|
|
return [];
|
|
|
|
},
|
2019-08-01 17:22:58 +05:30
|
|
|
routeTo(route) {
|
|
|
|
this.$router.push(route);
|
|
|
|
this.inputValue = '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
@import '../styles/variables';
|
|
|
|
.input-group-text {
|
|
|
|
background-color: var(--white);
|
|
|
|
border-right: none;
|
|
|
|
}
|
|
|
|
.seperator {
|
|
|
|
background-color: var(--light);
|
|
|
|
}
|
|
|
|
|
|
|
|
input {
|
|
|
|
border-left: none;
|
|
|
|
height: 27px;
|
|
|
|
}
|
|
|
|
|
|
|
|
input:focus {
|
|
|
|
border: 1px solid #ced4da;
|
|
|
|
border-left: none;
|
|
|
|
outline: none !important;
|
|
|
|
box-shadow: none !important;
|
|
|
|
}
|
2019-08-20 14:27:27 +05:30
|
|
|
.search-list {
|
2019-08-01 17:22:58 +05:30
|
|
|
z-index: 2;
|
2019-08-05 15:03:05 +05:30
|
|
|
max-height: 90vh;
|
|
|
|
overflow: auto;
|
2019-08-01 17:22:58 +05:30
|
|
|
}
|
2019-08-20 14:27:27 +05:30
|
|
|
|
|
|
|
.item-active {
|
|
|
|
background-color: var(--light);
|
|
|
|
}
|
2019-08-01 17:22:58 +05:30
|
|
|
</style>
|
|
|
|
|