Files
Kotyata/layers/ui/components/search/index.vue
2026-03-17 13:24:22 +03:00

107 lines
2.0 KiB
Vue

<template>
<div
:class="[
cn.b(),
cn.m(size),
cn.is('focused', focused),
cn.is('disabled', disabled),
]"
>
<label :class="cn.e('wrapper')">
<Component
:is="searchIcon"
:class="cn.e('icon')"
/>
<input
ref="inputRef"
v-model="value"
type="search"
:class="cn.e('input')"
:placeholder="label"
:disabled="disabled"
@focus="focused = true"
@blur="focused = false"
>
<UiButton
v-show="value.length > 0"
size="small"
type="link"
color="secondary"
:class="cn.e('clear')"
:icon="clearIcon"
@click="value = ''"
@mousedown.prevent
/>
</label>
</div>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { debounce } from 'lodash-es'
import useClassname from '../../composables/use-classname'
import UiButton from '../button/index.vue'
export interface Props {
label?: string
size?: 'small' | 'medium' | 'large'
disabled?: false
modelValue?: string
}
defineOptions({
name: 'UiSearch',
})
const props = withDefaults(defineProps<Props>(), {
size: 'medium',
disabled: false,
modelValue: '',
})
const emit = defineEmits<{
'update:modelValue': [term: string]
}>()
const cn = useClassname('ui-search')
const inputRef = ref(null)
const value = ref(props.modelValue)
const focused = ref(false)
const searchIcon = computed(() => {
return resolveComponent(props.size === 'small' ? 'ui-icon-s-search' : 'ui-icon-search')
})
const clearIcon = computed(() => {
return props.size === 'small' ? 's-cross' : 'cross'
})
const onInput = debounce(() => {
emit('update:modelValue', value.value)
}, 300)
watch(
() => props.modelValue,
(term) => {
value.value = term
},
)
watch(value, onInput)
function focus() {
inputRef.value?.focus()
}
defineExpose({
focus,
})
</script>
<style lang="scss">
@use 'styles';
</style>