89 lines
1.8 KiB
Vue
89 lines
1.8 KiB
Vue
<template>
|
||
<div class="g-input-wrapper">
|
||
<input
|
||
v-model="inputValue"
|
||
:type="type"
|
||
:placeholder="placeholder"
|
||
:disabled="disabled"
|
||
:class="inputClass"
|
||
@focus="handleFocus"
|
||
@blur="handleBlur"
|
||
/>
|
||
<button v-if="clearable && inputValue" @click="handleClear" class="clear-btn">×</button>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed } from 'vue'
|
||
|
||
interface Props {
|
||
modelValue: string | number
|
||
type?: 'text' | 'password' | 'email' | 'tel'
|
||
placeholder?: string
|
||
disabled?: boolean
|
||
clearable?: boolean
|
||
}
|
||
|
||
const props = withDefaults(defineProps<Props>(), {
|
||
type: 'text',
|
||
clearable: false
|
||
})
|
||
|
||
const emit = defineEmits<{
|
||
'update:modelValue': [value: string]
|
||
focus: [event: FocusEvent]
|
||
blur: [event: FocusEvent]
|
||
}>()
|
||
|
||
const inputValue = computed({
|
||
get: () => props.modelValue,
|
||
set: (val) => emit('update:modelValue', val)
|
||
})
|
||
|
||
const focused = ref(false)
|
||
|
||
const inputClass = computed(() => [
|
||
'g-input',
|
||
{
|
||
'is-focused': focused.value,
|
||
'is-disabled': props.disabled
|
||
}
|
||
])
|
||
|
||
const handleFocus = (e: FocusEvent) => {
|
||
focused.value = true
|
||
emit('focus', e)
|
||
}
|
||
|
||
const handleBlur = (e: FocusEvent) => {
|
||
focused.value = false
|
||
emit('blur', e)
|
||
}
|
||
|
||
const handleClear = () => {
|
||
inputValue.value = ''
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.g-input-wrapper {
|
||
@apply relative;
|
||
}
|
||
|
||
.g-input {
|
||
@apply w-full px-4 py-2.5 rounded-lg border-2 border-gray-200 transition-all duration-200;
|
||
|
||
&:focus {
|
||
@apply border-primary-500 outline-none shadow-md;
|
||
}
|
||
|
||
&.is-disabled {
|
||
@apply bg-gray-100 cursor-not-allowed;
|
||
}
|
||
}
|
||
|
||
.clear-btn {
|
||
@apply absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 text-xl;
|
||
}
|
||
</style>
|