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>
|