90 lines
2.7 KiB
Vue
90 lines
2.7 KiB
Vue
<script setup>
|
|
import { computed } from 'vue'
|
|
|
|
const props = defineProps({
|
|
id: { type: String, default: '' },
|
|
type: { type: String, default: 'text' },
|
|
label: { type: [String, Object],default: '' },
|
|
modelValue: { type: String, default: '' },
|
|
placeholder: { type: String, default: '' },
|
|
size: { type: String, default: 'md' },
|
|
error: { type: Boolean, default: false },
|
|
success: { type: Boolean, default: false },
|
|
isRequired: { type: Boolean, default: false },
|
|
isDisabled: { type: Boolean, default: false },
|
|
inputClass: { type: String, default: '' },
|
|
icon: { type: String, default: '' },
|
|
})
|
|
|
|
defineEmits(['update:modelValue'])
|
|
|
|
const labelText = computed(() =>
|
|
typeof props.label === 'string' ? props.label : props.label?.text ?? ''
|
|
)
|
|
const labelExtraClass = computed(() =>
|
|
typeof props.label === 'object' ? props.label?.class ?? '' : ''
|
|
)
|
|
|
|
const sizes = {
|
|
sm: 'py-1.5 text-xs',
|
|
md: 'py-2 text-sm',
|
|
lg: 'py-2.5 text-base',
|
|
}
|
|
|
|
const borderClass = computed(() => {
|
|
if (props.error) return 'border-danger focus:border-danger focus:ring-1 focus:ring-danger'
|
|
if (props.success) return 'border-success focus:border-success focus:ring-1 focus:ring-success'
|
|
return 'border-gray-200 focus:border-primary focus:ring-1 focus:ring-primary'
|
|
})
|
|
|
|
const inputClasses = computed(() => [
|
|
'w-full rounded-lg border bg-white text-dark outline-none transition-colors placeholder:text-gray-400',
|
|
props.icon ? 'pl-9 pr-3' : 'px-3',
|
|
sizes[props.size] ?? sizes.md,
|
|
borderClass.value,
|
|
props.isDisabled && 'cursor-not-allowed opacity-60 bg-gray-50',
|
|
props.inputClass,
|
|
])
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<label
|
|
v-if="labelText"
|
|
:for="id"
|
|
:class="['mb-1 block text-xs font-medium text-secondary', labelExtraClass]"
|
|
>
|
|
{{ labelText }}
|
|
<span v-if="isRequired" class="text-danger">*</span>
|
|
</label>
|
|
|
|
<div class="relative">
|
|
<span
|
|
v-if="icon"
|
|
class="material-icons pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-base text-secondary"
|
|
>
|
|
{{ icon }}
|
|
</span>
|
|
|
|
<input
|
|
:id="id"
|
|
:type="type"
|
|
:value="modelValue"
|
|
:placeholder="placeholder"
|
|
:required="isRequired"
|
|
:disabled="isDisabled"
|
|
:class="inputClasses"
|
|
v-bind="$attrs"
|
|
@input="$emit('update:modelValue', $event.target.value)"
|
|
/>
|
|
</div>
|
|
|
|
<p v-if="error && typeof error === 'string'" class="mt-1 text-xs text-danger">
|
|
{{ error }}
|
|
</p>
|
|
<p v-if="success && typeof success === 'string'" class="mt-1 text-xs text-success">
|
|
{{ success }}
|
|
</p>
|
|
</div>
|
|
</template>
|