mirror of
https://github.com/directus/directus.git
synced 2026-01-28 14:28:02 -05:00
Add search in v-select (#13867)
* Add search in v-select * only show search when nested items more than 20 * auto open when there's only one child * persist search input when item count reduces
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-list-group :clickable="item.selectable" :value="item.value">
|
||||
<v-list-group :clickable="item.selectable" :open="item.children?.length === 1" :value="item.value">
|
||||
<template #activator>
|
||||
<v-list-item-icon v-if="multiple === false && allowOther === false && item.icon">
|
||||
<v-icon :name="item.icon" />
|
||||
|
||||
@@ -48,6 +48,16 @@
|
||||
<v-divider />
|
||||
</template>
|
||||
|
||||
<v-list-item v-if="internalItemsCount > 20 || search">
|
||||
<v-list-item-content>
|
||||
<v-input v-model="search" autofocus small :placeholder="t('search')" @click.stop.prevent>
|
||||
<template #append>
|
||||
<v-icon small name="search" />
|
||||
</template>
|
||||
</v-input>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
<template v-for="(item, index) in internalItems" :key="index">
|
||||
<select-list-item-group
|
||||
v-if="item.children"
|
||||
@@ -117,14 +127,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { defineComponent, PropType, computed, toRefs, Ref } from 'vue';
|
||||
import { useCustomSelection, useCustomSelectionMultiple } from '@/composables/use-custom-selection';
|
||||
import { get } from 'lodash';
|
||||
import { Placement } from '@popperjs/core';
|
||||
import { debounce, get } from 'lodash';
|
||||
import { computed, defineComponent, PropType, Ref, ref, toRefs, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import SelectListItemGroup from './select-list-item-group.vue';
|
||||
import SelectListItem from './select-list-item.vue';
|
||||
import { Option } from './types';
|
||||
import { Placement } from '@popperjs/core';
|
||||
|
||||
type ItemsRaw = (string | any)[];
|
||||
type InputValue = string[] | string;
|
||||
@@ -217,7 +227,7 @@ export default defineComponent({
|
||||
setup(props, { emit }) {
|
||||
const { t } = useI18n();
|
||||
|
||||
const { internalItems } = useItems();
|
||||
const { internalItems, internalItemsCount, internalSearch } = useItems();
|
||||
const { displayValue } = useDisplayValue();
|
||||
const { modelValue } = toRefs(props);
|
||||
const { otherValue, usesOtherValue } = useCustomSelection(modelValue as Ref<string>, internalItems, (value) =>
|
||||
@@ -229,18 +239,30 @@ export default defineComponent({
|
||||
(value) => emit('update:modelValue', value)
|
||||
);
|
||||
|
||||
const search = ref<string | null>(null);
|
||||
watch(
|
||||
search,
|
||||
debounce((val: string | null) => {
|
||||
internalSearch.value = val;
|
||||
}, 250)
|
||||
);
|
||||
|
||||
return {
|
||||
t,
|
||||
internalItems,
|
||||
internalItemsCount,
|
||||
displayValue,
|
||||
otherValue,
|
||||
usesOtherValue,
|
||||
otherValues,
|
||||
addOtherValue,
|
||||
setOtherValue,
|
||||
search,
|
||||
};
|
||||
|
||||
function useItems() {
|
||||
const internalSearch = ref<string | null>(null);
|
||||
|
||||
const internalItems = computed(() => {
|
||||
const parseItem = (item: Record<string, any>): Option => {
|
||||
if (typeof item === 'string') {
|
||||
@@ -260,16 +282,41 @@ export default defineComponent({
|
||||
icon: get(item, props.itemIcon),
|
||||
disabled: get(item, props.itemDisabled),
|
||||
selectable: get(item, props.itemSelectable),
|
||||
children,
|
||||
children: children ? children.filter(filterItem) : children,
|
||||
};
|
||||
};
|
||||
|
||||
const items = props.items.map(parseItem);
|
||||
const filterItem = (item: Record<string, any>): boolean => {
|
||||
if (!internalSearch.value) return true;
|
||||
|
||||
const searchValue = internalSearch.value.toLowerCase();
|
||||
|
||||
return item?.children
|
||||
? item.children.some((item: Record<string, any>) => filterItem(item))
|
||||
: item.text?.toLowerCase().includes(searchValue) || item.value?.toLowerCase().includes(searchValue);
|
||||
};
|
||||
|
||||
const items = internalSearch.value ? props.items.filter(filterItem).map(parseItem) : props.items.map(parseItem);
|
||||
|
||||
return items;
|
||||
});
|
||||
|
||||
return { internalItems };
|
||||
const internalItemsCount = computed<number>(() => {
|
||||
const countItems = (items: Option[]): number => {
|
||||
const count = items.reduce((acc, item): number => {
|
||||
if (item?.children) {
|
||||
acc += countItems(item.children);
|
||||
}
|
||||
return acc + 1;
|
||||
}, 0);
|
||||
|
||||
return count;
|
||||
};
|
||||
|
||||
return countItems(props.items);
|
||||
});
|
||||
|
||||
return { internalItems, internalItemsCount, internalSearch };
|
||||
}
|
||||
|
||||
function useDisplayValue() {
|
||||
|
||||
Reference in New Issue
Block a user