Make order customer picker searchable and reload the list on open.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, nextTick, ref } from 'vue'
|
||||
import { onClickOutside } from '@vueuse/core'
|
||||
import AdminIcon from '@/components/icons/AdminIcon.vue'
|
||||
|
||||
@@ -15,11 +15,17 @@ const props = withDefaults(
|
||||
placeholder?: string
|
||||
ariaLabel?: string
|
||||
block?: boolean
|
||||
searchable?: boolean
|
||||
searchPlaceholder?: string
|
||||
id?: string
|
||||
}>(),
|
||||
{
|
||||
placeholder: 'انتخاب کنید',
|
||||
ariaLabel: 'انتخاب',
|
||||
block: false,
|
||||
searchable: false,
|
||||
searchPlaceholder: 'جستجو…',
|
||||
id: undefined,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -28,28 +34,44 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
|
||||
const open = ref(false)
|
||||
const searchQuery = ref('')
|
||||
const rootRef = ref<HTMLElement | null>(null)
|
||||
const searchInputRef = ref<HTMLInputElement | null>(null)
|
||||
|
||||
onClickOutside(rootRef, () => {
|
||||
open.value = false
|
||||
searchQuery.value = ''
|
||||
})
|
||||
|
||||
const selectedLabel = computed(
|
||||
() => props.options.find((option) => option.value === props.modelValue)?.label ?? props.placeholder,
|
||||
)
|
||||
|
||||
function toggle() {
|
||||
const filteredOptions = computed(() => {
|
||||
const q = searchQuery.value.trim().toLowerCase()
|
||||
if (!q) return props.options
|
||||
return props.options.filter((option) => option.label.toLowerCase().includes(q))
|
||||
})
|
||||
|
||||
async function toggle() {
|
||||
open.value = !open.value
|
||||
if (open.value && props.searchable) {
|
||||
searchQuery.value = ''
|
||||
await nextTick()
|
||||
searchInputRef.value?.focus()
|
||||
}
|
||||
}
|
||||
|
||||
function pick(value: string) {
|
||||
emit('update:modelValue', value)
|
||||
open.value = false
|
||||
searchQuery.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:id="id"
|
||||
ref="rootRef"
|
||||
class="admin-select"
|
||||
:class="{ 'admin-select--open': open, 'admin-select--block': block }"
|
||||
@@ -74,7 +96,21 @@ function pick(value: string) {
|
||||
role="listbox"
|
||||
:aria-label="ariaLabel"
|
||||
>
|
||||
<li v-for="option in options" :key="option.value" role="presentation">
|
||||
<li v-if="searchable" class="admin-select__search" role="presentation">
|
||||
<input
|
||||
ref="searchInputRef"
|
||||
v-model="searchQuery"
|
||||
type="search"
|
||||
class="admin-select__search-input"
|
||||
:placeholder="searchPlaceholder"
|
||||
@click.stop
|
||||
@keydown.esc.stop="open = false"
|
||||
/>
|
||||
</li>
|
||||
<li v-if="!filteredOptions.length" class="admin-select__empty" role="presentation">
|
||||
موردی یافت نشد
|
||||
</li>
|
||||
<li v-for="option in filteredOptions" :key="option.value" role="presentation">
|
||||
<button
|
||||
type="button"
|
||||
class="admin-select__option"
|
||||
@@ -169,6 +205,7 @@ function pick(value: string) {
|
||||
top: calc(100% + 6px);
|
||||
z-index: 40;
|
||||
width: max(100%, 220px);
|
||||
max-height: min(320px, 50vh);
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
list-style: none;
|
||||
@@ -179,6 +216,40 @@ function pick(value: string) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.admin-select__search {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
background: var(--color-surface);
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.admin-select__search-input {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 10px;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font: inherit;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.admin-select__search-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
|
||||
.admin-select__empty {
|
||||
padding: 12px;
|
||||
font-size: 0.8125rem;
|
||||
color: var(--color-text-muted);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.admin-select__option {
|
||||
|
||||
Reference in New Issue
Block a user