|
| 1 | +<template> |
| 2 | + <div class="input-group"> |
| 3 | + <label v-if="label" :for="name">{{ label }} <slot></slot></label> |
| 4 | + <div :class="{ 'custom-select': true, 'custom-select-open': open }" :data-uid="uid" @blur="open = false" @click.stop="openSelect"> |
| 5 | + <div class="selected" :class="{ open: open, inactive: inactive }">{{ selected }} <IconChevronDown class="selected-icon" /></div> |
| 6 | + <div class="rounded"> |
| 7 | + <input type="text" placeholder="Search" @click.stop="" @keyup="filterOptions" /> |
| 8 | + <div ref="items" class="items" :tabindex="tabindex"> |
| 9 | + <div v-if="search_check" class="select-search-error">{{ searchError }}</div> |
| 10 | + <div v-for="(o, index) in filtered_options" :key="index" :value="o.name" @click.stop="updateClick(o)" :class="{ highlight: o.name == modelValue }">{{ o.emoji }} {{ o.name }} <IconCheckmark v-if="o.name == modelValue" class="checkmark" /></div> |
| 11 | + </div> |
| 12 | + </div> |
| 13 | + <input ref="input" type="hidden" v-model="modelValue" :name="name" /> |
| 14 | + </div> |
| 15 | + </div> |
| 16 | +</template> |
| 17 | + |
| 18 | +<script setup lang="js"> |
| 19 | +import { ref, onMounted, toRefs } from 'vue' |
| 20 | +import options from './json/country.json' |
| 21 | +import IconChevronDown from './icons/IconChevronDown.vue' |
| 22 | +import IconCheckmark from './icons/IconCheckmark.vue' |
| 23 | +
|
| 24 | +const emit = defineEmits(['update:modelValue', 'change', 'click', 'blur']) |
| 25 | +const props = defineProps({ |
| 26 | + label: { type: String }, |
| 27 | + name: { type: String }, |
| 28 | + modelValue: { type: String, default: 'Poland' }, |
| 29 | + tabindex: { type: Number, default: 0 }, |
| 30 | + searchError: { type: String, default: 'Option does not exists.' }, |
| 31 | +}) |
| 32 | +let { label, name, modelValue, tabindex, searchError } = toRefs(props) |
| 33 | +const input = ref(null) |
| 34 | +const selected = ref(null) |
| 35 | +const inactive = ref(false) |
| 36 | +const open = ref(false) |
| 37 | +const search_check = ref(false) |
| 38 | +const filtered_options = ref(options) |
| 39 | +const uid = ref('custom-select-' + Date.now()) |
| 40 | +
|
| 41 | +function openSelect() { |
| 42 | + let all = document.querySelectorAll('.custom-select-open') |
| 43 | + all.forEach((el) => { |
| 44 | + let data = el.dataset.uid ?? null |
| 45 | + if (data != uid.value) { |
| 46 | + el.click() |
| 47 | + } |
| 48 | + }) |
| 49 | +
|
| 50 | + open.value = !open.value |
| 51 | +} |
| 52 | +
|
| 53 | +onMounted(() => { |
| 54 | + if (modelValue.value !== null) { |
| 55 | + let option = options?.find((o) => o.name === modelValue.value) |
| 56 | + if (option?.name) { |
| 57 | + selected.value = `${option.emoji} ${option.name}` |
| 58 | + } else { |
| 59 | + clear() |
| 60 | + } |
| 61 | + } else { |
| 62 | + clear() |
| 63 | + } |
| 64 | +
|
| 65 | + document.addEventListener('click', (e) => { |
| 66 | + let a = document.querySelectorAll('.custom-select-open') |
| 67 | + a.forEach((el) => el.click()) |
| 68 | + }) |
| 69 | +}) |
| 70 | +
|
| 71 | +function clear() { |
| 72 | + selected.value = `${options[0].emoji} ${options[0].name}` |
| 73 | + modelValue.value = options[0].name |
| 74 | + inactive.value = false |
| 75 | +} |
| 76 | +
|
| 77 | +function updateClick(option = null) { |
| 78 | + if (option == null) { |
| 79 | + clear() |
| 80 | + } else { |
| 81 | + modelValue.value = option.name ?? null |
| 82 | + selected.value = `${option.emoji} ${option.name}` |
| 83 | + inactive.value = false |
| 84 | + } |
| 85 | + open.value = false |
| 86 | + emit('update:modelValue', modelValue.value) |
| 87 | +} |
| 88 | +
|
| 89 | +function filterOptions(e) { |
| 90 | + filtered_options.value = options.filter((o) => o.name.toLowerCase().startsWith(e.target.value.toLowerCase())) |
| 91 | + search_check.value = filtered_options.value.length == 0 ? true : false |
| 92 | +} |
| 93 | +</script> |
| 94 | +
|
| 95 | +<style> |
| 96 | +@import './css/input-root.css'; |
| 97 | +</style> |
| 98 | +
|
| 99 | +<style scoped> |
| 100 | +@import './css/input.css'; |
| 101 | +</style> |
| 102 | +
|
| 103 | +<!-- |
| 104 | +<script setup> |
| 105 | + import SelectPrefix from '@/components/form/prefix/SelectPrefix.vue' |
| 106 | +
|
| 107 | + const prefix_selected = ref(48) |
| 108 | +
|
| 109 | + function onSubmit(e) { |
| 110 | + let data = new FormData(e.target); |
| 111 | + for (var pair of data.entries()) { |
| 112 | + console.log("Key:", pair[0], "Value:", pair[1]); |
| 113 | + } |
| 114 | + // axios request here send to server |
| 115 | + } |
| 116 | +</sctipt> |
| 117 | +<template> |
| 118 | + <form @submit.prevent="onSubmit"> |
| 119 | + <SelectPrefix label="Prefix" v-model="prefix_selected" name="prefix" /> |
| 120 | +
|
| 121 | + <button> Send </button> |
| 122 | + </form> |
| 123 | +</template> |
| 124 | +--> |
0 commit comments