301 lines
8.4 KiB
Vue
301 lines
8.4 KiB
Vue
<template>
|
|
<div class="ui-select-wrapper" :class="directionClass">
|
|
<label v-if="label" :for="id" class="ui-select-label">
|
|
{{ label }}
|
|
<span v-if="required" class="ui-select-required-indicator">*</span>
|
|
</label>
|
|
|
|
<div class="ui-select">
|
|
<div v-if="hasPrefixes" class="ui-select-wrapper-border" :class="prefixClasses">
|
|
<template v-if="direction === 'rtl'">
|
|
<i v-if="iconEnd" :class="[iconEnd, iconColorClass]" class="ui-select-icon-end !mt-2"></i>
|
|
<span v-if="prefixEnd" class="ui-select-prefix-end" :class="prefixColorClass">{{ prefixEnd }}</span>
|
|
<Dropdown
|
|
:id="id"
|
|
:model-value="internalModelValue"
|
|
:options="options"
|
|
:option-label="optionLabel"
|
|
:option-value="optionValue"
|
|
:dataKey="optionValue"
|
|
:placeholder="placeholder"
|
|
:disabled="disabled"
|
|
:class="selectClassesNoBorder"
|
|
empty-filter-message="نتیجهای یافت نشد"
|
|
:empty-message="emptyMessage"
|
|
:filter="filter"
|
|
:reset-filter-on-hide="true"
|
|
:filter-placeholder="filterPlaceholder"
|
|
:auto-filter-focus="false"
|
|
:show-clear="showClear"
|
|
class="ui-select-dropdown"
|
|
@update:model-value="handleUpdate"
|
|
@change="handleUpdate"
|
|
@focus="handleFocus"
|
|
@blur="handleBlur"
|
|
/>
|
|
<span v-if="prefixStart" class="ui-select-prefix-start" :class="prefixColorClass">{{ prefixStart }}</span>
|
|
<i v-if="iconStart" :class="[iconStart, iconColorClass]" class="ui-select-icon-start !mt-2"></i>
|
|
</template>
|
|
|
|
<template v-else>
|
|
<i v-if="iconStart" :class="[iconStart, iconColorClass]" class="ui-select-icon-start !mt-2"></i>
|
|
<span v-if="prefixStart" class="ui-select-prefix-start" :class="prefixColorClass">{{ prefixStart }}</span>
|
|
<Dropdown
|
|
:id="id"
|
|
:model-value="internalModelValue"
|
|
:options="options"
|
|
:option-label="optionLabel"
|
|
:option-value="optionValue"
|
|
:dataKey="optionValue"
|
|
:placeholder="placeholder"
|
|
:disabled="disabled"
|
|
:class="selectClassesNoBorder"
|
|
empty-filter-message="نتیجهای یافت نشد"
|
|
:empty-message="emptyMessage"
|
|
:filter="filter"
|
|
:reset-filter-on-hide="true"
|
|
:filter-placeholder="filterPlaceholder"
|
|
:auto-filter-focus="false"
|
|
:show-clear="showClear"
|
|
class="ui-select-dropdown"
|
|
@update:model-value="handleUpdate"
|
|
@change="handleUpdate"
|
|
@focus="handleFocus"
|
|
@blur="handleBlur"
|
|
/>
|
|
<span v-if="prefixEnd" class="ui-select-prefix-end" :class="prefixColorClass">{{ prefixEnd }}</span>
|
|
<i v-if="iconEnd" :class="[iconEnd, iconColorClass]" class="ui-select-icon-end !mt-2"></i>
|
|
</template>
|
|
</div>
|
|
|
|
<div v-else-if="prefix" class="ui-select-wrapper-border" :class="directionClass">
|
|
<span class="ui-select-prefix">{{ prefix }}</span>
|
|
<Dropdown
|
|
:id="id"
|
|
:model-value="internalModelValue"
|
|
:options="options"
|
|
:option-label="optionLabel"
|
|
:option-value="optionValue"
|
|
:dataKey="optionValue"
|
|
:placeholder="placeholder"
|
|
:disabled="disabled"
|
|
:class="selectClassesNoBorder"
|
|
empty-filter-message="نتیجهای یافت نشد"
|
|
:empty-message="emptyMessage"
|
|
:filter="filter"
|
|
:reset-filter-on-hide="true"
|
|
:filter-placeholder="filterPlaceholder"
|
|
:show-clear="showClear"
|
|
:auto-filter-focus="false"
|
|
class="ui-select-dropdown"
|
|
@update:model-value="handleUpdate"
|
|
@change="handleUpdate"
|
|
@focus="handleFocus"
|
|
@blur="handleBlur"
|
|
/>
|
|
</div>
|
|
|
|
<div v-else class="relative select">
|
|
<Dropdown
|
|
:id="id"
|
|
:model-value="internalModelValue"
|
|
:options="options"
|
|
:option-label="optionLabel"
|
|
:option-value="optionValue"
|
|
:dataKey="optionValue"
|
|
:placeholder="placeholder"
|
|
:disabled="disabled"
|
|
:class="selectClasses"
|
|
empty-filter-message="نتیجهای یافت نشد"
|
|
:empty-message="emptyMessage"
|
|
:filter="filter"
|
|
:reset-filter-on-hide="true"
|
|
:filter-placeholder="filterPlaceholder"
|
|
:show-clear="showClear"
|
|
class="ui-select-dropdown"
|
|
@update:model-value="handleUpdate"
|
|
@change="handleUpdate"
|
|
@focus="handleFocus"
|
|
@blur="handleBlur"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<small v-if="error" class="ui-select-error-message">{{ error }}</small>
|
|
<small v-if="hint" class="ui-select-hint-message">{{ hint }}</small>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed } from 'vue'
|
|
import Dropdown from 'primevue/dropdown'
|
|
|
|
const props = defineProps({
|
|
id: {
|
|
type: String,
|
|
default: () => `ui-select-${Math.random().toString(36).substr(2, 9)}`
|
|
},
|
|
modelValue: { type: [String, Number, Object, null], default: null },
|
|
options: {
|
|
type: Array,
|
|
default: () => []
|
|
},
|
|
optionLabel: { type: String, default: 'name' },
|
|
optionValue: { type: String, default: undefined },
|
|
placeholder: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
disabled: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
error: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
hint: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
required: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
label: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
direction: {
|
|
type: String,
|
|
default: 'rtl',
|
|
validator: (value) => ['rtl', 'ltr'].includes(value)
|
|
},
|
|
prefix: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
prefixStart: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
prefixEnd: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
prefixColor: {
|
|
type: String,
|
|
default: 'dominant-600'
|
|
},
|
|
iconStart: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
iconEnd: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
iconColor: {
|
|
type: String,
|
|
default: 'dominant-600'
|
|
},
|
|
filter: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
filterPlaceholder: {
|
|
type: String,
|
|
default: 'جستجو...'
|
|
},
|
|
emptyMessage: {
|
|
type: String,
|
|
default: 'نتیجهای یافت نشد'
|
|
},
|
|
showClear: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits(['update:modelValue','change','focus','blur'])
|
|
|
|
const internalModelValue = computed(() => {
|
|
if (props.optionValue && props.modelValue && typeof props.modelValue === 'object') {
|
|
return props.modelValue[props.optionValue]
|
|
}
|
|
return props.modelValue
|
|
})
|
|
|
|
const extractValue = (e) => (e && typeof e === 'object' && 'value' in e) ? e.value : e
|
|
|
|
const handleUpdate = (e) => {
|
|
const val = extractValue(e)
|
|
if (props.optionValue && val !== null && val !== undefined) {
|
|
const selectedOption = props.options.find(o => o[props.optionValue] === val) ?? null
|
|
emit('update:modelValue', selectedOption)
|
|
emit('change', selectedOption)
|
|
} else {
|
|
emit('update:modelValue', val)
|
|
emit('change', val)
|
|
}
|
|
}
|
|
|
|
const handleFocus = (e) => emit('focus', e)
|
|
const handleBlur = (e) => emit('blur', e)
|
|
|
|
const directionClass = computed(() => {
|
|
return props.direction === 'ltr' ? 'ui-select--ltr' : 'ui-select--rtl'
|
|
})
|
|
|
|
const hasPrefixes = computed(() => {
|
|
return props.prefixStart || props.prefixEnd || props.iconStart || props.iconEnd
|
|
})
|
|
|
|
const prefixClasses = computed(() => {
|
|
const classes = [directionClass.value]
|
|
|
|
if (props.prefixStart) classes.push('ui-select-with-prefix-start')
|
|
if (props.prefixEnd) classes.push('ui-select-with-prefix-end')
|
|
if (props.iconStart) classes.push('ui-select-with-icon-start')
|
|
if (props.iconEnd) classes.push('ui-select-with-icon-end')
|
|
|
|
return classes.join(' ')
|
|
})
|
|
|
|
const prefixColorClass = computed(() => {
|
|
return `text-${props.prefixColor}`
|
|
})
|
|
|
|
const iconColorClass = computed(() => {
|
|
return `text-${props.iconColor}`
|
|
})
|
|
|
|
const selectClassesNoBorder = computed(() => {
|
|
const classes = []
|
|
|
|
if (props.error) {
|
|
classes.push('has-error')
|
|
}
|
|
|
|
if (props.disabled) {
|
|
classes.push('disabled')
|
|
}
|
|
|
|
return classes.join(' ')
|
|
})
|
|
|
|
const selectClasses = computed(() => {
|
|
const classes = []
|
|
|
|
if (props.error) {
|
|
classes.push('has-error')
|
|
}
|
|
|
|
if (props.disabled) {
|
|
classes.push('disabled')
|
|
}
|
|
|
|
return classes.join(' ')
|
|
})
|
|
</script>
|