2019-10-30 01:03:26 +05:30
|
|
|
<template>
|
2019-11-20 01:59:25 +05:30
|
|
|
<div class="relative" v-on-outside-click="() => (isShown = false)">
|
2019-11-19 23:04:45 +05:30
|
|
|
<div class="h-full">
|
2019-10-30 01:03:26 +05:30
|
|
|
<slot
|
|
|
|
:toggleDropdown="toggleDropdown"
|
|
|
|
:highlightItemUp="highlightItemUp"
|
|
|
|
:highlightItemDown="highlightItemDown"
|
|
|
|
:selectHighlightedItem="selectHighlightedItem"
|
|
|
|
></slot>
|
|
|
|
</div>
|
|
|
|
<div
|
|
|
|
:class="right ? 'right-0' : 'left-0'"
|
2019-12-11 15:08:20 +05:30
|
|
|
class="mt-1 absolute z-10 bg-white rounded border w-full min-w-40 shadow-md"
|
2019-10-30 01:03:26 +05:30
|
|
|
v-if="isShown"
|
|
|
|
>
|
|
|
|
<div class="p-1 max-h-64 overflow-auto">
|
2019-11-20 01:59:25 +05:30
|
|
|
<div v-for="d in dropdownItems" :key="d.label">
|
|
|
|
<div
|
|
|
|
v-if="d.isGroup"
|
|
|
|
class="px-2 pt-3 pb-1 text-xs uppercase text-gray-700 font-semibold tracking-wider"
|
|
|
|
>
|
|
|
|
{{ d.label }}
|
|
|
|
</div>
|
|
|
|
<a
|
|
|
|
v-else
|
|
|
|
ref="items"
|
2019-12-11 15:08:20 +05:30
|
|
|
class="block p-2 rounded-md mt-1 first:mt-0 cursor-pointer truncate"
|
2019-11-20 01:59:25 +05:30
|
|
|
:class="d.index === highlightedIndex ? 'bg-gray-100' : ''"
|
|
|
|
@mouseenter="highlightedIndex = d.index"
|
|
|
|
@mouseleave="highlightedIndex = -1"
|
|
|
|
@click="selectItem(d)"
|
|
|
|
>
|
|
|
|
<component :is="d.component" v-if="d.component" />
|
|
|
|
<template v-else>{{ d.label }}</template>
|
|
|
|
</a>
|
|
|
|
</div>
|
2019-10-30 01:03:26 +05:30
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2019-11-20 01:59:25 +05:30
|
|
|
import uniq from 'lodash/uniq';
|
|
|
|
|
2019-10-30 01:03:26 +05:30
|
|
|
export default {
|
|
|
|
name: 'Dropdown',
|
|
|
|
props: {
|
|
|
|
items: {
|
|
|
|
type: Array,
|
|
|
|
default: () => []
|
|
|
|
},
|
2019-11-20 01:59:25 +05:30
|
|
|
groups: {
|
|
|
|
type: Array,
|
|
|
|
default: null
|
|
|
|
},
|
2019-10-30 01:03:26 +05:30
|
|
|
right: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false
|
|
|
|
}
|
|
|
|
},
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
isShown: false,
|
|
|
|
highlightedIndex: -1
|
|
|
|
};
|
|
|
|
},
|
2019-11-20 01:59:25 +05:30
|
|
|
computed: {
|
|
|
|
sortedGroups() {
|
|
|
|
if (Array.isArray(this.groups)) {
|
|
|
|
return this.groups;
|
|
|
|
}
|
|
|
|
let groupNames = uniq(
|
|
|
|
this.items
|
|
|
|
.map(d => d.group)
|
|
|
|
.filter(Boolean)
|
|
|
|
.sort()
|
|
|
|
);
|
|
|
|
if (groupNames.length > 0) {
|
|
|
|
return groupNames;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
dropdownItems() {
|
|
|
|
if (this.sortedGroups) {
|
|
|
|
let itemsByGroup = {};
|
|
|
|
|
|
|
|
for (let item of this.items) {
|
|
|
|
let group = item.group || '';
|
|
|
|
itemsByGroup[group] = itemsByGroup[group] || [];
|
|
|
|
itemsByGroup[group].push(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
let items = [];
|
|
|
|
let i = 0;
|
|
|
|
for (let group of this.sortedGroups) {
|
|
|
|
let groupItems = itemsByGroup[group];
|
|
|
|
groupItems = groupItems.map(d => {
|
|
|
|
d.index = i;
|
|
|
|
i++;
|
|
|
|
return d;
|
|
|
|
});
|
|
|
|
items = items.concat(
|
|
|
|
{
|
|
|
|
label: group,
|
|
|
|
isGroup: true
|
|
|
|
},
|
|
|
|
groupItems
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.items.map((d, i) => {
|
|
|
|
d.index = i;
|
|
|
|
return d;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
2019-10-30 01:03:26 +05:30
|
|
|
methods: {
|
|
|
|
selectItem(d) {
|
|
|
|
if (d.action) {
|
|
|
|
d.action();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
toggleDropdown(flag) {
|
|
|
|
if (flag == null) {
|
|
|
|
this.isShown = !this.isShown;
|
|
|
|
} else {
|
|
|
|
this.isShown = Boolean(flag);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
selectHighlightedItem() {
|
|
|
|
if (![-1, this.items.length].includes(this.highlightedIndex)) {
|
|
|
|
// valid selection
|
|
|
|
let item = this.items[this.highlightedIndex];
|
|
|
|
this.selectItem(item);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
highlightItemUp() {
|
|
|
|
this.highlightedIndex -= 1;
|
|
|
|
if (this.highlightedIndex < 0) {
|
|
|
|
this.highlightedIndex = 0;
|
|
|
|
}
|
|
|
|
this.$nextTick(() => {
|
|
|
|
let index = this.highlightedIndex;
|
|
|
|
if (index !== 0) {
|
|
|
|
index -= 1;
|
|
|
|
}
|
2019-12-03 13:51:50 +05:30
|
|
|
this.scrollToHighlighted();
|
2019-10-30 01:03:26 +05:30
|
|
|
});
|
|
|
|
},
|
|
|
|
highlightItemDown() {
|
|
|
|
this.highlightedIndex += 1;
|
|
|
|
if (this.highlightedIndex > this.items.length) {
|
|
|
|
this.highlightedIndex = this.items.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.$nextTick(() => {
|
2019-12-03 13:51:50 +05:30
|
|
|
this.scrollToHighlighted();
|
2019-10-30 01:03:26 +05:30
|
|
|
});
|
2019-12-03 13:51:50 +05:30
|
|
|
},
|
|
|
|
scrollToHighlighted() {
|
|
|
|
let highlightedElement = this.$refs.items[this.highlightedIndex];
|
|
|
|
highlightedElement &&
|
|
|
|
highlightedElement.scrollIntoView({ block: 'nearest' });
|
2019-10-30 01:03:26 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
</script>
|