2
0
mirror of https://github.com/frappe/books.git synced 2024-12-24 11:55:46 +00:00

fix: autocomplete behaviour, use fuzzy matching

This commit is contained in:
18alantom 2021-11-26 12:51:47 +05:30
parent 36f175c714
commit d491a46a21
3 changed files with 73 additions and 23 deletions

View File

@ -34,6 +34,7 @@
<script> <script>
import Base from './Base'; import Base from './Base';
import Dropdown from '@/components/Dropdown'; import Dropdown from '@/components/Dropdown';
import { fuzzyMatch } from '@/utils';
export default { export default {
name: 'AutoComplete', name: 'AutoComplete',
@ -97,13 +98,11 @@ export default {
return items; return items;
} }
return items.filter((d) => { return items
let key = keyword.toLowerCase(); .map((item) => ({ ...fuzzyMatch(keyword, item.value), item }))
return ( .filter(({ isMatch }) => isMatch)
d.label.toLowerCase().includes(key) || .sort((a, b) => a.distance - b.distance)
d.value.toLowerCase().includes(key) .map(({ item }) => item);
);
});
}, },
setSuggestion(suggestion) { setSuggestion(suggestion) {
this.linkValue = suggestion.value; this.linkValue = suggestion.value;
@ -121,7 +120,10 @@ export default {
this.triggerChange(''); this.triggerChange('');
} }
if (value && !this.suggestions.includes(value)) { if (
value &&
!this.suggestions.map(({ value }) => value).includes(value)
) {
const suggestion = await this.getSuggestions(value); const suggestion = await this.getSuggestions(value);
if (suggestion.length < 2) { if (suggestion.length < 2) {

View File

@ -25,17 +25,35 @@
<div v-for="d in dropdownItems" :key="d.label"> <div v-for="d in dropdownItems" :key="d.label">
<div <div
v-if="d.isGroup" v-if="d.isGroup"
class="px-2 pt-3 pb-1 text-xs uppercase text-gray-700 font-semibold tracking-wider" class="
px-2
pt-3
pb-1
text-xs
uppercase
text-gray-700
font-semibold
tracking-wider
"
> >
{{ d.label }} {{ d.label }}
</div> </div>
<a <a
v-else v-else
ref="items" ref="items"
class="block p-2 rounded-md mt-1 first:mt-0 cursor-pointer truncate" class="
block
p-2
rounded-md
mt-1
first:mt-0
cursor-pointer
truncate
"
:class="d.index === highlightedIndex ? 'bg-gray-100' : ''" :class="d.index === highlightedIndex ? 'bg-gray-100' : ''"
@mouseenter="highlightedIndex = d.index" @mouseenter="highlightedIndex = d.index"
@mouseleave="highlightedIndex = -1" @mouseleave="highlightedIndex = -1"
@mousedown.prevent
@click="selectItem(d)" @click="selectItem(d)"
> >
<component :is="d.component" v-if="d.component" /> <component :is="d.component" v-if="d.component" />
@ -57,28 +75,28 @@ export default {
props: { props: {
items: { items: {
type: Array, type: Array,
default: () => [] default: () => [],
}, },
groups: { groups: {
type: Array, type: Array,
default: null default: null,
}, },
right: { right: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
isLoading: { isLoading: {
type: Boolean, type: Boolean,
default: false default: false,
} },
}, },
components: { components: {
Popover Popover,
}, },
data() { data() {
return { return {
isShown: false, isShown: false,
highlightedIndex: -1 highlightedIndex: -1,
}; };
}, },
computed: { computed: {
@ -88,7 +106,7 @@ export default {
} }
let groupNames = uniq( let groupNames = uniq(
this.items this.items
.map(d => d.group) .map((d) => d.group)
.filter(Boolean) .filter(Boolean)
.sort() .sort()
); );
@ -111,7 +129,7 @@ export default {
let i = 0; let i = 0;
for (let group of this.sortedGroups) { for (let group of this.sortedGroups) {
let groupItems = itemsByGroup[group]; let groupItems = itemsByGroup[group];
groupItems = groupItems.map(d => { groupItems = groupItems.map((d) => {
d.index = i; d.index = i;
i++; i++;
return d; return d;
@ -119,7 +137,7 @@ export default {
items = items.concat( items = items.concat(
{ {
label: group, label: group,
isGroup: true isGroup: true,
}, },
groupItems groupItems
); );
@ -132,7 +150,7 @@ export default {
d.index = i; d.index = i;
return d; return d;
}); });
} },
}, },
methods: { methods: {
selectItem(d) { selectItem(d) {
@ -181,7 +199,7 @@ export default {
let highlightedElement = this.$refs.items[this.highlightedIndex]; let highlightedElement = this.$refs.items[this.highlightedIndex];
highlightedElement && highlightedElement &&
highlightedElement.scrollIntoView({ block: 'nearest' }); highlightedElement.scrollIntoView({ block: 'nearest' });
} },
} },
}; };
</script> </script>

View File

@ -351,3 +351,33 @@ export function purgeCache(purgeAll = false) {
delete frappe[d]; delete frappe[d];
}); });
} }
export function fuzzyMatch(keyword, candidate) {
const keywordLetters = [...keyword];
const candidateLetters = [...candidate];
let keywordLetter = keywordLetters.shift();
let candidateLetter = candidateLetters.shift();
let isMatch = true;
let distance = 0;
while (keywordLetter && candidateLetter) {
if (keywordLetter.toLowerCase() === candidateLetter.toLowerCase()) {
keywordLetter = keywordLetters.shift();
} else {
distance += 1;
}
candidateLetter = candidateLetters.shift();
}
if (keywordLetter !== undefined) {
distance = -1;
isMatch = false;
} else {
distance += candidateLetters.length;
}
return { isMatch, distance };
}